Merge "fs_mgr: Always mount tmpfs with MS_NOEXEC"
diff --git a/.clang-format-2 b/.clang-format-2
index 41591ce..ede5d7e 100644
--- a/.clang-format-2
+++ b/.clang-format-2
@@ -7,4 +7,3 @@
 PointerAlignment: Left
 TabWidth: 2
 UseTab: Never
-PenaltyExcessCharacter: 32
diff --git a/.clang-format-4 b/.clang-format-4
index ae4a451..55773a2 100644
--- a/.clang-format-4
+++ b/.clang-format-4
@@ -5,7 +5,7 @@
 CommentPragmas: NOLINT:.*
 DerivePointerAlignment: false
 IndentWidth: 4
+ContinuationIndentWidth: 8
 PointerAlignment: Left
 TabWidth: 4
 UseTab: Never
-PenaltyExcessCharacter: 32
diff --git a/CleanSpec.mk b/CleanSpec.mk
index 5b5eff4..0e43dae 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -60,3 +60,21 @@
 $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib64/hw/gatekeeper.$(TARGET_DEVICE).so)
 $(call add-clean-step, rm -rf $(PRODUCT_OUT)/root/vendor)
 $(call add-clean-step, rm -rf $(PRODUCT_OUT)/root/init.rc)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib/libtrusty.so)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib64/libtrusty.so)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib/hw/keystore.trusty.so)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib64/hw/keystore.trusty.so)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib/hw/gatekeeper.trusty.so)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib64/hw/gatekeeper.trusty.so)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/bin/secure-storage-unit-test)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/bin/storageproxyd)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/bin/tipc-test)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/bin/trusty_keymaster_tipc)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/root/root)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/etc/ld.config.txt)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/etc/llndk.libraries.txt)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/etc/vndksp.libraries.txt)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/etc/ld.config.txt)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/etc/llndk.libraries.txt)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/etc/vndksp.libraries.txt)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/recovery/root/)
diff --git a/OWNERS b/OWNERS
new file mode 100644
index 0000000..682a067
--- /dev/null
+++ b/OWNERS
@@ -0,0 +1 @@
+enh@google.com
diff --git a/adb/Android.bp b/adb/Android.bp
index 41f7b89..07f052f 100644
--- a/adb/Android.bp
+++ b/adb/Android.bp
@@ -12,40 +12,410 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-python_binary_host {
-  name: "adb_integration_test_adb",
-  main: "test_adb.py",
-  srcs: [
-    "test_adb.py",
-  ],
-  libs: [
-    "adb_py",
-  ],
-  version: {
-    py2: {
-      enabled: true,
+cc_defaults {
+    name: "adb_defaults",
+
+    cflags: [
+        "-Wall",
+        "-Wextra",
+        "-Werror",
+        "-Wexit-time-destructors",
+        "-Wno-unused-parameter",
+        "-Wno-missing-field-initializers",
+        "-Wvla",
+    ],
+    rtti: true,
+
+    use_version_lib: true,
+
+    compile_multilib: "first",
+    product_variables: {
+        debuggable: {
+            cflags: [
+                "-DALLOW_ADBD_ROOT",
+                "-DALLOW_ADBD_DISABLE_VERITY",
+                "-DALLOW_ADBD_NO_AUTH",
+            ],
+        },
     },
-    py3: {
-      enabled: false,
+
+    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",
+            ],
+        },
+
+        not_windows: {
+            cflags: [
+                "-Wthread-safety",
+            ],
+        },
     },
-  },
+}
+
+// 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_unique_fd.cpp",
+    "adb_utils.cpp",
+    "fdevent.cpp",
+    "services.cpp",
+    "sockets.cpp",
+    "socket_spec.cpp",
+    "sysdeps/errno.cpp",
+    "transport.cpp",
+    "transport_fd.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",
+    "types_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_benchmark {
+    name: "adb_benchmark",
+    defaults: ["adb_defaults"],
+
+    srcs: ["transport_benchmark.cpp"],
+    target: {
+        android: {
+            static_libs: [
+                "libadbd",
+            ],
+        },
+        host: {
+            static_libs: [
+                "libadb_host",
+            ],
+        },
+    },
+
+    static_libs: [
+        "libbase",
+        "libcutils",
+        "libcrypto_utils",
+        "libcrypto",
+        "libdiagnose_usb",
+        "liblog",
+        "libusb",
+    ],
+}
+
+cc_binary_host {
+    name: "adb",
+
+    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"],
+    recovery_available: true,
+
+    // 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",
+        "libcrypto_utils",
+        "libcrypto",
+        "libdiagnose_usb",
+        "libqemu_pipe",
+        "libbase",
+    ],
+
+    export_include_dirs: [
+        "daemon/include",
+    ],
+}
+
+cc_binary {
+    name: "adbd",
+    defaults: ["adb_defaults"],
+
+    recovery_available: 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/services.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",
+
+        "libbase",
+        "libcutils",
+    ],
+}
+
+cc_test {
+    name: "adbd_test",
+    defaults: ["adb_defaults"],
+    srcs: libadb_test_srcs + [
+        "daemon/services.cpp",
+        "daemon/shell_service.cpp",
+        "daemon/shell_service_test.cpp",
+        "shell_service_protocol.cpp",
+        "shell_service_protocol_test.cpp",
+    ],
+
+    static_libs: [
+        "libadbd",
+        "libbase",
+        "libbootloader_message",
+        "libcutils",
+        "libcrypto_utils",
+        "libcrypto",
+        "libdiagnose_usb",
+        "liblog",
+        "libusb",
+        "libmdnssd",
+        "libselinux",
+    ],
+    test_suites: ["device-tests"],
 }
 
 python_binary_host {
-  name: "adb_integration_test_device",
-  main: "test_device.py",
-  srcs: [
-    "test_device.py",
-  ],
-  libs: [
-    "adb_py",
-  ],
-  version: {
-    py2: {
-      enabled: true,
+    name: "adb_integration_test_adb",
+    main: "test_adb.py",
+    srcs: [
+        "test_adb.py",
+    ],
+    libs: [
+        "adb_py",
+    ],
+    version: {
+        py2: {
+            enabled: true,
+        },
+        py3: {
+            enabled: false,
+        },
     },
-    py3: {
-      enabled: false,
+}
+
+python_binary_host {
+    name: "adb_integration_test_device",
+    main: "test_device.py",
+    srcs: [
+        "test_device.py",
+    ],
+    libs: [
+        "adb_py",
+    ],
+    version: {
+        py2: {
+            enabled: true,
+        },
+        py3: {
+            enabled: false,
+        },
     },
-  },
 }
diff --git a/adb/Android.mk b/adb/Android.mk
index 1f6f194..8b2d558 100644
--- a/adb/Android.mk
+++ b/adb/Android.mk
@@ -1,395 +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 := \
-    -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_windows.cpp \
-
-LIBADB_TEST_windows_SRCS := \
-    sysdeps/win32/errno_test.cpp \
-    sysdeps_win32_test.cpp \
-
-include $(CLEAR_VARS)
-LOCAL_CLANG := true
-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
-
-include $(BUILD_STATIC_LIBRARY)
-
-include $(CLEAR_VARS)
-LOCAL_CLANG := true
-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 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
-LOCAL_STATIC_LIBRARIES_linux := libusb
-LOCAL_STATIC_LIBRARIES_darwin := libusb
-
-LOCAL_C_INCLUDES_windows := development/host/windows/usb/api/
-LOCAL_MULTILIB := first
-
-include $(BUILD_HOST_STATIC_LIBRARY)
-
-include $(CLEAR_VARS)
-LOCAL_CLANG := true
-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 \
-
-LOCAL_STATIC_LIBRARIES_linux := libusb
-LOCAL_STATIC_LIBRARIES_darwin := 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_STATIC_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_STATIC_LIBRARIES_windows := AdbWinApi
-LOCAL_REQUIRED_MODULES_windows := AdbWinApi 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 \
-
-# Don't use libcutils on Windows.
-LOCAL_STATIC_LIBRARIES_darwin := libcutils
-LOCAL_STATIC_LIBRARIES_linux := libcutils
-
-LOCAL_STATIC_LIBRARIES_darwin += libusb
-LOCAL_STATIC_LIBRARIES_linux += libusb
-
-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_CLANG := true
-
-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_MODULE_PATH := $(TARGET_ROOT_OUT_SBIN)
-LOCAL_UNSTRIPPED_PATH := $(TARGET_ROOT_OUT_SBIN_UNSTRIPPED)
-
-LOCAL_SANITIZE := $(adb_target_sanitize)
-LOCAL_STRIP_MODULE := keep_symbols
-LOCAL_STATIC_LIBRARIES := \
-    libadbd \
-    libavb_user \
-    libbase \
-    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/OVERVIEW.TXT b/adb/OVERVIEW.TXT
index c40695a..f0b184c 100644
--- a/adb/OVERVIEW.TXT
+++ b/adb/OVERVIEW.TXT
@@ -7,16 +7,16 @@
 - keep track of all Android devices and emulators instances
   connected to or running on a given host developer machine
 
-- implement various control commands (e.g. "adb shell", "adb pull", etc..)
+- implement various control commands (e.g. "adb shell", "adb pull", etc.)
   for the benefit of clients (command-line users, or helper programs like
-  DDMS). These commands are what is called a 'service' in ADB.
+  DDMS). These commands are called 'services' in ADB.
 
 As a whole, everything works through the following components:
 
   1. The ADB server
 
     This is a background process that runs on the host machine. Its purpose
-    if to sense the USB ports to know when devices are attached/removed,
+    is to sense the USB ports to know when devices are attached/removed,
     as well as when emulator instances start/stop.
 
     It thus maintains a list of "connected devices" and assigns a 'state'
@@ -40,7 +40,7 @@
     meaning that the ADB server detected a new device/emulator, but could not
     connect to the adbd daemon.
 
-    the BOOTLOADER and RECOVERY states correspond to alternate states of
+    The BOOTLOADER and RECOVERY states correspond to alternate states of
     devices when they are in the bootloader or recovery mode.
 
   3. The ADB command-line client
@@ -49,8 +49,7 @@
     or a script. It first tries to locate the ADB server on the host machine,
     and will start one automatically if none is found.
 
-    then, the client sends its service requests to the ADB server. It doesn't
-    need to know.
+    Then, the client sends its service requests to the ADB server.
 
     Currently, a single 'adb' binary is used for both the server and client.
     this makes distribution and starting the server easier.
@@ -61,13 +60,13 @@
     There are essentially two kinds of services that a client can talk to.
 
     Host Services:
-      these services run within the ADB Server and thus do not need to
+      These services run within the ADB Server and thus do not need to
       communicate with a device at all. A typical example is "adb devices"
       which is used to return the list of currently known devices and their
-      state. They are a few couple other services though.
+      states. They are a few other services though.
 
     Local Services:
-      these services either run within the adbd daemon, or are started by
+      These services either run within the adbd daemon, or are started by
       it on the device. The ADB server is used to multiplex streams
       between the client and the service running in adbd. In this case
       its role is to initiate the connection, then of being a pass-through
@@ -104,12 +103,9 @@
            4-byte hex length, followed by a string giving the reason
            for failure.
 
-        3. As a special exception, for 'host:version', a 4-byte
-           hex string corresponding to the server's internal version number
-
     Note that the connection is still alive after an OKAY, which allows the
     client to make other requests. But in certain cases, an OKAY will even
-    change the state of the connection. 
+    change the state of the connection.
 
     For example, the case of the 'host:transport:<serialnumber>' request,
     where '<serialnumber>' is used to identify a given device/emulator; after
diff --git a/adb/OWNERS b/adb/OWNERS
new file mode 100644
index 0000000..643b448
--- /dev/null
+++ b/adb/OWNERS
@@ -0,0 +1,2 @@
+jmgao@google.com
+yabinc@google.com
diff --git a/adb/SERVICES.TXT b/adb/SERVICES.TXT
index 30c21f7..3e18a54 100644
--- a/adb/SERVICES.TXT
+++ b/adb/SERVICES.TXT
@@ -7,10 +7,6 @@
 host:version
     Ask the ADB server for its internal version number.
 
-    As a special exception, the server will respond with a 4-byte
-    hex string corresponding to its internal version number, without
-    any OKAY or FAIL.
-
 host:kill
     Ask the ADB server to quit immediately. This is used when the
     ADB client detects that an obsolete server is running after an
diff --git a/adb/SYNC.TXT b/adb/SYNC.TXT
index 06d7804..4445a76 100644
--- a/adb/SYNC.TXT
+++ b/adb/SYNC.TXT
@@ -1,4 +1,4 @@
-This file tries to document file related requests a client can make
+This file tries to document file-related requests a client can make
 to the ADB server of an adbd daemon. See the OVERVIEW.TXT document
 to understand what's going on here. See the SERVICES.TXT to learn more
 about the other requests that are possible.
@@ -8,16 +8,16 @@
 
 Requesting the sync service ("sync:") using the protocol as described in
 SERVICES.TXT sets the connection in sync mode. This mode is a binary mode that
-differ from the regular adb protocol. The connection stays in sync mode until
+differs from the regular adb protocol. The connection stays in sync mode until
 explicitly terminated (see below).
 
 After the initial "sync:" command is sent the server must respond with either
-"OKAY" or "FAIL" as per usual. 
+"OKAY" or "FAIL" as per usual.
 
 In sync mode both the server and the client will frequently use eight-byte
-packets to communicate in this document called sync request and sync
-responses. The first four bytes is an id and specifies sync request is
-represented by four utf-8 characters. The last four bytes is a Little-Endian
+packets to communicate. In this document these are called sync requests and sync
+responses. The first four bytes are an id that specifies the sync request. It is
+represented by four utf-8 characters. The last four bytes are a Little-Endian
 integer, with various uses. This number will be called "length" below. In fact
 all binary integers are Little-Endian in the sync mode. Sync mode is
 implicitly exited after each sync request, and normal adb communication
@@ -29,8 +29,8 @@
 SEND - Send a file to device
 STAT - Stat a file
 
-For all of the sync request above the must be followed by length number of
-bytes containing an utf-8 string with a remote filename.
+All of the sync requests above must be followed by "length": the number of
+bytes containing a utf-8 string with a remote filename.
 
 LIST:
 Lists files in the directory specified by the remote filename. The server will
@@ -45,7 +45,7 @@
 6. length number of bytes containing an utf-8 string representing the file
    name.
 
-When an sync response "DONE" is received the listing is done.
+When a sync response "DONE" is received the listing is done.
 
 SEND:
 The remote file name is split into two parts separated by the last
@@ -65,7 +65,7 @@
 
 When the file is transferred a sync request "DONE" is sent, where length is set
 to the last modified time for the file. The server responds to this last
-request (but not to chuck requests) with an "OKAY" sync response (length can
+request (but not to chunk requests) with an "OKAY" sync response (length can
 be ignored).
 
 
@@ -73,9 +73,8 @@
 Retrieves a file from device to a local file. The remote path is the path to
 the file that will be returned. Just as for the SEND sync request the file
 received is split up into chunks. The sync response id is "DATA" and length is
-the chuck size. After follows chunk size number of bytes. This is repeated
-until the file is transferred. Each chuck will not be larger than 64k.
+the chunk size. After follows chunk size number of bytes. This is repeated
+until the file is transferred. Each chunk will not be larger than 64k.
 
 When the file is transferred a sync response "DONE" is retrieved where the
 length can be ignored.
-
diff --git a/adb/adb.bash b/adb/adb.bash
new file mode 100644
index 0000000..b1b3957
--- /dev/null
+++ b/adb/adb.bash
@@ -0,0 +1,499 @@
+# /* vim: set ai ts=4 ft=sh: */
+#
+# Copyright 2011, 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.
+#
+
+_adb() {
+    if ! check_type "$1" >/dev/null; then
+        return
+    fi
+
+    if check_type _init_completion >/dev/null; then
+        _init_completion || return
+    fi
+
+    local where i cur serial
+    COMPREPLY=()
+
+    serial="${ANDROID_SERIAL:-none}"
+    where=OPTIONS
+    for ((i=1; i <= COMP_CWORD; i++)); do
+        cur="${COMP_WORDS[i]}"
+        case "${cur}" in
+            -s)
+                where=OPT_SERIAL
+                ;;
+            -p)
+                where=OPT_PATH
+                ;;
+            -*)
+                where=OPTIONS
+                ;;
+            *)
+                if [[ $where == OPT_SERIAL ]]; then
+                    where=OPT_SERIAL_ARG
+                    serial=${cur}
+                else
+                    where=COMMAND
+                    break
+                fi
+                ;;
+        esac
+    done
+
+    if [[ $where == COMMAND && $i -ge $COMP_CWORD ]]; then
+        where=OPTIONS
+    fi
+
+    OPTIONS="-d -e -s -p"
+    COMMAND="devices connect disconnect push pull sync shell emu logcat lolcat forward jdwp install uninstall bugreport help version start-server kill-server get-state get-serialno status-window remount reboot reboot-bootloader root usb tcpip disable-verity"
+
+    case $where in
+        OPTIONS|OPT_SERIAL|OPT_PATH)
+            COMPREPLY=( $(compgen -W "$OPTIONS $COMMAND" -- "$cur") )
+            ;;
+        OPT_SERIAL_ARG)
+            local devices=$(command adb devices 2> /dev/null | grep -v "List of devices" | awk '{ print $1 }')
+            COMPREPLY=( $(compgen -W "${devices}" -- ${cur}) )
+            ;;
+        COMMAND)
+            if [[ $i -eq $COMP_CWORD ]]; then
+                COMPREPLY=( $(compgen -W "$COMMAND" -- "$cur") )
+            else
+                i=$((i+1))
+                case "${cur}" in
+                    install)
+                        _adb_cmd_install "$serial" $i
+                        ;;
+                    sideload)
+                        _adb_cmd_sideload "$serial" $i
+                        ;;
+                    pull)
+                        _adb_cmd_pull "$serial" $i
+                        ;;
+                    push)
+                        _adb_cmd_push "$serial" $i
+                        ;;
+                    reboot)
+                        if [[ $COMP_CWORD == $i ]]; then
+                            args="bootloader recovery"
+                            COMPREPLY=( $(compgen -W "${args}" -- "${COMP_WORDS[i]}") )
+                        fi
+                        ;;
+                    shell)
+                        _adb_cmd_shell "$serial" $i
+                        ;;
+                    uninstall)
+                        _adb_cmd_uninstall "$serial" $i
+                        ;;
+                esac
+            fi
+            ;;
+    esac
+
+    return 0
+}
+
+_adb_cmd_install() {
+    local serial i cur where
+
+    serial=$1
+    i=$2
+
+    where=OPTIONS
+    for ((; i <= COMP_CWORD; i++)); do
+        cur="${COMP_WORDS[i]}"
+        case "${cur}" in
+            -*)
+                where=OPTIONS
+                ;;
+            *)
+                where=FILE
+                break
+                ;;
+        esac
+    done
+
+    cur="${COMP_WORDS[COMP_CWORD]}"
+    if [[ $where == OPTIONS ]]; then
+        COMPREPLY=( $(compgen -W "-d -l -r -s" -- "${cur}") )
+        return
+    fi
+
+    _adb_util_complete_local_file "${cur}" '!*.apk'
+}
+
+_adb_cmd_sideload() {
+    local serial i cur
+
+    serial=$1
+    i=$2
+
+    cur="${COMP_WORDS[COMP_CWORD]}"
+
+    _adb_util_complete_local_file "${cur}" '!*.zip'
+}
+
+_adb_cmd_push() {
+    local serial IFS=$'\n' i cur
+
+    serial=$1
+    i=$2
+
+    cur="${COMP_WORDS[COMP_CWORD]}"
+
+    if [[ $COMP_CWORD == $i ]]; then
+        _adb_util_complete_local_file "${cur}"
+    elif [[ $COMP_CWORD == $(($i+1)) ]]; then
+        if [ "${cur}" == "" ]; then
+            cur="/"
+        fi
+        _adb_util_list_files $serial "${cur}"
+    fi
+}
+
+_adb_cmd_pull() {
+    local serial IFS=$'\n' i cur
+
+    serial=$1
+    i=$2
+
+    cur="${COMP_WORDS[COMP_CWORD]}"
+
+    if [[ $COMP_CWORD == $i ]]; then
+        if [ "${cur}" == "" ]; then
+            cur="/"
+        fi
+        _adb_util_list_files $serial "${cur}"
+    elif [[ $COMP_CWORD == $(($i+1)) ]]; then
+        _adb_util_complete_local_file "${cur}"
+    fi
+}
+
+_adb_cmd_shell() {
+    local serial IFS=$'\n' i cur
+    local -a args
+
+    serial=$1
+    i=$2
+
+    cur="${COMP_WORDS[i]}"
+    if [ "$serial" != "none" ]; then
+        args=(-s $serial)
+    fi
+
+    if [[ $i -eq $COMP_CWORD && ${cur:0:1} != "/" ]]; then
+        paths=$(command adb ${args[@]} shell echo '$'PATH 2> /dev/null | tr -d '\r' | tr : '\n')
+        COMMAND=$(command adb ${args[@]} shell ls $paths '2>' /dev/null | tr -d '\r' | {
+            while read -r tmp; do
+                command=${tmp##*/}
+                printf '%s\n' "$command"
+            done
+        })
+        COMPREPLY=( $(compgen -W "$COMMAND" -- "$cur") )
+        return 0
+    fi
+
+    i=$((i+1))
+    case "$cur" in
+        ls)
+            _adb_shell_file_command $serial $i "--color -A -C -F -H -L -R -S -Z -a -c -d -f -h -i -k -l -m -n -p -q -r -s -t -u -x -1"
+            ;;
+        cat)
+            _adb_shell_file_command $serial $i "-h -e -t -u -v"
+            ;;
+        dumpsys)
+            _adb_cmd_shell_dumpsys "$serial" $i
+            ;;
+        am)
+            _adb_cmd_shell_am "$serial" $i
+            ;;
+        pm)
+            _adb_cmd_shell_pm "$serial" $i
+            ;;
+        /*)
+            _adb_util_list_files $serial "$cur"
+            ;;
+        *)
+            COMPREPLY=( )
+            ;;
+    esac
+
+    return 0
+}
+
+_adb_cmd_shell_dumpsys() {
+    local serial i cur
+    local -a args
+    local candidates
+
+    unset IFS
+
+    serial=$1
+    i=$2
+
+    if [ "$serial" != "none" ]; then
+        args=(-s $serial)
+    fi
+
+    if (( $i == $COMP_CWORD )) ; then
+        cur="${COMP_WORDS[COMP_CWORD]}"
+        # First line is a header, so need "1d".
+        candidates=$(command adb ${args[@]} shell dumpsys -l 2> /dev/null | sed -e '1d;s/^  *//' | tr -d '\r')
+        candidates="-l $candidates"
+        COMPREPLY=( $(compgen -W "$candidates" -- "$cur") )
+        return 0
+    fi
+
+    COMPREPLY=( )
+    return 0
+}
+
+_adb_cmd_shell_am() {
+    local serial i cur
+    local candidates
+
+    unset IFS
+
+    serial=$1
+    i=$2
+
+    if (( $i == $COMP_CWORD )) ; then
+        cur="${COMP_WORDS[COMP_CWORD]}"
+        candidates="broadcast clear-debug-app clear-watch-heap dumpheap force-stop get-config get-inactive hang idle-maintenance instrument kill kill-all monitor package-importance profile restart screen-compat send-trim-memory set-debug-app set-inactive set-watch-heap stack start startservice start-user stopservice stop-user suppress-resize-config-changes switch-user task to-app-uri to-intent-uri to-uri"
+        COMPREPLY=( $(compgen -W "$candidates" -- "$cur") )
+        return 0
+    fi
+
+    COMPREPLY=( )
+    return 0
+}
+
+
+_adb_cmd_shell_pm() {
+    local serial i cur
+    local candidates
+
+    unset IFS
+
+    serial=$1
+    i=$2
+
+    if (( $i == $COMP_CWORD )) ; then
+        cur="${COMP_WORDS[COMP_CWORD]}"
+        candidates="-l -lf -p clear create-user default-state disable"
+        candidates+=" disable-until-used disable-user dump enable"
+        candidates+=" get-app-link get-install-location get-max-users"
+        candidates+=" get-max-running-users grant hide install"
+        candidates+=" install-abandon install-commit install-create"
+        candidates+=" install-write list move-package"
+        candidates+=" move-primary-storage path remove-user"
+        candidates+=" reset-permissions revoke set-app-link"
+        candidates+=" set-installer set-install-location"
+        candidates+=" set-permission-enforced trim-caches unhide"
+        candidates+=" uninstall"
+        COMPREPLY=( $(compgen -W "$candidates" -- "$cur") )
+        return 0
+    fi
+
+    if (( $i + 1 == $COMP_CWORD )) && [[ "${COMP_WORDS[COMP_CWORD -1]}" == "list" ]]  ; then
+        cur="${COMP_WORDS[COMP_CWORD]}"
+        candidates="packages permission-groups permissions instrumentation features libraries users"
+        COMPREPLY=( $(compgen -W "$candidates" -- "$cur") )
+        return 0
+    fi
+
+    COMPREPLY=( )
+    return 0
+}
+
+_adb_cmd_uninstall() {
+    local serial i where cur packages
+
+    serial=$1
+    i=$2
+    if [ "$serial" != "none" ]; then
+        args=(-s $serial)
+    fi
+
+    where=OPTIONS
+    for ((; i <= COMP_CWORD; i++)); do
+        cur="${COMP_WORDS[i]}"
+        case "${cur}" in
+            -*)
+                where=OPTIONS
+                ;;
+            *)
+                where=FILE
+                break
+                ;;
+        esac
+    done
+
+    cur="${COMP_WORDS[COMP_CWORD]}"
+    if [[ $where == OPTIONS ]]; then
+        COMPREPLY=( $(compgen -W "-k" -- "${cur}") )
+    fi
+
+    packages="$(
+        command adb ${args[@]} shell pm list packages '2>' /dev/null 2> /dev/null | tr -d '\r' | {
+            while read -r tmp; do
+                local package=${tmp#package:}
+                echo -n "${package} "
+            done
+        }
+    )"
+
+    COMPREPLY=( ${COMPREPLY[@]:-} $(compgen -W "${packages}" -- "${cur}") )
+}
+
+_adb_shell_file_command() {
+    local serial i cur file options
+    local -a args
+
+    serial=$1
+    i=$2
+    if [ "$serial" != "none" ]; then
+        args=(-s $serial)
+    fi
+    options=$3
+
+    where=OPTIONS
+    for ((; i <= COMP_CWORD; i++)); do
+        cur="${COMP_WORDS[i]}"
+        case "${cur}" in
+            -*)
+                where=OPTIONS
+                ;;
+            *)
+                where=FILE
+                break
+                ;;
+        esac
+    done
+
+    file="${COMP_WORDS[COMP_CWORD]}"
+    if [[ ${file} == "" ]]; then
+        file="/"
+    fi
+
+    case $where in
+        OPTIONS)
+            unset IFS
+            COMPREPLY=( $(compgen -W "$options" -- "$cur") )
+            ;;
+        FILE)
+            _adb_util_list_files $serial "$file"
+            ;;
+    esac
+
+    return 0
+}
+
+_adb_util_list_files() {
+    local serial dir IFS=$'\n'
+    local -a toks
+    local -a args
+
+    serial="$1"
+    file="$2"
+
+    if [ "$serial" != "none" ]; then
+        args=(-s $serial)
+    fi
+
+    if [[ $( command adb ${args[@]} shell ls -dF / '2>/dev/null' | tr -d '\r' ) == "d /" ]] ; then
+        toks=( ${toks[@]-} $(
+            command adb ${args[@]} shell ls -dF ${file}"*" '2>' /dev/null 2> /dev/null | tr -d '\r' | {
+                while read -r tmp; do
+                    filetype=${tmp%% *}
+                    filename=${tmp:${#filetype}+1}
+                    if [[ ${filetype:${#filetype}-1:1} == d ]]; then
+                        printf '%s/\n' "$filename"
+                    else
+                        printf '%s\n' "$filename"
+                    fi
+                done
+            }
+        ))
+    else
+        toks=( ${toks[@]-} $(
+            command adb ${args[@]} shell ls -dp ${file}"*" '2>/dev/null' 2> /dev/null | tr -d '\r'
+        ))
+    fi
+
+    # Since we're probably doing file completion here, don't add a space after.
+    if [[ $(check_type compopt) == "builtin" ]]; then
+        compopt -o nospace
+    fi
+
+    COMPREPLY=( ${COMPREPLY[@]:-} "${toks[@]}" )
+}
+
+_adb_util_complete_local_file()
+{
+    local file xspec i j IFS=$'\n'
+    local -a dirs files
+
+    file=$1
+    xspec=$2
+
+    # Since we're probably doing file completion here, don't add a space after.
+    if [[ $(check_type compopt) == "builtin" ]]; then
+        compopt -o plusdirs
+        if [[ "${xspec}" == "" ]]; then
+            COMPREPLY=( ${COMPREPLY[@]:-} $(compgen -f -- "${cur}") )
+        else
+            compopt +o filenames
+            COMPREPLY=( ${COMPREPLY[@]:-} $(compgen -f -X "${xspec}" -- "${cur}") )
+        fi
+    else
+        # Work-around for shells with no compopt
+
+        dirs=( $(compgen -d -- "${cur}" ) )
+
+        if [[ "${xspec}" == "" ]]; then
+            files=( ${COMPREPLY[@]:-} $(compgen -f -- "${cur}") )
+        else
+            files=( ${COMPREPLY[@]:-} $(compgen -f -X "${xspec}" -- "${cur}") )
+        fi
+
+        COMPREPLY=( $(
+            for i in "${files[@]}"; do
+                local skip=
+                for j in "${dirs[@]}"; do
+                    if [[ $i == $j ]]; then
+                        skip=1
+                        break
+                    fi
+                done
+                [[ -n $skip ]] || printf "%s\n" "$i"
+            done
+        ))
+
+        COMPREPLY=( ${COMPREPLY[@]:-} $(
+            for i in "${dirs[@]}"; do
+                printf "%s/\n" "$i"
+            done
+        ))
+    fi
+}
+
+
+if [[ $(check_type compopt) == "builtin" ]]; then
+    complete -F _adb adb
+else
+    complete -o nospace -F _adb adb
+fi
diff --git a/adb/adb.cpp b/adb/adb.cpp
index ff7b71f..38c6f62 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,43 +105,48 @@
 }
 
 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)
 {
     D("adb: online");
     t->online = 1;
+    t->SetConnectionEstablished(true);
 }
 
 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 +160,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 +177,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);
@@ -240,7 +244,10 @@
     D("Calling send_connect");
     apacket* cp = get_apacket();
     cp->msg.command = A_CNXN;
-    cp->msg.arg0 = t->get_protocol_version();
+    // Send the max supported version, but because the transport is
+    // initialized to A_VERSION_MIN, this will be compatible with every
+    // device.
+    cp->msg.arg0 = A_VERSION;
     cp->msg.arg1 = t->get_max_payload();
 
     std::string connection_str = get_connection_string();
@@ -251,34 +258,12 @@
                    << connection_str.length() << ")";
     }
 
-    memcpy(cp->data, connection_str.c_str(), connection_str.length());
-    cp->msg.data_length = connection_str.length();
+    cp->payload.assign(connection_str.begin(), connection_str.end());
+    cp->msg.data_length = cp->payload.size();
 
     send_packet(cp, t);
 }
 
-#if ADB_HOST
-
-void SendConnectOnHost(atransport* t) {
-    // Send an empty message before A_CNXN message. This is because the data toggle of the ep_out on
-    // host and ep_in on device may not be the same.
-    apacket* p = get_apacket();
-    CHECK(p);
-    send_packet(p, t);
-    send_connect(t);
-}
-
-#endif
-
-// qual_overwrite is used to overwrite a qualifier string.  dst is a
-// pointer to a char pointer.  It is assumed that if *dst is non-NULL, it
-// was malloc'ed and needs to freed.  *dst will be set to a dup of src.
-// TODO: switch to std::string for these atransport fields instead.
-static void qual_overwrite(char** dst, const std::string& src) {
-    free(*dst);
-    *dst = strdup(src.c_str());
-}
-
 void parse_banner(const std::string& banner, atransport* t) {
     D("parse_banner: %s", banner.c_str());
 
@@ -302,11 +287,11 @@
             const std::string& key = key_value[0];
             const std::string& value = key_value[1];
             if (key == "ro.product.name") {
-                qual_overwrite(&t->product, value);
+                t->product = value;
             } else if (key == "ro.product.model") {
-                qual_overwrite(&t->model, value);
+                t->model = value;
             } else if (key == "ro.product.device") {
-                qual_overwrite(&t->device, value);
+                t->device = value;
             } else if (key == "features") {
                 t->SetFeatures(value);
             }
@@ -333,14 +318,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);
+    std::string banner(p->payload.begin(), p->payload.end());
     parse_banner(banner, t);
 
 #if ADB_HOST
@@ -364,21 +345,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
-            SendConnectOnHost(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;
@@ -387,14 +356,16 @@
         switch (p->msg.arg0) {
 #if ADB_HOST
             case ADB_AUTH_TOKEN:
-                if (t->GetConnectionState() == kCsOffline) {
-                    t->SetConnectionState(kCsUnauthorized);
+                if (t->GetConnectionState() != kCsAuthorizing) {
+                    t->SetConnectionState(kCsAuthorizing);
                 }
-                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)) {
+            case ADB_AUTH_SIGNATURE: {
+                // TODO: Switch to string_view.
+                std::string signature(p->payload.begin(), p->payload.end());
+                if (adbd_auth_verify(t->token, sizeof(t->token), signature)) {
                     adbd_auth_verified(t);
                     t->failed_auth_attempts = 0;
                 } else {
@@ -402,9 +373,10 @@
                     send_auth_request(t);
                 }
                 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:
@@ -416,9 +388,9 @@
 
     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);
+            // TODO: Switch to string_view.
+            std::string address(p->payload.begin(), p->payload.end());
+            asocket* s = create_local_service_socket(address.c_str(), t);
             if (s == nullptr) {
                 send_close(0, p->msg.arg0, t);
             } else {
@@ -434,7 +406,7 @@
         if (t->online && p->msg.arg0 != 0 && p->msg.arg1 != 0) {
             asocket* s = find_local_socket(p->msg.arg1, 0);
             if (s) {
-                if(s->peer == 0) {
+                if(s->peer == nullptr) {
                     /* On first READY message, create the connection. */
                     s->peer = create_remote_socket(p->msg.arg0, t);
                     s->peer->peer = s;
@@ -443,8 +415,8 @@
                     /* Other READY messages must use the same local-id */
                     s->ready(s);
                 } else {
-                    D("Invalid A_OKAY(%d,%d), expected A_OKAY(%d,%d) on transport %s",
-                      p->msg.arg0, p->msg.arg1, s->peer->id, p->msg.arg1, t->serial);
+                    D("Invalid A_OKAY(%d,%d), expected A_OKAY(%d,%d) on transport %s", p->msg.arg0,
+                      p->msg.arg1, s->peer->id, p->msg.arg1, t->serial.c_str());
                 }
             } else {
                 // When receiving A_OKAY from device for A_OPEN request, the host server may
@@ -470,8 +442,8 @@
                  * socket has a peer on the same transport.
                  */
                 if (p->msg.arg0 == 0 && s->peer && s->peer->transport != t) {
-                    D("Invalid A_CLSE(0, %u) from transport %s, expected transport %s",
-                      p->msg.arg1, t->serial, s->peer->transport->serial);
+                    D("Invalid A_CLSE(0, %u) from transport %s, expected transport %s", p->msg.arg1,
+                      t->serial.c_str(), s->peer->transport->serial.c_str());
                 } else {
                     s->close(s);
                 }
@@ -484,13 +456,10 @@
             asocket* s = find_local_socket(p->msg.arg1, p->msg.arg0);
             if (s) {
                 unsigned rid = p->msg.arg0;
-                p->len = p->msg.data_length;
-
-                if (s->enqueue(s, p) == 0) {
+                if (s->enqueue(s, std::move(p->payload)) == 0) {
                     D("Enqueue the socket");
                     send_ready(s->id, rid, t);
                 }
-                return;
             }
         }
         break;
@@ -907,9 +876,8 @@
     }
 #else /* !defined(_WIN32) */
     // set up a pipe so the child can tell us when it is ready.
-    // fd[0] will be parent's end, and the child will write on fd[1]
-    int fd[2];
-    if (pipe(fd)) {
+    unique_fd pipe_read, pipe_write;
+    if (!Pipe(&pipe_read, &pipe_write)) {
         fprintf(stderr, "pipe failed in launch_server, errno: %d\n", errno);
         return -1;
     }
@@ -921,11 +889,10 @@
 
     if (pid == 0) {
         // child side of the fork
-
-        adb_close(fd[0]);
+        pipe_read.reset();
 
         char reply_fd[30];
-        snprintf(reply_fd, sizeof(reply_fd), "%d", fd[1]);
+        snprintf(reply_fd, sizeof(reply_fd), "%d", pipe_write.get());
         // child process
         int result = execl(path.c_str(), "adb", "-L", socket_spec.c_str(), "fork-server", "server",
                            "--reply-fd", reply_fd, NULL);
@@ -935,10 +902,10 @@
         // parent side of the fork
         char temp[3] = {};
         // wait for the "OK\n" message
-        adb_close(fd[1]);
-        int ret = adb_read(fd[0], temp, 3);
+        pipe_write.reset();
+        int ret = adb_read(pipe_read.get(), temp, 3);
         int saved_errno = errno;
-        adb_close(fd[0]);
+        pipe_read.reset();
         if (ret < 0) {
             fprintf(stderr, "could not read ok from ADB Server, errno = %d\n", saved_errno);
             return -1;
@@ -953,18 +920,23 @@
 }
 #endif /* ADB_HOST */
 
+bool handle_forward_request(const char* service, atransport* transport, int reply_fd) {
+    return handle_forward_request(service, [transport](std::string*) { return transport; },
+                                  reply_fd);
+}
+
 // Try to handle a network forwarding request.
-// This returns 1 on success, 0 on failure, and -1 to indicate this is not
-// a forwarding-related request.
-int handle_forward_request(const char* service, TransportType type, const char* serial, int reply_fd)
-{
+bool handle_forward_request(const char* service,
+                            std::function<atransport*(std::string* error)> transport_acquirer,
+                            int reply_fd) {
     if (!strcmp(service, "list-forward")) {
         // Create the list of forward redirections.
         std::string listeners = format_listeners();
 #if ADB_HOST
         SendOkay(reply_fd);
 #endif
-        return SendProtocolString(reply_fd, listeners);
+        SendProtocolString(reply_fd, listeners);
+        return true;
     }
 
     if (!strcmp(service, "killforward-all")) {
@@ -974,12 +946,19 @@
         SendOkay(reply_fd);
 #endif
         SendOkay(reply_fd);
-        return 1;
+        return true;
     }
 
     if (!strncmp(service, "forward:", 8) || !strncmp(service, "killforward:", 12)) {
         // killforward:local
         // forward:(norebind:)?local;remote
+        std::string error;
+        atransport* transport = transport_acquirer(&error);
+        if (!transport) {
+            SendFail(reply_fd, error);
+            return true;
+        }
+
         bool kill_forward = false;
         bool no_rebind = false;
         if (android::base::StartsWith(service, "killforward:")) {
@@ -999,24 +978,16 @@
             // Check killforward: parameter format: '<local>'
             if (pieces.size() != 1 || pieces[0].empty()) {
                 SendFail(reply_fd, android::base::StringPrintf("bad killforward: %s", service));
-                return 1;
+                return true;
             }
         } else {
             // Check forward: parameter format: '<local>;<remote>'
             if (pieces.size() != 2 || pieces[0].empty() || pieces[1].empty() || pieces[1][0] == '*') {
                 SendFail(reply_fd, android::base::StringPrintf("bad forward: %s", service));
-                return 1;
+                return true;
             }
         }
 
-        std::string error_msg;
-        atransport* transport = acquire_one_transport(type, serial, nullptr, &error_msg);
-        if (!transport) {
-            SendFail(reply_fd, error_msg);
-            return 1;
-        }
-
-        std::string error;
         InstallStatus r;
         int resolved_tcp_port = 0;
         if (kill_forward) {
@@ -1037,7 +1008,7 @@
                 SendProtocolString(reply_fd, android::base::StringPrintf("%d", resolved_tcp_port));
             }
 
-            return 1;
+            return true;
         }
 
         std::string message;
@@ -1056,9 +1027,10 @@
             break;
         }
         SendFail(reply_fd, message);
-        return 1;
+        return true;
     }
-    return 0;
+
+    return false;
 }
 
 #if ADB_HOST
@@ -1068,8 +1040,8 @@
     return 0;
 }
 
-int handle_host_request(const char* service, TransportType type,
-                        const char* serial, int reply_fd, asocket* s) {
+int handle_host_request(const char* service, TransportType type, const char* serial,
+                        TransportId transport_id, int reply_fd, asocket* s) {
     if (strcmp(service, "kill") == 0) {
         fprintf(stderr, "adb server killed by remote request\n");
         fflush(stdout);
@@ -1079,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
@@ -1089,7 +1061,14 @@
     if (!strncmp(service, "transport", strlen("transport"))) {
         TransportType type = kTransportAny;
 
-        if (!strncmp(service, "transport-usb", strlen("transport-usb"))) {
+        if (!strncmp(service, "transport-id:", strlen("transport-id:"))) {
+            service += strlen("transport-id:");
+            transport_id = strtoll(service, const_cast<char**>(&service), 10);
+            if (*service != '\0') {
+                SendFail(reply_fd, "invalid transport id");
+                return 1;
+            }
+        } else if (!strncmp(service, "transport-usb", strlen("transport-usb"))) {
             type = kTransportUsb;
         } else if (!strncmp(service, "transport-local", strlen("transport-local"))) {
             type = kTransportLocal;
@@ -1101,7 +1080,7 @@
         }
 
         std::string error;
-        atransport* t = acquire_one_transport(type, serial, nullptr, &error);
+        atransport* t = acquire_one_transport(type, serial, transport_id, nullptr, &error);
         if (t != nullptr) {
             s->transport = t;
             SendOkay(reply_fd);
@@ -1126,14 +1105,11 @@
     if (!strcmp(service, "reconnect-offline")) {
         std::string response;
         close_usb_devices([&response](const atransport* transport) {
-            switch (transport->GetConnectionState()) {
-                case kCsOffline:
-                case kCsUnauthorized:
-                    response += "reconnecting " + transport->serial_name() + "\n";
-                    return true;
-                default:
-                    return false;
+            if (!ConnectionStateIsOnline(transport->GetConnectionState())) {
+                response += "reconnecting " + transport->serial_name() + "\n";
+                return true;
             }
+            return false;
         });
         if (!response.empty()) {
             response.resize(response.size() - 1);
@@ -1144,7 +1120,7 @@
 
     if (!strcmp(service, "features")) {
         std::string error;
-        atransport* t = acquire_one_transport(type, serial, nullptr, &error);
+        atransport* t = acquire_one_transport(type, serial, transport_id, nullptr, &error);
         if (t != nullptr) {
             SendOkay(reply_fd, FeatureSetToString(t->features()));
         } else {
@@ -1197,25 +1173,25 @@
     // These always report "unknown" rather than the actual error, for scripts.
     if (!strcmp(service, "get-serialno")) {
         std::string error;
-        atransport* t = acquire_one_transport(type, serial, nullptr, &error);
+        atransport* t = acquire_one_transport(type, serial, transport_id, nullptr, &error);
         if (t) {
-            return SendOkay(reply_fd, t->serial ? t->serial : "unknown");
+            return SendOkay(reply_fd, !t->serial.empty() ? t->serial : "unknown");
         } else {
             return SendFail(reply_fd, error);
         }
     }
     if (!strcmp(service, "get-devpath")) {
         std::string error;
-        atransport* t = acquire_one_transport(type, serial, nullptr, &error);
+        atransport* t = acquire_one_transport(type, serial, transport_id, nullptr, &error);
         if (t) {
-            return SendOkay(reply_fd, t->devpath ? t->devpath : "unknown");
+            return SendOkay(reply_fd, !t->devpath.empty() ? t->devpath : "unknown");
         } else {
             return SendFail(reply_fd, error);
         }
     }
     if (!strcmp(service, "get-state")) {
         std::string error;
-        atransport* t = acquire_one_transport(type, serial, nullptr, &error);
+        atransport* t = acquire_one_transport(type, serial, transport_id, nullptr, &error);
         if (t) {
             return SendOkay(reply_fd, t->connection_state_name());
         } else {
@@ -1233,7 +1209,7 @@
 
     if (!strcmp(service, "reconnect")) {
         std::string response;
-        atransport* t = acquire_one_transport(type, serial, nullptr, &response, true);
+        atransport* t = acquire_one_transport(type, serial, transport_id, nullptr, &response, true);
         if (t != nullptr) {
             kick_transport(t);
             response =
@@ -1242,9 +1218,15 @@
         return SendOkay(reply_fd, response);
     }
 
-    int ret = handle_forward_request(service, type, serial, reply_fd);
-    if (ret >= 0)
-      return ret - 1;
+    if (handle_forward_request(service,
+                               [=](std::string* error) {
+                                   return acquire_one_transport(type, serial, transport_id, nullptr,
+                                                                error);
+                               },
+                               reply_fd)) {
+        return 0;
+    }
+
     return -1;
 }
 
diff --git a/adb/adb.h b/adb/adb.h
index d6b2b81..e6af780 100644
--- a/adb/adb.h
+++ b/adb/adb.h
@@ -28,11 +28,11 @@
 #include "adb_trace.h"
 #include "fdevent.h"
 #include "socket.h"
+#include "types.h"
 #include "usb.h"
 
 constexpr size_t MAX_PAYLOAD_V1 = 4 * 1024;
-constexpr size_t MAX_PAYLOAD_V2 = 256 * 1024;
-constexpr size_t MAX_PAYLOAD = MAX_PAYLOAD_V2;
+constexpr size_t MAX_PAYLOAD = 1024 * 1024;
 
 constexpr size_t LINUX_MAX_SOCKET_SIZE = 4194304;
 
@@ -45,7 +45,12 @@
 #define A_AUTH 0x48545541
 
 // ADB protocol version.
-#define A_VERSION 0x01000000
+// Version revision:
+// 0x01000000: original
+// 0x01000001: skip checksum (Dec 2017)
+#define A_VERSION_MIN 0x01000000
+#define A_VERSION_SKIP_CHECKSUM 0x01000001
+#define A_VERSION 0x01000001
 
 // Used for help/version information.
 #define ADB_VERSION_MAJOR 1
@@ -54,30 +59,11 @@
 std::string adb_version();
 
 // Increment this when we want to force users to start a new adb server.
-#define ADB_SERVER_VERSION 39
+#define ADB_SERVER_VERSION 40
 
+using TransportId = uint64_t;
 class atransport;
 
-struct amessage {
-    uint32_t command;     /* command identifier constant      */
-    uint32_t arg0;        /* first argument                   */
-    uint32_t arg1;        /* second argument                  */
-    uint32_t data_length; /* length of payload (0 is allowed) */
-    uint32_t data_check;  /* checksum of data payload         */
-    uint32_t magic;       /* command ^ 0xffffffff             */
-};
-
-struct apacket
-{
-    apacket *next;
-
-    size_t len;
-    char* ptr;
-
-    amessage msg;
-    char data[MAX_PAYLOAD];
-};
-
 uint32_t calculate_apacket_checksum(const apacket* packet);
 
 /* the adisconnect structure is used to record a callback that
@@ -85,13 +71,11 @@
 ** this should be used to cleanup objects that depend on the
 ** transport (e.g. remote sockets, listeners, etc...)
 */
-struct  adisconnect
-{
-    void        (*func)(void*  opaque, atransport*  t);
-    void*         opaque;
+struct adisconnect {
+    void (*func)(void* opaque, atransport* t);
+    void* opaque;
 };
 
-
 // A transport object models the connection to a remote device or emulator there
 // is one transport per connected device/emulator. A "local transport" connects
 // through TCP (for the emulator), while a "usb transport" through USB (for real
@@ -111,34 +95,45 @@
 
 enum ConnectionState {
     kCsAny = -1,
-    kCsOffline = 0,
+
+    kCsConnecting = 0,  // Haven't received a response from the device yet.
+    kCsAuthorizing,     // Authorizing with keys from ADB_VENDOR_KEYS.
+    kCsUnauthorized,    // ADB_VENDOR_KEYS exhausted, fell back to user prompt.
+    kCsNoPerm,          // Insufficient permissions to communicate with the device.
+    kCsOffline,
+
     kCsBootloader,
     kCsDevice,
     kCsHost,
     kCsRecovery,
-    kCsNoPerm,  // Insufficient permissions to communicate with the device.
     kCsSideload,
-    kCsUnauthorized,
 };
 
+inline bool ConnectionStateIsOnline(ConnectionState state) {
+    switch (state) {
+        case kCsBootloader:
+        case kCsDevice:
+        case kCsHost:
+        case kCsRecovery:
+        case kCsSideload:
+            return true;
+        default:
+            return false;
+    }
+}
 
-void print_packet(const char *label, apacket *p);
+void print_packet(const char* label, apacket* p);
 
-// These use the system (v)fprintf, not the adb prefixed ones defined in sysdeps.h, so they
-// shouldn't be tagged with ADB_FORMAT_ARCHETYPE.
 void fatal(const char* fmt, ...) __attribute__((noreturn, format(__printf__, 1, 2)));
 void fatal_errno(const char* fmt, ...) __attribute__((noreturn, format(__printf__, 1, 2)));
 
-void handle_packet(apacket *p, atransport *t);
+void handle_packet(apacket* p, atransport* t);
 
 int launch_server(const std::string& socket_spec);
 int adb_server_main(int is_daemon, const std::string& socket_spec, int ack_reply_fd);
 
 /* initialize a transport object's func pointers and state */
-#if ADB_HOST
-int get_available_local_transport_index();
-#endif
-int  init_socket_transport(atransport *t, int s, int port, int local);
+int init_socket_transport(atransport* t, unique_fd s, int port, int local);
 void init_usb_transport(atransport* t, usb_handle* usb);
 
 std::string getEmulatorSerialString(int console_port);
@@ -147,84 +142,81 @@
 atransport* find_emulator_transport_by_console_port(int console_port);
 #endif
 
-int service_to_fd(const char* name, const atransport* transport);
+int service_to_fd(const char* name, atransport* transport);
+#if !ADB_HOST
+unique_fd daemon_service_to_fd(const char* name, atransport* transport);
+#endif
+
 #if ADB_HOST
-asocket *host_service_to_socket(const char*  name, const char *serial);
+asocket* host_service_to_socket(const char* name, const char* serial, TransportId transport_id);
 #endif
 
 #if !ADB_HOST
-int       init_jdwp(void);
-asocket*  create_jdwp_service_socket();
-asocket*  create_jdwp_tracker_service_socket();
-int       create_jdwp_connection_fd(int  jdwp_pid);
+int init_jdwp(void);
+asocket* create_jdwp_service_socket();
+asocket* create_jdwp_tracker_service_socket();
+unique_fd create_jdwp_connection_fd(int jdwp_pid);
 #endif
 
-int handle_forward_request(const char* service, TransportType type, const char* serial, int reply_fd);
-
-#if !ADB_HOST
-void framebuffer_service(int fd, void *cookie);
-void set_verity_enabled_state_service(int fd, void* cookie);
-#endif
+bool handle_forward_request(const char* service, atransport* transport, int reply_fd);
+bool handle_forward_request(const char* service,
+                            std::function<atransport*(std::string* error)> transport_acquirer,
+                            int reply_fd);
 
 /* packet allocator */
-apacket *get_apacket(void);
-void put_apacket(apacket *p);
+apacket* get_apacket(void);
+void put_apacket(apacket* p);
 
 // Define it if you want to dump packets.
 #define DEBUG_PACKETS 0
 
 #if !DEBUG_PACKETS
-#define print_packet(tag,p) do {} while (0)
+#define print_packet(tag, p) \
+    do {                     \
+    } while (0)
 #endif
 
 #if ADB_HOST_ON_TARGET
 /* adb and adbd are coexisting on the target, so use 5038 for adb
  * to avoid conflicting with adbd's usage of 5037
  */
-#  define DEFAULT_ADB_PORT 5038
+#define DEFAULT_ADB_PORT 5038
 #else
-#  define DEFAULT_ADB_PORT 5037
+#define DEFAULT_ADB_PORT 5037
 #endif
 
 #define DEFAULT_ADB_LOCAL_TRANSPORT_PORT 5555
 
-#define ADB_CLASS              0xff
-#define ADB_SUBCLASS           0x42
-#define ADB_PROTOCOL           0x1
-
+#define ADB_CLASS 0xff
+#define ADB_SUBCLASS 0x42
+#define ADB_PROTOCOL 0x1
 
 void local_init(int port);
 bool local_connect(int port);
-int  local_connect_arbitrary_ports(int console_port, int adb_port, std::string* error);
+int local_connect_arbitrary_ports(int console_port, int adb_port, std::string* error);
 
-ConnectionState connection_state(atransport *t);
+ConnectionState connection_state(atransport* t);
 
 extern const char* adb_device_banner;
 
-#if !ADB_HOST
-extern int SHELL_EXIT_NOTIFY_FD;
-#endif // !ADB_HOST
-
-#define CHUNK_SIZE (64*1024)
+#define CHUNK_SIZE (64 * 1024)
 
 #if !ADB_HOST
-#define USB_FFS_ADB_PATH  "/dev/usb-ffs/adb/"
-#define USB_FFS_ADB_EP(x) USB_FFS_ADB_PATH#x
+#define USB_FFS_ADB_PATH "/dev/usb-ffs/adb/"
+#define USB_FFS_ADB_EP(x) USB_FFS_ADB_PATH #x
 
-#define USB_FFS_ADB_EP0   USB_FFS_ADB_EP(ep0)
-#define USB_FFS_ADB_OUT   USB_FFS_ADB_EP(ep1)
-#define USB_FFS_ADB_IN    USB_FFS_ADB_EP(ep2)
+#define USB_FFS_ADB_EP0 USB_FFS_ADB_EP(ep0)
+#define USB_FFS_ADB_OUT USB_FFS_ADB_EP(ep1)
+#define USB_FFS_ADB_IN USB_FFS_ADB_EP(ep2)
 #endif
 
-int handle_host_request(const char* service, TransportType type, const char* serial, int reply_fd, asocket *s);
+int handle_host_request(const char* service, TransportType type, const char* serial,
+                        TransportId transport_id, int reply_fd, asocket* s);
 
-void handle_online(atransport *t);
-void handle_offline(atransport *t);
+void handle_online(atransport* t);
+void handle_offline(atransport* t);
 
-void send_connect(atransport *t);
-#if ADB_HOST
-void SendConnectOnHost(atransport* t);
-#endif
+void send_connect(atransport* t);
 
 void parse_banner(const std::string&, atransport* t);
 
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_auth_host.cpp b/adb/adb_auth_host.cpp
deleted file mode 100644
index 365bf77..0000000
--- a/adb/adb_auth_host.cpp
+++ /dev/null
@@ -1,481 +0,0 @@
-/*
- * Copyright (C) 2012 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.
- */
-
-#define TRACE_TAG AUTH
-
-#include <dirent.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#if defined(__linux__)
-#include <sys/inotify.h>
-#endif
-
-#include <map>
-#include <mutex>
-#include <set>
-#include <string>
-
-#include <android-base/errors.h>
-#include <android-base/file.h>
-#include <android-base/stringprintf.h>
-#include <android-base/strings.h>
-#include <crypto_utils/android_pubkey.h>
-#include <openssl/base64.h>
-#include <openssl/evp.h>
-#include <openssl/objects.h>
-#include <openssl/pem.h>
-#include <openssl/rsa.h>
-#include <openssl/sha.h>
-
-#include "adb.h"
-#include "adb_auth.h"
-#include "adb_utils.h"
-#include "sysdeps.h"
-#include "transport.h"
-
-static std::mutex& g_keys_mutex = *new std::mutex;
-static std::map<std::string, std::shared_ptr<RSA>>& g_keys =
-    *new std::map<std::string, std::shared_ptr<RSA>>;
-static std::map<int, std::string>& g_monitored_paths = *new std::map<int, std::string>;
-
-static std::string get_user_info() {
-    LOG(INFO) << "get_user_info...";
-
-    std::string hostname;
-    if (getenv("HOSTNAME")) hostname = getenv("HOSTNAME");
-#if !defined(_WIN32)
-    char buf[64];
-    if (hostname.empty() && gethostname(buf, sizeof(buf)) != -1) hostname = buf;
-#endif
-    if (hostname.empty()) hostname = "unknown";
-
-    std::string username;
-    if (getenv("LOGNAME")) username = getenv("LOGNAME");
-#if !defined _WIN32 && !defined ADB_HOST_ON_TARGET
-    if (username.empty() && getlogin()) username = getlogin();
-#endif
-    if (username.empty()) hostname = "unknown";
-
-    return " " + username + "@" + hostname;
-}
-
-static bool write_public_keyfile(RSA* private_key, const std::string& private_key_path) {
-    LOG(INFO) << "write_public_keyfile...";
-
-    uint8_t binary_key_data[ANDROID_PUBKEY_ENCODED_SIZE];
-    if (!android_pubkey_encode(private_key, binary_key_data, sizeof(binary_key_data))) {
-        LOG(ERROR) << "Failed to convert to public key";
-        return false;
-    }
-
-    size_t expected_length;
-    if (!EVP_EncodedLength(&expected_length, sizeof(binary_key_data))) {
-        LOG(ERROR) << "Public key too large to base64 encode";
-        return false;
-    }
-
-    std::string content;
-    content.resize(expected_length);
-    size_t actual_length = EVP_EncodeBlock(reinterpret_cast<uint8_t*>(&content[0]), binary_key_data,
-                                           sizeof(binary_key_data));
-    content.resize(actual_length);
-
-    content += get_user_info();
-
-    std::string path(private_key_path + ".pub");
-    if (!android::base::WriteStringToFile(content, path)) {
-        PLOG(ERROR) << "Failed to write public key to '" << path << "'";
-        return false;
-    }
-
-    return true;
-}
-
-static int generate_key(const std::string& file) {
-    LOG(INFO) << "generate_key(" << file << ")...";
-
-    mode_t old_mask;
-    FILE *f = NULL;
-    int ret = 0;
-
-    EVP_PKEY* pkey = EVP_PKEY_new();
-    BIGNUM* exponent = BN_new();
-    RSA* rsa = RSA_new();
-    if (!pkey || !exponent || !rsa) {
-        LOG(ERROR) << "Failed to allocate key";
-        goto out;
-    }
-
-    BN_set_word(exponent, RSA_F4);
-    RSA_generate_key_ex(rsa, 2048, exponent, NULL);
-    EVP_PKEY_set1_RSA(pkey, rsa);
-
-    old_mask = umask(077);
-
-    f = fopen(file.c_str(), "w");
-    if (!f) {
-        PLOG(ERROR) << "Failed to open " << file;
-        umask(old_mask);
-        goto out;
-    }
-
-    umask(old_mask);
-
-    if (!PEM_write_PrivateKey(f, pkey, NULL, NULL, 0, NULL, NULL)) {
-        D("Failed to write key");
-        goto out;
-    }
-
-    if (!write_public_keyfile(rsa, file)) {
-        D("Failed to write public key");
-        goto out;
-    }
-
-    ret = 1;
-
-out:
-    if (f) fclose(f);
-    EVP_PKEY_free(pkey);
-    RSA_free(rsa);
-    BN_free(exponent);
-    return ret;
-}
-
-static std::string hash_key(RSA* key) {
-    unsigned char* pubkey = nullptr;
-    int len = i2d_RSA_PUBKEY(key, &pubkey);
-    if (len < 0) {
-        LOG(ERROR) << "failed to encode RSA public key";
-        return std::string();
-    }
-
-    std::string result;
-    result.resize(SHA256_DIGEST_LENGTH);
-    SHA256(pubkey, len, reinterpret_cast<unsigned char*>(&result[0]));
-    OPENSSL_free(pubkey);
-    return result;
-}
-
-static bool read_key_file(const std::string& file) {
-    LOG(INFO) << "read_key_file '" << file << "'...";
-
-    std::unique_ptr<FILE, decltype(&fclose)> fp(fopen(file.c_str(), "r"), fclose);
-    if (!fp) {
-        PLOG(ERROR) << "Failed to open '" << file << "'";
-        return false;
-    }
-
-    RSA* key = RSA_new();
-    if (!PEM_read_RSAPrivateKey(fp.get(), &key, nullptr, nullptr)) {
-        LOG(ERROR) << "Failed to read key";
-        RSA_free(key);
-        return false;
-    }
-
-    std::lock_guard<std::mutex> lock(g_keys_mutex);
-    std::string fingerprint = hash_key(key);
-    if (g_keys.find(fingerprint) != g_keys.end()) {
-        LOG(INFO) << "ignoring already-loaded key: " << file;
-        RSA_free(key);
-    } else {
-        g_keys[fingerprint] = std::shared_ptr<RSA>(key, RSA_free);
-    }
-
-    return true;
-}
-
-static bool read_keys(const std::string& path, bool allow_dir = true) {
-    LOG(INFO) << "read_keys '" << path << "'...";
-
-    struct stat st;
-    if (stat(path.c_str(), &st) != 0) {
-        PLOG(ERROR) << "failed to stat '" << path << "'";
-        return false;
-    }
-
-    if (S_ISREG(st.st_mode)) {
-        return read_key_file(path);
-    } else if (S_ISDIR(st.st_mode)) {
-        if (!allow_dir) {
-            // inotify isn't recursive. It would break expectations to load keys in nested
-            // directories but not monitor them for new keys.
-            LOG(WARNING) << "refusing to recurse into directory '" << path << "'";
-            return false;
-        }
-
-        std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(path.c_str()), closedir);
-        if (!dir) {
-            PLOG(ERROR) << "failed to open directory '" << path << "'";
-            return false;
-        }
-
-        bool result = false;
-        while (struct dirent* dent = readdir(dir.get())) {
-            std::string name = dent->d_name;
-
-            // We can't use dent->d_type here because it's not available on Windows.
-            if (name == "." || name == "..") {
-                continue;
-            }
-
-            if (!android::base::EndsWith(name, ".adb_key")) {
-                LOG(INFO) << "skipping non-adb_key '" << path << "/" << name << "'";
-                continue;
-            }
-
-            result |= read_key_file((path + OS_PATH_SEPARATOR + name));
-        }
-        return result;
-    }
-
-    LOG(ERROR) << "unexpected type for '" << path << "': 0x" << std::hex << st.st_mode;
-    return false;
-}
-
-static std::string get_user_key_path() {
-    return adb_get_android_dir_path() + OS_PATH_SEPARATOR + "adbkey";
-}
-
-static bool get_user_key() {
-    std::string path = get_user_key_path();
-    if (path.empty()) {
-        PLOG(ERROR) << "Error getting user key filename";
-        return false;
-    }
-
-    struct stat buf;
-    if (stat(path.c_str(), &buf) == -1) {
-        LOG(INFO) << "User key '" << path << "' does not exist...";
-        if (!generate_key(path)) {
-            LOG(ERROR) << "Failed to generate new key";
-            return false;
-        }
-    }
-
-    return read_key_file(path);
-}
-
-static std::set<std::string> get_vendor_keys() {
-    const char* adb_keys_path = getenv("ADB_VENDOR_KEYS");
-    if (adb_keys_path == nullptr) {
-        return std::set<std::string>();
-    }
-
-    std::set<std::string> result;
-    for (const auto& path : android::base::Split(adb_keys_path, ENV_PATH_SEPARATOR_STR)) {
-        result.emplace(path);
-    }
-    return result;
-}
-
-std::deque<std::shared_ptr<RSA>> adb_auth_get_private_keys() {
-    std::deque<std::shared_ptr<RSA>> result;
-
-    // Copy all the currently known keys.
-    std::lock_guard<std::mutex> lock(g_keys_mutex);
-    for (const auto& it : g_keys) {
-        result.push_back(it.second);
-    }
-
-    // Add a sentinel to the list. Our caller uses this to mean "out of private keys,
-    // but try using the public key" (the empty deque could otherwise mean this _or_
-    // that this function hasn't been called yet to request the keys).
-    result.push_back(nullptr);
-
-    return result;
-}
-
-static int adb_auth_sign(RSA* key, const char* token, size_t token_size, char* sig) {
-    if (token_size != TOKEN_SIZE) {
-        D("Unexpected token size %zd", token_size);
-        return 0;
-    }
-
-    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;
-    }
-
-    D("adb_auth_sign len=%d", len);
-    return (int)len;
-}
-
-std::string adb_auth_get_userkey() {
-    std::string path = get_user_key_path();
-    if (path.empty()) {
-        PLOG(ERROR) << "Error getting user key filename";
-        return "";
-    }
-    path += ".pub";
-
-    std::string content;
-    if (!android::base::ReadFileToString(path, &content)) {
-        PLOG(ERROR) << "Can't load '" << path << "'";
-        return "";
-    }
-    return content;
-}
-
-int adb_auth_keygen(const char* filename) {
-    return (generate_key(filename) == 0);
-}
-
-#if defined(__linux__)
-static void adb_auth_inotify_update(int fd, unsigned fd_event, void*) {
-    LOG(INFO) << "adb_auth_inotify_update called";
-    if (!(fd_event & FDE_READ)) {
-        return;
-    }
-
-    char buf[sizeof(struct inotify_event) + NAME_MAX + 1];
-    while (true) {
-        ssize_t rc = TEMP_FAILURE_RETRY(unix_read(fd, buf, sizeof(buf)));
-        if (rc == -1) {
-            if (errno == EAGAIN) {
-                LOG(INFO) << "done reading inotify fd";
-                break;
-            }
-            PLOG(FATAL) << "read of inotify event failed";
-        }
-
-        // The read potentially returned multiple events.
-        char* start = buf;
-        char* end = buf + rc;
-
-        while (start < end) {
-            inotify_event* event = reinterpret_cast<inotify_event*>(start);
-            auto root_it = g_monitored_paths.find(event->wd);
-            if (root_it == g_monitored_paths.end()) {
-                LOG(FATAL) << "observed inotify event for unmonitored path, wd = " << event->wd;
-            }
-
-            std::string path = root_it->second;
-            if (event->len > 0) {
-                path += '/';
-                path += event->name;
-            }
-
-            if (event->mask & (IN_CREATE | IN_MOVED_TO)) {
-                if (event->mask & IN_ISDIR) {
-                    LOG(INFO) << "ignoring new directory at '" << path << "'";
-                } else {
-                    LOG(INFO) << "observed new file at '" << path << "'";
-                    read_keys(path, false);
-                }
-            } else {
-                LOG(WARNING) << "unmonitored event for " << path << ": 0x" << std::hex
-                             << event->mask;
-            }
-
-            start += sizeof(struct inotify_event) + event->len;
-        }
-    }
-}
-
-static void adb_auth_inotify_init(const std::set<std::string>& paths) {
-    LOG(INFO) << "adb_auth_inotify_init...";
-
-    int infd = inotify_init1(IN_CLOEXEC | IN_NONBLOCK);
-    if (infd < 0) {
-        PLOG(ERROR) << "failed to create inotify fd";
-        return;
-    }
-
-    for (const std::string& path : paths) {
-        int wd = inotify_add_watch(infd, path.c_str(), IN_CREATE | IN_MOVED_TO);
-        if (wd < 0) {
-            PLOG(ERROR) << "failed to inotify_add_watch on path '" << path;
-            continue;
-        }
-
-        g_monitored_paths[wd] = path;
-        LOG(INFO) << "watch descriptor " << wd << " registered for " << path;
-    }
-
-    fdevent* event = fdevent_create(infd, adb_auth_inotify_update, nullptr);
-    fdevent_add(event, FDE_READ);
-}
-#endif
-
-void adb_auth_init() {
-    LOG(INFO) << "adb_auth_init...";
-
-    if (!get_user_key()) {
-        LOG(ERROR) << "Failed to get user key";
-        return;
-    }
-
-    const auto& key_paths = get_vendor_keys();
-
-#if defined(__linux__)
-    adb_auth_inotify_init(key_paths);
-#endif
-
-    for (const std::string& path : key_paths) {
-        read_keys(path.c_str());
-    }
-}
-
-static void send_auth_publickey(atransport* t) {
-    LOG(INFO) << "Calling send_auth_publickey";
-
-    std::string key = adb_auth_get_userkey();
-    if (key.empty()) {
-        D("Failed to get user public key");
-        return;
-    }
-
-    if (key.size() >= MAX_PAYLOAD_V1) {
-        D("User public key too large (%zu B)", key.size());
-        return;
-    }
-
-    apacket* p = get_apacket();
-    memcpy(p->data, key.c_str(), key.size() + 1);
-
-    p->msg.command = A_AUTH;
-    p->msg.arg0 = ADB_AUTH_RSAPUBLICKEY;
-
-    // adbd expects a null-terminated string.
-    p->msg.data_length = key.size() + 1;
-    send_packet(p, t);
-}
-
-void send_auth_response(const char* token, size_t token_size, atransport* t) {
-    std::shared_ptr<RSA> key = t->NextKey();
-    if (key == nullptr) {
-        // No more private keys to try, send the public key.
-        send_auth_publickey(t);
-        return;
-    }
-
-    LOG(INFO) << "Calling send_auth_response";
-    apacket* p = get_apacket();
-
-    int ret = adb_auth_sign(key.get(), token, token_size, p->data);
-    if (!ret) {
-        D("Error signing the token");
-        put_apacket(p);
-        return;
-    }
-
-    p->msg.command = A_AUTH;
-    p->msg.arg0 = ADB_AUTH_SIGNATURE;
-    p->msg.data_length = ret;
-    send_packet(p, t);
-}
diff --git a/adb/adb_client.cpp b/adb/adb_client.cpp
deleted file mode 100644
index e533a00..0000000
--- a/adb/adb_client.cpp
+++ /dev/null
@@ -1,317 +0,0 @@
-/*
- * Copyright (C) 2015 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.
- */
-
-#define TRACE_TAG ADB
-
-#include "sysdeps.h"
-#include "adb_client.h"
-
-#include <errno.h>
-#include <limits.h>
-#include <stdarg.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-
-#include <condition_variable>
-#include <mutex>
-#include <string>
-#include <thread>
-#include <vector>
-
-#include <android-base/stringprintf.h>
-#include <android-base/strings.h>
-#include <android-base/thread_annotations.h>
-#include <cutils/sockets.h>
-
-#include "adb_io.h"
-#include "adb_utils.h"
-#include "socket_spec.h"
-#include "sysdeps/chrono.h"
-
-static TransportType __adb_transport = kTransportAny;
-static const char* __adb_serial = NULL;
-
-static const char* __adb_server_socket_spec;
-
-void adb_set_transport(TransportType type, const char* serial) {
-    __adb_transport = type;
-    __adb_serial = serial;
-}
-
-void adb_set_socket_spec(const char* socket_spec) {
-    if (__adb_server_socket_spec) {
-        LOG(FATAL) << "attempted to reinitialize adb_server_socket_spec " << socket_spec << " (was " << __adb_server_socket_spec << ")";
-    }
-    __adb_server_socket_spec = socket_spec;
-}
-
-static int switch_socket_transport(int fd, std::string* error) {
-    std::string service;
-    if (__adb_serial) {
-        service += "host:transport:";
-        service += __adb_serial;
-    } else {
-        const char* transport_type = "???";
-        switch (__adb_transport) {
-          case kTransportUsb:
-            transport_type = "transport-usb";
-            break;
-          case kTransportLocal:
-            transport_type = "transport-local";
-            break;
-          case kTransportAny:
-            transport_type = "transport-any";
-            break;
-          case kTransportHost:
-            // no switch necessary
-            return 0;
-        }
-        service += "host:";
-        service += transport_type;
-    }
-
-    if (!SendProtocolString(fd, service)) {
-        *error = perror_str("write failure during connection");
-        adb_close(fd);
-        return -1;
-    }
-    D("Switch transport in progress");
-
-    if (!adb_status(fd, error)) {
-        adb_close(fd);
-        D("Switch transport failed: %s", error->c_str());
-        return -1;
-    }
-    D("Switch transport success");
-    return 0;
-}
-
-bool adb_status(int fd, std::string* error) {
-    char buf[5];
-    if (!ReadFdExactly(fd, buf, 4)) {
-        *error = perror_str("protocol fault (couldn't read status)");
-        return false;
-    }
-
-    if (!memcmp(buf, "OKAY", 4)) {
-        return true;
-    }
-
-    if (memcmp(buf, "FAIL", 4)) {
-        *error = android::base::StringPrintf("protocol fault (status %02x %02x %02x %02x?!)",
-                                             buf[0], buf[1], buf[2], buf[3]);
-        return false;
-    }
-
-    ReadProtocolString(fd, error, error);
-    return false;
-}
-
-static int _adb_connect(const std::string& service, std::string* error) {
-    D("_adb_connect: %s", service.c_str());
-    if (service.empty() || service.size() > MAX_PAYLOAD) {
-        *error = android::base::StringPrintf("bad service name length (%zd)",
-                                             service.size());
-        return -1;
-    }
-
-    std::string reason;
-    int fd = socket_spec_connect(__adb_server_socket_spec, &reason);
-    if (fd < 0) {
-        *error = android::base::StringPrintf("cannot connect to daemon at %s: %s",
-                                             __adb_server_socket_spec, reason.c_str());
-        return -2;
-    }
-
-    if (memcmp(&service[0], "host", 4) != 0 && switch_socket_transport(fd, error)) {
-        return -1;
-    }
-
-    if (!SendProtocolString(fd, service)) {
-        *error = perror_str("write failure during connection");
-        adb_close(fd);
-        return -1;
-    }
-
-    if (!adb_status(fd, error)) {
-        adb_close(fd);
-        return -1;
-    }
-
-    D("_adb_connect: return fd %d", fd);
-    return fd;
-}
-
-bool adb_kill_server() {
-    D("adb_kill_server");
-    std::string reason;
-    int fd = socket_spec_connect(__adb_server_socket_spec, &reason);
-    if (fd < 0) {
-        fprintf(stderr, "cannot connect to daemon at %s: %s\n", __adb_server_socket_spec,
-                reason.c_str());
-        return true;
-    }
-
-    if (!SendProtocolString(fd, "host:kill")) {
-        fprintf(stderr, "error: write failure during connection: %s\n", strerror(errno));
-        return false;
-    }
-
-    ReadOrderlyShutdown(fd);
-    return true;
-}
-
-int adb_connect(const std::string& service, std::string* error) {
-    // first query the adb server's version
-    int fd = _adb_connect("host:version", error);
-
-    D("adb_connect: service %s", service.c_str());
-    if (fd == -2 && !is_local_socket_spec(__adb_server_socket_spec)) {
-        fprintf(stderr, "* cannot start server on remote host\n");
-        // error is the original network connection error
-        return fd;
-    } else if (fd == -2) {
-        fprintf(stderr, "* daemon not running; starting now at %s\n", __adb_server_socket_spec);
-    start_server:
-        if (launch_server(__adb_server_socket_spec)) {
-            fprintf(stderr, "* failed to start daemon\n");
-            // launch_server() has already printed detailed error info, so just
-            // return a generic error string about the overall adb_connect()
-            // that the caller requested.
-            *error = "cannot connect to daemon";
-            return -1;
-        } else {
-            fprintf(stderr, "* daemon started successfully\n");
-        }
-        // The server will wait until it detects all of its connected devices before acking.
-        // Fall through to _adb_connect.
-    } else {
-        // If a server is already running, check its version matches.
-        int version = ADB_SERVER_VERSION - 1;
-
-        // If we have a file descriptor, then parse version result.
-        if (fd >= 0) {
-            std::string version_string;
-            if (!ReadProtocolString(fd, &version_string, error)) {
-                adb_close(fd);
-                return -1;
-            }
-
-            ReadOrderlyShutdown(fd);
-            adb_close(fd);
-
-            if (sscanf(&version_string[0], "%04x", &version) != 1) {
-                *error = android::base::StringPrintf("cannot parse version string: %s",
-                                                     version_string.c_str());
-                return -1;
-            }
-        } else {
-            // If fd is -1 check for "unknown host service" which would
-            // indicate a version of adb that does not support the
-            // version command, in which case we should fall-through to kill it.
-            if (*error != "unknown host service") {
-                return fd;
-            }
-        }
-
-        if (version != ADB_SERVER_VERSION) {
-            fprintf(stderr, "adb server version (%d) doesn't match this client (%d); killing...\n",
-                    version, ADB_SERVER_VERSION);
-            adb_kill_server();
-            goto start_server;
-        }
-    }
-
-    // if the command is start-server, we are done.
-    if (service == "host:start-server") {
-        return 0;
-    }
-
-    fd = _adb_connect(service, error);
-    if (fd == -1) {
-        D("_adb_connect error: %s", error->c_str());
-    } else if(fd == -2) {
-        fprintf(stderr, "* daemon still not running\n");
-    }
-    D("adb_connect: return fd %d", fd);
-
-    return fd;
-}
-
-
-bool adb_command(const std::string& service) {
-    std::string error;
-    int fd = adb_connect(service, &error);
-    if (fd < 0) {
-        fprintf(stderr, "error: %s\n", error.c_str());
-        return false;
-    }
-
-    if (!adb_status(fd, &error)) {
-        fprintf(stderr, "error: %s\n", error.c_str());
-        adb_close(fd);
-        return false;
-    }
-
-    ReadOrderlyShutdown(fd);
-    adb_close(fd);
-    return true;
-}
-
-bool adb_query(const std::string& service, std::string* result, std::string* error) {
-    D("adb_query: %s", service.c_str());
-    int fd = adb_connect(service, error);
-    if (fd < 0) {
-        return false;
-    }
-
-    result->clear();
-    if (!ReadProtocolString(fd, result, error)) {
-        adb_close(fd);
-        return false;
-    }
-
-    ReadOrderlyShutdown(fd);
-    adb_close(fd);
-    return true;
-}
-
-std::string format_host_command(const char* command, TransportType type, const char* serial) {
-    if (serial) {
-        return android::base::StringPrintf("host-serial:%s:%s", serial, command);
-    }
-
-    const char* prefix = "host";
-    if (type == kTransportUsb) {
-        prefix = "host-usb";
-    } else if (type == kTransportLocal) {
-        prefix = "host-local";
-    }
-    return android::base::StringPrintf("%s:%s", prefix, command);
-}
-
-bool adb_get_feature_set(FeatureSet* feature_set, std::string* error) {
-    std::string result;
-    if (adb_query(format_host_command("features", __adb_transport, __adb_serial), &result, error)) {
-        *feature_set = StringToFeatureSet(result);
-        return true;
-    }
-    feature_set->clear();
-    return false;
-}
diff --git a/adb/adb_client.h b/adb/adb_client.h
deleted file mode 100644
index fabec00..0000000
--- a/adb/adb_client.h
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright (C) 2015 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 _ADB_CLIENT_H_
-#define _ADB_CLIENT_H_
-
-#include "adb.h"
-#include "sysdeps.h"
-#include "transport.h"
-
-#include <string>
-
-// Connect to adb, connect to the named service, and return a valid fd for
-// interacting with that service upon success or a negative number on failure.
-int adb_connect(const std::string& service, std::string* _Nonnull error);
-
-// Kill the currently running adb server, if it exists.
-bool adb_kill_server();
-
-// Connect to adb, connect to the named service, returns true if the connection
-// succeeded AND the service returned OKAY. Outputs any returned error otherwise.
-bool adb_command(const std::string& service);
-
-// Connects to the named adb service and fills 'result' with the response.
-// Returns true on success; returns false and fills 'error' on failure.
-bool adb_query(const std::string& service, std::string* _Nonnull result,
-               std::string* _Nonnull error);
-
-// Set the preferred transport to connect to.
-void adb_set_transport(TransportType type, const char* _Nullable serial);
-
-// Set the socket specification for the adb server.
-// This function can only be called once, and the argument must live to the end of the process.
-void adb_set_socket_spec(const char* _Nonnull socket_spec);
-
-// Send commands to the current emulator instance. Will fail if there is not
-// exactly one emulator connected (or if you use -s <serial> with a <serial>
-// that does not designate an emulator).
-int adb_send_emulator_command(int argc, const char* _Nonnull* _Nonnull argv,
-                              const char* _Nullable serial);
-
-// Reads a standard adb status response (OKAY|FAIL) and returns true in the
-// event of OKAY, false in the event of FAIL or protocol error.
-bool adb_status(int fd, std::string* _Nonnull error);
-
-// Create a host command corresponding to selected transport type/serial.
-std::string format_host_command(const char* _Nonnull command, TransportType type,
-                                const char* _Nullable serial);
-
-// Get the feature set of the current preferred transport.
-bool adb_get_feature_set(FeatureSet* _Nonnull feature_set, std::string* _Nonnull error);
-
-#endif
diff --git a/adb/adb_io.cpp b/adb/adb_io.cpp
index 38e3116..6cc274b 100644
--- a/adb/adb_io.cpp
+++ b/adb/adb_io.cpp
@@ -49,7 +49,7 @@
     }
     buf[4] = 0;
 
-    unsigned long len = strtoul(buf, 0, 16);
+    unsigned long len = strtoul(buf, nullptr, 16);
     s->resize(len, '\0');
     if (!ReadFdExactly(fd, &(*s)[0], len)) {
         *error = perror_str("protocol fault (couldn't read status message)");
diff --git a/adb/adb_listeners.cpp b/adb/adb_listeners.cpp
index 30cb29b..f4a92e3 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
@@ -41,7 +42,7 @@
     alistener(const std::string& _local_name, const std::string& _connect_to);
     ~alistener();
 
-    fdevent fde;
+    fdevent* fde = nullptr;
     int fd = -1;
 
     std::string local_name;
@@ -59,7 +60,7 @@
 
 alistener::~alistener() {
     // Closes the corresponding fd.
-    fdevent_remove(&fde);
+    fdevent_destroy(fde);
 
     if (transport) {
         transport->RemoveDisconnect(&disconnect);
@@ -135,9 +136,10 @@
         }
         //  <device-serial> " " <local-name> " " <remote-name> "\n"
         // Entries from "adb reverse" have no serial.
-        android::base::StringAppendF(&result, "%s %s %s\n",
-                                     l->transport->serial ? l->transport->serial : "(reverse)",
-                                     l->local_name.c_str(), l->connect_to.c_str());
+        android::base::StringAppendF(
+                &result, "%s %s %s\n",
+                !l->transport->serial.empty() ? l->transport->serial.c_str() : "(reverse)",
+                l->local_name.c_str(), l->connect_to.c_str());
     }
     return result;
 }
@@ -172,7 +174,7 @@
     auto pred = [](const std::unique_ptr<alistener>& listener) {
         return listener->local_name == "*smartsocket*";
     };
-    listener_list.erase(std::remove_if(listener_list.begin(), listener_list.end(), pred));
+    listener_list.remove_if(pred);
 }
 
 InstallStatus install_listener(const std::string& local_name, const char* connect_to,
@@ -203,7 +205,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);
@@ -221,11 +223,11 @@
 
     close_on_exec(listener->fd);
     if (listener->connect_to == "*smartsocket*") {
-        fdevent_install(&listener->fde, listener->fd, ss_listener_event_func, listener.get());
+        listener->fde = fdevent_create(listener->fd, ss_listener_event_func, listener.get());
     } else {
-        fdevent_install(&listener->fde, listener->fd, listener_event_func, listener.get());
+        listener->fde = fdevent_create(listener->fd, listener_event_func, listener.get());
     }
-    fdevent_set(&listener->fde, FDE_READ);
+    fdevent_set(listener->fde, FDE_READ);
 
     listener->transport = transport;
 
diff --git a/adb/adb_trace.cpp b/adb/adb_trace.cpp
index eac923d..a8ec5fb 100644
--- a/adb/adb_trace.cpp
+++ b/adb/adb_trace.cpp
@@ -42,7 +42,11 @@
                const char* message) {
     android::base::StderrLogger(id, severity, tag, file, line, message);
 #if !ADB_HOST
-    gLogdLogger(id, severity, tag, file, line, message);
+    // Only print logs of INFO or higher to logcat, so that `adb logcat` with adbd tracing on
+    // doesn't result in exponential logging.
+    if (severity >= android::base::INFO) {
+        gLogdLogger(id, severity, tag, file, line, message);
+    }
 #endif
 }
 
@@ -137,11 +141,15 @@
             // -1 is used for the special values "1" and "all" that enable all
             // tracing.
             adb_trace_mask = ~0;
-            return;
+            break;
         } else {
             adb_trace_mask |= 1 << flag->second;
         }
     }
+
+    if (adb_trace_mask != 0) {
+        android::base::SetMinimumLogSeverity(android::base::VERBOSE);
+    }
 }
 
 void adb_trace_init(char** argv) {
diff --git a/adb/adb_trace.h b/adb/adb_trace.h
index fc6560c..1d2c8c7 100644
--- a/adb/adb_trace.h
+++ b/adb/adb_trace.h
@@ -43,11 +43,11 @@
 #define VLOG_IS_ON(TAG) \
     ((adb_trace_mask & (1 << (TAG))) != 0)
 
-#define VLOG(TAG)         \
+#define VLOG(TAG)                 \
     if (LIKELY(!VLOG_IS_ON(TAG))) \
-        ;                 \
-    else                  \
-        LOG(INFO)
+        ;                         \
+    else                          \
+        LOG(DEBUG)
 
 // You must define TRACE_TAG before using this macro.
 #define D(...) \
diff --git a/adb/adb_unique_fd.cpp b/adb/adb_unique_fd.cpp
new file mode 100644
index 0000000..2079be1
--- /dev/null
+++ b/adb/adb_unique_fd.cpp
@@ -0,0 +1,65 @@
+/*
+ * 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 "adb_unique_fd.h"
+
+#include <errno.h>
+#include <unistd.h>
+
+#include "sysdeps.h"
+
+#if !defined(_WIN32)
+bool Pipe(unique_fd* read, unique_fd* write, int flags) {
+    int pipefd[2];
+#if !defined(__APPLE__)
+    if (pipe2(pipefd, flags) != 0) {
+        return false;
+    }
+#else
+    // Darwin doesn't have pipe2. Implement it ourselves.
+    if (flags != 0 && (flags & ~(O_CLOEXEC | O_NONBLOCK)) != 0) {
+        errno = EINVAL;
+        return false;
+    }
+
+    if (pipe(pipefd) != 0) {
+        return false;
+    }
+
+    if (flags & O_CLOEXEC) {
+        if (fcntl(pipefd[0], F_SETFD, FD_CLOEXEC) != 0 ||
+            fcntl(pipefd[1], F_SETFD, FD_CLOEXEC) != 0) {
+            adb_close(pipefd[0]);
+            adb_close(pipefd[1]);
+            return false;
+        }
+    }
+
+    if (flags & O_NONBLOCK) {
+        if (fcntl(pipefd[0], F_SETFL, O_NONBLOCK) != 0 ||
+            fcntl(pipefd[1], F_SETFL, O_NONBLOCK) != 0) {
+            adb_close(pipefd[0]);
+            adb_close(pipefd[1]);
+            return false;
+        }
+    }
+#endif
+
+    read->reset(pipefd[0]);
+    write->reset(pipefd[1]);
+    return true;
+}
+#endif
diff --git a/adb/adb_unique_fd.h b/adb/adb_unique_fd.h
index 34c1bbc..bad501a 100644
--- a/adb/adb_unique_fd.h
+++ b/adb/adb_unique_fd.h
@@ -16,6 +16,9 @@
 
 #pragma once
 
+#include <errno.h>
+#include <unistd.h>
+
 #include <android-base/unique_fd.h>
 
 // Helper to automatically close an FD when it goes out of scope.
@@ -24,3 +27,11 @@
 };
 
 using unique_fd = android::base::unique_fd_impl<AdbCloser>;
+
+#if !defined(_WIN32)
+bool Pipe(unique_fd* read, unique_fd* write, int flags = 0);
+#endif
+
+template <typename T>
+int adb_close(const android::base::unique_fd_impl<T>&)
+        __attribute__((__unavailable__("adb_close called on unique_fd")));
diff --git a/adb/adb_utils.cpp b/adb/adb_utils.cpp
index b236fb3..0c3327f 100644
--- a/adb/adb_utils.cpp
+++ b/adb/adb_utils.cpp
@@ -79,22 +79,24 @@
 }
 
 std::string escape_arg(const std::string& s) {
-  std::string result = s;
-
   // Escape any ' in the string (before we single-quote the whole thing).
   // The correct way to do this for the shell is to replace ' with '\'' --- that is,
   // close the existing single-quoted string, escape a single single-quote, and start
   // a new single-quoted string. Like the C preprocessor, the shell will concatenate
   // these pieces into one string.
-  for (size_t i = 0; i < s.size(); ++i) {
-    if (s[i] == '\'') {
-      result.insert(i, "'\\'");
-      i += 2;
-    }
+
+  std::string result;
+  result.push_back('\'');
+
+  size_t base = 0;
+  while (true) {
+    size_t found = s.find('\'', base);
+    result.append(s, base, found - base);
+    if (found == s.npos) break;
+    result.append("'\\''");
+    base = found + 1;
   }
 
-  // Prefix and suffix the whole string with '.
-  result.insert(result.begin(), '\'');
   result.push_back('\'');
   return result;
 }
diff --git a/adb/adb_utils_test.cpp b/adb/adb_utils_test.cpp
index e1b6287..341323f 100644
--- a/adb/adb_utils_test.cpp
+++ b/adb/adb_utils_test.cpp
@@ -82,30 +82,38 @@
 #endif
 
 TEST(adb_utils, escape_arg) {
-  ASSERT_EQ(R"('')", escape_arg(""));
+  EXPECT_EQ(R"('')", escape_arg(""));
 
-  ASSERT_EQ(R"('abc')", escape_arg("abc"));
+  EXPECT_EQ(R"('abc')", escape_arg("abc"));
 
-  ASSERT_EQ(R"(' abc')", escape_arg(" abc"));
-  ASSERT_EQ(R"(''\''abc')", escape_arg("'abc"));
-  ASSERT_EQ(R"('"abc')", escape_arg("\"abc"));
-  ASSERT_EQ(R"('\abc')", escape_arg("\\abc"));
-  ASSERT_EQ(R"('(abc')", escape_arg("(abc"));
-  ASSERT_EQ(R"(')abc')", escape_arg(")abc"));
+  auto wrap = [](const std::string& x) { return '\'' + x + '\''; };
+  const std::string q = R"('\'')";
+  EXPECT_EQ(wrap(q), escape_arg("'"));
+  EXPECT_EQ(wrap(q + q), escape_arg("''"));
+  EXPECT_EQ(wrap(q + "abc" + q), escape_arg("'abc'"));
+  EXPECT_EQ(wrap(q + "abc"), escape_arg("'abc"));
+  EXPECT_EQ(wrap("abc" + q), escape_arg("abc'"));
+  EXPECT_EQ(wrap("abc" + q + "def"), escape_arg("abc'def"));
+  EXPECT_EQ(wrap("a" + q + "b" + q + "c"), escape_arg("a'b'c"));
+  EXPECT_EQ(wrap("a" + q + "bcde" + q + "f"), escape_arg("a'bcde'f"));
 
-  ASSERT_EQ(R"('abc abc')", escape_arg("abc abc"));
-  ASSERT_EQ(R"('abc'\''abc')", escape_arg("abc'abc"));
-  ASSERT_EQ(R"('abc"abc')", escape_arg("abc\"abc"));
-  ASSERT_EQ(R"('abc\abc')", escape_arg("abc\\abc"));
-  ASSERT_EQ(R"('abc(abc')", escape_arg("abc(abc"));
-  ASSERT_EQ(R"('abc)abc')", escape_arg("abc)abc"));
+  EXPECT_EQ(R"(' abc')", escape_arg(" abc"));
+  EXPECT_EQ(R"('"abc')", escape_arg("\"abc"));
+  EXPECT_EQ(R"('\abc')", escape_arg("\\abc"));
+  EXPECT_EQ(R"('(abc')", escape_arg("(abc"));
+  EXPECT_EQ(R"(')abc')", escape_arg(")abc"));
 
-  ASSERT_EQ(R"('abc ')", escape_arg("abc "));
-  ASSERT_EQ(R"('abc'\''')", escape_arg("abc'"));
-  ASSERT_EQ(R"('abc"')", escape_arg("abc\""));
-  ASSERT_EQ(R"('abc\')", escape_arg("abc\\"));
-  ASSERT_EQ(R"('abc(')", escape_arg("abc("));
-  ASSERT_EQ(R"('abc)')", escape_arg("abc)"));
+  EXPECT_EQ(R"('abc abc')", escape_arg("abc abc"));
+  EXPECT_EQ(R"('abc"abc')", escape_arg("abc\"abc"));
+  EXPECT_EQ(R"('abc\abc')", escape_arg("abc\\abc"));
+  EXPECT_EQ(R"('abc(abc')", escape_arg("abc(abc"));
+  EXPECT_EQ(R"('abc)abc')", escape_arg("abc)abc"));
+
+  EXPECT_EQ(R"('abc ')", escape_arg("abc "));
+  EXPECT_EQ(R"('abc"')", escape_arg("abc\""));
+  EXPECT_EQ(R"('abc\')", escape_arg("abc\\"));
+  EXPECT_EQ(R"('abc(')", escape_arg("abc("));
+  EXPECT_EQ(R"('abc)')", escape_arg("abc)"));
 }
 
 void test_mkdirs(const std::string& basepath) {
diff --git a/adb/adbd_auth.cpp b/adb/adbd_auth.cpp
deleted file mode 100644
index b5f87be..0000000
--- a/adb/adbd_auth.cpp
+++ /dev/null
@@ -1,224 +0,0 @@
-/*
- * Copyright (C) 2012 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.
- */
-
-#define TRACE_TAG AUTH
-
-#include "adb.h"
-#include "adb_auth.h"
-#include "fdevent.h"
-#include "sysdeps.h"
-#include "transport.h"
-
-#include <resolv.h>
-#include <stdio.h>
-#include <string.h>
-
-#include <memory>
-
-#include <android-base/file.h>
-#include <android-base/strings.h>
-#include <crypto_utils/android_pubkey.h>
-#include <openssl/obj_mac.h>
-#include <openssl/rsa.h>
-#include <openssl/sha.h>
-
-static fdevent listener_fde;
-static fdevent framework_fde;
-static int framework_fd = -1;
-
-static void usb_disconnected(void* unused, atransport* t);
-static struct adisconnect usb_disconnect = { usb_disconnected, nullptr};
-static atransport* usb_transport;
-static bool needs_retry = false;
-
-bool auth_required = true;
-
-bool adbd_auth_verify(const char* token, size_t token_size, const char* sig, int sig_len) {
-    static constexpr const char* key_paths[] = { "/adb_keys", "/data/misc/adb/adb_keys", nullptr };
-
-    for (const auto& path : key_paths) {
-        if (access(path, R_OK) == 0) {
-            LOG(INFO) << "Loading keys from " << path;
-
-            std::string content;
-            if (!android::base::ReadFileToString(path, &content)) {
-                PLOG(ERROR) << "Couldn't read " << path;
-                continue;
-            }
-
-            for (const auto& line : android::base::Split(content, "\n")) {
-                // TODO: do we really have to support both ' ' and '\t'?
-                char* sep = strpbrk(const_cast<char*>(line.c_str()), " \t");
-                if (sep) *sep = '\0';
-
-                // b64_pton requires one additional byte in the target buffer for
-                // decoding to succeed. See http://b/28035006 for details.
-                uint8_t keybuf[ANDROID_PUBKEY_ENCODED_SIZE + 1];
-                if (__b64_pton(line.c_str(), keybuf, sizeof(keybuf)) != ANDROID_PUBKEY_ENCODED_SIZE) {
-                    LOG(ERROR) << "Invalid base64 key " << line.c_str() << " in " << path;
-                    continue;
-                }
-
-                RSA* key = nullptr;
-                if (!android_pubkey_decode(keybuf, ANDROID_PUBKEY_ENCODED_SIZE, &key)) {
-                    LOG(ERROR) << "Failed to parse key " << line.c_str() << " in " << path;
-                    continue;
-                }
-
-                bool verified =
-                    (RSA_verify(NID_sha1, reinterpret_cast<const uint8_t*>(token), token_size,
-                                reinterpret_cast<const uint8_t*>(sig), sig_len, key) == 1);
-                RSA_free(key);
-                if (verified) return true;
-            }
-        }
-    }
-    return false;
-}
-
-static bool adbd_auth_generate_token(void* token, size_t token_size) {
-    FILE* fp = fopen("/dev/urandom", "re");
-    if (!fp) return false;
-    bool okay = (fread(token, token_size, 1, fp) == 1);
-    fclose(fp);
-    return okay;
-}
-
-static void usb_disconnected(void* unused, atransport* t) {
-    LOG(INFO) << "USB disconnect";
-    usb_transport = NULL;
-    needs_retry = false;
-}
-
-static void framework_disconnected() {
-    LOG(INFO) << "Framework disconnect";
-    fdevent_remove(&framework_fde);
-    framework_fd = -1;
-}
-
-static void adbd_auth_event(int fd, unsigned events, void*) {
-    if (events & FDE_READ) {
-        char response[2];
-        int ret = unix_read(fd, response, sizeof(response));
-        if (ret <= 0) {
-            framework_disconnected();
-        } else if (ret == 2 && response[0] == 'O' && response[1] == 'K') {
-            if (usb_transport) {
-                adbd_auth_verified(usb_transport);
-            }
-        }
-    }
-}
-
-void adbd_auth_confirm_key(const char* key, size_t len, atransport* t) {
-    if (!usb_transport) {
-        usb_transport = t;
-        t->AddDisconnect(&usb_disconnect);
-    }
-
-    if (framework_fd < 0) {
-        LOG(ERROR) << "Client not connected";
-        needs_retry = true;
-        return;
-    }
-
-    if (key[len - 1] != '\0') {
-        LOG(ERROR) << "Key must be a null-terminated string";
-        return;
-    }
-
-    char msg[MAX_PAYLOAD_V1];
-    int msg_len = snprintf(msg, sizeof(msg), "PK%s", key);
-    if (msg_len >= static_cast<int>(sizeof(msg))) {
-        LOG(ERROR) << "Key too long (" << msg_len << ")";
-        return;
-    }
-    LOG(DEBUG) << "Sending '" << msg << "'";
-
-    if (unix_write(framework_fd, msg, msg_len) == -1) {
-        PLOG(ERROR) << "Failed to write PK";
-        return;
-    }
-}
-
-static void adbd_auth_listener(int fd, unsigned events, void* data) {
-    int s = adb_socket_accept(fd, nullptr, nullptr);
-    if (s < 0) {
-        PLOG(ERROR) << "Failed to accept";
-        return;
-    }
-
-    if (framework_fd >= 0) {
-        LOG(WARNING) << "adb received framework auth socket connection again";
-        framework_disconnected();
-    }
-
-    framework_fd = s;
-    fdevent_install(&framework_fde, framework_fd, adbd_auth_event, nullptr);
-    fdevent_add(&framework_fde, FDE_READ);
-
-    if (needs_retry) {
-        needs_retry = false;
-        send_auth_request(usb_transport);
-    }
-}
-
-void adbd_cloexec_auth_socket() {
-    int fd = android_get_control_socket("adbd");
-    if (fd == -1) {
-        PLOG(ERROR) << "Failed to get adbd socket";
-        return;
-    }
-    fcntl(fd, F_SETFD, FD_CLOEXEC);
-}
-
-void adbd_auth_init(void) {
-    int fd = android_get_control_socket("adbd");
-    if (fd == -1) {
-        PLOG(ERROR) << "Failed to get adbd socket";
-        return;
-    }
-
-    if (listen(fd, 4) == -1) {
-        PLOG(ERROR) << "Failed to listen on '" << fd << "'";
-        return;
-    }
-
-    fdevent_install(&listener_fde, fd, adbd_auth_listener, NULL);
-    fdevent_add(&listener_fde, FDE_READ);
-}
-
-void send_auth_request(atransport* t) {
-    LOG(INFO) << "Calling send_auth_request...";
-
-    if (!adbd_auth_generate_token(t->token, sizeof(t->token))) {
-        PLOG(ERROR) << "Error generating token";
-        return;
-    }
-
-    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);
-    send_packet(p, t);
-}
-
-void adbd_auth_verified(atransport *t)
-{
-    handle_online(t);
-    send_connect(t);
-}
diff --git a/adb/benchmark_device.py b/adb/benchmark_device.py
new file mode 100755
index 0000000..00c2315
--- /dev/null
+++ b/adb/benchmark_device.py
@@ -0,0 +1,120 @@
+#!/usr/bin/env python3
+#
+# 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.
+#
+
+import os
+import statistics
+import time
+
+import adb
+
+def lock_min(device):
+    device.shell_nocheck(["""
+        for x in /sys/devices/system/cpu/cpu?/cpufreq; do
+            echo userspace > $x/scaling_governor
+            cat $x/scaling_min_freq > $x/scaling_setspeed
+        done
+    """])
+
+def lock_max(device):
+    device.shell_nocheck(["""
+        for x in /sys/devices/system/cpu/cpu?/cpufreq; do
+            echo userspace > $x/scaling_governor
+            cat $x/scaling_max_freq > $x/scaling_setspeed
+        done
+    """])
+
+def unlock(device):
+    device.shell_nocheck(["""
+        for x in /sys/devices/system/cpu/cpu?/cpufreq; do
+            echo ondemand > $x/scaling_governor
+            echo sched > $x/scaling_governor
+            echo schedutil > $x/scaling_governor
+        done
+    """])
+
+def harmonic_mean(xs):
+    return 1.0 / statistics.mean([1.0 / x for x in xs])
+
+def analyze(name, speeds):
+    median = statistics.median(speeds)
+    mean = harmonic_mean(speeds)
+    stddev = statistics.stdev(speeds)
+    msg = "%s: %d runs: median %.2f MiB/s, mean %.2f MiB/s, stddev: %.2f MiB/s"
+    print(msg % (name, len(speeds), median, mean, stddev))
+
+def benchmark_push(device=None, file_size_mb=100):
+    if device == None:
+        device = adb.get_device()
+
+    lock_max(device)
+
+    remote_path = "/dev/null"
+    local_path = "/tmp/adb_benchmark_temp"
+
+    with open(local_path, "wb") as f:
+        f.truncate(file_size_mb * 1024 * 1024)
+
+    speeds = list()
+    for _ in range(0, 5):
+        begin = time.time()
+        device.push(local=local_path, remote=remote_path)
+        end = time.time()
+        speeds.append(file_size_mb / float(end - begin))
+
+    analyze("push %dMiB" % file_size_mb, speeds)
+
+def benchmark_pull(device=None, file_size_mb=100):
+    if device == None:
+        device = adb.get_device()
+
+    lock_max(device)
+
+    remote_path = "/data/local/tmp/adb_benchmark_temp"
+    local_path = "/tmp/adb_benchmark_temp"
+
+    device.shell(["dd", "if=/dev/zero", "of=" + remote_path, "bs=1m",
+                  "count=" + str(file_size_mb)])
+    speeds = list()
+    for _ in range(0, 5):
+        begin = time.time()
+        device.pull(remote=remote_path, local=local_path)
+        end = time.time()
+        speeds.append(file_size_mb / float(end - begin))
+
+    analyze("pull %dMiB" % file_size_mb, speeds)
+
+def benchmark_shell(device=None, file_size_mb=100):
+    if device == None:
+        device = adb.get_device()
+
+    lock_max(device)
+
+    speeds = list()
+    for _ in range(0, 5):
+        begin = time.time()
+        device.shell(["dd", "if=/dev/zero", "bs=1m",
+                      "count=" + str(file_size_mb)])
+        end = time.time()
+        speeds.append(file_size_mb / float(end - begin))
+
+    analyze("shell %dMiB" % file_size_mb, speeds)
+
+def main():
+    benchmark_pull()
+
+if __name__ == "__main__":
+    main()
diff --git a/adb/bugreport.cpp b/adb/bugreport.cpp
deleted file mode 100644
index 372a3b4..0000000
--- a/adb/bugreport.cpp
+++ /dev/null
@@ -1,286 +0,0 @@
-/*
- * Copyright (C) 2016 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.
- */
-
-#define TRACE_TAG ADB
-
-#include "bugreport.h"
-
-#include <string>
-#include <vector>
-
-#include <android-base/file.h>
-#include <android-base/strings.h>
-
-#include "sysdeps.h"
-#include "adb_utils.h"
-#include "file_sync_service.h"
-
-static constexpr char BUGZ_BEGIN_PREFIX[] = "BEGIN:";
-static constexpr char BUGZ_PROGRESS_PREFIX[] = "PROGRESS:";
-static constexpr char BUGZ_PROGRESS_SEPARATOR[] = "/";
-static constexpr char BUGZ_OK_PREFIX[] = "OK:";
-static constexpr char BUGZ_FAIL_PREFIX[] = "FAIL:";
-
-// Custom callback used to handle the output of zipped bugreports.
-class BugreportStandardStreamsCallback : public StandardStreamsCallbackInterface {
-  public:
-    BugreportStandardStreamsCallback(const std::string& dest_dir, const std::string& dest_file,
-                                     bool show_progress, Bugreport* br)
-        : br_(br),
-          src_file_(),
-          dest_dir_(dest_dir),
-          dest_file_(dest_file),
-          line_message_(),
-          invalid_lines_(),
-          show_progress_(show_progress),
-          status_(0),
-          line_(),
-          last_progress_percentage_(0) {
-        SetLineMessage("generating");
-    }
-
-    void OnStdout(const char* buffer, int length) {
-        for (int i = 0; i < length; i++) {
-            char c = buffer[i];
-            if (c == '\n') {
-                ProcessLine(line_);
-                line_.clear();
-            } else {
-                line_.append(1, c);
-            }
-        }
-    }
-
-    void OnStderr(const char* buffer, int length) {
-        OnStream(nullptr, stderr, buffer, length);
-    }
-
-    int Done(int unused_) {
-        // Process remaining line, if any.
-        ProcessLine(line_);
-
-        // Warn about invalid lines, if any.
-        if (!invalid_lines_.empty()) {
-            fprintf(stderr,
-                    "WARNING: bugreportz generated %zu line(s) with unknown commands, "
-                    "device might not support zipped bugreports:\n",
-                    invalid_lines_.size());
-            for (const auto& line : invalid_lines_) {
-                fprintf(stderr, "\t%s\n", line.c_str());
-            }
-            fprintf(stderr,
-                    "If the zipped bugreport was not generated, try 'adb bugreport' instead.\n");
-        }
-
-        // Pull the generated bug report.
-        if (status_ == 0) {
-            if (src_file_.empty()) {
-                fprintf(stderr, "bugreportz did not return a '%s' or '%s' line\n", BUGZ_OK_PREFIX,
-                        BUGZ_FAIL_PREFIX);
-                return -1;
-            }
-            std::string destination;
-            if (dest_dir_.empty()) {
-                destination = dest_file_;
-            } else {
-                destination = android::base::StringPrintf("%s%c%s", dest_dir_.c_str(),
-                                                          OS_PATH_SEPARATOR, dest_file_.c_str());
-            }
-            std::vector<const char*> srcs{src_file_.c_str()};
-            SetLineMessage("pulling");
-            status_ =
-                br_->DoSyncPull(srcs, destination.c_str(), true, line_message_.c_str()) ? 0 : 1;
-            if (status_ != 0) {
-                fprintf(stderr,
-                        "Bug report finished but could not be copied to '%s'.\n"
-                        "Try to run 'adb pull %s <directory>'\n"
-                        "to copy it to a directory that can be written.\n",
-                        destination.c_str(), src_file_.c_str());
-            }
-        }
-        return status_;
-    }
-
-  private:
-    void SetLineMessage(const std::string& action) {
-        line_message_ = action + " " + android::base::Basename(dest_file_);
-    }
-
-    void SetSrcFile(const std::string path) {
-        src_file_ = path;
-        if (!dest_dir_.empty()) {
-            // Only uses device-provided name when user passed a directory.
-            dest_file_ = android::base::Basename(path);
-            SetLineMessage("generating");
-        }
-    }
-
-    void ProcessLine(const std::string& line) {
-        if (line.empty()) return;
-
-        if (android::base::StartsWith(line, BUGZ_BEGIN_PREFIX)) {
-            SetSrcFile(&line[strlen(BUGZ_BEGIN_PREFIX)]);
-        } else if (android::base::StartsWith(line, BUGZ_OK_PREFIX)) {
-            SetSrcFile(&line[strlen(BUGZ_OK_PREFIX)]);
-        } else if (android::base::StartsWith(line, BUGZ_FAIL_PREFIX)) {
-            const char* error_message = &line[strlen(BUGZ_FAIL_PREFIX)];
-            fprintf(stderr, "adb: device failed to take a zipped bugreport: %s\n", error_message);
-            status_ = -1;
-        } else if (show_progress_ && android::base::StartsWith(line, BUGZ_PROGRESS_PREFIX)) {
-            // progress_line should have the following format:
-            //
-            // BUGZ_PROGRESS_PREFIX:PROGRESS/TOTAL
-            //
-            size_t idx1 = line.rfind(BUGZ_PROGRESS_PREFIX) + strlen(BUGZ_PROGRESS_PREFIX);
-            size_t idx2 = line.rfind(BUGZ_PROGRESS_SEPARATOR);
-            int progress = std::stoi(line.substr(idx1, (idx2 - idx1)));
-            int total = std::stoi(line.substr(idx2 + 1));
-            int progress_percentage = (progress * 100 / total);
-            if (progress_percentage != 0 && progress_percentage <= last_progress_percentage_) {
-                // Ignore.
-                return;
-            }
-            last_progress_percentage_ = progress_percentage;
-            br_->UpdateProgress(line_message_, progress_percentage);
-        } else {
-            invalid_lines_.push_back(line);
-        }
-    }
-
-    Bugreport* br_;
-
-    // Path of bugreport on device.
-    std::string src_file_;
-
-    // Bugreport destination on host, depending on argument passed on constructor:
-    // - if argument is a directory, dest_dir_ is set with it and dest_file_ will be the name
-    //   of the bugreport reported by the device.
-    // - if argument is empty, dest_dir is set as the current directory and dest_file_ will be the
-    //   name of the bugreport reported by the device.
-    // - otherwise, dest_dir_ is not set and dest_file_ is set with the value passed on constructor.
-    std::string dest_dir_, dest_file_;
-
-    // Message displayed on LinePrinter, it's updated every time the destination above change.
-    std::string line_message_;
-
-    // Lines sent by bugreportz that contain invalid commands; will be displayed at the end.
-    std::vector<std::string> invalid_lines_;
-
-    // Whether PROGRESS_LINES should be interpreted as progress.
-    bool show_progress_;
-
-    // Overall process of the operation, as returned by Done().
-    int status_;
-
-    // Temporary buffer containing the characters read since the last newline (\n).
-    std::string line_;
-
-    // Last displayed progress.
-    // Since dumpstate progress can recede, only forward progress should be displayed
-    int last_progress_percentage_;
-
-    DISALLOW_COPY_AND_ASSIGN(BugreportStandardStreamsCallback);
-};
-
-int Bugreport::DoIt(TransportType transport_type, const char* serial, int argc, const char** argv) {
-    if (argc > 2) return syntax_error("adb bugreport [PATH]");
-
-    // Gets bugreportz version.
-    std::string bugz_stdout, bugz_stderr;
-    DefaultStandardStreamsCallback version_callback(&bugz_stdout, &bugz_stderr);
-    int status = SendShellCommand(transport_type, serial, "bugreportz -v", false, &version_callback);
-    std::string bugz_version = android::base::Trim(bugz_stderr);
-    std::string bugz_output = android::base::Trim(bugz_stdout);
-
-    if (status != 0 || bugz_version.empty()) {
-        D("'bugreportz' -v results: status=%d, stdout='%s', stderr='%s'", status,
-          bugz_output.c_str(), bugz_version.c_str());
-        if (argc == 1) {
-            // Device does not support bugreportz: if called as 'adb bugreport', just falls out to
-            // the flat-file version.
-            fprintf(stderr,
-                    "Failed to get bugreportz version, which is only available on devices "
-                    "running Android 7.0 or later.\nTrying a plain-text bug report instead.\n");
-            return SendShellCommand(transport_type, serial, "bugreport", false);
-        }
-
-        // But if user explicitly asked for a zipped bug report, fails instead (otherwise calling
-        // 'bugreport' would generate a lot of output the user might not be prepared to handle).
-        fprintf(stderr,
-                "Failed to get bugreportz version: 'bugreportz -v' returned '%s' (code %d).\n"
-                "If the device does not run Android 7.0 or above, try 'adb bugreport' instead.\n",
-                bugz_output.c_str(), status);
-        return status != 0 ? status : -1;
-    }
-
-    std::string dest_file, dest_dir;
-
-    if (argc == 1) {
-        // No args - use current directory
-        if (!getcwd(&dest_dir)) {
-            perror("adb: getcwd failed");
-            return 1;
-        }
-    } else {
-        // Check whether argument is a directory or file
-        if (directory_exists(argv[1])) {
-            dest_dir = argv[1];
-        } else {
-            dest_file = argv[1];
-        }
-    }
-
-    if (dest_file.empty()) {
-        // Uses a default value until device provides the proper name
-        dest_file = "bugreport.zip";
-    } else {
-        if (!android::base::EndsWithIgnoreCase(dest_file, ".zip")) {
-            dest_file += ".zip";
-        }
-    }
-
-    bool show_progress = true;
-    std::string bugz_command = "bugreportz -p";
-    if (bugz_version == "1.0") {
-        // 1.0 does not support progress notifications, so print a disclaimer
-        // message instead.
-        fprintf(stderr,
-                "Bugreport is in progress and it could take minutes to complete.\n"
-                "Please be patient and do not cancel or disconnect your device "
-                "until it completes.\n");
-        show_progress = false;
-        bugz_command = "bugreportz";
-    }
-    BugreportStandardStreamsCallback bugz_callback(dest_dir, dest_file, show_progress, this);
-    return SendShellCommand(transport_type, serial, bugz_command, false, &bugz_callback);
-}
-
-void Bugreport::UpdateProgress(const std::string& message, int progress_percentage) {
-    line_printer_.Print(
-        android::base::StringPrintf("[%3d%%] %s", progress_percentage, message.c_str()),
-        LinePrinter::INFO);
-}
-
-int Bugreport::SendShellCommand(TransportType transport_type, const char* serial,
-                                const std::string& command, bool disable_shell_protocol,
-                                StandardStreamsCallbackInterface* callback) {
-    return send_shell_command(transport_type, serial, command, disable_shell_protocol, callback);
-}
-
-bool Bugreport::DoSyncPull(const std::vector<const char*>& srcs, const char* dst, bool copy_attrs,
-                           const char* name) {
-    return do_sync_pull(srcs, dst, copy_attrs, name);
-}
diff --git a/adb/bugreport.h b/adb/bugreport.h
deleted file mode 100644
index d9a4468..0000000
--- a/adb/bugreport.h
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright (C) 2016 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 BUGREPORT_H
-#define BUGREPORT_H
-
-#include <vector>
-
-#include "adb.h"
-#include "commandline.h"
-#include "line_printer.h"
-
-class Bugreport {
-    friend class BugreportStandardStreamsCallback;
-
-  public:
-    Bugreport() : line_printer_() {
-    }
-    int DoIt(TransportType transport_type, const char* serial, int argc, const char** argv);
-
-  protected:
-    // Functions below are abstractions of external functions so they can be
-    // mocked on tests.
-    virtual int SendShellCommand(
-        TransportType transport_type, const char* serial, const std::string& command,
-        bool disable_shell_protocol,
-        StandardStreamsCallbackInterface* callback = &DEFAULT_STANDARD_STREAMS_CALLBACK);
-
-    virtual bool DoSyncPull(const std::vector<const char*>& srcs, const char* dst, bool copy_attrs,
-                            const char* name);
-
-  private:
-    virtual void UpdateProgress(const std::string& file_name, int progress_percentage);
-    LinePrinter line_printer_;
-    DISALLOW_COPY_AND_ASSIGN(Bugreport);
-};
-
-#endif  // BUGREPORT_H
diff --git a/adb/bugreport_test.cpp b/adb/bugreport_test.cpp
index d3787b4..72ca59a 100644
--- a/adb/bugreport_test.cpp
+++ b/adb/bugreport_test.cpp
@@ -51,8 +51,8 @@
 // Empty functions so tests don't need to be linked against commandline.cpp
 DefaultStandardStreamsCallback DEFAULT_STANDARD_STREAMS_CALLBACK(nullptr, nullptr);
 
-int send_shell_command(TransportType transport_type, const char* serial, const std::string& command,
-                       bool disable_shell_protocol, StandardStreamsCallbackInterface* callback) {
+int send_shell_command(const std::string& command, bool disable_shell_protocol,
+                       StandardStreamsCallbackInterface* callback) {
     ADD_FAILURE() << "send_shell_command() should have been mocked";
     return -42;
 }
@@ -62,7 +62,7 @@
     kStreamStderr,
 };
 
-// gmock black magic to provide a WithArg<4>(WriteOnStdout(output)) matcher
+// gmock black magic to provide a WithArg<2>(WriteOnStdout(output)) matcher
 typedef void OnStandardStreamsCallbackFunction(StandardStreamsCallbackInterface*);
 
 class OnStandardStreamsCallbackAction : public ActionInterface<OnStandardStreamsCallbackFunction> {
@@ -118,9 +118,8 @@
 
 class BugreportMock : public Bugreport {
   public:
-    MOCK_METHOD5(SendShellCommand,
-                 int(TransportType transport_type, const char* serial, const std::string& command,
-                     bool disable_shell_protocol, StandardStreamsCallbackInterface* callback));
+    MOCK_METHOD3(SendShellCommand, int(const std::string& command, bool disable_shell_protocol,
+                                       StandardStreamsCallbackInterface* callback));
     MOCK_METHOD4(DoSyncPull, bool(const std::vector<const char*>& srcs, const char* dst,
                                   bool copy_attrs, const char* name));
     MOCK_METHOD2(UpdateProgress, void(const std::string&, int));
@@ -136,10 +135,9 @@
     }
 
     void ExpectBugreportzVersion(const std::string& version) {
-        EXPECT_CALL(br_,
-                    SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz -v", false, _))
-            .WillOnce(DoAll(WithArg<4>(WriteOnStderr(version.c_str())),
-                            WithArg<4>(ReturnCallbackDone(0))));
+        EXPECT_CALL(br_, SendShellCommand("bugreportz -v", false, _))
+            .WillOnce(DoAll(WithArg<2>(WriteOnStderr(version.c_str())),
+                            WithArg<2>(ReturnCallbackDone(0))));
     }
 
     void ExpectProgress(int progress_percentage, const std::string& file = "file.zip") {
@@ -153,26 +151,26 @@
 // Tests when called with invalid number of arguments
 TEST_F(BugreportTest, InvalidNumberArgs) {
     const char* args[] = {"bugreport", "to", "principal"};
-    ASSERT_EQ(1, br_.DoIt(kTransportLocal, "HannibalLecter", 3, args));
+    ASSERT_EQ(1, br_.DoIt(3, args));
 }
 
 // Tests the 'adb bugreport' option when the device does not support 'bugreportz' - it falls back
 // to the flat-file format ('bugreport' binary on device)
 TEST_F(BugreportTest, NoArgumentsPreNDevice) {
     // clang-format off
-    EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz -v", false, _))
-        .WillOnce(DoAll(WithArg<4>(WriteOnStderr("")),
+    EXPECT_CALL(br_, SendShellCommand("bugreportz -v", false, _))
+        .WillOnce(DoAll(WithArg<2>(WriteOnStderr("")),
                         // Write some bogus output on stdout to make sure it's ignored
-                        WithArg<4>(WriteOnStdout("Dude, where is my bugreportz?")),
-                        WithArg<4>(ReturnCallbackDone(0))));
+                        WithArg<2>(WriteOnStdout("Dude, where is my bugreportz?")),
+                        WithArg<2>(ReturnCallbackDone(0))));
     // clang-format on
     std::string bugreport = "Reported the bug was.";
     CaptureStdout();
-    EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreport", false, _))
-        .WillOnce(DoAll(WithArg<4>(WriteOnStdout(bugreport)), Return(0)));
+    EXPECT_CALL(br_, SendShellCommand("bugreport", false, _))
+        .WillOnce(DoAll(WithArg<2>(WriteOnStdout(bugreport)), Return(0)));
 
     const char* args[] = {"bugreport"};
-    ASSERT_EQ(0, br_.DoIt(kTransportLocal, "HannibalLecter", 1, args));
+    ASSERT_EQ(0, br_.DoIt(1, args));
     ASSERT_THAT(GetCapturedStdout(), StrEq(bugreport));
 }
 
@@ -183,15 +181,15 @@
 
     std::string dest_file =
         android::base::StringPrintf("%s%cda_bugreport.zip", cwd_.c_str(), OS_PATH_SEPARATOR);
-    EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz", false, _))
-        .WillOnce(DoAll(WithArg<4>(WriteOnStdout("OK:/device/da_bugreport.zip")),
-                        WithArg<4>(ReturnCallbackDone())));
+    EXPECT_CALL(br_, SendShellCommand("bugreportz", false, _))
+        .WillOnce(DoAll(WithArg<2>(WriteOnStdout("OK:/device/da_bugreport.zip")),
+                        WithArg<2>(ReturnCallbackDone())));
     EXPECT_CALL(br_, DoSyncPull(ElementsAre(StrEq("/device/da_bugreport.zip")), StrEq(dest_file),
-                                true, StrEq("pulling da_bugreport.zip")))
+                                false, StrEq("pulling da_bugreport.zip")))
         .WillOnce(Return(true));
 
     const char* args[] = {"bugreport"};
-    ASSERT_EQ(0, br_.DoIt(kTransportLocal, "HannibalLecter", 1, args));
+    ASSERT_EQ(0, br_.DoIt(1, args));
 }
 
 // Tests the 'adb bugreport' option when the device supports 'bugreportz' version 1.1 - it will
@@ -201,47 +199,47 @@
     std::string dest_file =
         android::base::StringPrintf("%s%cda_bugreport.zip", cwd_.c_str(), OS_PATH_SEPARATOR);
     ExpectProgress(50, "da_bugreport.zip");
-    EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz -p", false, _))
-        .WillOnce(DoAll(WithArg<4>(WriteOnStdout("BEGIN:/device/da_bugreport.zip\n")),
-                        WithArg<4>(WriteOnStdout("PROGRESS:50/100\n")),
-                        WithArg<4>(WriteOnStdout("OK:/device/da_bugreport.zip\n")),
-                        WithArg<4>(ReturnCallbackDone())));
+    EXPECT_CALL(br_, SendShellCommand("bugreportz -p", false, _))
+        .WillOnce(DoAll(WithArg<2>(WriteOnStdout("BEGIN:/device/da_bugreport.zip\n")),
+                        WithArg<2>(WriteOnStdout("PROGRESS:50/100\n")),
+                        WithArg<2>(WriteOnStdout("OK:/device/da_bugreport.zip\n")),
+                        WithArg<2>(ReturnCallbackDone())));
     EXPECT_CALL(br_, DoSyncPull(ElementsAre(StrEq("/device/da_bugreport.zip")), StrEq(dest_file),
-                                true, StrEq("pulling da_bugreport.zip")))
+                                false, StrEq("pulling da_bugreport.zip")))
         .WillOnce(Return(true));
 
     const char* args[] = {"bugreport"};
-    ASSERT_EQ(0, br_.DoIt(kTransportLocal, "HannibalLecter", 1, args));
+    ASSERT_EQ(0, br_.DoIt(1, args));
 }
 
 // Tests 'adb bugreport file.zip' when it succeeds and device does not support progress.
 TEST_F(BugreportTest, OkNDevice) {
     ExpectBugreportzVersion("1.0");
-    EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz", false, _))
-        .WillOnce(DoAll(WithArg<4>(WriteOnStdout("OK:/device/bugreport.zip")),
-                        WithArg<4>(ReturnCallbackDone())));
+    EXPECT_CALL(br_, SendShellCommand("bugreportz", false, _))
+        .WillOnce(DoAll(WithArg<2>(WriteOnStdout("OK:/device/bugreport.zip")),
+                        WithArg<2>(ReturnCallbackDone())));
     EXPECT_CALL(br_, DoSyncPull(ElementsAre(StrEq("/device/bugreport.zip")), StrEq("file.zip"),
-                                true, StrEq("pulling file.zip")))
+                                false, StrEq("pulling file.zip")))
         .WillOnce(Return(true));
 
     const char* args[] = {"bugreport", "file.zip"};
-    ASSERT_EQ(0, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args));
+    ASSERT_EQ(0, br_.DoIt(2, args));
 }
 
 // Tests 'adb bugreport file.zip' when it succeeds but response was sent in
 // multiple buffer writers and without progress updates.
 TEST_F(BugreportTest, OkNDeviceSplitBuffer) {
     ExpectBugreportzVersion("1.0");
-    EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz", false, _))
-        .WillOnce(DoAll(WithArg<4>(WriteOnStdout("OK:/device")),
-                        WithArg<4>(WriteOnStdout("/bugreport.zip")),
-                        WithArg<4>(ReturnCallbackDone())));
+    EXPECT_CALL(br_, SendShellCommand("bugreportz", false, _))
+        .WillOnce(DoAll(WithArg<2>(WriteOnStdout("OK:/device")),
+                        WithArg<2>(WriteOnStdout("/bugreport.zip")),
+                        WithArg<2>(ReturnCallbackDone())));
     EXPECT_CALL(br_, DoSyncPull(ElementsAre(StrEq("/device/bugreport.zip")), StrEq("file.zip"),
-                                true, StrEq("pulling file.zip")))
+                                false, StrEq("pulling file.zip")))
         .WillOnce(Return(true));
 
     const char* args[] = {"bugreport", "file.zip"};
-    ASSERT_EQ(0, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args));
+    ASSERT_EQ(0, br_.DoIt(2, args));
 }
 
 // Tests 'adb bugreport file.zip' when it succeeds and displays progress.
@@ -252,32 +250,32 @@
     ExpectProgress(50);
     ExpectProgress(99);
     // clang-format off
-    EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz -p", false, _))
+    EXPECT_CALL(br_, SendShellCommand("bugreportz -p", false, _))
         // NOTE: DoAll accepts at most 10 arguments, and we're almost reached that limit...
         .WillOnce(DoAll(
             // Name might change on OK, so make sure the right one is picked.
-            WithArg<4>(WriteOnStdout("BEGIN:/device/bugreport___NOT.zip\n")),
+            WithArg<2>(WriteOnStdout("BEGIN:/device/bugreport___NOT.zip\n")),
             // Progress line in one write
-            WithArg<4>(WriteOnStdout("PROGRESS:1/100\n")),
+            WithArg<2>(WriteOnStdout("PROGRESS:1/100\n")),
             // Add some bogus lines
-            WithArg<4>(WriteOnStdout("\nDUDE:SWEET\n\nBLA\n\nBLA\nBLA\n\n")),
+            WithArg<2>(WriteOnStdout("\nDUDE:SWEET\n\nBLA\n\nBLA\nBLA\n\n")),
             // Multiple progress lines in one write
-            WithArg<4>(WriteOnStdout("PROGRESS:10/100\nPROGRESS:50/100\n")),
+            WithArg<2>(WriteOnStdout("PROGRESS:10/100\nPROGRESS:50/100\n")),
             // Progress line in multiple writes
-            WithArg<4>(WriteOnStdout("PROG")),
-            WithArg<4>(WriteOnStdout("RESS:99")),
-            WithArg<4>(WriteOnStdout("/100\n")),
+            WithArg<2>(WriteOnStdout("PROG")),
+            WithArg<2>(WriteOnStdout("RESS:99")),
+            WithArg<2>(WriteOnStdout("/100\n")),
             // Split last message as well, just in case
-            WithArg<4>(WriteOnStdout("OK:/device/bugreport")),
-            WithArg<4>(WriteOnStdout(".zip")),
-            WithArg<4>(ReturnCallbackDone())));
+            WithArg<2>(WriteOnStdout("OK:/device/bugreport")),
+            WithArg<2>(WriteOnStdout(".zip")),
+            WithArg<2>(ReturnCallbackDone())));
     // clang-format on
     EXPECT_CALL(br_, DoSyncPull(ElementsAre(StrEq("/device/bugreport.zip")), StrEq("file.zip"),
-                                true, StrEq("pulling file.zip")))
+                                false, StrEq("pulling file.zip")))
         .WillOnce(Return(true));
 
     const char* args[] = {"bugreport", "file.zip"};
-    ASSERT_EQ(0, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args));
+    ASSERT_EQ(0, br_.DoIt(2, args));
 }
 
 // Tests 'adb bugreport file.zip' when it succeeds and displays progress, even if progress recedes.
@@ -287,28 +285,28 @@
     ExpectProgress(50);
     ExpectProgress(75);
     // clang-format off
-    EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz -p", false, _))
+    EXPECT_CALL(br_, SendShellCommand("bugreportz -p", false, _))
         // NOTE: DoAll accepts at most 10 arguments, and we're almost reached that limit...
         .WillOnce(DoAll(
-            WithArg<4>(WriteOnStdout("BEGIN:/device/bugreport.zip\n")),
-            WithArg<4>(WriteOnStdout("PROGRESS:1/100\n")), // 1%
-            WithArg<4>(WriteOnStdout("PROGRESS:50/100\n")), // 50%
+            WithArg<2>(WriteOnStdout("BEGIN:/device/bugreport.zip\n")),
+            WithArg<2>(WriteOnStdout("PROGRESS:1/100\n")), // 1%
+            WithArg<2>(WriteOnStdout("PROGRESS:50/100\n")), // 50%
             // 25% should be ignored becaused it receded.
-            WithArg<4>(WriteOnStdout("PROGRESS:25/100\n")), // 25%
-            WithArg<4>(WriteOnStdout("PROGRESS:75/100\n")), // 75%
+            WithArg<2>(WriteOnStdout("PROGRESS:25/100\n")), // 25%
+            WithArg<2>(WriteOnStdout("PROGRESS:75/100\n")), // 75%
             // 75% should be ignored becaused it didn't change.
-            WithArg<4>(WriteOnStdout("PROGRESS:75/100\n")), // 75%
+            WithArg<2>(WriteOnStdout("PROGRESS:75/100\n")), // 75%
             // Try a receeding percentage with a different max progress
-            WithArg<4>(WriteOnStdout("PROGRESS:700/1000\n")), // 70%
-            WithArg<4>(WriteOnStdout("OK:/device/bugreport.zip")),
-            WithArg<4>(ReturnCallbackDone())));
+            WithArg<2>(WriteOnStdout("PROGRESS:700/1000\n")), // 70%
+            WithArg<2>(WriteOnStdout("OK:/device/bugreport.zip")),
+            WithArg<2>(ReturnCallbackDone())));
     // clang-format on
     EXPECT_CALL(br_, DoSyncPull(ElementsAre(StrEq("/device/bugreport.zip")), StrEq("file.zip"),
-                                true, StrEq("pulling file.zip")))
+                                false, StrEq("pulling file.zip")))
         .WillOnce(Return(true));
 
     const char* args[] = {"bugreport", "file.zip"};
-    ASSERT_EQ(0, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args));
+    ASSERT_EQ(0, br_.DoIt(2, args));
 }
 
 // Tests 'adb bugreport file.zip' when it succeeds and displays the initial progress of 0%
@@ -317,21 +315,21 @@
     ExpectProgress(0);
     ExpectProgress(1);
     // clang-format off
-    EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz -p", false, _))
+    EXPECT_CALL(br_, SendShellCommand("bugreportz -p", false, _))
         // NOTE: DoAll accepts at most 10 arguments, and we're almost reached that limit...
         .WillOnce(DoAll(
-            WithArg<4>(WriteOnStdout("BEGIN:/device/bugreport.zip\n")),
-            WithArg<4>(WriteOnStdout("PROGRESS:1/100000\n")),
-            WithArg<4>(WriteOnStdout("PROGRESS:1/100\n")), // 1%
-            WithArg<4>(WriteOnStdout("OK:/device/bugreport.zip")),
-            WithArg<4>(ReturnCallbackDone())));
+            WithArg<2>(WriteOnStdout("BEGIN:/device/bugreport.zip\n")),
+            WithArg<2>(WriteOnStdout("PROGRESS:1/100000\n")),
+            WithArg<2>(WriteOnStdout("PROGRESS:1/100\n")), // 1%
+            WithArg<2>(WriteOnStdout("OK:/device/bugreport.zip")),
+            WithArg<2>(ReturnCallbackDone())));
     // clang-format on
     EXPECT_CALL(br_, DoSyncPull(ElementsAre(StrEq("/device/bugreport.zip")), StrEq("file.zip"),
-                                true, StrEq("pulling file.zip")))
+                                false, StrEq("pulling file.zip")))
         .WillOnce(Return(true));
 
     const char* args[] = {"bugreport", "file.zip"};
-    ASSERT_EQ(0, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args));
+    ASSERT_EQ(0, br_.DoIt(2, args));
 }
 
 // Tests 'adb bugreport dir' when it succeeds and destination is a directory.
@@ -341,30 +339,30 @@
     std::string dest_file =
         android::base::StringPrintf("%s%cda_bugreport.zip", td.path, OS_PATH_SEPARATOR);
 
-    EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz -p", false, _))
-        .WillOnce(DoAll(WithArg<4>(WriteOnStdout("BEGIN:/device/da_bugreport.zip\n")),
-                        WithArg<4>(WriteOnStdout("OK:/device/da_bugreport.zip")),
-                        WithArg<4>(ReturnCallbackDone())));
+    EXPECT_CALL(br_, SendShellCommand("bugreportz -p", false, _))
+        .WillOnce(DoAll(WithArg<2>(WriteOnStdout("BEGIN:/device/da_bugreport.zip\n")),
+                        WithArg<2>(WriteOnStdout("OK:/device/da_bugreport.zip")),
+                        WithArg<2>(ReturnCallbackDone())));
     EXPECT_CALL(br_, DoSyncPull(ElementsAre(StrEq("/device/da_bugreport.zip")), StrEq(dest_file),
-                                true, StrEq("pulling da_bugreport.zip")))
+                                false, StrEq("pulling da_bugreport.zip")))
         .WillOnce(Return(true));
 
     const char* args[] = {"bugreport", td.path};
-    ASSERT_EQ(0, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args));
+    ASSERT_EQ(0, br_.DoIt(2, args));
 }
 
 // Tests 'adb bugreport file' when it succeeds
 TEST_F(BugreportTest, OkNoExtension) {
     ExpectBugreportzVersion("1.1");
-    EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz -p", false, _))
-        .WillOnce(DoAll(WithArg<4>(WriteOnStdout("OK:/device/bugreport.zip\n")),
-                        WithArg<4>(ReturnCallbackDone())));
+    EXPECT_CALL(br_, SendShellCommand("bugreportz -p", false, _))
+        .WillOnce(DoAll(WithArg<2>(WriteOnStdout("OK:/device/bugreport.zip\n")),
+                        WithArg<2>(ReturnCallbackDone())));
     EXPECT_CALL(br_, DoSyncPull(ElementsAre(StrEq("/device/bugreport.zip")), StrEq("file.zip"),
-                                true, StrEq("pulling file.zip")))
+                                false, StrEq("pulling file.zip")))
         .WillOnce(Return(true));
 
     const char* args[] = {"bugreport", "file"};
-    ASSERT_EQ(0, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args));
+    ASSERT_EQ(0, br_.DoIt(2, args));
 }
 
 // Tests 'adb bugreport dir' when it succeeds and destination is a directory and device runs N.
@@ -374,28 +372,28 @@
     std::string dest_file =
         android::base::StringPrintf("%s%cda_bugreport.zip", td.path, OS_PATH_SEPARATOR);
 
-    EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz", false, _))
-        .WillOnce(DoAll(WithArg<4>(WriteOnStdout("BEGIN:/device/da_bugreport.zip\n")),
-                        WithArg<4>(WriteOnStdout("OK:/device/da_bugreport.zip")),
-                        WithArg<4>(ReturnCallbackDone())));
+    EXPECT_CALL(br_, SendShellCommand("bugreportz", false, _))
+        .WillOnce(DoAll(WithArg<2>(WriteOnStdout("BEGIN:/device/da_bugreport.zip\n")),
+                        WithArg<2>(WriteOnStdout("OK:/device/da_bugreport.zip")),
+                        WithArg<2>(ReturnCallbackDone())));
     EXPECT_CALL(br_, DoSyncPull(ElementsAre(StrEq("/device/da_bugreport.zip")), StrEq(dest_file),
-                                true, StrEq("pulling da_bugreport.zip")))
+                                false, StrEq("pulling da_bugreport.zip")))
         .WillOnce(Return(true));
 
     const char* args[] = {"bugreport", td.path};
-    ASSERT_EQ(0, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args));
+    ASSERT_EQ(0, br_.DoIt(2, args));
 }
 
 // Tests 'adb bugreport file.zip' when the bugreport itself failed
 TEST_F(BugreportTest, BugreportzReturnedFail) {
     ExpectBugreportzVersion("1.1");
-    EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz -p", false, _))
+    EXPECT_CALL(br_, SendShellCommand("bugreportz -p", false, _))
         .WillOnce(
-            DoAll(WithArg<4>(WriteOnStdout("FAIL:D'OH!\n")), WithArg<4>(ReturnCallbackDone())));
+            DoAll(WithArg<2>(WriteOnStdout("FAIL:D'OH!\n")), WithArg<2>(ReturnCallbackDone())));
 
     CaptureStderr();
     const char* args[] = {"bugreport", "file.zip"};
-    ASSERT_EQ(-1, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args));
+    ASSERT_EQ(-1, br_.DoIt(2, args));
     ASSERT_THAT(GetCapturedStderr(), HasSubstr("D'OH!"));
 }
 
@@ -404,13 +402,13 @@
 // multiple buffer writes
 TEST_F(BugreportTest, BugreportzReturnedFailSplitBuffer) {
     ExpectBugreportzVersion("1.1");
-    EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz -p", false, _))
-        .WillOnce(DoAll(WithArg<4>(WriteOnStdout("FAIL")), WithArg<4>(WriteOnStdout(":D'OH!\n")),
-                        WithArg<4>(ReturnCallbackDone())));
+    EXPECT_CALL(br_, SendShellCommand("bugreportz -p", false, _))
+        .WillOnce(DoAll(WithArg<2>(WriteOnStdout("FAIL")), WithArg<2>(WriteOnStdout(":D'OH!\n")),
+                        WithArg<2>(ReturnCallbackDone())));
 
     CaptureStderr();
     const char* args[] = {"bugreport", "file.zip"};
-    ASSERT_EQ(-1, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args));
+    ASSERT_EQ(-1, br_.DoIt(2, args));
     ASSERT_THAT(GetCapturedStderr(), HasSubstr("D'OH!"));
 }
 
@@ -418,23 +416,22 @@
 // response.
 TEST_F(BugreportTest, BugreportzReturnedUnsupported) {
     ExpectBugreportzVersion("1.1");
-    EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz -p", false, _))
-        .WillOnce(DoAll(WithArg<4>(WriteOnStdout("bugreportz? What am I, a zombie?")),
-                        WithArg<4>(ReturnCallbackDone())));
+    EXPECT_CALL(br_, SendShellCommand("bugreportz -p", false, _))
+        .WillOnce(DoAll(WithArg<2>(WriteOnStdout("bugreportz? What am I, a zombie?")),
+                        WithArg<2>(ReturnCallbackDone())));
 
     CaptureStderr();
     const char* args[] = {"bugreport", "file.zip"};
-    ASSERT_EQ(-1, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args));
+    ASSERT_EQ(-1, br_.DoIt(2, args));
     ASSERT_THAT(GetCapturedStderr(), HasSubstr("bugreportz? What am I, a zombie?"));
 }
 
 // Tests 'adb bugreport file.zip' when the bugreportz -v command failed
 TEST_F(BugreportTest, BugreportzVersionFailed) {
-    EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz -v", false, _))
-        .WillOnce(Return(666));
+    EXPECT_CALL(br_, SendShellCommand("bugreportz -v", false, _)).WillOnce(Return(666));
 
     const char* args[] = {"bugreport", "file.zip"};
-    ASSERT_EQ(666, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args));
+    ASSERT_EQ(666, br_.DoIt(2, args));
 }
 
 // Tests 'adb bugreport file.zip' when the bugreportz -v returns status 0 but with no output.
@@ -442,29 +439,28 @@
     ExpectBugreportzVersion("");
 
     const char* args[] = {"bugreport", "file.zip"};
-    ASSERT_EQ(-1, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args));
+    ASSERT_EQ(-1, br_.DoIt(2, args));
 }
 
 // Tests 'adb bugreport file.zip' when the main bugreportz command failed
 TEST_F(BugreportTest, BugreportzFailed) {
     ExpectBugreportzVersion("1.1");
-    EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz -p", false, _))
-        .WillOnce(Return(666));
+    EXPECT_CALL(br_, SendShellCommand("bugreportz -p", false, _)).WillOnce(Return(666));
 
     const char* args[] = {"bugreport", "file.zip"};
-    ASSERT_EQ(666, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args));
+    ASSERT_EQ(666, br_.DoIt(2, args));
 }
 
 // Tests 'adb bugreport file.zip' when the bugreport could not be pulled
 TEST_F(BugreportTest, PullFails) {
     ExpectBugreportzVersion("1.1");
-    EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz -p", false, _))
-        .WillOnce(DoAll(WithArg<4>(WriteOnStdout("OK:/device/bugreport.zip")),
-                        WithArg<4>(ReturnCallbackDone())));
+    EXPECT_CALL(br_, SendShellCommand("bugreportz -p", false, _))
+        .WillOnce(DoAll(WithArg<2>(WriteOnStdout("OK:/device/bugreport.zip")),
+                        WithArg<2>(ReturnCallbackDone())));
     EXPECT_CALL(br_, DoSyncPull(ElementsAre(StrEq("/device/bugreport.zip")), StrEq("file.zip"),
-                                true, HasSubstr("file.zip")))
+                                false, HasSubstr("file.zip")))
         .WillOnce(Return(false));
 
     const char* args[] = {"bugreport", "file.zip"};
-    ASSERT_EQ(1, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args));
+    ASSERT_EQ(1, br_.DoIt(2, args));
 }
diff --git a/adb/client/adb_client.cpp b/adb/client/adb_client.cpp
new file mode 100644
index 0000000..1959258
--- /dev/null
+++ b/adb/client/adb_client.cpp
@@ -0,0 +1,332 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+
+#define TRACE_TAG ADB
+
+#include "sysdeps.h"
+#include "adb_client.h"
+
+#include <errno.h>
+#include <inttypes.h>
+#include <limits.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <condition_variable>
+#include <mutex>
+#include <string>
+#include <thread>
+#include <vector>
+
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+#include <android-base/thread_annotations.h>
+#include <cutils/sockets.h>
+
+#include "adb_io.h"
+#include "adb_utils.h"
+#include "socket_spec.h"
+#include "sysdeps/chrono.h"
+
+static TransportType __adb_transport = kTransportAny;
+static const char* __adb_serial = nullptr;
+static TransportId __adb_transport_id = 0;
+
+static const char* __adb_server_socket_spec;
+
+void adb_set_transport(TransportType type, const char* serial, TransportId transport_id) {
+    __adb_transport = type;
+    __adb_serial = serial;
+    __adb_transport_id = transport_id;
+}
+
+void adb_get_transport(TransportType* type, const char** serial, TransportId* transport_id) {
+    if (type) *type = __adb_transport;
+    if (serial) *serial = __adb_serial;
+    if (transport_id) *transport_id = __adb_transport_id;
+}
+
+void adb_set_socket_spec(const char* socket_spec) {
+    if (__adb_server_socket_spec) {
+        LOG(FATAL) << "attempted to reinitialize adb_server_socket_spec " << socket_spec << " (was " << __adb_server_socket_spec << ")";
+    }
+    __adb_server_socket_spec = socket_spec;
+}
+
+static int switch_socket_transport(int fd, std::string* error) {
+    std::string service;
+    if (__adb_transport_id) {
+        service += "host:transport-id:";
+        service += std::to_string(__adb_transport_id);
+    } else if (__adb_serial) {
+        service += "host:transport:";
+        service += __adb_serial;
+    } else {
+        const char* transport_type = "???";
+        switch (__adb_transport) {
+          case kTransportUsb:
+            transport_type = "transport-usb";
+            break;
+          case kTransportLocal:
+            transport_type = "transport-local";
+            break;
+          case kTransportAny:
+            transport_type = "transport-any";
+            break;
+          case kTransportHost:
+            // no switch necessary
+            return 0;
+        }
+        service += "host:";
+        service += transport_type;
+    }
+
+    if (!SendProtocolString(fd, service)) {
+        *error = perror_str("write failure during connection");
+        adb_close(fd);
+        return -1;
+    }
+    D("Switch transport in progress");
+
+    if (!adb_status(fd, error)) {
+        adb_close(fd);
+        D("Switch transport failed: %s", error->c_str());
+        return -1;
+    }
+    D("Switch transport success");
+    return 0;
+}
+
+bool adb_status(int fd, std::string* error) {
+    char buf[5];
+    if (!ReadFdExactly(fd, buf, 4)) {
+        *error = perror_str("protocol fault (couldn't read status)");
+        return false;
+    }
+
+    if (!memcmp(buf, "OKAY", 4)) {
+        return true;
+    }
+
+    if (memcmp(buf, "FAIL", 4)) {
+        *error = android::base::StringPrintf("protocol fault (status %02x %02x %02x %02x?!)",
+                                             buf[0], buf[1], buf[2], buf[3]);
+        return false;
+    }
+
+    ReadProtocolString(fd, error, error);
+    return false;
+}
+
+static int _adb_connect(const std::string& service, std::string* error) {
+    D("_adb_connect: %s", service.c_str());
+    if (service.empty() || service.size() > MAX_PAYLOAD) {
+        *error = android::base::StringPrintf("bad service name length (%zd)",
+                                             service.size());
+        return -1;
+    }
+
+    std::string reason;
+    int fd = socket_spec_connect(__adb_server_socket_spec, &reason);
+    if (fd < 0) {
+        *error = android::base::StringPrintf("cannot connect to daemon at %s: %s",
+                                             __adb_server_socket_spec, reason.c_str());
+        return -2;
+    }
+
+    if (memcmp(&service[0], "host", 4) != 0 && switch_socket_transport(fd, error)) {
+        return -1;
+    }
+
+    if (!SendProtocolString(fd, service)) {
+        *error = perror_str("write failure during connection");
+        adb_close(fd);
+        return -1;
+    }
+
+    if (!adb_status(fd, error)) {
+        adb_close(fd);
+        return -1;
+    }
+
+    D("_adb_connect: return fd %d", fd);
+    return fd;
+}
+
+bool adb_kill_server() {
+    D("adb_kill_server");
+    std::string reason;
+    int fd = socket_spec_connect(__adb_server_socket_spec, &reason);
+    if (fd < 0) {
+        fprintf(stderr, "cannot connect to daemon at %s: %s\n", __adb_server_socket_spec,
+                reason.c_str());
+        return true;
+    }
+
+    if (!SendProtocolString(fd, "host:kill")) {
+        fprintf(stderr, "error: write failure during connection: %s\n", strerror(errno));
+        return false;
+    }
+
+    ReadOrderlyShutdown(fd);
+    return true;
+}
+
+int adb_connect(const std::string& service, std::string* error) {
+    // first query the adb server's version
+    int fd = _adb_connect("host:version", error);
+
+    D("adb_connect: service %s", service.c_str());
+    if (fd == -2 && !is_local_socket_spec(__adb_server_socket_spec)) {
+        fprintf(stderr, "* cannot start server on remote host\n");
+        // error is the original network connection error
+        return fd;
+    } else if (fd == -2) {
+        fprintf(stderr, "* daemon not running; starting now at %s\n", __adb_server_socket_spec);
+    start_server:
+        if (launch_server(__adb_server_socket_spec)) {
+            fprintf(stderr, "* failed to start daemon\n");
+            // launch_server() has already printed detailed error info, so just
+            // return a generic error string about the overall adb_connect()
+            // that the caller requested.
+            *error = "cannot connect to daemon";
+            return -1;
+        } else {
+            fprintf(stderr, "* daemon started successfully\n");
+        }
+        // The server will wait until it detects all of its connected devices before acking.
+        // Fall through to _adb_connect.
+    } else {
+        // If a server is already running, check its version matches.
+        int version = ADB_SERVER_VERSION - 1;
+
+        // If we have a file descriptor, then parse version result.
+        if (fd >= 0) {
+            std::string version_string;
+            if (!ReadProtocolString(fd, &version_string, error)) {
+                adb_close(fd);
+                return -1;
+            }
+
+            ReadOrderlyShutdown(fd);
+            adb_close(fd);
+
+            if (sscanf(&version_string[0], "%04x", &version) != 1) {
+                *error = android::base::StringPrintf("cannot parse version string: %s",
+                                                     version_string.c_str());
+                return -1;
+            }
+        } else {
+            // If fd is -1 check for "unknown host service" which would
+            // indicate a version of adb that does not support the
+            // version command, in which case we should fall-through to kill it.
+            if (*error != "unknown host service") {
+                return fd;
+            }
+        }
+
+        if (version != ADB_SERVER_VERSION) {
+            fprintf(stderr, "adb server version (%d) doesn't match this client (%d); killing...\n",
+                    version, ADB_SERVER_VERSION);
+            adb_kill_server();
+            goto start_server;
+        }
+    }
+
+    // if the command is start-server, we are done.
+    if (service == "host:start-server") {
+        return 0;
+    }
+
+    fd = _adb_connect(service, error);
+    if (fd == -1) {
+        D("_adb_connect error: %s", error->c_str());
+    } else if(fd == -2) {
+        fprintf(stderr, "* daemon still not running\n");
+    }
+    D("adb_connect: return fd %d", fd);
+
+    return fd;
+}
+
+
+bool adb_command(const std::string& service) {
+    std::string error;
+    int fd = adb_connect(service, &error);
+    if (fd < 0) {
+        fprintf(stderr, "error: %s\n", error.c_str());
+        return false;
+    }
+
+    if (!adb_status(fd, &error)) {
+        fprintf(stderr, "error: %s\n", error.c_str());
+        adb_close(fd);
+        return false;
+    }
+
+    ReadOrderlyShutdown(fd);
+    adb_close(fd);
+    return true;
+}
+
+bool adb_query(const std::string& service, std::string* result, std::string* error) {
+    D("adb_query: %s", service.c_str());
+    int fd = adb_connect(service, error);
+    if (fd < 0) {
+        return false;
+    }
+
+    result->clear();
+    if (!ReadProtocolString(fd, result, error)) {
+        adb_close(fd);
+        return false;
+    }
+
+    ReadOrderlyShutdown(fd);
+    adb_close(fd);
+    return true;
+}
+
+std::string format_host_command(const char* command) {
+    if (__adb_transport_id) {
+        return android::base::StringPrintf("host-transport-id:%" PRIu64 ":%s", __adb_transport_id,
+                                           command);
+    } else if (__adb_serial) {
+        return android::base::StringPrintf("host-serial:%s:%s", __adb_serial, command);
+    }
+
+    const char* prefix = "host";
+    if (__adb_transport == kTransportUsb) {
+        prefix = "host-usb";
+    } else if (__adb_transport == kTransportLocal) {
+        prefix = "host-local";
+    }
+    return android::base::StringPrintf("%s:%s", prefix, command);
+}
+
+bool adb_get_feature_set(FeatureSet* feature_set, std::string* error) {
+    std::string result;
+    if (adb_query(format_host_command("features"), &result, error)) {
+        *feature_set = StringToFeatureSet(result);
+        return true;
+    }
+    feature_set->clear();
+    return false;
+}
diff --git a/adb/client/adb_client.h b/adb/client/adb_client.h
new file mode 100644
index 0000000..fca435e
--- /dev/null
+++ b/adb/client/adb_client.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2015 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 _ADB_CLIENT_H_
+#define _ADB_CLIENT_H_
+
+#include "adb.h"
+#include "sysdeps.h"
+#include "transport.h"
+
+#include <string>
+
+// Connect to adb, connect to the named service, and return a valid fd for
+// interacting with that service upon success or a negative number on failure.
+int adb_connect(const std::string& service, std::string* _Nonnull error);
+
+// Kill the currently running adb server, if it exists.
+bool adb_kill_server();
+
+// Connect to adb, connect to the named service, returns true if the connection
+// succeeded AND the service returned OKAY. Outputs any returned error otherwise.
+bool adb_command(const std::string& service);
+
+// Connects to the named adb service and fills 'result' with the response.
+// Returns true on success; returns false and fills 'error' on failure.
+bool adb_query(const std::string& service, std::string* _Nonnull result,
+               std::string* _Nonnull error);
+
+// Set the preferred transport to connect to.
+void adb_set_transport(TransportType type, const char* _Nullable serial, TransportId transport_id);
+void adb_get_transport(TransportType* _Nullable type, const char* _Nullable* _Nullable serial,
+                       TransportId* _Nullable transport_id);
+
+// Set the socket specification for the adb server.
+// This function can only be called once, and the argument must live to the end of the process.
+void adb_set_socket_spec(const char* _Nonnull socket_spec);
+
+// Send commands to the current emulator instance. Will fail if there is not
+// exactly one emulator connected (or if you use -s <serial> with a <serial>
+// that does not designate an emulator).
+int adb_send_emulator_command(int argc, const char* _Nonnull* _Nonnull argv,
+                              const char* _Nullable serial);
+
+// Reads a standard adb status response (OKAY|FAIL) and returns true in the
+// event of OKAY, false in the event of FAIL or protocol error.
+bool adb_status(int fd, std::string* _Nonnull error);
+
+// Create a host command corresponding to selected transport type/serial.
+std::string format_host_command(const char* _Nonnull command);
+
+// Get the feature set of the current preferred transport.
+bool adb_get_feature_set(FeatureSet* _Nonnull feature_set, std::string* _Nonnull error);
+
+#endif
diff --git a/adb/client/auth.cpp b/adb/client/auth.cpp
new file mode 100644
index 0000000..5fbef09
--- /dev/null
+++ b/adb/client/auth.cpp
@@ -0,0 +1,487 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+#define TRACE_TAG AUTH
+
+#include <dirent.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#if defined(__linux__)
+#include <sys/inotify.h>
+#endif
+
+#include <map>
+#include <mutex>
+#include <set>
+#include <string>
+
+#include <android-base/errors.h>
+#include <android-base/file.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+#include <crypto_utils/android_pubkey.h>
+#include <openssl/base64.h>
+#include <openssl/evp.h>
+#include <openssl/objects.h>
+#include <openssl/pem.h>
+#include <openssl/rsa.h>
+#include <openssl/sha.h>
+
+#include "adb.h"
+#include "adb_auth.h"
+#include "adb_utils.h"
+#include "sysdeps.h"
+#include "transport.h"
+
+static std::mutex& g_keys_mutex = *new std::mutex;
+static std::map<std::string, std::shared_ptr<RSA>>& g_keys =
+    *new std::map<std::string, std::shared_ptr<RSA>>;
+static std::map<int, std::string>& g_monitored_paths = *new std::map<int, std::string>;
+
+static std::string get_user_info() {
+    LOG(INFO) << "get_user_info...";
+
+    std::string hostname;
+    if (getenv("HOSTNAME")) hostname = getenv("HOSTNAME");
+#if !defined(_WIN32)
+    char buf[64];
+    if (hostname.empty() && gethostname(buf, sizeof(buf)) != -1) hostname = buf;
+#endif
+    if (hostname.empty()) hostname = "unknown";
+
+    std::string username;
+    if (getenv("LOGNAME")) username = getenv("LOGNAME");
+#if !defined _WIN32 && !defined ADB_HOST_ON_TARGET
+    if (username.empty() && getlogin()) username = getlogin();
+#endif
+    if (username.empty()) hostname = "unknown";
+
+    return " " + username + "@" + hostname;
+}
+
+static bool write_public_keyfile(RSA* private_key, const std::string& private_key_path) {
+    LOG(INFO) << "write_public_keyfile...";
+
+    uint8_t binary_key_data[ANDROID_PUBKEY_ENCODED_SIZE];
+    if (!android_pubkey_encode(private_key, binary_key_data, sizeof(binary_key_data))) {
+        LOG(ERROR) << "Failed to convert to public key";
+        return false;
+    }
+
+    size_t expected_length;
+    if (!EVP_EncodedLength(&expected_length, sizeof(binary_key_data))) {
+        LOG(ERROR) << "Public key too large to base64 encode";
+        return false;
+    }
+
+    std::string content;
+    content.resize(expected_length);
+    size_t actual_length = EVP_EncodeBlock(reinterpret_cast<uint8_t*>(&content[0]), binary_key_data,
+                                           sizeof(binary_key_data));
+    content.resize(actual_length);
+
+    content += get_user_info();
+
+    std::string path(private_key_path + ".pub");
+    if (!android::base::WriteStringToFile(content, path)) {
+        PLOG(ERROR) << "Failed to write public key to '" << path << "'";
+        return false;
+    }
+
+    return true;
+}
+
+static int generate_key(const std::string& file) {
+    LOG(INFO) << "generate_key(" << file << ")...";
+
+    mode_t old_mask;
+    FILE *f = nullptr;
+    int ret = 0;
+
+    EVP_PKEY* pkey = EVP_PKEY_new();
+    BIGNUM* exponent = BN_new();
+    RSA* rsa = RSA_new();
+    if (!pkey || !exponent || !rsa) {
+        LOG(ERROR) << "Failed to allocate key";
+        goto out;
+    }
+
+    BN_set_word(exponent, RSA_F4);
+    RSA_generate_key_ex(rsa, 2048, exponent, nullptr);
+    EVP_PKEY_set1_RSA(pkey, rsa);
+
+    old_mask = umask(077);
+
+    f = fopen(file.c_str(), "w");
+    if (!f) {
+        PLOG(ERROR) << "Failed to open " << file;
+        umask(old_mask);
+        goto out;
+    }
+
+    umask(old_mask);
+
+    if (!PEM_write_PrivateKey(f, pkey, nullptr, nullptr, 0, nullptr, nullptr)) {
+        D("Failed to write key");
+        goto out;
+    }
+
+    if (!write_public_keyfile(rsa, file)) {
+        D("Failed to write public key");
+        goto out;
+    }
+
+    ret = 1;
+
+out:
+    if (f) fclose(f);
+    EVP_PKEY_free(pkey);
+    RSA_free(rsa);
+    BN_free(exponent);
+    return ret;
+}
+
+static std::string hash_key(RSA* key) {
+    unsigned char* pubkey = nullptr;
+    int len = i2d_RSA_PUBKEY(key, &pubkey);
+    if (len < 0) {
+        LOG(ERROR) << "failed to encode RSA public key";
+        return std::string();
+    }
+
+    std::string result;
+    result.resize(SHA256_DIGEST_LENGTH);
+    SHA256(pubkey, len, reinterpret_cast<unsigned char*>(&result[0]));
+    OPENSSL_free(pubkey);
+    return result;
+}
+
+static bool read_key_file(const std::string& file) {
+    LOG(INFO) << "read_key_file '" << file << "'...";
+
+    std::unique_ptr<FILE, decltype(&fclose)> fp(fopen(file.c_str(), "r"), fclose);
+    if (!fp) {
+        PLOG(ERROR) << "Failed to open '" << file << "'";
+        return false;
+    }
+
+    RSA* key = RSA_new();
+    if (!PEM_read_RSAPrivateKey(fp.get(), &key, nullptr, nullptr)) {
+        LOG(ERROR) << "Failed to read key";
+        RSA_free(key);
+        return false;
+    }
+
+    std::lock_guard<std::mutex> lock(g_keys_mutex);
+    std::string fingerprint = hash_key(key);
+    if (g_keys.find(fingerprint) != g_keys.end()) {
+        LOG(INFO) << "ignoring already-loaded key: " << file;
+        RSA_free(key);
+    } else {
+        g_keys[fingerprint] = std::shared_ptr<RSA>(key, RSA_free);
+    }
+
+    return true;
+}
+
+static bool read_keys(const std::string& path, bool allow_dir = true) {
+    LOG(INFO) << "read_keys '" << path << "'...";
+
+    struct stat st;
+    if (stat(path.c_str(), &st) != 0) {
+        PLOG(ERROR) << "failed to stat '" << path << "'";
+        return false;
+    }
+
+    if (S_ISREG(st.st_mode)) {
+        return read_key_file(path);
+    } else if (S_ISDIR(st.st_mode)) {
+        if (!allow_dir) {
+            // inotify isn't recursive. It would break expectations to load keys in nested
+            // directories but not monitor them for new keys.
+            LOG(WARNING) << "refusing to recurse into directory '" << path << "'";
+            return false;
+        }
+
+        std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(path.c_str()), closedir);
+        if (!dir) {
+            PLOG(ERROR) << "failed to open directory '" << path << "'";
+            return false;
+        }
+
+        bool result = false;
+        while (struct dirent* dent = readdir(dir.get())) {
+            std::string name = dent->d_name;
+
+            // We can't use dent->d_type here because it's not available on Windows.
+            if (name == "." || name == "..") {
+                continue;
+            }
+
+            if (!android::base::EndsWith(name, ".adb_key")) {
+                LOG(INFO) << "skipping non-adb_key '" << path << "/" << name << "'";
+                continue;
+            }
+
+            result |= read_key_file((path + OS_PATH_SEPARATOR + name));
+        }
+        return result;
+    }
+
+    LOG(ERROR) << "unexpected type for '" << path << "': 0x" << std::hex << st.st_mode;
+    return false;
+}
+
+static std::string get_user_key_path() {
+    return adb_get_android_dir_path() + OS_PATH_SEPARATOR + "adbkey";
+}
+
+static bool get_user_key() {
+    std::string path = get_user_key_path();
+    if (path.empty()) {
+        PLOG(ERROR) << "Error getting user key filename";
+        return false;
+    }
+
+    struct stat buf;
+    if (stat(path.c_str(), &buf) == -1) {
+        LOG(INFO) << "User key '" << path << "' does not exist...";
+        if (!generate_key(path)) {
+            LOG(ERROR) << "Failed to generate new key";
+            return false;
+        }
+    }
+
+    return read_key_file(path);
+}
+
+static std::set<std::string> get_vendor_keys() {
+    const char* adb_keys_path = getenv("ADB_VENDOR_KEYS");
+    if (adb_keys_path == nullptr) {
+        return std::set<std::string>();
+    }
+
+    std::set<std::string> result;
+    for (const auto& path : android::base::Split(adb_keys_path, ENV_PATH_SEPARATOR_STR)) {
+        result.emplace(path);
+    }
+    return result;
+}
+
+std::deque<std::shared_ptr<RSA>> adb_auth_get_private_keys() {
+    std::deque<std::shared_ptr<RSA>> result;
+
+    // Copy all the currently known keys.
+    std::lock_guard<std::mutex> lock(g_keys_mutex);
+    for (const auto& it : g_keys) {
+        result.push_back(it.second);
+    }
+
+    // Add a sentinel to the list. Our caller uses this to mean "out of private keys,
+    // but try using the public key" (the empty deque could otherwise mean this _or_
+    // that this function hasn't been called yet to request the keys).
+    result.push_back(nullptr);
+
+    return result;
+}
+
+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 nullptr;
+    }
+
+    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*>(&result[0]), &len, key)) {
+        return std::string();
+    }
+
+    result.resize(len);
+
+    D("adb_auth_sign len=%d", len);
+    return result;
+}
+
+std::string adb_auth_get_userkey() {
+    std::string path = get_user_key_path();
+    if (path.empty()) {
+        PLOG(ERROR) << "Error getting user key filename";
+        return "";
+    }
+    path += ".pub";
+
+    std::string content;
+    if (!android::base::ReadFileToString(path, &content)) {
+        PLOG(ERROR) << "Can't load '" << path << "'";
+        return "";
+    }
+    return content;
+}
+
+int adb_auth_keygen(const char* filename) {
+    return (generate_key(filename) == 0);
+}
+
+#if defined(__linux__)
+static void adb_auth_inotify_update(int fd, unsigned fd_event, void*) {
+    LOG(INFO) << "adb_auth_inotify_update called";
+    if (!(fd_event & FDE_READ)) {
+        return;
+    }
+
+    char buf[sizeof(struct inotify_event) + NAME_MAX + 1];
+    while (true) {
+        ssize_t rc = TEMP_FAILURE_RETRY(unix_read(fd, buf, sizeof(buf)));
+        if (rc == -1) {
+            if (errno == EAGAIN) {
+                LOG(INFO) << "done reading inotify fd";
+                break;
+            }
+            PLOG(FATAL) << "read of inotify event failed";
+        }
+
+        // The read potentially returned multiple events.
+        char* start = buf;
+        char* end = buf + rc;
+
+        while (start < end) {
+            inotify_event* event = reinterpret_cast<inotify_event*>(start);
+            auto root_it = g_monitored_paths.find(event->wd);
+            if (root_it == g_monitored_paths.end()) {
+                LOG(FATAL) << "observed inotify event for unmonitored path, wd = " << event->wd;
+            }
+
+            std::string path = root_it->second;
+            if (event->len > 0) {
+                path += '/';
+                path += event->name;
+            }
+
+            if (event->mask & (IN_CREATE | IN_MOVED_TO)) {
+                if (event->mask & IN_ISDIR) {
+                    LOG(INFO) << "ignoring new directory at '" << path << "'";
+                } else {
+                    LOG(INFO) << "observed new file at '" << path << "'";
+                    read_keys(path, false);
+                }
+            } else {
+                LOG(WARNING) << "unmonitored event for " << path << ": 0x" << std::hex
+                             << event->mask;
+            }
+
+            start += sizeof(struct inotify_event) + event->len;
+        }
+    }
+}
+
+static void adb_auth_inotify_init(const std::set<std::string>& paths) {
+    LOG(INFO) << "adb_auth_inotify_init...";
+
+    int infd = inotify_init1(IN_CLOEXEC | IN_NONBLOCK);
+    if (infd < 0) {
+        PLOG(ERROR) << "failed to create inotify fd";
+        return;
+    }
+
+    for (const std::string& path : paths) {
+        int wd = inotify_add_watch(infd, path.c_str(), IN_CREATE | IN_MOVED_TO);
+        if (wd < 0) {
+            PLOG(ERROR) << "failed to inotify_add_watch on path '" << path;
+            continue;
+        }
+
+        g_monitored_paths[wd] = path;
+        LOG(INFO) << "watch descriptor " << wd << " registered for " << path;
+    }
+
+    fdevent* event = fdevent_create(infd, adb_auth_inotify_update, nullptr);
+    fdevent_add(event, FDE_READ);
+}
+#endif
+
+void adb_auth_init() {
+    LOG(INFO) << "adb_auth_init...";
+
+    if (!get_user_key()) {
+        LOG(ERROR) << "Failed to get user key";
+        return;
+    }
+
+    const auto& key_paths = get_vendor_keys();
+
+#if defined(__linux__)
+    adb_auth_inotify_init(key_paths);
+#endif
+
+    for (const std::string& path : key_paths) {
+        read_keys(path.c_str());
+    }
+}
+
+static void send_auth_publickey(atransport* t) {
+    LOG(INFO) << "Calling send_auth_publickey";
+
+    std::string key = adb_auth_get_userkey();
+    if (key.empty()) {
+        D("Failed to get user public key");
+        return;
+    }
+
+    if (key.size() >= MAX_PAYLOAD_V1) {
+        D("User public key too large (%zu B)", key.size());
+        return;
+    }
+
+    apacket* p = get_apacket();
+    p->msg.command = A_AUTH;
+    p->msg.arg0 = ADB_AUTH_RSAPUBLICKEY;
+
+    // adbd expects a null-terminated string.
+    p->payload.assign(key.data(), key.data() + key.size() + 1);
+    p->msg.data_length = p->payload.size();
+    send_packet(p, t);
+}
+
+void send_auth_response(const char* token, size_t token_size, atransport* t) {
+    std::shared_ptr<RSA> key = t->NextKey();
+    if (key == nullptr) {
+        // No more private keys to try, send the public key.
+        t->SetConnectionState(kCsUnauthorized);
+        send_auth_publickey(t);
+        return;
+    }
+
+    LOG(INFO) << "Calling send_auth_response";
+    apacket* p = get_apacket();
+
+    std::string result = adb_auth_sign(key.get(), token, token_size);
+    if (result.empty()) {
+        D("Error signing the token");
+        put_apacket(p);
+        return;
+    }
+
+    p->msg.command = A_AUTH;
+    p->msg.arg0 = ADB_AUTH_SIGNATURE;
+    p->payload.assign(result.begin(), result.end());
+    p->msg.data_length = p->payload.size();
+    send_packet(p, t);
+}
diff --git a/adb/client/bugreport.cpp b/adb/client/bugreport.cpp
new file mode 100644
index 0000000..fe98737
--- /dev/null
+++ b/adb/client/bugreport.cpp
@@ -0,0 +1,286 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#define TRACE_TAG ADB
+
+#include "sysdeps.h"
+
+#include "bugreport.h"
+
+#include <string>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/strings.h>
+
+#include "adb_utils.h"
+#include "client/file_sync_client.h"
+
+static constexpr char BUGZ_BEGIN_PREFIX[] = "BEGIN:";
+static constexpr char BUGZ_PROGRESS_PREFIX[] = "PROGRESS:";
+static constexpr char BUGZ_PROGRESS_SEPARATOR[] = "/";
+static constexpr char BUGZ_OK_PREFIX[] = "OK:";
+static constexpr char BUGZ_FAIL_PREFIX[] = "FAIL:";
+
+// Custom callback used to handle the output of zipped bugreports.
+class BugreportStandardStreamsCallback : public StandardStreamsCallbackInterface {
+  public:
+    BugreportStandardStreamsCallback(const std::string& dest_dir, const std::string& dest_file,
+                                     bool show_progress, Bugreport* br)
+        : br_(br),
+          src_file_(),
+          dest_dir_(dest_dir),
+          dest_file_(dest_file),
+          line_message_(),
+          invalid_lines_(),
+          show_progress_(show_progress),
+          status_(0),
+          line_(),
+          last_progress_percentage_(0) {
+        SetLineMessage("generating");
+    }
+
+    void OnStdout(const char* buffer, int length) {
+        for (int i = 0; i < length; i++) {
+            char c = buffer[i];
+            if (c == '\n') {
+                ProcessLine(line_);
+                line_.clear();
+            } else {
+                line_.append(1, c);
+            }
+        }
+    }
+
+    void OnStderr(const char* buffer, int length) {
+        OnStream(nullptr, stderr, buffer, length);
+    }
+
+    int Done(int unused_) {
+        // Process remaining line, if any.
+        ProcessLine(line_);
+
+        // Warn about invalid lines, if any.
+        if (!invalid_lines_.empty()) {
+            fprintf(stderr,
+                    "WARNING: bugreportz generated %zu line(s) with unknown commands, "
+                    "device might not support zipped bugreports:\n",
+                    invalid_lines_.size());
+            for (const auto& line : invalid_lines_) {
+                fprintf(stderr, "\t%s\n", line.c_str());
+            }
+            fprintf(stderr,
+                    "If the zipped bugreport was not generated, try 'adb bugreport' instead.\n");
+        }
+
+        // Pull the generated bug report.
+        if (status_ == 0) {
+            if (src_file_.empty()) {
+                fprintf(stderr, "bugreportz did not return a '%s' or '%s' line\n", BUGZ_OK_PREFIX,
+                        BUGZ_FAIL_PREFIX);
+                return -1;
+            }
+            std::string destination;
+            if (dest_dir_.empty()) {
+                destination = dest_file_;
+            } else {
+                destination = android::base::StringPrintf("%s%c%s", dest_dir_.c_str(),
+                                                          OS_PATH_SEPARATOR, dest_file_.c_str());
+            }
+            std::vector<const char*> srcs{src_file_.c_str()};
+            SetLineMessage("pulling");
+            status_ =
+                br_->DoSyncPull(srcs, destination.c_str(), false, line_message_.c_str()) ? 0 : 1;
+            if (status_ != 0) {
+                fprintf(stderr,
+                        "Bug report finished but could not be copied to '%s'.\n"
+                        "Try to run 'adb pull %s <directory>'\n"
+                        "to copy it to a directory that can be written.\n",
+                        destination.c_str(), src_file_.c_str());
+            }
+        }
+        return status_;
+    }
+
+  private:
+    void SetLineMessage(const std::string& action) {
+        line_message_ = action + " " + android::base::Basename(dest_file_);
+    }
+
+    void SetSrcFile(const std::string path) {
+        src_file_ = path;
+        if (!dest_dir_.empty()) {
+            // Only uses device-provided name when user passed a directory.
+            dest_file_ = android::base::Basename(path);
+            SetLineMessage("generating");
+        }
+    }
+
+    void ProcessLine(const std::string& line) {
+        if (line.empty()) return;
+
+        if (android::base::StartsWith(line, BUGZ_BEGIN_PREFIX)) {
+            SetSrcFile(&line[strlen(BUGZ_BEGIN_PREFIX)]);
+        } else if (android::base::StartsWith(line, BUGZ_OK_PREFIX)) {
+            SetSrcFile(&line[strlen(BUGZ_OK_PREFIX)]);
+        } else if (android::base::StartsWith(line, BUGZ_FAIL_PREFIX)) {
+            const char* error_message = &line[strlen(BUGZ_FAIL_PREFIX)];
+            fprintf(stderr, "adb: device failed to take a zipped bugreport: %s\n", error_message);
+            status_ = -1;
+        } else if (show_progress_ && android::base::StartsWith(line, BUGZ_PROGRESS_PREFIX)) {
+            // progress_line should have the following format:
+            //
+            // BUGZ_PROGRESS_PREFIX:PROGRESS/TOTAL
+            //
+            size_t idx1 = line.rfind(BUGZ_PROGRESS_PREFIX) + strlen(BUGZ_PROGRESS_PREFIX);
+            size_t idx2 = line.rfind(BUGZ_PROGRESS_SEPARATOR);
+            int progress = std::stoi(line.substr(idx1, (idx2 - idx1)));
+            int total = std::stoi(line.substr(idx2 + 1));
+            int progress_percentage = (progress * 100 / total);
+            if (progress_percentage != 0 && progress_percentage <= last_progress_percentage_) {
+                // Ignore.
+                return;
+            }
+            last_progress_percentage_ = progress_percentage;
+            br_->UpdateProgress(line_message_, progress_percentage);
+        } else {
+            invalid_lines_.push_back(line);
+        }
+    }
+
+    Bugreport* br_;
+
+    // Path of bugreport on device.
+    std::string src_file_;
+
+    // Bugreport destination on host, depending on argument passed on constructor:
+    // - if argument is a directory, dest_dir_ is set with it and dest_file_ will be the name
+    //   of the bugreport reported by the device.
+    // - if argument is empty, dest_dir is set as the current directory and dest_file_ will be the
+    //   name of the bugreport reported by the device.
+    // - otherwise, dest_dir_ is not set and dest_file_ is set with the value passed on constructor.
+    std::string dest_dir_, dest_file_;
+
+    // Message displayed on LinePrinter, it's updated every time the destination above change.
+    std::string line_message_;
+
+    // Lines sent by bugreportz that contain invalid commands; will be displayed at the end.
+    std::vector<std::string> invalid_lines_;
+
+    // Whether PROGRESS_LINES should be interpreted as progress.
+    bool show_progress_;
+
+    // Overall process of the operation, as returned by Done().
+    int status_;
+
+    // Temporary buffer containing the characters read since the last newline (\n).
+    std::string line_;
+
+    // Last displayed progress.
+    // Since dumpstate progress can recede, only forward progress should be displayed
+    int last_progress_percentage_;
+
+    DISALLOW_COPY_AND_ASSIGN(BugreportStandardStreamsCallback);
+};
+
+int Bugreport::DoIt(int argc, const char** argv) {
+    if (argc > 2) return syntax_error("adb bugreport [PATH]");
+
+    // Gets bugreportz version.
+    std::string bugz_stdout, bugz_stderr;
+    DefaultStandardStreamsCallback version_callback(&bugz_stdout, &bugz_stderr);
+    int status = SendShellCommand("bugreportz -v", false, &version_callback);
+    std::string bugz_version = android::base::Trim(bugz_stderr);
+    std::string bugz_output = android::base::Trim(bugz_stdout);
+
+    if (status != 0 || bugz_version.empty()) {
+        D("'bugreportz' -v results: status=%d, stdout='%s', stderr='%s'", status,
+          bugz_output.c_str(), bugz_version.c_str());
+        if (argc == 1) {
+            // Device does not support bugreportz: if called as 'adb bugreport', just falls out to
+            // the flat-file version.
+            fprintf(stderr,
+                    "Failed to get bugreportz version, which is only available on devices "
+                    "running Android 7.0 or later.\nTrying a plain-text bug report instead.\n");
+            return SendShellCommand("bugreport", false);
+        }
+
+        // But if user explicitly asked for a zipped bug report, fails instead (otherwise calling
+        // 'bugreport' would generate a lot of output the user might not be prepared to handle).
+        fprintf(stderr,
+                "Failed to get bugreportz version: 'bugreportz -v' returned '%s' (code %d).\n"
+                "If the device does not run Android 7.0 or above, try 'adb bugreport' instead.\n",
+                bugz_output.c_str(), status);
+        return status != 0 ? status : -1;
+    }
+
+    std::string dest_file, dest_dir;
+
+    if (argc == 1) {
+        // No args - use current directory
+        if (!getcwd(&dest_dir)) {
+            perror("adb: getcwd failed");
+            return 1;
+        }
+    } else {
+        // Check whether argument is a directory or file
+        if (directory_exists(argv[1])) {
+            dest_dir = argv[1];
+        } else {
+            dest_file = argv[1];
+        }
+    }
+
+    if (dest_file.empty()) {
+        // Uses a default value until device provides the proper name
+        dest_file = "bugreport.zip";
+    } else {
+        if (!android::base::EndsWithIgnoreCase(dest_file, ".zip")) {
+            dest_file += ".zip";
+        }
+    }
+
+    bool show_progress = true;
+    std::string bugz_command = "bugreportz -p";
+    if (bugz_version == "1.0") {
+        // 1.0 does not support progress notifications, so print a disclaimer
+        // message instead.
+        fprintf(stderr,
+                "Bugreport is in progress and it could take minutes to complete.\n"
+                "Please be patient and do not cancel or disconnect your device "
+                "until it completes.\n");
+        show_progress = false;
+        bugz_command = "bugreportz";
+    }
+    BugreportStandardStreamsCallback bugz_callback(dest_dir, dest_file, show_progress, this);
+    return SendShellCommand(bugz_command, false, &bugz_callback);
+}
+
+void Bugreport::UpdateProgress(const std::string& message, int progress_percentage) {
+    line_printer_.Print(
+        android::base::StringPrintf("[%3d%%] %s", progress_percentage, message.c_str()),
+        LinePrinter::INFO);
+}
+
+int Bugreport::SendShellCommand(const std::string& command, bool disable_shell_protocol,
+                                StandardStreamsCallbackInterface* callback) {
+    return send_shell_command(command, disable_shell_protocol, callback);
+}
+
+bool Bugreport::DoSyncPull(const std::vector<const char*>& srcs, const char* dst, bool copy_attrs,
+                           const char* name) {
+    return do_sync_pull(srcs, dst, copy_attrs, name);
+}
diff --git a/adb/client/bugreport.h b/adb/client/bugreport.h
new file mode 100644
index 0000000..413439b
--- /dev/null
+++ b/adb/client/bugreport.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2016 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 BUGREPORT_H
+#define BUGREPORT_H
+
+#include <vector>
+
+#include "adb.h"
+#include "commandline.h"
+#include "line_printer.h"
+
+class Bugreport {
+    friend class BugreportStandardStreamsCallback;
+
+  public:
+    Bugreport() : line_printer_() {
+    }
+    int DoIt(int argc, const char** argv);
+
+  protected:
+    // Functions below are abstractions of external functions so they can be
+    // mocked on tests.
+    virtual int SendShellCommand(
+        const std::string& command, bool disable_shell_protocol,
+        StandardStreamsCallbackInterface* callback = &DEFAULT_STANDARD_STREAMS_CALLBACK);
+
+    virtual bool DoSyncPull(const std::vector<const char*>& srcs, const char* dst, bool copy_attrs,
+                            const char* name);
+
+  private:
+    virtual void UpdateProgress(const std::string& file_name, int progress_percentage);
+    LinePrinter line_printer_;
+    DISALLOW_COPY_AND_ASSIGN(Bugreport);
+};
+
+#endif  // BUGREPORT_H
diff --git a/adb/client/commandline.cpp b/adb/client/commandline.cpp
new file mode 100644
index 0000000..3fb14f3
--- /dev/null
+++ b/adb/client/commandline.cpp
@@ -0,0 +1,2113 @@
+/*
+ * Copyright (C) 2007 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.
+ */
+
+#define TRACE_TAG ADB
+
+#include "sysdeps.h"
+
+#include <assert.h>
+#include <ctype.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <limits.h>
+#include <stdarg.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <memory>
+#include <string>
+#include <thread>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/parseint.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+
+#if !defined(_WIN32)
+#include <signal.h>
+#include <sys/ioctl.h>
+#include <termios.h>
+#include <unistd.h>
+#endif
+
+#include "adb.h"
+#include "adb_auth.h"
+#include "adb_client.h"
+#include "adb_io.h"
+#include "adb_unique_fd.h"
+#include "adb_utils.h"
+#include "bugreport.h"
+#include "client/file_sync_client.h"
+#include "commandline.h"
+#include "services.h"
+#include "shell_protocol.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);
+static int uninstall_app(int argc, const char** argv);
+static int install_app_legacy(int argc, const char** argv);
+static int uninstall_app_legacy(int argc, const char** argv);
+
+extern int gListenAll;
+
+DefaultStandardStreamsCallback DEFAULT_STANDARD_STREAMS_CALLBACK(nullptr, nullptr);
+
+static std::string product_file(const std::string& file) {
+    const char* ANDROID_PRODUCT_OUT = getenv("ANDROID_PRODUCT_OUT");
+    if (ANDROID_PRODUCT_OUT == nullptr) {
+        fprintf(stderr, "adb: product directory not specified; set $ANDROID_PRODUCT_OUT\n");
+        exit(1);
+    }
+    return std::string{ANDROID_PRODUCT_OUT} + OS_PATH_SEPARATOR_STR + file;
+}
+
+static void help() {
+    fprintf(stdout, "%s\n", adb_version().c_str());
+    // clang-format off
+    fprintf(stdout,
+        "global options:\n"
+        " -a         listen on all network interfaces, not just localhost\n"
+        " -d         use USB device (error if multiple devices connected)\n"
+        " -e         use TCP/IP device (error if multiple TCP/IP devices available)\n"
+        " -s SERIAL  use device with given serial (overrides $ANDROID_SERIAL)\n"
+        " -t ID      use device with given transport id\n"
+        " -H         name of adb server host [default=localhost]\n"
+        " -P         port of adb server [default=5037]\n"
+        " -L SOCKET  listen on given socket for adb server [default=tcp:localhost:5037]\n"
+        "\n"
+        "general commands:\n"
+        " devices [-l]             list connected devices (-l for long output)\n"
+        " help                     show this help message\n"
+        " version                  show version num\n"
+        "\n"
+        "networking:\n"
+        " connect HOST[:PORT]      connect to a device via TCP/IP [default port=5555]\n"
+        " disconnect [HOST[:PORT]]\n"
+        "     disconnect from given TCP/IP device [default port=5555], or all\n"
+        " forward --list           list all forward socket connections\n"
+        " forward [--no-rebind] LOCAL REMOTE\n"
+        "     forward socket connection using:\n"
+        "       tcp:<port> (<local> may be \"tcp:0\" to pick any open port)\n"
+        "       localabstract:<unix domain socket name>\n"
+        "       localreserved:<unix domain socket name>\n"
+        "       localfilesystem:<unix domain socket name>\n"
+        "       dev:<character device name>\n"
+        "       jdwp:<process pid> (remote only)\n"
+        " forward --remove LOCAL   remove specific forward socket connection\n"
+        " forward --remove-all     remove all forward socket connections\n"
+        " ppp TTY [PARAMETER...]   run PPP over USB\n"
+        " reverse --list           list all reverse socket connections from device\n"
+        " reverse [--no-rebind] REMOTE LOCAL\n"
+        "     reverse socket connection using:\n"
+        "       tcp:<port> (<remote> may be \"tcp:0\" to pick any open port)\n"
+        "       localabstract:<unix domain socket name>\n"
+        "       localreserved:<unix domain socket name>\n"
+        "       localfilesystem:<unix domain socket name>\n"
+        " reverse --remove REMOTE  remove specific reverse socket connection\n"
+        " reverse --remove-all     remove all reverse socket connections from device\n"
+        "\n"
+        "file transfer:\n"
+        " push [--sync] LOCAL... REMOTE\n"
+        "     copy local files/directories to device\n"
+        "     --sync: only push files that are newer on the host than the device\n"
+        " pull [-a] REMOTE... LOCAL\n"
+        "     copy files/dirs from device\n"
+        "     -a: preserve file timestamp and mode\n"
+        " sync [all|data|odm|oem|product|system|vendor]\n"
+        "     sync a local build from $ANDROID_PRODUCT_OUT to the device (default all)\n"
+        "     -l: list but don't copy\n"
+        "\n"
+        "shell:\n"
+        " shell [-e ESCAPE] [-n] [-Tt] [-x] [COMMAND...]\n"
+        "     run remote shell command (interactive shell if no command given)\n"
+        "     -e: choose escape character, or \"none\"; default '~'\n"
+        "     -n: don't read from stdin\n"
+        "     -T: disable PTY allocation\n"
+        "     -t: force PTY allocation\n"
+        "     -x: disable remote exit codes and stdout/stderr separation\n"
+        " emu COMMAND              run emulator console command\n"
+        "\n"
+        "app installation:\n"
+        " install [-lrtsdg] [--instant] PACKAGE\n"
+        " install-multiple [-lrtsdpg] [--instant] PACKAGE...\n"
+        "     push package(s) to the device and install them\n"
+        "     -l: forward lock application\n"
+        "     -r: replace existing application\n"
+        "     -t: allow test packages\n"
+        "     -s: install application on sdcard\n"
+        "     -d: allow version code downgrade (debuggable packages only)\n"
+        "     -p: partial application install (install-multiple only)\n"
+        "     -g: grant all runtime permissions\n"
+        "     --instant: cause the app to be installed as an ephemeral install app\n"
+        " uninstall [-k] PACKAGE\n"
+        "     remove this app package from the device\n"
+        "     '-k': keep the data and cache directories\n"
+        "\n"
+        "backup/restore:\n"
+        "   to show usage run \"adb shell bu help\"\n"
+        "\n"
+        "debugging:\n"
+        " bugreport [PATH]\n"
+        "     write bugreport to given PATH [default=bugreport.zip];\n"
+        "     if PATH is a directory, the bug report is saved in that directory.\n"
+        "     devices that don't support zipped bug reports output to stdout.\n"
+        " jdwp                     list pids of processes hosting a JDWP transport\n"
+        " logcat                   show device log (logcat --help for more)\n"
+        "\n"
+        "security:\n"
+        " disable-verity           disable dm-verity checking on userdebug builds\n"
+        " enable-verity            re-enable dm-verity checking on userdebug builds\n"
+        " keygen FILE\n"
+        "     generate adb public/private key; private key stored in FILE,\n"
+        "     public key stored in FILE.pub (existing files overwritten)\n"
+        "\n"
+        "scripting:\n"
+        " wait-for[-TRANSPORT]-STATE\n"
+        "     wait for device to be in the given state\n"
+        "     State: device, recovery, sideload, or bootloader\n"
+        "     Transport: usb, local, or any [default=any]\n"
+        " get-state                print offline | bootloader | device\n"
+        " get-serialno             print <serial-number>\n"
+        " get-devpath              print <device-path>\n"
+        " remount                  remount partitions read-write\n"
+        " reboot [bootloader|recovery|sideload|sideload-auto-reboot]\n"
+        "     reboot the device; defaults to booting system image but\n"
+        "     supports bootloader and recovery too. sideload reboots\n"
+        "     into recovery and automatically starts sideload mode,\n"
+        "     sideload-auto-reboot is the same but reboots after sideloading.\n"
+        " sideload OTAPACKAGE      sideload the given full OTA package\n"
+        " root                     restart adbd with root permissions\n"
+        " unroot                   restart adbd without root permissions\n"
+        " usb                      restart adb server listening on USB\n"
+        " tcpip PORT               restart adb server listening on TCP on PORT\n"
+        "\n"
+        "internal debugging:\n"
+        " start-server             ensure that there is a server running\n"
+        " kill-server              kill the server if it is running\n"
+        " reconnect                kick connection from host side to force reconnect\n"
+        " reconnect device         kick connection from device side to force reconnect\n"
+        " reconnect offline        reset offline/unauthorized devices to force reconnect\n"
+        "\n"
+        "environment variables:\n"
+        " $ADB_TRACE\n"
+        "     comma-separated list of debug info to log:\n"
+        "     all,adb,sockets,packets,rwx,usb,sync,sysdeps,transport,jdwp\n"
+        " $ADB_VENDOR_KEYS         colon-separated list of keys (files or directories)\n"
+        " $ANDROID_SERIAL          serial number to connect to (see -s)\n"
+        " $ANDROID_LOG_TAGS        tags to be used by logcat (see logcat --help)\n");
+    // clang-format on
+}
+
+#if defined(_WIN32)
+
+// Implemented in sysdeps_win32.cpp.
+void stdin_raw_init();
+void stdin_raw_restore();
+
+#else
+static termios g_saved_terminal_state;
+
+static void stdin_raw_init() {
+    if (tcgetattr(STDIN_FILENO, &g_saved_terminal_state)) return;
+
+    termios tio;
+    if (tcgetattr(STDIN_FILENO, &tio)) return;
+
+    cfmakeraw(&tio);
+
+    // No timeout but request at least one character per read.
+    tio.c_cc[VTIME] = 0;
+    tio.c_cc[VMIN] = 1;
+
+    tcsetattr(STDIN_FILENO, TCSAFLUSH, &tio);
+}
+
+static void stdin_raw_restore() {
+    tcsetattr(STDIN_FILENO, TCSAFLUSH, &g_saved_terminal_state);
+}
+#endif
+
+// Reads from |fd| and prints received data. If |use_shell_protocol| is true
+// this expects that incoming data will use the shell protocol, in which case
+// stdout/stderr are routed independently and the remote exit code will be
+// returned.
+// if |callback| is non-null, stdout/stderr output will be handled by it.
+int read_and_dump(int fd, bool use_shell_protocol = false,
+                  StandardStreamsCallbackInterface* callback = &DEFAULT_STANDARD_STREAMS_CALLBACK) {
+    int exit_code = 0;
+    if (fd < 0) return exit_code;
+
+    std::unique_ptr<ShellProtocol> protocol;
+    int length = 0;
+
+    char raw_buffer[BUFSIZ];
+    char* buffer_ptr = raw_buffer;
+    if (use_shell_protocol) {
+        protocol = std::make_unique<ShellProtocol>(fd);
+        if (!protocol) {
+            LOG(ERROR) << "failed to allocate memory for ShellProtocol object";
+            return 1;
+        }
+        buffer_ptr = protocol->data();
+    }
+
+    while (true) {
+        if (use_shell_protocol) {
+            if (!protocol->Read()) {
+                break;
+            }
+            length = protocol->data_length();
+            switch (protocol->id()) {
+                case ShellProtocol::kIdStdout:
+                    callback->OnStdout(buffer_ptr, length);
+                    break;
+                case ShellProtocol::kIdStderr:
+                    callback->OnStderr(buffer_ptr, length);
+                    break;
+                case ShellProtocol::kIdExit:
+                    exit_code = protocol->data()[0];
+                    continue;
+                default:
+                    continue;
+            }
+            length = protocol->data_length();
+        } else {
+            D("read_and_dump(): pre adb_read(fd=%d)", fd);
+            length = adb_read(fd, raw_buffer, sizeof(raw_buffer));
+            D("read_and_dump(): post adb_read(fd=%d): length=%d", fd, length);
+            if (length <= 0) {
+                break;
+            }
+            callback->OnStdout(buffer_ptr, length);
+        }
+    }
+
+    return callback->Done(exit_code);
+}
+
+static void read_status_line(int fd, char* buf, size_t count)
+{
+    count--;
+    while (count > 0) {
+        int len = adb_read(fd, buf, count);
+        if (len <= 0) {
+            break;
+        }
+
+        buf += len;
+        count -= len;
+    }
+    *buf = '\0';
+}
+
+static void stdinout_raw_prologue(int inFd, int outFd, int& old_stdin_mode, int& old_stdout_mode) {
+    if (inFd == STDIN_FILENO) {
+        stdin_raw_init();
+#ifdef _WIN32
+        old_stdin_mode = _setmode(STDIN_FILENO, _O_BINARY);
+        if (old_stdin_mode == -1) {
+            fatal_errno("could not set stdin to binary");
+        }
+#endif
+    }
+
+#ifdef _WIN32
+    if (outFd == STDOUT_FILENO) {
+        old_stdout_mode = _setmode(STDOUT_FILENO, _O_BINARY);
+        if (old_stdout_mode == -1) {
+            fatal_errno("could not set stdout to binary");
+        }
+    }
+#endif
+}
+
+static void stdinout_raw_epilogue(int inFd, int outFd, int old_stdin_mode, int old_stdout_mode) {
+    if (inFd == STDIN_FILENO) {
+        stdin_raw_restore();
+#ifdef _WIN32
+        if (_setmode(STDIN_FILENO, old_stdin_mode) == -1) {
+            fatal_errno("could not restore stdin mode");
+        }
+#endif
+    }
+
+#ifdef _WIN32
+    if (outFd == STDOUT_FILENO) {
+        if (_setmode(STDOUT_FILENO, old_stdout_mode) == -1) {
+            fatal_errno("could not restore stdout mode");
+        }
+    }
+#endif
+}
+
+static void copy_to_file(int inFd, int outFd) {
+    constexpr size_t BUFSIZE = 32 * 1024;
+    std::vector<char> buf(BUFSIZE);
+    int len;
+    long total = 0;
+    int old_stdin_mode = -1;
+    int old_stdout_mode = -1;
+
+    D("copy_to_file(%d -> %d)", inFd, outFd);
+
+    stdinout_raw_prologue(inFd, outFd, old_stdin_mode, old_stdout_mode);
+
+    while (true) {
+        if (inFd == STDIN_FILENO) {
+            len = unix_read(inFd, buf.data(), BUFSIZE);
+        } else {
+            len = adb_read(inFd, buf.data(), BUFSIZE);
+        }
+        if (len == 0) {
+            D("copy_to_file() : read 0 bytes; exiting");
+            break;
+        }
+        if (len < 0) {
+            D("copy_to_file(): read failed: %s", strerror(errno));
+            break;
+        }
+        if (outFd == STDOUT_FILENO) {
+            fwrite(buf.data(), 1, len, stdout);
+            fflush(stdout);
+        } else {
+            adb_write(outFd, buf.data(), len);
+        }
+        total += len;
+    }
+
+    stdinout_raw_epilogue(inFd, outFd, old_stdin_mode, old_stdout_mode);
+
+    D("copy_to_file() finished after %lu bytes", total);
+}
+
+static void send_window_size_change(int fd, std::unique_ptr<ShellProtocol>& shell) {
+    // Old devices can't handle window size changes.
+    if (shell == nullptr) return;
+
+#if defined(_WIN32)
+    struct winsize {
+        unsigned short ws_row;
+        unsigned short ws_col;
+        unsigned short ws_xpixel;
+        unsigned short ws_ypixel;
+    };
+#endif
+
+    winsize ws;
+
+#if defined(_WIN32)
+    // If stdout is redirected to a non-console, we won't be able to get the
+    // console size, but that makes sense.
+    const intptr_t intptr_handle = _get_osfhandle(STDOUT_FILENO);
+    if (intptr_handle == -1) return;
+
+    const HANDLE handle = reinterpret_cast<const HANDLE>(intptr_handle);
+
+    CONSOLE_SCREEN_BUFFER_INFO info;
+    memset(&info, 0, sizeof(info));
+    if (!GetConsoleScreenBufferInfo(handle, &info)) return;
+
+    memset(&ws, 0, sizeof(ws));
+    // The number of visible rows, excluding offscreen scroll-back rows which are in info.dwSize.Y.
+    ws.ws_row = info.srWindow.Bottom - info.srWindow.Top + 1;
+    // If the user has disabled "Wrap text output on resize", they can make the screen buffer wider
+    // than the window, in which case we should use the width of the buffer.
+    ws.ws_col = info.dwSize.X;
+#else
+    if (ioctl(fd, TIOCGWINSZ, &ws) == -1) return;
+#endif
+
+    // Send the new window size as human-readable ASCII for debugging convenience.
+    size_t l = snprintf(shell->data(), shell->data_capacity(), "%dx%d,%dx%d",
+                        ws.ws_row, ws.ws_col, ws.ws_xpixel, ws.ws_ypixel);
+    shell->Write(ShellProtocol::kIdWindowSizeChange, l + 1);
+}
+
+// Used to pass multiple values to the stdin read thread.
+struct StdinReadArgs {
+    int stdin_fd, write_fd;
+    bool raw_stdin;
+    std::unique_ptr<ShellProtocol> protocol;
+    char escape_char;
+};
+
+// Loops to read from stdin and push the data to the given FD.
+// The argument should be a pointer to a StdinReadArgs object. This function
+// will take ownership of the object and delete it when finished.
+static void stdin_read_thread_loop(void* x) {
+    std::unique_ptr<StdinReadArgs> args(reinterpret_cast<StdinReadArgs*>(x));
+
+#if !defined(_WIN32)
+    // Mask SIGTTIN in case we're in a backgrounded process.
+    sigset_t sigset;
+    sigemptyset(&sigset);
+    sigaddset(&sigset, SIGTTIN);
+    pthread_sigmask(SIG_BLOCK, &sigset, nullptr);
+#endif
+
+#if defined(_WIN32)
+    // _get_interesting_input_record_uncached() causes unix_read_interruptible()
+    // to return -1 with errno == EINTR if the window size changes.
+#else
+    // Unblock SIGWINCH for this thread, so our read(2) below will be
+    // interrupted if the window size changes.
+    sigset_t mask;
+    sigemptyset(&mask);
+    sigaddset(&mask, SIGWINCH);
+    pthread_sigmask(SIG_UNBLOCK, &mask, nullptr);
+#endif
+
+    // Set up the initial window size.
+    send_window_size_change(args->stdin_fd, args->protocol);
+
+    char raw_buffer[BUFSIZ];
+    char* buffer_ptr = raw_buffer;
+    size_t buffer_size = sizeof(raw_buffer);
+    if (args->protocol != nullptr) {
+        buffer_ptr = args->protocol->data();
+        buffer_size = args->protocol->data_capacity();
+    }
+
+    // If we need to parse escape sequences, make life easy.
+    if (args->raw_stdin && args->escape_char != '\0') {
+        buffer_size = 1;
+    }
+
+    enum EscapeState { kMidFlow, kStartOfLine, kInEscape };
+    EscapeState state = kStartOfLine;
+
+    while (true) {
+        // Use unix_read_interruptible() rather than adb_read() for stdin.
+        D("stdin_read_thread_loop(): pre unix_read_interruptible(fdi=%d,...)", args->stdin_fd);
+        int r = unix_read_interruptible(args->stdin_fd, buffer_ptr,
+                                        buffer_size);
+        if (r == -1 && errno == EINTR) {
+            send_window_size_change(args->stdin_fd, args->protocol);
+            continue;
+        }
+        D("stdin_read_thread_loop(): post unix_read_interruptible(fdi=%d,...)", args->stdin_fd);
+        if (r <= 0) {
+            // Only devices using the shell protocol know to close subprocess
+            // stdin. For older devices we want to just leave the connection
+            // open, otherwise an unpredictable amount of return data could
+            // be lost due to the FD closing before all data has been received.
+            if (args->protocol) {
+                args->protocol->Write(ShellProtocol::kIdCloseStdin, 0);
+            }
+            break;
+        }
+        // If we made stdin raw, check input for escape sequences. In
+        // this situation signals like Ctrl+C are sent remotely rather than
+        // interpreted locally so this provides an emergency out if the remote
+        // process starts ignoring the signal. SSH also does this, see the
+        // "escape characters" section on the ssh man page for more info.
+        if (args->raw_stdin && args->escape_char != '\0') {
+            char ch = buffer_ptr[0];
+            if (ch == args->escape_char) {
+                if (state == kStartOfLine) {
+                    state = kInEscape;
+                    // Swallow the escape character.
+                    continue;
+                } else {
+                    state = kMidFlow;
+                }
+            } else {
+                if (state == kInEscape) {
+                    if (ch == '.') {
+                        fprintf(stderr,"\r\n[ disconnected ]\r\n");
+                        stdin_raw_restore();
+                        exit(0);
+                    } else {
+                        // We swallowed an escape character that wasn't part of
+                        // a valid escape sequence; time to cough it up.
+                        buffer_ptr[0] = args->escape_char;
+                        buffer_ptr[1] = ch;
+                        ++r;
+                    }
+                }
+                state = (ch == '\n' || ch == '\r') ? kStartOfLine : kMidFlow;
+            }
+        }
+        if (args->protocol) {
+            if (!args->protocol->Write(ShellProtocol::kIdStdin, r)) {
+                break;
+            }
+        } else {
+            if (!WriteFdExactly(args->write_fd, buffer_ptr, r)) {
+                break;
+            }
+        }
+    }
+}
+
+// Returns a shell service string with the indicated arguments and command.
+static std::string ShellServiceString(bool use_shell_protocol,
+                                      const std::string& type_arg,
+                                      const std::string& command) {
+    std::vector<std::string> args;
+    if (use_shell_protocol) {
+        args.push_back(kShellServiceArgShellProtocol);
+
+        const char* terminal_type = getenv("TERM");
+        if (terminal_type != nullptr) {
+            args.push_back(std::string("TERM=") + terminal_type);
+        }
+    }
+    if (!type_arg.empty()) {
+        args.push_back(type_arg);
+    }
+
+    // Shell service string can look like: shell[,arg1,arg2,...]:[command].
+    return android::base::StringPrintf("shell%s%s:%s",
+                                       args.empty() ? "" : ",",
+                                       android::base::Join(args, ',').c_str(),
+                                       command.c_str());
+}
+
+// Connects to a shell on the device and read/writes data.
+//
+// Note: currently this function doesn't properly clean up resources; the
+// FD connected to the adb server is never closed and the stdin read thread
+// may never exit.
+//
+// On success returns the remote exit code if |use_shell_protocol| is true,
+// 0 otherwise. On failure returns 1.
+static int RemoteShell(bool use_shell_protocol, const std::string& type_arg,
+                       char escape_char,
+                       const std::string& command) {
+    std::string service_string = ShellServiceString(use_shell_protocol,
+                                                    type_arg, command);
+
+    // Old devices can't handle a service string that's longer than MAX_PAYLOAD_V1.
+    // Use |use_shell_protocol| to determine whether to allow a command longer than that.
+    if (service_string.size() > MAX_PAYLOAD_V1 && !use_shell_protocol) {
+        fprintf(stderr, "error: shell command too long\n");
+        return 1;
+    }
+
+    // Make local stdin raw if the device allocates a PTY, which happens if:
+    //   1. We are explicitly asking for a PTY shell, or
+    //   2. We don't specify shell type and are starting an interactive session.
+    bool raw_stdin = (type_arg == kShellServiceArgPty ||
+                      (type_arg.empty() && command.empty()));
+
+    std::string error;
+    int fd = adb_connect(service_string, &error);
+    if (fd < 0) {
+        fprintf(stderr,"error: %s\n", error.c_str());
+        return 1;
+    }
+
+    StdinReadArgs* args = new StdinReadArgs;
+    if (!args) {
+        LOG(ERROR) << "couldn't allocate StdinReadArgs object";
+        return 1;
+    }
+    args->stdin_fd = STDIN_FILENO;
+    args->write_fd = fd;
+    args->raw_stdin = raw_stdin;
+    args->escape_char = escape_char;
+    if (use_shell_protocol) {
+        args->protocol = std::make_unique<ShellProtocol>(args->write_fd);
+    }
+
+    if (raw_stdin) stdin_raw_init();
+
+#if !defined(_WIN32)
+    // Ensure our process is notified if the local window size changes.
+    // We use sigaction(2) to ensure that the SA_RESTART flag is not set,
+    // because the whole reason we're sending signals is to unblock the read(2)!
+    // That also means we don't need to do anything in the signal handler:
+    // the side effect of delivering the signal is all we need.
+    struct sigaction sa;
+    memset(&sa, 0, sizeof(sa));
+    sa.sa_handler = [](int) {};
+    sa.sa_flags = 0;
+    sigaction(SIGWINCH, &sa, nullptr);
+
+    // Now block SIGWINCH in this thread (the main thread) and all threads spawned
+    // from it. The stdin read thread will unblock this signal to ensure that it's
+    // the thread that receives the signal.
+    sigset_t mask;
+    sigemptyset(&mask);
+    sigaddset(&mask, SIGWINCH);
+    pthread_sigmask(SIG_BLOCK, &mask, nullptr);
+#endif
+
+    // TODO: combine read_and_dump with stdin_read_thread to make life simpler?
+    std::thread(stdin_read_thread_loop, args).detach();
+    int exit_code = read_and_dump(fd, use_shell_protocol);
+
+    // TODO: properly exit stdin_read_thread_loop and close |fd|.
+
+    // TODO: we should probably install signal handlers for this.
+    // TODO: can we use atexit? even on Windows?
+    if (raw_stdin) stdin_raw_restore();
+
+    return exit_code;
+}
+
+static int adb_shell(int argc, const char** argv) {
+    FeatureSet features;
+    std::string error;
+    if (!adb_get_feature_set(&features, &error)) {
+        fprintf(stderr, "error: %s\n", error.c_str());
+        return 1;
+    }
+
+    enum PtyAllocationMode { kPtyAuto, kPtyNo, kPtyYes, kPtyDefinitely };
+
+    // Defaults.
+    char escape_char = '~'; // -e
+    bool use_shell_protocol = CanUseFeature(features, kFeatureShell2); // -x
+    PtyAllocationMode tty = use_shell_protocol ? kPtyAuto : kPtyDefinitely; // -t/-T
+
+    // Parse shell-specific command-line options.
+    argv[0] = "adb shell"; // So getopt(3) error messages start "adb shell".
+#ifdef _WIN32
+    // fixes "adb shell -l" crash on Windows, b/37284906
+    __argv = const_cast<char**>(argv);
+#endif
+    optind = 1; // argv[0] is always "shell", so set `optind` appropriately.
+    int opt;
+    while ((opt = getopt(argc, const_cast<char**>(argv), "+e:ntTx")) != -1) {
+        switch (opt) {
+            case 'e':
+                if (!(strlen(optarg) == 1 || strcmp(optarg, "none") == 0)) {
+                    return syntax_error("-e requires a single-character argument or 'none'");
+                }
+                escape_char = (strcmp(optarg, "none") == 0) ? 0 : optarg[0];
+                break;
+            case 'n':
+                close_stdin();
+                break;
+            case 'x':
+                // This option basically asks for historical behavior, so set options that
+                // correspond to the historical defaults. This is slightly weird in that -Tx
+                // is fine (because we'll undo the -T) but -xT isn't, but that does seem to
+                // be our least worst choice...
+                use_shell_protocol = false;
+                tty = kPtyDefinitely;
+                escape_char = '~';
+                break;
+            case 't':
+                // Like ssh, -t arguments are cumulative so that multiple -t's
+                // are needed to force a PTY.
+                tty = (tty >= kPtyYes) ? kPtyDefinitely : kPtyYes;
+                break;
+            case 'T':
+                tty = kPtyNo;
+                break;
+            default:
+                // getopt(3) already printed an error message for us.
+                return 1;
+        }
+    }
+
+    bool is_interactive = (optind == argc);
+
+    std::string shell_type_arg = kShellServiceArgPty;
+    if (tty == kPtyNo) {
+        shell_type_arg = kShellServiceArgRaw;
+    } else if (tty == kPtyAuto) {
+        // If stdin isn't a TTY, default to a raw shell; this lets
+        // things like `adb shell < my_script.sh` work as expected.
+        // Non-interactive shells should also not have a pty.
+        if (!unix_isatty(STDIN_FILENO) || !is_interactive) {
+            shell_type_arg = kShellServiceArgRaw;
+        }
+    } else if (tty == kPtyYes) {
+        // A single -t arg isn't enough to override implicit -T.
+        if (!unix_isatty(STDIN_FILENO)) {
+            fprintf(stderr,
+                    "Remote PTY will not be allocated because stdin is not a terminal.\n"
+                    "Use multiple -t options to force remote PTY allocation.\n");
+            shell_type_arg = kShellServiceArgRaw;
+        }
+    }
+
+    D("shell -e 0x%x t=%d use_shell_protocol=%s shell_type_arg=%s\n",
+      escape_char, tty,
+      use_shell_protocol ? "true" : "false",
+      (shell_type_arg == kShellServiceArgPty) ? "pty" : "raw");
+
+    // Raw mode is only supported when talking to a new device *and* using the shell protocol.
+    if (!use_shell_protocol) {
+        if (shell_type_arg != kShellServiceArgPty) {
+            fprintf(stderr, "error: %s only supports allocating a pty\n",
+                    !CanUseFeature(features, kFeatureShell2) ? "device" : "-x");
+            return 1;
+        } else {
+            // If we're not using the shell protocol, the type argument must be empty.
+            shell_type_arg = "";
+        }
+    }
+
+    std::string command;
+    if (optind < argc) {
+        // We don't escape here, just like ssh(1). http://b/20564385.
+        command = android::base::Join(std::vector<const char*>(argv + optind, argv + argc), ' ');
+    }
+
+    return RemoteShell(use_shell_protocol, shell_type_arg, escape_char, command);
+}
+
+static int adb_sideload_legacy(const char* filename, int in_fd, int size) {
+    std::string error;
+    int out_fd = adb_connect(android::base::StringPrintf("sideload:%d", size), &error);
+    if (out_fd < 0) {
+        fprintf(stderr, "adb: pre-KitKat sideload connection failed: %s\n", error.c_str());
+        return -1;
+    }
+
+    int opt = CHUNK_SIZE;
+    opt = adb_setsockopt(out_fd, SOL_SOCKET, SO_SNDBUF, &opt, sizeof(opt));
+
+    char buf[CHUNK_SIZE];
+    int total = size;
+    while (size > 0) {
+        unsigned xfer = (size > CHUNK_SIZE) ? CHUNK_SIZE : size;
+        if (!ReadFdExactly(in_fd, buf, xfer)) {
+            fprintf(stderr, "adb: failed to read data from %s: %s\n", filename, strerror(errno));
+            adb_close(out_fd);
+            return -1;
+        }
+        if (!WriteFdExactly(out_fd, buf, xfer)) {
+            std::string error;
+            adb_status(out_fd, &error);
+            fprintf(stderr, "adb: failed to write data: %s\n", error.c_str());
+            adb_close(out_fd);
+            return -1;
+        }
+        size -= xfer;
+        printf("sending: '%s' %4d%%    \r", filename, (int)(100LL - ((100LL * size) / (total))));
+        fflush(stdout);
+    }
+    printf("\n");
+
+    if (!adb_status(out_fd, &error)) {
+        fprintf(stderr, "adb: error response: %s\n", error.c_str());
+        adb_close(out_fd);
+        return -1;
+    }
+
+    adb_close(out_fd);
+    return 0;
+}
+
+#define SIDELOAD_HOST_BLOCK_SIZE (CHUNK_SIZE)
+
+/*
+ * The sideload-host protocol serves the data in a file (given on the
+ * command line) to the client, using a simple protocol:
+ *
+ * - The connect message includes the total number of bytes in the
+ *   file and a block size chosen by us.
+ *
+ * - The other side sends the desired block number as eight decimal
+ *   digits (eg "00000023" for block 23).  Blocks are numbered from
+ *   zero.
+ *
+ * - We send back the data of the requested block.  The last block is
+ *   likely to be partial; when the last block is requested we only
+ *   send the part of the block that exists, it's not padded up to the
+ *   block size.
+ *
+ * - When the other side sends "DONEDONE" instead of a block number,
+ *   we hang up.
+ */
+static int adb_sideload_host(const char* filename) {
+    // TODO: use a LinePrinter instead...
+    struct stat sb;
+    if (stat(filename, &sb) == -1) {
+        fprintf(stderr, "adb: failed to stat file %s: %s\n", filename, strerror(errno));
+        return -1;
+    }
+    unique_fd package_fd(adb_open(filename, O_RDONLY));
+    if (package_fd == -1) {
+        fprintf(stderr, "adb: failed to open file %s: %s\n", filename, strerror(errno));
+        return -1;
+    }
+
+    std::string service = android::base::StringPrintf(
+        "sideload-host:%d:%d", static_cast<int>(sb.st_size), SIDELOAD_HOST_BLOCK_SIZE);
+    std::string error;
+    unique_fd device_fd(adb_connect(service, &error));
+    if (device_fd < 0) {
+        // Try falling back to the older (<= K) sideload method. Maybe this
+        // is an older device that doesn't support sideload-host.
+        fprintf(stderr, "adb: sideload connection failed: %s\n", error.c_str());
+        fprintf(stderr, "adb: trying pre-KitKat sideload method...\n");
+        return adb_sideload_legacy(filename, package_fd, static_cast<int>(sb.st_size));
+    }
+
+    int opt = SIDELOAD_HOST_BLOCK_SIZE;
+    adb_setsockopt(device_fd, SOL_SOCKET, SO_SNDBUF, &opt, sizeof(opt));
+
+    char buf[SIDELOAD_HOST_BLOCK_SIZE];
+
+    size_t xfer = 0;
+    int last_percent = -1;
+    while (true) {
+        if (!ReadFdExactly(device_fd, buf, 8)) {
+            fprintf(stderr, "adb: failed to read command: %s\n", strerror(errno));
+            return -1;
+        }
+        buf[8] = '\0';
+
+        if (strcmp("DONEDONE", buf) == 0) {
+            printf("\rTotal xfer: %.2fx%*s\n",
+                   static_cast<double>(xfer) / (sb.st_size ? sb.st_size : 1),
+                   static_cast<int>(strlen(filename) + 10), "");
+            return 0;
+        }
+
+        int block = strtol(buf, nullptr, 10);
+
+        size_t offset = block * SIDELOAD_HOST_BLOCK_SIZE;
+        if (offset >= static_cast<size_t>(sb.st_size)) {
+            fprintf(stderr, "adb: failed to read block %d past end\n", block);
+            return -1;
+        }
+
+        size_t to_write = SIDELOAD_HOST_BLOCK_SIZE;
+        if ((offset + SIDELOAD_HOST_BLOCK_SIZE) > static_cast<size_t>(sb.st_size)) {
+            to_write = sb.st_size - offset;
+        }
+
+        if (adb_lseek(package_fd, offset, SEEK_SET) != static_cast<int>(offset)) {
+            fprintf(stderr, "adb: failed to seek to package block: %s\n", strerror(errno));
+            return -1;
+        }
+        if (!ReadFdExactly(package_fd, buf, to_write)) {
+            fprintf(stderr, "adb: failed to read package block: %s\n", strerror(errno));
+            return -1;
+        }
+
+        if (!WriteFdExactly(device_fd, buf, to_write)) {
+            adb_status(device_fd, &error);
+            fprintf(stderr, "adb: failed to write data '%s' *\n", error.c_str());
+            return -1;
+        }
+        xfer += to_write;
+
+        // For normal OTA packages, we expect to transfer every byte
+        // twice, plus a bit of overhead (one read during
+        // verification, one read of each byte for installation, plus
+        // extra access to things like the zip central directory).
+        // This estimate of the completion becomes 100% when we've
+        // transferred ~2.13 (=100/47) times the package size.
+        int percent = static_cast<int>(xfer * 47LL / (sb.st_size ? sb.st_size : 1));
+        if (percent != last_percent) {
+            printf("\rserving: '%s'  (~%d%%)    ", filename, percent);
+            fflush(stdout);
+            last_percent = percent;
+        }
+    }
+}
+
+/**
+ * Run ppp in "notty" mode against a resource listed as the first parameter
+ * eg:
+ *
+ * ppp dev:/dev/omap_csmi_tty0 <ppp options>
+ *
+ */
+static int ppp(int argc, const char** argv) {
+#if defined(_WIN32)
+    fprintf(stderr, "error: adb %s not implemented on Win32\n", argv[0]);
+    return -1;
+#else
+    if (argc < 2) return syntax_error("adb %s <adb service name> [ppp opts]", argv[0]);
+
+    const char* adb_service_name = argv[1];
+    std::string error;
+    int fd = adb_connect(adb_service_name, &error);
+    if (fd < 0) {
+        fprintf(stderr, "adb: could not open adb service %s: %s\n", adb_service_name, error.c_str());
+        return 1;
+    }
+
+    pid_t pid = fork();
+
+    if (pid < 0) {
+        perror("from fork()");
+        return 1;
+    } else if (pid == 0) {
+        int err;
+        int i;
+        const char **ppp_args;
+
+        // copy args
+        ppp_args = (const char **) alloca(sizeof(char *) * argc + 1);
+        ppp_args[0] = "pppd";
+        for (i = 2 ; i < argc ; i++) {
+            //argv[2] and beyond become ppp_args[1] and beyond
+            ppp_args[i - 1] = argv[i];
+        }
+        ppp_args[i-1] = nullptr;
+
+        // child side
+
+        dup2(fd, STDIN_FILENO);
+        dup2(fd, STDOUT_FILENO);
+        adb_close(STDERR_FILENO);
+        adb_close(fd);
+
+        err = execvp("pppd", (char * const *)ppp_args);
+
+        if (err < 0) {
+            perror("execing pppd");
+        }
+        exit(-1);
+    } else {
+        // parent side
+
+        adb_close(fd);
+        return 0;
+    }
+#endif /* !defined(_WIN32) */
+}
+
+static bool wait_for_device(const char* service) {
+    std::vector<std::string> components = android::base::Split(service, "-");
+    if (components.size() < 3 || components.size() > 4) {
+        fprintf(stderr, "adb: couldn't parse 'wait-for' command: %s\n", service);
+        return false;
+    }
+
+    TransportType t;
+    adb_get_transport(&t, nullptr, nullptr);
+
+    // Was the caller vague about what they'd like us to wait for?
+    // If so, check they weren't more specific in their choice of transport type.
+    if (components.size() == 3) {
+        auto it = components.begin() + 2;
+        if (t == kTransportUsb) {
+            components.insert(it, "usb");
+        } else if (t == kTransportLocal) {
+            components.insert(it, "local");
+        } else {
+            components.insert(it, "any");
+        }
+    } else if (components[2] != "any" && components[2] != "local" && components[2] != "usb") {
+        fprintf(stderr, "adb: unknown type %s; expected 'any', 'local', or 'usb'\n",
+                components[2].c_str());
+        return false;
+    }
+
+    if (components[3] != "any" && components[3] != "bootloader" && components[3] != "device" &&
+        components[3] != "recovery" && components[3] != "sideload") {
+        fprintf(stderr,
+                "adb: unknown state %s; "
+                "expected 'any', 'bootloader', 'device', 'recovery', or 'sideload'\n",
+                components[3].c_str());
+        return false;
+    }
+
+    std::string cmd = format_host_command(android::base::Join(components, "-").c_str());
+    return adb_command(cmd);
+}
+
+static bool adb_root(const char* command) {
+    std::string error;
+
+    unique_fd fd(adb_connect(android::base::StringPrintf("%s:", command), &error));
+    if (fd < 0) {
+        fprintf(stderr, "adb: unable to connect for %s: %s\n", command, error.c_str());
+        return false;
+    }
+
+    // Figure out whether we actually did anything.
+    char buf[256];
+    char* cur = buf;
+    ssize_t bytes_left = sizeof(buf);
+    while (bytes_left > 0) {
+        ssize_t bytes_read = adb_read(fd, cur, bytes_left);
+        if (bytes_read == 0) {
+            break;
+        } else if (bytes_read < 0) {
+            fprintf(stderr, "adb: error while reading for %s: %s\n", command, strerror(errno));
+            return false;
+        }
+        cur += bytes_read;
+        bytes_left -= bytes_read;
+    }
+
+    if (bytes_left == 0) {
+        fprintf(stderr, "adb: unexpected output length for %s\n", command);
+        return false;
+    }
+
+    fflush(stdout);
+    WriteFdExactly(STDOUT_FILENO, buf, sizeof(buf) - bytes_left);
+    if (cur != buf && strstr(buf, "restarting") == nullptr) {
+        return true;
+    }
+
+    // Give adbd some time to kill itself and come back up.
+    // We can't use wait-for-device because devices (e.g. adb over network) might not come back.
+    std::this_thread::sleep_for(3s);
+    return true;
+}
+
+int send_shell_command(const std::string& command, bool disable_shell_protocol,
+                       StandardStreamsCallbackInterface* callback) {
+    int fd;
+    bool use_shell_protocol = false;
+
+    while (true) {
+        bool attempt_connection = true;
+
+        // Use shell protocol if it's supported and the caller doesn't explicitly
+        // disable it.
+        if (!disable_shell_protocol) {
+            FeatureSet features;
+            std::string error;
+            if (adb_get_feature_set(&features, &error)) {
+                use_shell_protocol = CanUseFeature(features, kFeatureShell2);
+            } else {
+                // Device was unreachable.
+                attempt_connection = false;
+            }
+        }
+
+        if (attempt_connection) {
+            std::string error;
+            std::string service_string = ShellServiceString(use_shell_protocol, "", command);
+
+            fd = adb_connect(service_string, &error);
+            if (fd >= 0) {
+                break;
+            }
+        }
+
+        fprintf(stderr, "- waiting for device -\n");
+        if (!wait_for_device("wait-for-device")) {
+            return 1;
+        }
+    }
+
+    int exit_code = read_and_dump(fd, use_shell_protocol, callback);
+
+    if (adb_close(fd) < 0) {
+        PLOG(ERROR) << "failure closing FD " << fd;
+    }
+
+    return exit_code;
+}
+
+static int logcat(int argc, const char** argv) {
+    char* log_tags = getenv("ANDROID_LOG_TAGS");
+    std::string quoted = escape_arg(log_tags == nullptr ? "" : log_tags);
+
+    std::string cmd = "export ANDROID_LOG_TAGS=\"" + quoted + "\"; exec logcat";
+
+    if (!strcmp(argv[0], "longcat")) {
+        cmd += " -v long";
+    }
+
+    --argc;
+    ++argv;
+    while (argc-- > 0) {
+        cmd += " " + escape_arg(*argv++);
+    }
+
+    return send_shell_command(cmd);
+}
+
+static void write_zeros(int bytes, int fd) {
+    int old_stdin_mode = -1;
+    int old_stdout_mode = -1;
+    std::vector<char> buf(bytes);
+
+    D("write_zeros(%d) -> %d", bytes, fd);
+
+    stdinout_raw_prologue(-1, fd, old_stdin_mode, old_stdout_mode);
+
+    if (fd == STDOUT_FILENO) {
+        fwrite(buf.data(), 1, bytes, stdout);
+        fflush(stdout);
+    } else {
+        adb_write(fd, buf.data(), bytes);
+    }
+
+    stdinout_raw_prologue(-1, fd, old_stdin_mode, old_stdout_mode);
+
+    D("write_zeros() finished");
+}
+
+static int backup(int argc, const char** argv) {
+    const char* filename = "backup.ab";
+
+    /* find, extract, and use any -f argument */
+    for (int i = 1; i < argc; i++) {
+        if (!strcmp("-f", argv[i])) {
+            if (i == argc - 1) return syntax_error("backup -f passed with no filename");
+            filename = argv[i+1];
+            for (int j = i+2; j <= argc; ) {
+                argv[i++] = argv[j++];
+            }
+            argc -= 2;
+            argv[argc] = nullptr;
+        }
+    }
+
+    // Bare "adb backup" or "adb backup -f filename" are not valid invocations ---
+    // a list of packages is required.
+    if (argc < 2) return syntax_error("backup either needs a list of packages or -all/-shared");
+
+    adb_unlink(filename);
+    int outFd = adb_creat(filename, 0640);
+    if (outFd < 0) {
+        fprintf(stderr, "adb: backup unable to create file '%s': %s\n", filename, strerror(errno));
+        return EXIT_FAILURE;
+    }
+
+    std::string cmd = "backup:";
+    --argc;
+    ++argv;
+    while (argc-- > 0) {
+        cmd += " " + escape_arg(*argv++);
+    }
+
+    D("backup. filename=%s cmd=%s", filename, cmd.c_str());
+    std::string error;
+    int fd = adb_connect(cmd, &error);
+    if (fd < 0) {
+        fprintf(stderr, "adb: unable to connect for backup: %s\n", error.c_str());
+        adb_close(outFd);
+        return EXIT_FAILURE;
+    }
+
+    fprintf(stdout, "Now unlock your device and confirm the backup operation...\n");
+    fflush(stdout);
+
+    copy_to_file(fd, outFd);
+
+    adb_close(fd);
+    adb_close(outFd);
+    return EXIT_SUCCESS;
+}
+
+static int restore(int argc, const char** argv) {
+    if (argc != 2) return syntax_error("restore requires an argument");
+
+    const char* filename = argv[1];
+    int tarFd = adb_open(filename, O_RDONLY);
+    if (tarFd < 0) {
+        fprintf(stderr, "adb: unable to open file %s: %s\n", filename, strerror(errno));
+        return -1;
+    }
+
+    std::string error;
+    int fd = adb_connect("restore:", &error);
+    if (fd < 0) {
+        fprintf(stderr, "adb: unable to connect for restore: %s\n", error.c_str());
+        adb_close(tarFd);
+        return -1;
+    }
+
+    fprintf(stdout, "Now unlock your device and confirm the restore operation.\n");
+    fflush(stdout);
+
+    copy_to_file(tarFd, fd);
+
+    // Provide an in-band EOD marker in case the archive file is malformed
+    write_zeros(512*2, fd);
+
+    // Wait until the other side finishes, or it'll get sent SIGHUP.
+    copy_to_file(fd, STDOUT_FILENO);
+
+    adb_close(fd);
+    adb_close(tarFd);
+    return 0;
+}
+
+static void parse_push_pull_args(const char** arg, int narg, std::vector<const char*>* srcs,
+                                 const char** dst, bool* copy_attrs, bool* sync) {
+    *copy_attrs = false;
+
+    srcs->clear();
+    bool ignore_flags = false;
+    while (narg > 0) {
+        if (ignore_flags || *arg[0] != '-') {
+            srcs->push_back(*arg);
+        } else {
+            if (!strcmp(*arg, "-p")) {
+                // Silently ignore for backwards compatibility.
+            } else if (!strcmp(*arg, "-a")) {
+                *copy_attrs = true;
+            } else if (!strcmp(*arg, "--sync")) {
+                if (sync != nullptr) {
+                    *sync = true;
+                }
+            } else if (!strcmp(*arg, "--")) {
+                ignore_flags = true;
+            } else {
+                syntax_error("unrecognized option '%s'", *arg);
+                exit(1);
+            }
+        }
+        ++arg;
+        --narg;
+    }
+
+    if (srcs->size() > 1) {
+        *dst = srcs->back();
+        srcs->pop_back();
+    }
+}
+
+static int adb_connect_command(const std::string& command) {
+    std::string error;
+    int fd = adb_connect(command, &error);
+    if (fd < 0) {
+        fprintf(stderr, "error: %s\n", error.c_str());
+        return 1;
+    }
+    read_and_dump(fd);
+    adb_close(fd);
+    return 0;
+}
+
+static int adb_query_command(const std::string& command) {
+    std::string result;
+    std::string error;
+    if (!adb_query(command, &result, &error)) {
+        fprintf(stderr, "error: %s\n", error.c_str());
+        return 1;
+    }
+    printf("%s\n", result.c_str());
+    return 0;
+}
+
+// Disallow stdin, stdout, and stderr.
+static bool _is_valid_ack_reply_fd(const int ack_reply_fd) {
+#ifdef _WIN32
+    const HANDLE ack_reply_handle = cast_int_to_handle(ack_reply_fd);
+    return (GetStdHandle(STD_INPUT_HANDLE) != ack_reply_handle) &&
+           (GetStdHandle(STD_OUTPUT_HANDLE) != ack_reply_handle) &&
+           (GetStdHandle(STD_ERROR_HANDLE) != ack_reply_handle);
+#else
+    return ack_reply_fd > 2;
+#endif
+}
+
+static bool _use_legacy_install() {
+    FeatureSet features;
+    std::string error;
+    if (!adb_get_feature_set(&features, &error)) {
+        fprintf(stderr, "error: %s\n", error.c_str());
+        return true;
+    }
+    return !CanUseFeature(features, kFeatureCmd);
+}
+
+int adb_commandline(int argc, const char** argv) {
+    int no_daemon = 0;
+    int is_daemon = 0;
+    int is_server = 0;
+    int r;
+    TransportType transport_type = kTransportAny;
+    int ack_reply_fd = -1;
+
+#if !defined(_WIN32)
+    // We'd rather have EPIPE than SIGPIPE.
+    signal(SIGPIPE, SIG_IGN);
+#endif
+
+    const char* server_host_str = nullptr;
+    const char* server_port_str = nullptr;
+    const char* server_socket_str = nullptr;
+
+    // We need to check for -d and -e before we look at $ANDROID_SERIAL.
+    const char* serial = nullptr;
+    TransportId transport_id = 0;
+
+    while (argc > 0) {
+        if (!strcmp(argv[0],"server")) {
+            is_server = 1;
+        } else if (!strcmp(argv[0],"nodaemon")) {
+            no_daemon = 1;
+        } else if (!strcmp(argv[0], "fork-server")) {
+            /* this is a special flag used only when the ADB client launches the ADB Server */
+            is_daemon = 1;
+        } else if (!strcmp(argv[0], "--reply-fd")) {
+            if (argc < 2) return syntax_error("--reply-fd requires an argument");
+            const char* reply_fd_str = argv[1];
+            argc--;
+            argv++;
+            ack_reply_fd = strtol(reply_fd_str, nullptr, 10);
+            if (!_is_valid_ack_reply_fd(ack_reply_fd)) {
+                fprintf(stderr, "adb: invalid reply fd \"%s\"\n", reply_fd_str);
+                return 1;
+            }
+        } else if (!strncmp(argv[0], "-s", 2)) {
+            if (isdigit(argv[0][2])) {
+                serial = argv[0] + 2;
+            } else {
+                if (argc < 2 || argv[0][2] != '\0') return syntax_error("-s requires an argument");
+                serial = argv[1];
+                argc--;
+                argv++;
+            }
+        } else if (!strncmp(argv[0], "-t", 2)) {
+            const char* id;
+            if (isdigit(argv[0][2])) {
+                id = argv[0] + 2;
+            } else {
+                id = argv[1];
+                argc--;
+                argv++;
+            }
+            transport_id = strtoll(id, const_cast<char**>(&id), 10);
+            if (*id != '\0') {
+                return syntax_error("invalid transport id");
+            }
+        } else if (!strcmp(argv[0],"-d")) {
+            transport_type = kTransportUsb;
+        } else if (!strcmp(argv[0],"-e")) {
+            transport_type = kTransportLocal;
+        } else if (!strcmp(argv[0],"-a")) {
+            gListenAll = 1;
+        } else if (!strncmp(argv[0], "-H", 2)) {
+            if (argv[0][2] == '\0') {
+                if (argc < 2) return syntax_error("-H requires an argument");
+                server_host_str = argv[1];
+                argc--;
+                argv++;
+            } else {
+                server_host_str = argv[0] + 2;
+            }
+        } else if (!strncmp(argv[0], "-P", 2)) {
+            if (argv[0][2] == '\0') {
+                if (argc < 2) return syntax_error("-P requires an argument");
+                server_port_str = argv[1];
+                argc--;
+                argv++;
+            } else {
+                server_port_str = argv[0] + 2;
+            }
+        } else if (!strcmp(argv[0], "-L")) {
+            if (argc < 2) return syntax_error("-L requires an argument");
+            server_socket_str = argv[1];
+            argc--;
+            argv++;
+        } else {
+            /* out of recognized modifiers and flags */
+            break;
+        }
+        argc--;
+        argv++;
+    }
+
+    if ((server_host_str || server_port_str) && server_socket_str) {
+        return syntax_error("-L is incompatible with -H or -P");
+    }
+
+    // If -L, -H, or -P are specified, ignore environment variables.
+    // Otherwise, prefer ADB_SERVER_SOCKET over ANDROID_ADB_SERVER_ADDRESS/PORT.
+    if (!server_host_str && !server_port_str && !server_socket_str) {
+        server_socket_str = getenv("ADB_SERVER_SOCKET");
+    }
+
+    if (!server_socket_str) {
+        // tcp:1234 and tcp:localhost:1234 are different with -a, so don't default to localhost
+        server_host_str = server_host_str ? server_host_str : getenv("ANDROID_ADB_SERVER_ADDRESS");
+
+        int server_port = DEFAULT_ADB_PORT;
+        server_port_str = server_port_str ? server_port_str : getenv("ANDROID_ADB_SERVER_PORT");
+        if (server_port_str && strlen(server_port_str) > 0) {
+            if (!android::base::ParseInt(server_port_str, &server_port, 1, 65535)) {
+                fprintf(stderr,
+                        "adb: Env var ANDROID_ADB_SERVER_PORT must be a positive"
+                        " number less than 65535. Got \"%s\"\n",
+                        server_port_str);
+                exit(1);
+            }
+        }
+
+        int rc;
+        char* temp;
+        if (server_host_str) {
+            rc = asprintf(&temp, "tcp:%s:%d", server_host_str, server_port);
+        } else {
+            rc = asprintf(&temp, "tcp:%d", server_port);
+        }
+        if (rc < 0) {
+            fatal("failed to allocate server socket specification");
+        }
+        server_socket_str = temp;
+    }
+
+    adb_set_socket_spec(server_socket_str);
+
+    // If none of -d, -e, or -s were specified, try $ANDROID_SERIAL.
+    if (transport_type == kTransportAny && serial == nullptr) {
+        serial = getenv("ANDROID_SERIAL");
+    }
+
+    adb_set_transport(transport_type, serial, transport_id);
+
+    if (is_server) {
+        if (no_daemon || is_daemon) {
+            if (is_daemon && (ack_reply_fd == -1)) {
+                fprintf(stderr, "reply fd for adb server to client communication not specified.\n");
+                return 1;
+            }
+            r = adb_server_main(is_daemon, server_socket_str, ack_reply_fd);
+        } else {
+            r = launch_server(server_socket_str);
+        }
+        if (r) {
+            fprintf(stderr,"* could not start server *\n");
+        }
+        return r;
+    }
+
+    if (argc == 0) {
+        help();
+        return 1;
+    }
+
+    /* handle wait-for-* prefix */
+    if (!strncmp(argv[0], "wait-for-", strlen("wait-for-"))) {
+        const char* service = argv[0];
+
+        if (!wait_for_device(service)) {
+            return 1;
+        }
+
+        // Allow a command to be run after wait-for-device,
+        // e.g. 'adb wait-for-device shell'.
+        if (argc == 1) {
+            return 0;
+        }
+
+        /* Fall through */
+        argc--;
+        argv++;
+    }
+
+    /* adb_connect() commands */
+    if (!strcmp(argv[0], "devices")) {
+        const char *listopt;
+        if (argc < 2) {
+            listopt = "";
+        } else if (argc == 2 && !strcmp(argv[1], "-l")) {
+            listopt = argv[1];
+        } else {
+            return syntax_error("adb devices [-l]");
+        }
+
+        std::string query = android::base::StringPrintf("host:%s%s", argv[0], listopt);
+        printf("List of devices attached\n");
+        return adb_query_command(query);
+    }
+    else if (!strcmp(argv[0], "connect")) {
+        if (argc != 2) return syntax_error("adb connect <host>[:<port>]");
+
+        std::string query = android::base::StringPrintf("host:connect:%s", argv[1]);
+        return adb_query_command(query);
+    }
+    else if (!strcmp(argv[0], "disconnect")) {
+        if (argc > 2) return syntax_error("adb disconnect [<host>[:<port>]]");
+
+        std::string query = android::base::StringPrintf("host:disconnect:%s",
+                                                        (argc == 2) ? argv[1] : "");
+        return adb_query_command(query);
+    }
+    else if (!strcmp(argv[0], "emu")) {
+        return adb_send_emulator_command(argc, argv, serial);
+    }
+    else if (!strcmp(argv[0], "shell")) {
+        return adb_shell(argc, argv);
+    }
+    else if (!strcmp(argv[0], "exec-in") || !strcmp(argv[0], "exec-out")) {
+        int exec_in = !strcmp(argv[0], "exec-in");
+
+        if (argc < 2) return syntax_error("adb %s command", argv[0]);
+
+        std::string cmd = "exec:";
+        cmd += argv[1];
+        argc -= 2;
+        argv += 2;
+        while (argc-- > 0) {
+            cmd += " " + escape_arg(*argv++);
+        }
+
+        std::string error;
+        int fd = adb_connect(cmd, &error);
+        if (fd < 0) {
+            fprintf(stderr, "error: %s\n", error.c_str());
+            return -1;
+        }
+
+        if (exec_in) {
+            copy_to_file(STDIN_FILENO, fd);
+        } else {
+            copy_to_file(fd, STDOUT_FILENO);
+        }
+
+        adb_close(fd);
+        return 0;
+    }
+    else if (!strcmp(argv[0], "kill-server")) {
+        return adb_kill_server() ? 0 : 1;
+    }
+    else if (!strcmp(argv[0], "sideload")) {
+        if (argc != 2) return syntax_error("sideload requires an argument");
+        if (adb_sideload_host(argv[1])) {
+            return 1;
+        } else {
+            return 0;
+        }
+    } else if (!strcmp(argv[0], "tcpip")) {
+        if (argc != 2) return syntax_error("tcpip requires an argument");
+        int port;
+        if (!android::base::ParseInt(argv[1], &port, 1, 65535)) {
+            return syntax_error("tcpip: invalid port: %s", argv[1]);
+        }
+        return adb_connect_command(android::base::StringPrintf("tcpip:%d", port));
+    }
+    else if (!strcmp(argv[0], "remount") ||
+             !strcmp(argv[0], "reboot") ||
+             !strcmp(argv[0], "reboot-bootloader") ||
+             !strcmp(argv[0], "usb") ||
+             !strcmp(argv[0], "disable-verity") ||
+             !strcmp(argv[0], "enable-verity")) {
+        std::string command;
+        if (!strcmp(argv[0], "reboot-bootloader")) {
+            command = "reboot:bootloader";
+        } else if (argc > 1) {
+            command = android::base::StringPrintf("%s:%s", argv[0], argv[1]);
+        } else {
+            command = android::base::StringPrintf("%s:", argv[0]);
+        }
+        return adb_connect_command(command);
+    } else if (!strcmp(argv[0], "root") || !strcmp(argv[0], "unroot")) {
+        return adb_root(argv[0]) ? 0 : 1;
+    } else if (!strcmp(argv[0], "bugreport")) {
+        Bugreport bugreport;
+        return bugreport.DoIt(argc, argv);
+    } else if (!strcmp(argv[0], "forward") || !strcmp(argv[0], "reverse")) {
+        bool reverse = !strcmp(argv[0], "reverse");
+        --argc;
+        if (argc < 1) return syntax_error("%s requires an argument", argv[0]);
+        ++argv;
+
+        // Determine the <host-prefix> for this command.
+        std::string host_prefix;
+        if (reverse) {
+            host_prefix = "reverse";
+        } else {
+            if (serial) {
+                host_prefix = android::base::StringPrintf("host-serial:%s", serial);
+            } else if (transport_type == kTransportUsb) {
+                host_prefix = "host-usb";
+            } else if (transport_type == kTransportLocal) {
+                host_prefix = "host-local";
+            } else {
+                host_prefix = "host";
+            }
+        }
+
+        std::string cmd, error;
+        if (strcmp(argv[0], "--list") == 0) {
+            if (argc != 1) return syntax_error("--list doesn't take any arguments");
+            return adb_query_command(host_prefix + ":list-forward");
+        } else if (strcmp(argv[0], "--remove-all") == 0) {
+            if (argc != 1) return syntax_error("--remove-all doesn't take any arguments");
+            cmd = host_prefix + ":killforward-all";
+        } else if (strcmp(argv[0], "--remove") == 0) {
+            // forward --remove <local>
+            if (argc != 2) return syntax_error("--remove requires an argument");
+            cmd = host_prefix + ":killforward:" + argv[1];
+        } else if (strcmp(argv[0], "--no-rebind") == 0) {
+            // forward --no-rebind <local> <remote>
+            if (argc != 3) return syntax_error("--no-rebind takes two arguments");
+            if (forward_targets_are_valid(argv[1], argv[2], &error)) {
+                cmd = host_prefix + ":forward:norebind:" + argv[1] + ";" + argv[2];
+            }
+        } else {
+            // forward <local> <remote>
+            if (argc != 2) return syntax_error("forward takes two arguments");
+            if (forward_targets_are_valid(argv[0], argv[1], &error)) {
+                cmd = host_prefix + ":forward:" + argv[0] + ";" + argv[1];
+            }
+        }
+
+        if (!error.empty()) {
+            fprintf(stderr, "error: %s\n", error.c_str());
+            return 1;
+        }
+
+        int fd = adb_connect(cmd, &error);
+        if (fd < 0 || !adb_status(fd, &error)) {
+            adb_close(fd);
+            fprintf(stderr, "error: %s\n", error.c_str());
+            return 1;
+        }
+
+        // Server or device may optionally return a resolved TCP port number.
+        std::string resolved_port;
+        if (ReadProtocolString(fd, &resolved_port, &error) && !resolved_port.empty()) {
+            printf("%s\n", resolved_port.c_str());
+        }
+
+        ReadOrderlyShutdown(fd);
+        return 0;
+    }
+    /* do_sync_*() commands */
+    else if (!strcmp(argv[0], "ls")) {
+        if (argc != 2) return syntax_error("ls requires an argument");
+        return do_sync_ls(argv[1]) ? 0 : 1;
+    }
+    else if (!strcmp(argv[0], "push")) {
+        bool copy_attrs = false;
+        bool sync = false;
+        std::vector<const char*> srcs;
+        const char* dst = nullptr;
+
+        parse_push_pull_args(&argv[1], argc - 1, &srcs, &dst, &copy_attrs, &sync);
+        if (srcs.empty() || !dst) return syntax_error("push requires an argument");
+        return do_sync_push(srcs, dst, sync) ? 0 : 1;
+    }
+    else if (!strcmp(argv[0], "pull")) {
+        bool copy_attrs = false;
+        std::vector<const char*> srcs;
+        const char* dst = ".";
+
+        parse_push_pull_args(&argv[1], argc - 1, &srcs, &dst, &copy_attrs, nullptr);
+        if (srcs.empty()) return syntax_error("pull requires an argument");
+        return do_sync_pull(srcs, dst, copy_attrs) ? 0 : 1;
+    }
+    else if (!strcmp(argv[0], "install")) {
+        if (argc < 2) return syntax_error("install requires an argument");
+        if (_use_legacy_install()) {
+            return install_app_legacy(argc, argv);
+        }
+        return install_app(argc, argv);
+    }
+    else if (!strcmp(argv[0], "install-multiple")) {
+        if (argc < 2) return syntax_error("install-multiple requires an argument");
+        return install_multiple_app(argc, argv);
+    }
+    else if (!strcmp(argv[0], "uninstall")) {
+        if (argc < 2) return syntax_error("uninstall requires an argument");
+        if (_use_legacy_install()) {
+            return uninstall_app_legacy(argc, argv);
+        }
+        return uninstall_app(argc, argv);
+    }
+    else if (!strcmp(argv[0], "sync")) {
+        std::string src;
+        bool list_only = false;
+        if (argc < 2) {
+            // No partition specified: sync all of them.
+        } else if (argc >= 2 && strcmp(argv[1], "-l") == 0) {
+            list_only = true;
+            if (argc == 3) src = argv[2];
+        } else if (argc == 2) {
+            src = argv[1];
+        } else {
+            return syntax_error("adb sync [-l] [PARTITION]");
+        }
+
+        if (src.empty()) src = "all";
+        std::vector<std::string> partitions{"data", "odm", "oem", "product", "system", "vendor"};
+        bool found = false;
+        for (const auto& partition : partitions) {
+            if (src == "all" || src == partition) {
+                std::string src_dir{product_file(partition)};
+                if (!directory_exists(src_dir)) continue;
+                found = true;
+                if (!do_sync_sync(src_dir, "/" + partition, list_only)) return 1;
+            }
+        }
+        return found ? 0 : syntax_error("don't know how to sync %s partition", src.c_str());
+    }
+    /* passthrough commands */
+    else if (!strcmp(argv[0],"get-state") ||
+        !strcmp(argv[0],"get-serialno") ||
+        !strcmp(argv[0],"get-devpath"))
+    {
+        return adb_query_command(format_host_command(argv[0]));
+    }
+    /* other commands */
+    else if (!strcmp(argv[0],"logcat") || !strcmp(argv[0],"lolcat") || !strcmp(argv[0],"longcat")) {
+        return logcat(argc, argv);
+    }
+    else if (!strcmp(argv[0],"ppp")) {
+        return ppp(argc, argv);
+    }
+    else if (!strcmp(argv[0], "start-server")) {
+        std::string error;
+        const int result = adb_connect("host:start-server", &error);
+        if (result < 0) {
+            fprintf(stderr, "error: %s\n", error.c_str());
+        }
+        return result;
+    }
+    else if (!strcmp(argv[0], "backup")) {
+        return backup(argc, argv);
+    }
+    else if (!strcmp(argv[0], "restore")) {
+        return restore(argc, argv);
+    }
+    else if (!strcmp(argv[0], "keygen")) {
+        if (argc != 2) return syntax_error("keygen requires an argument");
+        // Always print key generation information for keygen command.
+        adb_trace_enable(AUTH);
+        return adb_auth_keygen(argv[1]);
+    }
+    else if (!strcmp(argv[0], "jdwp")) {
+        return adb_connect_command("jdwp");
+    }
+    else if (!strcmp(argv[0], "track-jdwp")) {
+        return adb_connect_command("track-jdwp");
+    }
+    else if (!strcmp(argv[0], "track-devices")) {
+        return adb_connect_command("host:track-devices");
+    } else if (!strcmp(argv[0], "raw")) {
+        if (argc != 2) {
+            return syntax_error("adb raw SERVICE");
+        }
+        return adb_connect_command(argv[1]);
+    }
+
+
+    /* "adb /?" is a common idiom under Windows */
+    else if (!strcmp(argv[0], "--help") || !strcmp(argv[0], "help") || !strcmp(argv[0], "/?")) {
+        help();
+        return 0;
+    }
+    else if (!strcmp(argv[0], "--version") || !strcmp(argv[0], "version")) {
+        fprintf(stdout, "%s", adb_version().c_str());
+        return 0;
+    } else if (!strcmp(argv[0], "features")) {
+        // Only list the features common to both the adb client and the device.
+        FeatureSet features;
+        std::string error;
+        if (!adb_get_feature_set(&features, &error)) {
+            fprintf(stderr, "error: %s\n", error.c_str());
+            return 1;
+        }
+
+        for (const std::string& name : features) {
+            if (CanUseFeature(features, name)) {
+                printf("%s\n", name.c_str());
+            }
+        }
+        return 0;
+    } else if (!strcmp(argv[0], "host-features")) {
+        return adb_query_command("host:host-features");
+    } else if (!strcmp(argv[0], "reconnect")) {
+        if (argc == 1) {
+            return adb_query_command(format_host_command(argv[0]));
+        } else if (argc == 2) {
+            if (!strcmp(argv[1], "device")) {
+                std::string err;
+                adb_connect("reconnect", &err);
+                return 0;
+            } else if (!strcmp(argv[1], "offline")) {
+                std::string err;
+                return adb_query_command("host:reconnect-offline");
+            } else {
+                return syntax_error("adb reconnect [device|offline]");
+            }
+        }
+    }
+
+    syntax_error("unknown command %s", argv[0]);
+    return 1;
+}
+
+static int uninstall_app(int argc, const char** argv) {
+    // 'adb uninstall' takes the same arguments as 'cmd package uninstall' on device
+    std::string cmd = "cmd package";
+    while (argc-- > 0) {
+        // deny the '-k' option until the remaining data/cache can be removed with adb/UI
+        if (strcmp(*argv, "-k") == 0) {
+            printf(
+                "The -k option uninstalls the application while retaining the data/cache.\n"
+                "At the moment, there is no way to remove the remaining data.\n"
+                "You will have to reinstall the application with the same signature, and fully uninstall it.\n"
+                "If you truly wish to continue, execute 'adb shell cmd package uninstall -k'.\n");
+            return EXIT_FAILURE;
+        }
+        cmd += " " + escape_arg(*argv++);
+    }
+
+    return send_shell_command(cmd);
+}
+
+static int install_app(int argc, const char** argv) {
+    // The last argument must be the APK file
+    const char* file = argv[argc - 1];
+    if (!android::base::EndsWithIgnoreCase(file, ".apk")) {
+        return syntax_error("filename doesn't end .apk: %s", file);
+    }
+
+    struct stat sb;
+    if (stat(file, &sb) == -1) {
+        fprintf(stderr, "adb: failed to stat %s: %s\n", file, strerror(errno));
+        return 1;
+    }
+
+    int localFd = adb_open(file, O_RDONLY);
+    if (localFd < 0) {
+        fprintf(stderr, "adb: failed to open %s: %s\n", file, strerror(errno));
+        return 1;
+    }
+
+    std::string error;
+    std::string cmd = "exec:cmd package";
+
+    // don't copy the APK name, but, copy the rest of the arguments as-is
+    while (argc-- > 1) {
+        cmd += " " + escape_arg(std::string(*argv++));
+    }
+
+    // add size parameter [required for streaming installs]
+    // do last to override any user specified value
+    cmd += " " + android::base::StringPrintf("-S %" PRIu64, static_cast<uint64_t>(sb.st_size));
+
+    int remoteFd = adb_connect(cmd, &error);
+    if (remoteFd < 0) {
+        fprintf(stderr, "adb: connect error for write: %s\n", error.c_str());
+        adb_close(localFd);
+        return 1;
+    }
+
+    char buf[BUFSIZ];
+    copy_to_file(localFd, remoteFd);
+    read_status_line(remoteFd, buf, sizeof(buf));
+
+    adb_close(localFd);
+    adb_close(remoteFd);
+
+    if (!strncmp("Success", buf, 7)) {
+        fputs(buf, stdout);
+        return 0;
+    }
+    fprintf(stderr, "adb: failed to install %s: %s", file, buf);
+    return 1;
+}
+
+static int install_multiple_app(int argc, const char** argv) {
+    // Find all APK arguments starting at end.
+    // All other arguments passed through verbatim.
+    int first_apk = -1;
+    uint64_t total_size = 0;
+    for (int i = argc - 1; i >= 0; i--) {
+        const char* file = argv[i];
+
+        if (android::base::EndsWithIgnoreCase(file, ".apk")) {
+            struct stat sb;
+            if (stat(file, &sb) != -1) total_size += sb.st_size;
+            first_apk = i;
+        } else {
+            break;
+        }
+    }
+
+    if (first_apk == -1) return syntax_error("need APK file on command line");
+
+    std::string install_cmd;
+    if (_use_legacy_install()) {
+        install_cmd = "exec:pm";
+    } else {
+        install_cmd = "exec:cmd package";
+    }
+
+    std::string cmd = android::base::StringPrintf("%s install-create -S %" PRIu64, install_cmd.c_str(), total_size);
+    for (int i = 1; i < first_apk; i++) {
+        cmd += " " + escape_arg(argv[i]);
+    }
+
+    // Create install session
+    std::string error;
+    int fd = adb_connect(cmd, &error);
+    if (fd < 0) {
+        fprintf(stderr, "adb: connect error for create: %s\n", error.c_str());
+        return EXIT_FAILURE;
+    }
+    char buf[BUFSIZ];
+    read_status_line(fd, buf, sizeof(buf));
+    adb_close(fd);
+
+    int session_id = -1;
+    if (!strncmp("Success", buf, 7)) {
+        char* start = strrchr(buf, '[');
+        char* end = strrchr(buf, ']');
+        if (start && end) {
+            *end = '\0';
+            session_id = strtol(start + 1, nullptr, 10);
+        }
+    }
+    if (session_id < 0) {
+        fprintf(stderr, "adb: failed to create session\n");
+        fputs(buf, stderr);
+        return EXIT_FAILURE;
+    }
+
+    // Valid session, now stream the APKs
+    int success = 1;
+    for (int i = first_apk; i < argc; i++) {
+        const char* file = argv[i];
+        struct stat sb;
+        if (stat(file, &sb) == -1) {
+            fprintf(stderr, "adb: failed to stat %s: %s\n", file, strerror(errno));
+            success = 0;
+            goto finalize_session;
+        }
+
+        std::string cmd = android::base::StringPrintf(
+                "%s install-write -S %" PRIu64 " %d %d_%s -",
+                install_cmd.c_str(), static_cast<uint64_t>(sb.st_size), session_id, i,
+                android::base::Basename(file).c_str());
+
+        int localFd = adb_open(file, O_RDONLY);
+        if (localFd < 0) {
+            fprintf(stderr, "adb: failed to open %s: %s\n", file, strerror(errno));
+            success = 0;
+            goto finalize_session;
+        }
+
+        std::string error;
+        int remoteFd = adb_connect(cmd, &error);
+        if (remoteFd < 0) {
+            fprintf(stderr, "adb: connect error for write: %s\n", error.c_str());
+            adb_close(localFd);
+            success = 0;
+            goto finalize_session;
+        }
+
+        copy_to_file(localFd, remoteFd);
+        read_status_line(remoteFd, buf, sizeof(buf));
+
+        adb_close(localFd);
+        adb_close(remoteFd);
+
+        if (strncmp("Success", buf, 7)) {
+            fprintf(stderr, "adb: failed to write %s\n", file);
+            fputs(buf, stderr);
+            success = 0;
+            goto finalize_session;
+        }
+    }
+
+finalize_session:
+    // Commit session if we streamed everything okay; otherwise abandon
+    std::string service =
+            android::base::StringPrintf("%s install-%s %d",
+                                        install_cmd.c_str(), success ? "commit" : "abandon", session_id);
+    fd = adb_connect(service, &error);
+    if (fd < 0) {
+        fprintf(stderr, "adb: connect error for finalize: %s\n", error.c_str());
+        return EXIT_FAILURE;
+    }
+    read_status_line(fd, buf, sizeof(buf));
+    adb_close(fd);
+
+    if (!strncmp("Success", buf, 7)) {
+        fputs(buf, stdout);
+        return 0;
+    }
+    fprintf(stderr, "adb: failed to finalize session\n");
+    fputs(buf, stderr);
+    return EXIT_FAILURE;
+}
+
+static int pm_command(int argc, const char** argv) {
+    std::string cmd = "pm";
+
+    while (argc-- > 0) {
+        cmd += " " + escape_arg(*argv++);
+    }
+
+    return send_shell_command(cmd);
+}
+
+static int uninstall_app_legacy(int argc, const char** argv) {
+    /* if the user choose the -k option, we refuse to do it until devices are
+       out with the option to uninstall the remaining data somehow (adb/ui) */
+    int i;
+    for (i = 1; i < argc; i++) {
+        if (!strcmp(argv[i], "-k")) {
+            printf(
+                "The -k option uninstalls the application while retaining the data/cache.\n"
+                "At the moment, there is no way to remove the remaining data.\n"
+                "You will have to reinstall the application with the same signature, and fully uninstall it.\n"
+                "If you truly wish to continue, execute 'adb shell pm uninstall -k'\n.");
+            return EXIT_FAILURE;
+        }
+    }
+
+    /* 'adb uninstall' takes the same arguments as 'pm uninstall' on device */
+    return pm_command(argc, argv);
+}
+
+static int delete_file(const std::string& filename) {
+    std::string cmd = "rm -f " + escape_arg(filename);
+    return send_shell_command(cmd);
+}
+
+static int install_app_legacy(int argc, const char** argv) {
+    static const char *const DATA_DEST = "/data/local/tmp/%s";
+    static const char *const SD_DEST = "/sdcard/tmp/%s";
+    const char* where = DATA_DEST;
+
+    for (int i = 1; i < argc; i++) {
+        if (!strcmp(argv[i], "-s")) {
+            where = SD_DEST;
+        }
+    }
+
+    // Find last APK argument.
+    // All other arguments passed through verbatim.
+    int last_apk = -1;
+    for (int i = argc - 1; i >= 0; i--) {
+        if (android::base::EndsWithIgnoreCase(argv[i], ".apk")) {
+            last_apk = i;
+            break;
+        }
+    }
+
+    if (last_apk == -1) return syntax_error("need APK file on command line");
+
+    int result = -1;
+    std::vector<const char*> apk_file = {argv[last_apk]};
+    std::string apk_dest = android::base::StringPrintf(
+        where, android::base::Basename(argv[last_apk]).c_str());
+    if (!do_sync_push(apk_file, apk_dest.c_str(), false)) goto cleanup_apk;
+    argv[last_apk] = apk_dest.c_str(); /* destination name, not source location */
+    result = pm_command(argc, argv);
+
+cleanup_apk:
+    delete_file(apk_dest);
+    return result;
+}
diff --git a/adb/client/commandline.h b/adb/client/commandline.h
new file mode 100644
index 0000000..3aa03a7
--- /dev/null
+++ b/adb/client/commandline.h
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2016 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 COMMANDLINE_H
+#define COMMANDLINE_H
+
+#include "adb.h"
+
+// Callback used to handle the standard streams (stdout and stderr) sent by the
+// device's upon receiving a command.
+//
+class StandardStreamsCallbackInterface {
+  public:
+    StandardStreamsCallbackInterface() {
+    }
+    // Handles the stdout output from devices supporting the Shell protocol.
+    virtual void OnStdout(const char* buffer, int length) = 0;
+
+    // Handles the stderr output from devices supporting the Shell protocol.
+    virtual void OnStderr(const char* buffer, int length) = 0;
+
+    // Indicates the communication is finished and returns the appropriate error
+    // code.
+    //
+    // |status| has the status code returning by the underlying communication
+    // channels
+    virtual int Done(int status) = 0;
+
+  protected:
+    static void OnStream(std::string* string, FILE* stream, const char* buffer, int length) {
+        if (string != nullptr) {
+            string->append(buffer, length);
+        } else {
+            fwrite(buffer, 1, length, stream);
+            fflush(stream);
+        }
+    }
+
+  private:
+    DISALLOW_COPY_AND_ASSIGN(StandardStreamsCallbackInterface);
+};
+
+// Default implementation that redirects the streams to the equilavent host
+// stream or to a string
+// passed to the constructor.
+class DefaultStandardStreamsCallback : public StandardStreamsCallbackInterface {
+  public:
+    // If |stdout_str| is non-null, OnStdout will append to it.
+    // If |stderr_str| is non-null, OnStderr will append to it.
+    DefaultStandardStreamsCallback(std::string* stdout_str, std::string* stderr_str)
+        : stdout_str_(stdout_str), stderr_str_(stderr_str) {
+    }
+
+    void OnStdout(const char* buffer, int length) {
+        OnStream(stdout_str_, stdout, buffer, length);
+    }
+
+    void OnStderr(const char* buffer, int length) {
+        OnStream(stderr_str_, stderr, buffer, length);
+    }
+
+    int Done(int status) {
+        return status;
+    }
+
+  private:
+    std::string* stdout_str_;
+    std::string* stderr_str_;
+
+    DISALLOW_COPY_AND_ASSIGN(DefaultStandardStreamsCallback);
+};
+
+class SilentStandardStreamsCallbackInterface : public StandardStreamsCallbackInterface {
+  public:
+    SilentStandardStreamsCallbackInterface() = default;
+    void OnStdout(const char*, int) override final {}
+    void OnStderr(const char*, int) override final {}
+    int Done(int status) override final { return status; }
+};
+
+// Singleton.
+extern DefaultStandardStreamsCallback DEFAULT_STANDARD_STREAMS_CALLBACK;
+
+int adb_commandline(int argc, const char** argv);
+
+// Connects to the device "shell" service with |command| and prints the
+// resulting output.
+// if |callback| is non-null, stdout/stderr output will be handled by it.
+int send_shell_command(
+        const std::string& command, bool disable_shell_protocol = false,
+        StandardStreamsCallbackInterface* callback = &DEFAULT_STANDARD_STREAMS_CALLBACK);
+
+#endif  // 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/client/file_sync_client.cpp b/adb/client/file_sync_client.cpp
new file mode 100644
index 0000000..cb9bcfa
--- /dev/null
+++ b/adb/client/file_sync_client.cpp
@@ -0,0 +1,1303 @@
+/*
+ * Copyright (C) 2007 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 "client/file_sync_client.h"
+
+#include <dirent.h>
+#include <inttypes.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+#include <utime.h>
+
+#include <chrono>
+#include <functional>
+#include <memory>
+#include <sstream>
+#include <string>
+#include <vector>
+
+#include "sysdeps.h"
+
+#include "adb.h"
+#include "adb_client.h"
+#include "adb_io.h"
+#include "adb_utils.h"
+#include "file_sync_protocol.h"
+#include "line_printer.h"
+#include "sysdeps/errno.h"
+#include "sysdeps/stat.h"
+
+#include "client/commandline.h"
+
+#include <android-base/file.h>
+#include <android-base/strings.h>
+#include <android-base/stringprintf.h>
+
+struct syncsendbuf {
+    unsigned id;
+    unsigned size;
+    char data[SYNC_DATA_MAX];
+};
+
+static void ensure_trailing_separators(std::string& local_path, std::string& remote_path) {
+    if (!adb_is_separator(local_path.back())) {
+        local_path.push_back(OS_PATH_SEPARATOR);
+    }
+    if (remote_path.back() != '/') {
+        remote_path.push_back('/');
+    }
+}
+
+static bool should_pull_file(mode_t mode) {
+    return S_ISREG(mode) || S_ISBLK(mode) || S_ISCHR(mode);
+}
+
+static bool should_push_file(mode_t mode) {
+    return S_ISREG(mode) || S_ISLNK(mode);
+}
+
+struct copyinfo {
+    std::string lpath;
+    std::string rpath;
+    int64_t time = 0;
+    uint32_t mode;
+    uint64_t size = 0;
+    bool skip = false;
+
+    copyinfo(const std::string& local_path,
+             const std::string& remote_path,
+             const std::string& name,
+             unsigned int mode)
+            : lpath(local_path), rpath(remote_path), mode(mode) {
+        ensure_trailing_separators(lpath, rpath);
+        lpath.append(name);
+        rpath.append(name);
+        if (S_ISDIR(mode)) {
+            ensure_trailing_separators(lpath, rpath);
+        }
+    }
+};
+
+enum class TransferDirection {
+    push,
+    pull,
+};
+
+struct TransferLedger {
+    std::chrono::steady_clock::time_point start_time;
+    uint64_t files_transferred;
+    uint64_t files_skipped;
+    uint64_t bytes_transferred;
+    uint64_t bytes_expected;
+    bool expect_multiple_files;
+
+    TransferLedger() {
+        Reset();
+    }
+
+    bool operator==(const TransferLedger& other) const {
+        return files_transferred == other.files_transferred &&
+               files_skipped == other.files_skipped && bytes_transferred == other.bytes_transferred;
+    }
+
+    bool operator!=(const TransferLedger& other) const {
+        return !(*this == other);
+    }
+
+    void Reset() {
+        start_time = std::chrono::steady_clock::now();
+        files_transferred = 0;
+        files_skipped = 0;
+        bytes_transferred = 0;
+        bytes_expected = 0;
+    }
+
+    std::string TransferRate() {
+        if (bytes_transferred == 0) return "";
+
+        std::chrono::duration<double> duration;
+        duration = std::chrono::steady_clock::now() - start_time;
+
+        double s = duration.count();
+        if (s == 0) {
+            return "";
+        }
+        double rate = (static_cast<double>(bytes_transferred) / s) / (1024 * 1024);
+        return android::base::StringPrintf(" %.1f MB/s (%" PRIu64 " bytes in %.3fs)", rate,
+                                           bytes_transferred, s);
+    }
+
+    void ReportProgress(LinePrinter& lp, const std::string& file, uint64_t file_copied_bytes,
+                        uint64_t file_total_bytes) {
+        char overall_percentage_str[5] = "?";
+        if (bytes_expected != 0 && bytes_transferred <= bytes_expected) {
+            int overall_percentage = static_cast<int>(bytes_transferred * 100 / bytes_expected);
+            // If we're pulling symbolic links, we'll pull the target of the link rather than
+            // just create a local link, and that will cause us to go over 100%.
+            if (overall_percentage <= 100) {
+                snprintf(overall_percentage_str, sizeof(overall_percentage_str), "%d%%",
+                         overall_percentage);
+            }
+        }
+
+        std::string output;
+        if (file_copied_bytes > file_total_bytes || file_total_bytes == 0) {
+            // This case can happen if we're racing against something that wrote to the file
+            // between our stat and our read, or if we're reading a magic file that lies about
+            // its size. Just show how much we've copied.
+            output = android::base::StringPrintf("[%4s] %s: %" PRId64 "/?", overall_percentage_str,
+                                                 file.c_str(), file_copied_bytes);
+        } else {
+            // If we're transferring multiple files, we want to know how far through the current
+            // file we are, as well as the overall percentage.
+            if (expect_multiple_files) {
+                int file_percentage = static_cast<int>(file_copied_bytes * 100 / file_total_bytes);
+                output = android::base::StringPrintf("[%4s] %s: %d%%", overall_percentage_str,
+                                                     file.c_str(), file_percentage);
+            } else {
+                output =
+                    android::base::StringPrintf("[%4s] %s", overall_percentage_str, file.c_str());
+            }
+        }
+        lp.Print(output, LinePrinter::LineType::INFO);
+    }
+
+    void ReportTransferRate(LinePrinter& lp, const std::string& name, TransferDirection direction) {
+        const char* direction_str = (direction == TransferDirection::push) ? "pushed" : "pulled";
+        std::stringstream ss;
+        if (!name.empty()) {
+            ss << name << ": ";
+        }
+        ss << files_transferred << " file" << ((files_transferred == 1) ? "" : "s") << " "
+           << direction_str << ".";
+        if (files_skipped > 0) {
+            ss << " " << files_skipped << " file" << ((files_skipped == 1) ? "" : "s")
+               << " skipped.";
+        }
+        ss << TransferRate();
+
+        lp.Print(ss.str(), LinePrinter::LineType::INFO);
+        lp.KeepInfoLine();
+    }
+};
+
+class SyncConnection {
+  public:
+    SyncConnection() : expect_done_(false) {
+        max = SYNC_DATA_MAX; // TODO: decide at runtime.
+
+        std::string error;
+        if (!adb_get_feature_set(&features_, &error)) {
+            fd = -1;
+            Error("failed to get feature set: %s", error.c_str());
+        } else {
+            have_stat_v2_ = CanUseFeature(features_, kFeatureStat2);
+            fd = adb_connect("sync:", &error);
+            if (fd < 0) {
+                Error("connect failed: %s", error.c_str());
+            }
+        }
+    }
+
+    ~SyncConnection() {
+        if (!IsValid()) return;
+
+        if (SendQuit()) {
+            // We sent a quit command, so the server should be doing orderly
+            // shutdown soon. But if we encountered an error while we were using
+            // the connection, the server might still be sending data (before
+            // doing orderly shutdown), in which case we won't wait for all of
+            // the data nor the coming orderly shutdown. In the common success
+            // case, this will wait for the server to do orderly shutdown.
+            ReadOrderlyShutdown(fd);
+        }
+        adb_close(fd);
+
+        line_printer_.KeepInfoLine();
+    }
+
+    const FeatureSet& Features() const { return features_; }
+
+    bool IsValid() { return fd >= 0; }
+
+    bool ReceivedError(const char* from, const char* to) {
+        adb_pollfd pfd = {.fd = fd, .events = POLLIN};
+        int rc = adb_poll(&pfd, 1, 0);
+        if (rc < 0) {
+            Error("failed to poll: %s", strerror(errno));
+            return true;
+        }
+        return rc != 0;
+    }
+
+    void NewTransfer() {
+        current_ledger_.Reset();
+    }
+
+    void RecordBytesTransferred(size_t bytes) {
+        current_ledger_.bytes_transferred += bytes;
+        global_ledger_.bytes_transferred += bytes;
+    }
+
+    void RecordFilesTransferred(size_t files) {
+        current_ledger_.files_transferred += files;
+        global_ledger_.files_transferred += files;
+    }
+
+    void RecordFilesSkipped(size_t files) {
+        current_ledger_.files_skipped += files;
+        global_ledger_.files_skipped += files;
+    }
+
+    void ReportProgress(const std::string& file, uint64_t file_copied_bytes,
+                        uint64_t file_total_bytes) {
+        current_ledger_.ReportProgress(line_printer_, file, file_copied_bytes, file_total_bytes);
+    }
+
+    void ReportTransferRate(const std::string& file, TransferDirection direction) {
+        current_ledger_.ReportTransferRate(line_printer_, file, direction);
+    }
+
+    void ReportOverallTransferRate(TransferDirection direction) {
+        if (current_ledger_ != global_ledger_) {
+            global_ledger_.ReportTransferRate(line_printer_, "", direction);
+        }
+    }
+
+    bool SendRequest(int id, const char* path_and_mode) {
+        size_t path_length = strlen(path_and_mode);
+        if (path_length > 1024) {
+            Error("SendRequest failed: path too long: %zu", path_length);
+            errno = ENAMETOOLONG;
+            return false;
+        }
+
+        // Sending header and payload in a single write makes a noticeable
+        // difference to "adb sync" performance.
+        std::vector<char> buf(sizeof(SyncRequest) + path_length);
+        SyncRequest* req = reinterpret_cast<SyncRequest*>(&buf[0]);
+        req->id = id;
+        req->path_length = path_length;
+        char* data = reinterpret_cast<char*>(req + 1);
+        memcpy(data, path_and_mode, path_length);
+
+        return WriteFdExactly(fd, &buf[0], buf.size());
+    }
+
+    bool SendStat(const char* path_and_mode) {
+        if (!have_stat_v2_) {
+            errno = ENOTSUP;
+            return false;
+        }
+        return SendRequest(ID_STAT_V2, path_and_mode);
+    }
+
+    bool SendLstat(const char* path_and_mode) {
+        if (have_stat_v2_) {
+            return SendRequest(ID_LSTAT_V2, path_and_mode);
+        } else {
+            return SendRequest(ID_LSTAT_V1, path_and_mode);
+        }
+    }
+
+    bool FinishStat(struct stat* st) {
+        syncmsg msg;
+
+        memset(st, 0, sizeof(*st));
+        if (have_stat_v2_) {
+            if (!ReadFdExactly(fd, &msg.stat_v2, sizeof(msg.stat_v2))) {
+                fatal_errno("protocol fault: failed to read stat response");
+            }
+
+            if (msg.stat_v2.id != ID_LSTAT_V2 && msg.stat_v2.id != ID_STAT_V2) {
+                fatal_errno("protocol fault: stat response has wrong message id: %" PRIx32,
+                            msg.stat_v2.id);
+            }
+
+            if (msg.stat_v2.error != 0) {
+                errno = errno_from_wire(msg.stat_v2.error);
+                return false;
+            }
+
+            st->st_dev = msg.stat_v2.dev;
+            st->st_ino = msg.stat_v2.ino;
+            st->st_mode = msg.stat_v2.mode;
+            st->st_nlink = msg.stat_v2.nlink;
+            st->st_uid = msg.stat_v2.uid;
+            st->st_gid = msg.stat_v2.gid;
+            st->st_size = msg.stat_v2.size;
+            st->st_atime = msg.stat_v2.atime;
+            st->st_mtime = msg.stat_v2.mtime;
+            st->st_ctime = msg.stat_v2.ctime;
+            return true;
+        } else {
+            if (!ReadFdExactly(fd, &msg.stat_v1, sizeof(msg.stat_v1))) {
+                fatal_errno("protocol fault: failed to read stat response");
+            }
+
+            if (msg.stat_v1.id != ID_LSTAT_V1) {
+                fatal_errno("protocol fault: stat response has wrong message id: %" PRIx32,
+                            msg.stat_v1.id);
+            }
+
+            if (msg.stat_v1.mode == 0 && msg.stat_v1.size == 0 && msg.stat_v1.time == 0) {
+                // There's no way for us to know what the error was.
+                errno = ENOPROTOOPT;
+                return false;
+            }
+
+            st->st_mode = msg.stat_v1.mode;
+            st->st_size = msg.stat_v1.size;
+            st->st_ctime = msg.stat_v1.time;
+            st->st_mtime = msg.stat_v1.time;
+        }
+
+        return true;
+    }
+
+    // Sending header, payload, and footer in a single write makes a huge
+    // difference to "adb sync" performance.
+    bool SendSmallFile(const char* path_and_mode,
+                       const char* lpath, const char* rpath,
+                       unsigned mtime,
+                       const char* data, size_t data_length) {
+        size_t path_length = strlen(path_and_mode);
+        if (path_length > 1024) {
+            Error("SendSmallFile failed: path too long: %zu", path_length);
+            errno = ENAMETOOLONG;
+            return false;
+        }
+
+        std::vector<char> buf(sizeof(SyncRequest) + path_length +
+                              sizeof(SyncRequest) + data_length +
+                              sizeof(SyncRequest));
+        char* p = &buf[0];
+
+        SyncRequest* req_send = reinterpret_cast<SyncRequest*>(p);
+        req_send->id = ID_SEND;
+        req_send->path_length = path_length;
+        p += sizeof(SyncRequest);
+        memcpy(p, path_and_mode, path_length);
+        p += path_length;
+
+        SyncRequest* req_data = reinterpret_cast<SyncRequest*>(p);
+        req_data->id = ID_DATA;
+        req_data->path_length = data_length;
+        p += sizeof(SyncRequest);
+        memcpy(p, data, data_length);
+        p += data_length;
+
+        SyncRequest* req_done = reinterpret_cast<SyncRequest*>(p);
+        req_done->id = ID_DONE;
+        req_done->path_length = mtime;
+        p += sizeof(SyncRequest);
+
+        WriteOrDie(lpath, rpath, &buf[0], (p - &buf[0]));
+        expect_done_ = true;
+
+        // RecordFilesTransferred gets called in CopyDone.
+        RecordBytesTransferred(data_length);
+        ReportProgress(rpath, data_length, data_length);
+        return true;
+    }
+
+    bool SendLargeFile(const char* path_and_mode,
+                       const char* lpath, const char* rpath,
+                       unsigned mtime) {
+        if (!SendRequest(ID_SEND, path_and_mode)) {
+            Error("failed to send ID_SEND message '%s': %s", path_and_mode, strerror(errno));
+            return false;
+        }
+
+        struct stat st;
+        if (stat(lpath, &st) == -1) {
+            Error("cannot stat '%s': %s", lpath, strerror(errno));
+            return false;
+        }
+
+        uint64_t total_size = st.st_size;
+        uint64_t bytes_copied = 0;
+
+        int lfd = adb_open(lpath, O_RDONLY);
+        if (lfd < 0) {
+            Error("opening '%s' locally failed: %s", lpath, strerror(errno));
+            return false;
+        }
+
+        syncsendbuf sbuf;
+        sbuf.id = ID_DATA;
+        while (true) {
+            int bytes_read = adb_read(lfd, sbuf.data, max - sizeof(SyncRequest));
+            if (bytes_read == -1) {
+                Error("reading '%s' locally failed: %s", lpath, strerror(errno));
+                adb_close(lfd);
+                return false;
+            } else if (bytes_read == 0) {
+                break;
+            }
+
+            sbuf.size = bytes_read;
+            WriteOrDie(lpath, rpath, &sbuf, sizeof(SyncRequest) + bytes_read);
+
+            RecordBytesTransferred(bytes_read);
+            bytes_copied += bytes_read;
+
+            // Check to see if we've received an error from the other side.
+            if (ReceivedError(lpath, rpath)) {
+                break;
+            }
+
+            ReportProgress(rpath, bytes_copied, total_size);
+        }
+
+        adb_close(lfd);
+
+        syncmsg msg;
+        msg.data.id = ID_DONE;
+        msg.data.size = mtime;
+        expect_done_ = true;
+
+        // RecordFilesTransferred gets called in CopyDone.
+        return WriteOrDie(lpath, rpath, &msg.data, sizeof(msg.data));
+    }
+
+    bool CopyDone(const char* from, const char* to) {
+        syncmsg msg;
+        if (!ReadFdExactly(fd, &msg.status, sizeof(msg.status))) {
+            Error("failed to copy '%s' to '%s': couldn't read from device", from, to);
+            return false;
+        }
+        if (msg.status.id == ID_OKAY) {
+            if (expect_done_) {
+                expect_done_ = false;
+                RecordFilesTransferred(1);
+                return true;
+            } else {
+                Error("failed to copy '%s' to '%s': received premature success", from, to);
+                return true;
+            }
+        }
+        if (msg.status.id != ID_FAIL) {
+            Error("failed to copy '%s' to '%s': unknown reason %d", from, to, msg.status.id);
+            return false;
+        }
+        return ReportCopyFailure(from, to, msg);
+    }
+
+    bool ReportCopyFailure(const char* from, const char* to, const syncmsg& msg) {
+        std::vector<char> buf(msg.status.msglen + 1);
+        if (!ReadFdExactly(fd, &buf[0], msg.status.msglen)) {
+            Error("failed to copy '%s' to '%s'; failed to read reason (!): %s",
+                  from, to, strerror(errno));
+            return false;
+        }
+        buf[msg.status.msglen] = 0;
+        Error("failed to copy '%s' to '%s': remote %s", from, to, &buf[0]);
+        return false;
+    }
+
+    void Printf(const char* fmt, ...) __attribute__((__format__(__printf__, 2, 3))) {
+        std::string s;
+
+        va_list ap;
+        va_start(ap, fmt);
+        android::base::StringAppendV(&s, fmt, ap);
+        va_end(ap);
+
+        line_printer_.Print(s, LinePrinter::INFO);
+    }
+
+    void Println(const char* fmt, ...) __attribute__((__format__(__printf__, 2, 3))) {
+        std::string s;
+
+        va_list ap;
+        va_start(ap, fmt);
+        android::base::StringAppendV(&s, fmt, ap);
+        va_end(ap);
+
+        line_printer_.Print(s, LinePrinter::INFO);
+        line_printer_.KeepInfoLine();
+    }
+
+    void Error(const char* fmt, ...) __attribute__((__format__(__printf__, 2, 3))) {
+        std::string s = "adb: error: ";
+
+        va_list ap;
+        va_start(ap, fmt);
+        android::base::StringAppendV(&s, fmt, ap);
+        va_end(ap);
+
+        line_printer_.Print(s, LinePrinter::ERROR);
+    }
+
+    void Warning(const char* fmt, ...) __attribute__((__format__(__printf__, 2, 3))) {
+        std::string s = "adb: warning: ";
+
+        va_list ap;
+        va_start(ap, fmt);
+        android::base::StringAppendV(&s, fmt, ap);
+        va_end(ap);
+
+        line_printer_.Print(s, LinePrinter::WARNING);
+    }
+
+    void ComputeExpectedTotalBytes(const std::vector<copyinfo>& file_list) {
+        current_ledger_.bytes_expected = 0;
+        for (const copyinfo& ci : file_list) {
+            // Unfortunately, this doesn't work for symbolic links, because we'll copy the
+            // target of the link rather than just creating a link. (But ci.size is the link size.)
+            if (!ci.skip) current_ledger_.bytes_expected += ci.size;
+        }
+        current_ledger_.expect_multiple_files = true;
+    }
+
+    void SetExpectedTotalBytes(uint64_t expected_total_bytes) {
+        current_ledger_.bytes_expected = expected_total_bytes;
+        current_ledger_.expect_multiple_files = false;
+    }
+
+    // TODO: add a char[max] buffer here, to replace syncsendbuf...
+    int fd;
+    size_t max;
+
+  private:
+    bool expect_done_;
+    FeatureSet features_;
+    bool have_stat_v2_;
+
+    TransferLedger global_ledger_;
+    TransferLedger current_ledger_;
+    LinePrinter line_printer_;
+
+    bool SendQuit() {
+        return SendRequest(ID_QUIT, ""); // TODO: add a SendResponse?
+    }
+
+    bool WriteOrDie(const char* from, const char* to, const void* data, size_t data_length) {
+        if (!WriteFdExactly(fd, data, data_length)) {
+            if (errno == ECONNRESET) {
+                // Assume adbd told us why it was closing the connection, and
+                // try to read failure reason from adbd.
+                syncmsg msg;
+                if (!ReadFdExactly(fd, &msg.status, sizeof(msg.status))) {
+                    Error("failed to copy '%s' to '%s': no response: %s", from, to, strerror(errno));
+                } else if (msg.status.id != ID_FAIL) {
+                    Error("failed to copy '%s' to '%s': not ID_FAIL: %d", from, to, msg.status.id);
+                } else {
+                    ReportCopyFailure(from, to, msg);
+                }
+            } else {
+                Error("%zu-byte write failed: %s", data_length, strerror(errno));
+            }
+            _exit(1);
+        }
+        return true;
+    }
+};
+
+typedef void (sync_ls_cb)(unsigned mode, unsigned size, unsigned time, const char* name);
+
+static bool sync_ls(SyncConnection& sc, const char* path,
+                    const std::function<sync_ls_cb>& func) {
+    if (!sc.SendRequest(ID_LIST, path)) return false;
+
+    while (true) {
+        syncmsg msg;
+        if (!ReadFdExactly(sc.fd, &msg.dent, sizeof(msg.dent))) return false;
+
+        if (msg.dent.id == ID_DONE) return true;
+        if (msg.dent.id != ID_DENT) return false;
+
+        size_t len = msg.dent.namelen;
+        if (len > 256) return false; // TODO: resize buffer? continue?
+
+        char buf[257];
+        if (!ReadFdExactly(sc.fd, buf, len)) return false;
+        buf[len] = 0;
+
+        func(msg.dent.mode, msg.dent.size, msg.dent.time, buf);
+    }
+}
+
+static bool sync_stat(SyncConnection& sc, const char* path, struct stat* st) {
+    return sc.SendStat(path) && sc.FinishStat(st);
+}
+
+static bool sync_lstat(SyncConnection& sc, const char* path, struct stat* st) {
+    return sc.SendLstat(path) && sc.FinishStat(st);
+}
+
+static bool sync_stat_fallback(SyncConnection& sc, const char* path, struct stat* st) {
+    if (sync_stat(sc, path, st)) {
+        return true;
+    }
+
+    if (errno != ENOTSUP) {
+        return false;
+    }
+
+    // Try to emulate the parts we can when talking to older adbds.
+    bool lstat_result = sync_lstat(sc, path, st);
+    if (!lstat_result) {
+        return false;
+    }
+
+    if (S_ISLNK(st->st_mode)) {
+        // If the target is a symlink, figure out whether it's a file or a directory.
+        // Also, zero out the st_size field, since no one actually cares what the path length is.
+        st->st_size = 0;
+        std::string dir_path = path;
+        dir_path.push_back('/');
+        struct stat tmp_st;
+
+        st->st_mode &= ~S_IFMT;
+        if (sync_lstat(sc, dir_path.c_str(), &tmp_st)) {
+            st->st_mode |= S_IFDIR;
+        } else {
+            st->st_mode |= S_IFREG;
+        }
+    }
+    return true;
+}
+
+static bool sync_send(SyncConnection& sc, const char* lpath, const char* rpath, unsigned mtime,
+                      mode_t mode, bool sync) {
+    std::string path_and_mode = android::base::StringPrintf("%s,%d", rpath, mode);
+
+    if (sync) {
+        struct stat st;
+        if (sync_lstat(sc, rpath, &st)) {
+            // For links, we cannot update the atime/mtime.
+            if ((S_ISREG(mode & st.st_mode) && st.st_mtime == static_cast<time_t>(mtime)) ||
+                (S_ISLNK(mode & st.st_mode) && st.st_mtime >= static_cast<time_t>(mtime))) {
+                sc.RecordFilesSkipped(1);
+                return true;
+            }
+        }
+    }
+
+    if (S_ISLNK(mode)) {
+#if !defined(_WIN32)
+        char buf[PATH_MAX];
+        ssize_t data_length = readlink(lpath, buf, PATH_MAX - 1);
+        if (data_length == -1) {
+            sc.Error("readlink '%s' failed: %s", lpath, strerror(errno));
+            return false;
+        }
+        buf[data_length++] = '\0';
+
+        if (!sc.SendSmallFile(path_and_mode.c_str(), lpath, rpath, mtime, buf, data_length)) {
+            return false;
+        }
+        return sc.CopyDone(lpath, rpath);
+#endif
+    }
+
+    struct stat st;
+    if (stat(lpath, &st) == -1) {
+        sc.Error("failed to stat local file '%s': %s", lpath, strerror(errno));
+        return false;
+    }
+    if (st.st_size < SYNC_DATA_MAX) {
+        std::string data;
+        if (!android::base::ReadFileToString(lpath, &data, true)) {
+            sc.Error("failed to read all of '%s': %s", lpath, strerror(errno));
+            return false;
+        }
+        if (!sc.SendSmallFile(path_and_mode.c_str(), lpath, rpath, mtime,
+                              data.data(), data.size())) {
+            return false;
+        }
+    } else {
+        if (!sc.SendLargeFile(path_and_mode.c_str(), lpath, rpath, mtime)) {
+            return false;
+        }
+    }
+    return sc.CopyDone(lpath, rpath);
+}
+
+static bool sync_recv(SyncConnection& sc, const char* rpath, const char* lpath,
+                      const char* name, uint64_t expected_size) {
+    if (!sc.SendRequest(ID_RECV, rpath)) return false;
+
+    adb_unlink(lpath);
+    int lfd = adb_creat(lpath, 0644);
+    if (lfd < 0) {
+        sc.Error("cannot create '%s': %s", lpath, strerror(errno));
+        return false;
+    }
+
+    uint64_t bytes_copied = 0;
+    while (true) {
+        syncmsg msg;
+        if (!ReadFdExactly(sc.fd, &msg.data, sizeof(msg.data))) {
+            adb_close(lfd);
+            adb_unlink(lpath);
+            return false;
+        }
+
+        if (msg.data.id == ID_DONE) break;
+
+        if (msg.data.id != ID_DATA) {
+            adb_close(lfd);
+            adb_unlink(lpath);
+            sc.ReportCopyFailure(rpath, lpath, msg);
+            return false;
+        }
+
+        if (msg.data.size > sc.max) {
+            sc.Error("msg.data.size too large: %u (max %zu)", msg.data.size, sc.max);
+            adb_close(lfd);
+            adb_unlink(lpath);
+            return false;
+        }
+
+        char buffer[SYNC_DATA_MAX];
+        if (!ReadFdExactly(sc.fd, buffer, msg.data.size)) {
+            adb_close(lfd);
+            adb_unlink(lpath);
+            return false;
+        }
+
+        if (!WriteFdExactly(lfd, buffer, msg.data.size)) {
+            sc.Error("cannot write '%s': %s", lpath, strerror(errno));
+            adb_close(lfd);
+            adb_unlink(lpath);
+            return false;
+        }
+
+        bytes_copied += msg.data.size;
+
+        sc.RecordBytesTransferred(msg.data.size);
+        sc.ReportProgress(name != nullptr ? name : rpath, bytes_copied, expected_size);
+    }
+
+    sc.RecordFilesTransferred(1);
+    adb_close(lfd);
+    return true;
+}
+
+bool do_sync_ls(const char* path) {
+    SyncConnection sc;
+    if (!sc.IsValid()) return false;
+
+    return sync_ls(sc, path, [](unsigned mode, unsigned size, unsigned time,
+                                const char* name) {
+        printf("%08x %08x %08x %s\n", mode, size, time, name);
+    });
+}
+
+static bool IsDotOrDotDot(const char* name) {
+    return name[0] == '.' && (name[1] == '\0' || (name[1] == '.' && name[2] == '\0'));
+}
+
+static bool local_build_list(SyncConnection& sc, std::vector<copyinfo>* file_list,
+                             std::vector<std::string>* directory_list, const std::string& lpath,
+                             const std::string& rpath) {
+    std::vector<copyinfo> dirlist;
+    std::unique_ptr<DIR, int (*)(DIR*)> dir(opendir(lpath.c_str()), closedir);
+    if (!dir) {
+        sc.Error("cannot open '%s': %s", lpath.c_str(), strerror(errno));
+        return false;
+    }
+
+    bool empty_dir = true;
+    dirent* de;
+    while ((de = readdir(dir.get()))) {
+        if (IsDotOrDotDot(de->d_name)) {
+            continue;
+        }
+
+        empty_dir = false;
+        std::string stat_path = lpath + de->d_name;
+
+        struct stat st;
+        if (lstat(stat_path.c_str(), &st) == -1) {
+            sc.Error("cannot lstat '%s': %s", stat_path.c_str(),
+                     strerror(errno));
+            continue;
+        }
+
+        copyinfo ci(lpath, rpath, de->d_name, st.st_mode);
+        if (S_ISDIR(st.st_mode)) {
+            dirlist.push_back(ci);
+        } else {
+            if (!should_push_file(st.st_mode)) {
+                sc.Warning("skipping special file '%s' (mode = 0o%o)", lpath.c_str(), st.st_mode);
+                ci.skip = true;
+            }
+            ci.time = st.st_mtime;
+            ci.size = st.st_size;
+            file_list->push_back(ci);
+        }
+    }
+
+    // Close this directory and recurse.
+    dir.reset();
+
+    for (const copyinfo& ci : dirlist) {
+        directory_list->push_back(ci.rpath);
+        local_build_list(sc, file_list, directory_list, ci.lpath, ci.rpath);
+    }
+
+    return true;
+}
+
+static bool copy_local_dir_remote(SyncConnection& sc, std::string lpath,
+                                  std::string rpath, bool check_timestamps,
+                                  bool list_only) {
+    sc.NewTransfer();
+
+    // Make sure that both directory paths end in a slash.
+    // Both paths are known to be nonempty, so we don't need to check.
+    ensure_trailing_separators(lpath, rpath);
+
+    // Recursively build the list of files to copy.
+    std::vector<copyinfo> file_list;
+    std::vector<std::string> directory_list;
+
+    for (std::string dirpath = rpath; dirpath != "/"; dirpath = android::base::Dirname(dirpath)) {
+        directory_list.push_back(dirpath);
+    }
+    std::reverse(directory_list.begin(), directory_list.end());
+
+    int skipped = 0;
+    if (!local_build_list(sc, &file_list, &directory_list, lpath, rpath)) {
+        return false;
+    }
+
+    // b/110953234:
+    // P shipped with a bug that causes directory creation as a side-effect of a push to fail.
+    // Work around this by explicitly doing a mkdir via shell.
+    //
+    // Devices that don't support shell_v2 are unhappy if we try to send a too-long packet to them,
+    // but they're not affected by this bug, so only apply the workaround if we have shell_v2.
+    //
+    // TODO(b/25457350): We don't preserve permissions on directories.
+    // TODO: Find all of the leaves and `mkdir -p` them instead?
+    if (CanUseFeature(sc.Features(), kFeatureShell2)) {
+        SilentStandardStreamsCallbackInterface cb;
+        std::string cmd = "mkdir";
+        for (const auto& dir : directory_list) {
+            std::string escaped_path = escape_arg(dir);
+            if (escaped_path.size() > 16384) {
+                // Somewhat arbitrarily limit that probably won't ever happen.
+                sc.Error("path too long: %s", escaped_path.c_str());
+                return false;
+            }
+
+            // The maximum should be 64kiB, but that's not including other stuff that gets tacked
+            // onto the command line, so let's be a bit conservative.
+            if (cmd.size() + escaped_path.size() > 32768) {
+                // Dispatch the command, ignoring failure (since the directory might already exist).
+                send_shell_command(cmd, false, &cb);
+                cmd = "mkdir";
+            }
+            cmd += " ";
+            cmd += escaped_path;
+        }
+
+        if (cmd != "mkdir") {
+            send_shell_command(cmd, false, &cb);
+        }
+    }
+
+    if (check_timestamps) {
+        for (const copyinfo& ci : file_list) {
+            if (!sc.SendLstat(ci.rpath.c_str())) {
+                sc.Error("failed to send lstat");
+                return false;
+            }
+        }
+        for (copyinfo& ci : file_list) {
+            struct stat st;
+            if (sc.FinishStat(&st)) {
+                if (st.st_size == static_cast<off_t>(ci.size)) {
+                    // For links, we cannot update the atime/mtime.
+                    if ((S_ISREG(ci.mode & st.st_mode) && st.st_mtime == ci.time) ||
+                        (S_ISLNK(ci.mode & st.st_mode) && st.st_mtime >= ci.time)) {
+                        ci.skip = true;
+                    }
+                }
+            }
+        }
+    }
+
+    sc.ComputeExpectedTotalBytes(file_list);
+
+    for (const copyinfo& ci : file_list) {
+        if (!ci.skip) {
+            if (list_only) {
+                sc.Println("would push: %s -> %s", ci.lpath.c_str(), ci.rpath.c_str());
+            } else {
+                if (!sync_send(sc, ci.lpath.c_str(), ci.rpath.c_str(), ci.time, ci.mode, false)) {
+                    return false;
+                }
+            }
+        } else {
+            skipped++;
+        }
+    }
+
+    sc.RecordFilesSkipped(skipped);
+    sc.ReportTransferRate(lpath, TransferDirection::push);
+    return true;
+}
+
+bool do_sync_push(const std::vector<const char*>& srcs, const char* dst, bool sync) {
+    SyncConnection sc;
+    if (!sc.IsValid()) return false;
+
+    bool success = true;
+    bool dst_exists;
+    bool dst_isdir;
+
+    struct stat st;
+    if (sync_stat_fallback(sc, dst, &st)) {
+        dst_exists = true;
+        dst_isdir = S_ISDIR(st.st_mode);
+    } else {
+        if (errno == ENOENT || errno == ENOPROTOOPT) {
+            dst_exists = false;
+            dst_isdir = false;
+        } else {
+            sc.Error("stat failed when trying to push to %s: %s", dst, strerror(errno));
+            return false;
+        }
+    }
+
+    if (!dst_isdir) {
+        if (srcs.size() > 1) {
+            sc.Error("target '%s' is not a directory", dst);
+            return false;
+        } else {
+            size_t dst_len = strlen(dst);
+
+            // A path that ends with a slash doesn't have to be a directory if
+            // it doesn't exist yet.
+            if (dst[dst_len - 1] == '/' && dst_exists) {
+                sc.Error("failed to access '%s': Not a directory", dst);
+                return false;
+            }
+        }
+    }
+
+    for (const char* src_path : srcs) {
+        const char* dst_path = dst;
+        struct stat st;
+        if (stat(src_path, &st) == -1) {
+            sc.Error("cannot stat '%s': %s", src_path, strerror(errno));
+            success = false;
+            continue;
+        }
+
+        if (S_ISDIR(st.st_mode)) {
+            std::string dst_dir = dst;
+
+            // If the destination path existed originally, the source directory
+            // should be copied as a child of the destination.
+            if (dst_exists) {
+                if (!dst_isdir) {
+                    sc.Error("target '%s' is not a directory", dst);
+                    return false;
+                }
+                // dst is a POSIX path, so we don't want to use the sysdeps
+                // helpers here.
+                if (dst_dir.back() != '/') {
+                    dst_dir.push_back('/');
+                }
+                dst_dir.append(android::base::Basename(src_path));
+            }
+
+            success &= copy_local_dir_remote(sc, src_path, dst_dir.c_str(), sync, false);
+            continue;
+        } else if (!should_push_file(st.st_mode)) {
+            sc.Warning("skipping special file '%s' (mode = 0o%o)", src_path, st.st_mode);
+            continue;
+        }
+
+        std::string path_holder;
+        if (dst_isdir) {
+            // If we're copying a local file to a remote directory,
+            // we really want to copy to remote_dir + "/" + local_filename.
+            path_holder = dst_path;
+            if (path_holder.back() != '/') {
+                path_holder.push_back('/');
+            }
+            path_holder += android::base::Basename(src_path);
+            dst_path = path_holder.c_str();
+        }
+
+        sc.NewTransfer();
+        sc.SetExpectedTotalBytes(st.st_size);
+        success &= sync_send(sc, src_path, dst_path, st.st_mtime, st.st_mode, sync);
+        sc.ReportTransferRate(src_path, TransferDirection::push);
+    }
+
+    sc.ReportOverallTransferRate(TransferDirection::push);
+    return success;
+}
+
+static bool remote_build_list(SyncConnection& sc, std::vector<copyinfo>* file_list,
+                              const std::string& rpath, const std::string& lpath) {
+    std::vector<copyinfo> dirlist;
+    std::vector<copyinfo> linklist;
+
+    // Add an entry for the current directory to ensure it gets created before pulling its contents.
+    copyinfo ci(android::base::Dirname(lpath), android::base::Dirname(rpath),
+                android::base::Basename(lpath), S_IFDIR);
+    file_list->push_back(ci);
+
+    // Put the files/dirs in rpath on the lists.
+    auto callback = [&](unsigned mode, unsigned size, unsigned time, const char* name) {
+        if (IsDotOrDotDot(name)) {
+            return;
+        }
+
+        copyinfo ci(lpath, rpath, name, mode);
+        if (S_ISDIR(mode)) {
+            dirlist.push_back(ci);
+        } else if (S_ISLNK(mode)) {
+            linklist.push_back(ci);
+        } else {
+            if (!should_pull_file(ci.mode)) {
+                sc.Warning("skipping special file '%s' (mode = 0o%o)", ci.rpath.c_str(), ci.mode);
+                ci.skip = true;
+            }
+            ci.time = time;
+            ci.size = size;
+            file_list->push_back(ci);
+        }
+    };
+
+    if (!sync_ls(sc, rpath.c_str(), callback)) {
+        return false;
+    }
+
+    // Check each symlink we found to see whether it's a file or directory.
+    for (copyinfo& link_ci : linklist) {
+        struct stat st;
+        if (!sync_stat_fallback(sc, link_ci.rpath.c_str(), &st)) {
+            sc.Warning("stat failed for path %s: %s", link_ci.rpath.c_str(), strerror(errno));
+            continue;
+        }
+
+        if (S_ISDIR(st.st_mode)) {
+            dirlist.emplace_back(std::move(link_ci));
+        } else {
+            file_list->emplace_back(std::move(link_ci));
+        }
+    }
+
+    // Recurse into each directory we found.
+    while (!dirlist.empty()) {
+        copyinfo current = dirlist.back();
+        dirlist.pop_back();
+        if (!remote_build_list(sc, file_list, current.rpath, current.lpath)) {
+            return false;
+        }
+    }
+
+    return true;
+}
+
+static int set_time_and_mode(const std::string& lpath, time_t time,
+                             unsigned int mode) {
+    struct utimbuf times = { time, time };
+    int r1 = utime(lpath.c_str(), &times);
+
+    /* use umask for permissions */
+    mode_t mask = umask(0000);
+    umask(mask);
+    int r2 = chmod(lpath.c_str(), mode & ~mask);
+
+    return r1 ? r1 : r2;
+}
+
+static bool copy_remote_dir_local(SyncConnection& sc, std::string rpath,
+                                  std::string lpath, bool copy_attrs) {
+    sc.NewTransfer();
+
+    // Make sure that both directory paths end in a slash.
+    // Both paths are known to be nonempty, so we don't need to check.
+    ensure_trailing_separators(lpath, rpath);
+
+    // Recursively build the list of files to copy.
+    sc.Printf("pull: building file list...");
+    std::vector<copyinfo> file_list;
+    if (!remote_build_list(sc, &file_list, rpath.c_str(), lpath.c_str())) {
+        return false;
+    }
+
+    sc.ComputeExpectedTotalBytes(file_list);
+
+    int skipped = 0;
+    for (const copyinfo &ci : file_list) {
+        if (!ci.skip) {
+            if (S_ISDIR(ci.mode)) {
+                // Entry is for an empty directory, create it and continue.
+                // TODO(b/25457350): We don't preserve permissions on directories.
+                if (!mkdirs(ci.lpath))  {
+                    sc.Error("failed to create directory '%s': %s",
+                             ci.lpath.c_str(), strerror(errno));
+                    return false;
+                }
+                continue;
+            }
+
+            if (!sync_recv(sc, ci.rpath.c_str(), ci.lpath.c_str(), nullptr, ci.size)) {
+                return false;
+            }
+
+            if (copy_attrs && set_time_and_mode(ci.lpath, ci.time, ci.mode)) {
+                return false;
+            }
+        } else {
+            skipped++;
+        }
+    }
+
+    sc.RecordFilesSkipped(skipped);
+    sc.ReportTransferRate(rpath, TransferDirection::pull);
+    return true;
+}
+
+bool do_sync_pull(const std::vector<const char*>& srcs, const char* dst,
+                  bool copy_attrs, const char* name) {
+    SyncConnection sc;
+    if (!sc.IsValid()) return false;
+
+    bool success = true;
+    struct stat st;
+    bool dst_exists = true;
+
+    if (stat(dst, &st) == -1) {
+        dst_exists = false;
+
+        // If we're only pulling one path, the destination path might point to
+        // a path that doesn't exist yet.
+        if (srcs.size() == 1 && errno == ENOENT) {
+            // However, its parent must exist.
+            struct stat parent_st;
+            if (stat(android::base::Dirname(dst).c_str(), &parent_st) == -1) {
+                sc.Error("cannot create file/directory '%s': %s", dst, strerror(errno));
+                return false;
+            }
+        } else {
+            sc.Error("failed to access '%s': %s", dst, strerror(errno));
+            return false;
+        }
+    }
+
+    bool dst_isdir = dst_exists && S_ISDIR(st.st_mode);
+    if (!dst_isdir) {
+        if (srcs.size() > 1) {
+            sc.Error("target '%s' is not a directory", dst);
+            return false;
+        } else {
+            size_t dst_len = strlen(dst);
+
+            // A path that ends with a slash doesn't have to be a directory if
+            // it doesn't exist yet.
+            if (adb_is_separator(dst[dst_len - 1]) && dst_exists) {
+                sc.Error("failed to access '%s': Not a directory", dst);
+                return false;
+            }
+        }
+    }
+
+    for (const char* src_path : srcs) {
+        const char* dst_path = dst;
+        struct stat src_st;
+        if (!sync_stat_fallback(sc, src_path, &src_st)) {
+            if (errno == ENOPROTOOPT) {
+                sc.Error("remote object '%s' does not exist", src_path);
+            } else {
+                sc.Error("failed to stat remote object '%s': %s", src_path, strerror(errno));
+            }
+
+            success = false;
+            continue;
+        }
+
+        bool src_isdir = S_ISDIR(src_st.st_mode);
+        if (src_isdir) {
+            std::string dst_dir = dst;
+
+            // If the destination path existed originally, the source directory
+            // should be copied as a child of the destination.
+            if (dst_exists) {
+                if (!dst_isdir) {
+                    sc.Error("target '%s' is not a directory", dst);
+                    return false;
+                }
+                if (!adb_is_separator(dst_dir.back())) {
+                    dst_dir.push_back(OS_PATH_SEPARATOR);
+                }
+                dst_dir.append(android::base::Basename(src_path));
+            }
+
+            success &= copy_remote_dir_local(sc, src_path, dst_dir.c_str(), copy_attrs);
+            continue;
+        } else if (!should_pull_file(src_st.st_mode)) {
+            sc.Warning("skipping special file '%s' (mode = 0o%o)", src_path, src_st.st_mode);
+            continue;
+        }
+
+        std::string path_holder;
+        if (dst_isdir) {
+            // If we're copying a remote file to a local directory, we
+            // really want to copy to local_dir + OS_PATH_SEPARATOR +
+            // basename(remote).
+            path_holder = android::base::StringPrintf("%s%c%s", dst_path, OS_PATH_SEPARATOR,
+                                                      android::base::Basename(src_path).c_str());
+            dst_path = path_holder.c_str();
+        }
+
+        sc.NewTransfer();
+        sc.SetExpectedTotalBytes(src_st.st_size);
+        if (!sync_recv(sc, src_path, dst_path, name, src_st.st_size)) {
+            success = false;
+            continue;
+        }
+
+        if (copy_attrs && set_time_and_mode(dst_path, src_st.st_mtime, src_st.st_mode) != 0) {
+            success = false;
+            continue;
+        }
+        sc.ReportTransferRate(src_path, TransferDirection::pull);
+    }
+
+    sc.ReportOverallTransferRate(TransferDirection::pull);
+    return success;
+}
+
+bool do_sync_sync(const std::string& lpath, const std::string& rpath, bool list_only) {
+    SyncConnection sc;
+    if (!sc.IsValid()) return false;
+
+    bool success = copy_local_dir_remote(sc, lpath, rpath, true, list_only);
+    if (!list_only) {
+        sc.ReportOverallTransferRate(TransferDirection::push);
+    }
+    return success;
+}
diff --git a/adb/client/file_sync_client.h b/adb/client/file_sync_client.h
new file mode 100644
index 0000000..df7f14c
--- /dev/null
+++ b/adb/client/file_sync_client.h
@@ -0,0 +1,27 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <string>
+#include <vector>
+
+bool do_sync_ls(const char* path);
+bool do_sync_push(const std::vector<const char*>& srcs, const char* dst, bool sync);
+bool do_sync_pull(const std::vector<const char*>& srcs, const char* dst, bool copy_attrs,
+                  const char* name = nullptr);
+
+bool do_sync_sync(const std::string& lpath, const std::string& rpath, bool list_only);
diff --git a/adb/client/line_printer.cpp b/adb/client/line_printer.cpp
new file mode 100644
index 0000000..9758526
--- /dev/null
+++ b/adb/client/line_printer.cpp
@@ -0,0 +1,128 @@
+// Copyright 2013 Google Inc. All Rights Reserved.
+//
+// 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 "line_printer.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#ifdef _WIN32
+#include <windows.h>
+#else
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <termios.h>
+#include <sys/time.h>
+#endif
+
+// Make sure printf is really adb_printf which works for UTF-8 on Windows.
+#include <sysdeps.h>
+
+// Stuff from ninja's util.h that's needed below.
+#include <vector>
+using namespace std;
+string ElideMiddle(const string& str, size_t width) {
+  const int kMargin = 3;  // Space for "...".
+  string result = str;
+  if (result.size() + kMargin > width) {
+    size_t elide_size = (width - kMargin) / 2;
+    result = result.substr(0, elide_size)
+      + "..."
+      + result.substr(result.size() - elide_size, elide_size);
+  }
+  return result;
+}
+
+LinePrinter::LinePrinter() : have_blank_line_(true) {
+#ifndef _WIN32
+  const char* term = getenv("TERM");
+  smart_terminal_ = unix_isatty(1) && term && string(term) != "dumb";
+#else
+  // Disable output buffer.  It'd be nice to use line buffering but
+  // MSDN says: "For some systems, [_IOLBF] provides line
+  // buffering. However, for Win32, the behavior is the same as _IOFBF
+  // - Full Buffering."
+  setvbuf(stdout, nullptr, _IONBF, 0);
+  console_ = GetStdHandle(STD_OUTPUT_HANDLE);
+  CONSOLE_SCREEN_BUFFER_INFO csbi;
+  smart_terminal_ = GetConsoleScreenBufferInfo(console_, &csbi);
+#endif
+}
+
+static void Out(const std::string& s) {
+  // Avoid printf and C strings, since the actual output might contain null
+  // bytes like UTF-16 does (yuck).
+  fwrite(s.data(), 1, s.size(), stdout);
+}
+
+void LinePrinter::Print(string to_print, LineType type) {
+  if (!smart_terminal_) {
+    Out(to_print + "\n");
+    return;
+  }
+
+  // Print over previous line, if any.
+  // On Windows, calling a C library function writing to stdout also handles
+  // pausing the executable when the "Pause" key or Ctrl-S is pressed.
+  printf("\r");
+
+  if (type == INFO) {
+#ifdef _WIN32
+    CONSOLE_SCREEN_BUFFER_INFO csbi;
+    GetConsoleScreenBufferInfo(console_, &csbi);
+
+    // TODO: std::wstring to_print_wide; if (!android::base::UTF8ToWide(to_print, &to_print_wide)...
+    // TODO: wstring ElideMiddle.
+    to_print = ElideMiddle(to_print, static_cast<size_t>(csbi.dwSize.X));
+    // We don't want to have the cursor spamming back and forth, so instead of
+    // printf use WriteConsoleOutput which updates the contents of the buffer,
+    // but doesn't move the cursor position.
+    COORD buf_size = { csbi.dwSize.X, 1 };
+    COORD zero_zero = { 0, 0 };
+    SMALL_RECT target = {
+      csbi.dwCursorPosition.X, csbi.dwCursorPosition.Y,
+      static_cast<SHORT>(csbi.dwCursorPosition.X + csbi.dwSize.X - 1),
+      csbi.dwCursorPosition.Y
+    };
+    vector<CHAR_INFO> char_data(csbi.dwSize.X);
+    for (size_t i = 0; i < static_cast<size_t>(csbi.dwSize.X); ++i) {
+      // TODO: UnicodeChar instead of AsciiChar, to_print_wide[i].
+      char_data[i].Char.AsciiChar = i < to_print.size() ? to_print[i] : ' ';
+      char_data[i].Attributes = csbi.wAttributes;
+    }
+    // TODO: WriteConsoleOutputW.
+    WriteConsoleOutput(console_, &char_data[0], buf_size, zero_zero, &target);
+#else
+    // Limit output to width of the terminal if provided so we don't cause
+    // line-wrapping.
+    winsize size;
+    if ((ioctl(0, TIOCGWINSZ, &size) == 0) && size.ws_col) {
+      to_print = ElideMiddle(to_print, size.ws_col);
+    }
+    Out(to_print);
+    printf("\x1B[K");  // Clear to end of line.
+    fflush(stdout);
+#endif
+
+    have_blank_line_ = false;
+  } else {
+    Out(to_print);
+    Out("\n");
+    have_blank_line_ = true;
+  }
+}
+
+void LinePrinter::KeepInfoLine() {
+  if (!have_blank_line_) Out("\n");
+  have_blank_line_ = true;
+}
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..de6c723 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
@@ -84,10 +89,10 @@
     // unbuffer stdout and stderr just like if we were run at the console.
     // This also keeps stderr unbuffered when it is redirected to adb.log.
     if (is_daemon) {
-        if (setvbuf(stdout, NULL, _IONBF, 0) == -1) {
+        if (setvbuf(stdout, nullptr, _IONBF, 0) == -1) {
             fatal("cannot make stdout unbuffered: %s", strerror(errno));
         }
-        if (setvbuf(stderr, NULL, _IONBF, 0) == -1) {
+        if (setvbuf(stderr, nullptr, _IONBF, 0) == -1) {
             fatal("cannot make stderr unbuffered: %s", strerror(errno));
         }
     }
@@ -95,18 +100,24 @@
     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_reconnect_handler();
     init_mdns_transport_discovery();
 
     usb_init();
diff --git a/adb/client/transport_mdns.cpp b/adb/client/transport_mdns.cpp
new file mode 100644
index 0000000..283fac5
--- /dev/null
+++ b/adb/client/transport_mdns.cpp
@@ -0,0 +1,282 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#define TRACE_TAG TRANSPORT
+
+#include "transport.h"
+
+#ifdef _WIN32
+#include <winsock2.h>
+#else
+#include <arpa/inet.h>
+#endif
+
+#include <thread>
+
+#include <android-base/stringprintf.h>
+#include <dns_sd.h>
+
+#include "adb_mdns.h"
+#include "adb_trace.h"
+#include "fdevent.h"
+#include "sysdeps.h"
+
+static DNSServiceRef service_ref;
+static fdevent* service_ref_fde;
+
+// Use adb_DNSServiceRefSockFD() instead of calling DNSServiceRefSockFD()
+// directly so that the socket is put through the appropriate compatibility
+// layers to work with the rest of ADB's internal APIs.
+static inline int adb_DNSServiceRefSockFD(DNSServiceRef ref) {
+    return adb_register_socket(DNSServiceRefSockFD(ref));
+}
+#define DNSServiceRefSockFD ___xxx_DNSServiceRefSockFD
+
+static void DNSSD_API register_service_ip(DNSServiceRef sdRef,
+                                          DNSServiceFlags flags,
+                                          uint32_t interfaceIndex,
+                                          DNSServiceErrorType errorCode,
+                                          const char* hostname,
+                                          const sockaddr* address,
+                                          uint32_t ttl,
+                                          void* context);
+
+static void pump_service_ref(int /*fd*/, unsigned ev, void* data) {
+    DNSServiceRef* ref = reinterpret_cast<DNSServiceRef*>(data);
+
+    if (ev & FDE_READ)
+        DNSServiceProcessResult(*ref);
+}
+
+class AsyncServiceRef {
+  public:
+    bool Initialized() {
+        return initialized_;
+    }
+
+    virtual ~AsyncServiceRef() {
+        if (!initialized_) {
+            return;
+        }
+
+        DNSServiceRefDeallocate(sdRef_);
+        fdevent_destroy(fde_);
+    }
+
+  protected:
+    DNSServiceRef sdRef_;
+
+    void Initialize() {
+        fde_ = fdevent_create(adb_DNSServiceRefSockFD(sdRef_), pump_service_ref, &sdRef_);
+        fdevent_set(fde_, FDE_READ);
+        initialized_ = true;
+    }
+
+  private:
+    bool initialized_ = false;
+    fdevent* fde_;
+};
+
+class ResolvedService : public AsyncServiceRef {
+  public:
+    virtual ~ResolvedService() = default;
+
+    ResolvedService(std::string name, uint32_t interfaceIndex,
+                    const char* hosttarget, uint16_t port) :
+            name_(name),
+            port_(port) {
+
+        /* TODO: We should be able to get IPv6 support by adding
+         * kDNSServiceProtocol_IPv6 to the flags below. However, when we do
+         * this, we get served link-local addresses that are usually useless to
+         * connect to. What's more, we seem to /only/ get those and nothing else.
+         * If we want IPv6 in the future we'll have to figure out why.
+         */
+        DNSServiceErrorType ret =
+            DNSServiceGetAddrInfo(
+                &sdRef_, 0, interfaceIndex,
+                kDNSServiceProtocol_IPv4, hosttarget,
+                register_service_ip, reinterpret_cast<void*>(this));
+
+        if (ret != kDNSServiceErr_NoError) {
+            D("Got %d from DNSServiceGetAddrInfo.", ret);
+        } else {
+            Initialize();
+        }
+    }
+
+    void Connect(const sockaddr* address) {
+        char ip_addr[INET6_ADDRSTRLEN];
+        const void* ip_addr_data;
+        const char* addr_format;
+
+        if (address->sa_family == AF_INET) {
+            ip_addr_data =
+                &reinterpret_cast<const sockaddr_in*>(address)->sin_addr;
+            addr_format = "%s:%hu";
+        } else if (address->sa_family == AF_INET6) {
+            ip_addr_data =
+                &reinterpret_cast<const sockaddr_in6*>(address)->sin6_addr;
+            addr_format = "[%s]:%hu";
+        } else { // Should be impossible
+            D("mDNS resolved non-IP address.");
+            return;
+        }
+
+        // Winsock version requires the const cast Because Microsoft.
+        if (!inet_ntop(address->sa_family, const_cast<void*>(ip_addr_data),
+                       ip_addr, INET6_ADDRSTRLEN)) {
+            D("Could not convert IP address to string.");
+            return;
+        }
+
+        std::string response;
+        connect_device(android::base::StringPrintf(addr_format, ip_addr, port_),
+                       &response);
+        D("Connect to %s (%s:%hu) : %s", name_.c_str(), ip_addr, port_,
+          response.c_str());
+    }
+
+  private:
+    std::string name_;
+    const uint16_t port_;
+};
+
+static void DNSSD_API register_service_ip(DNSServiceRef /*sdRef*/,
+                                          DNSServiceFlags /*flags*/,
+                                          uint32_t /*interfaceIndex*/,
+                                          DNSServiceErrorType /*errorCode*/,
+                                          const char* /*hostname*/,
+                                          const sockaddr* address,
+                                          uint32_t /*ttl*/,
+                                          void* context) {
+    D("Got IP for service.");
+    std::unique_ptr<ResolvedService> data(
+        reinterpret_cast<ResolvedService*>(context));
+    data->Connect(address);
+}
+
+static void DNSSD_API register_resolved_mdns_service(DNSServiceRef sdRef,
+                                                     DNSServiceFlags flags,
+                                                     uint32_t interfaceIndex,
+                                                     DNSServiceErrorType errorCode,
+                                                     const char* fullname,
+                                                     const char* hosttarget,
+                                                     uint16_t port,
+                                                     uint16_t txtLen,
+                                                     const unsigned char* txtRecord,
+                                                     void* context);
+
+class DiscoveredService : public AsyncServiceRef {
+  public:
+    DiscoveredService(uint32_t interfaceIndex, const char* serviceName,
+                      const char* regtype, const char* domain)
+        : serviceName_(serviceName) {
+
+        DNSServiceErrorType ret =
+            DNSServiceResolve(&sdRef_, 0, interfaceIndex, serviceName, regtype,
+                              domain, register_resolved_mdns_service,
+                              reinterpret_cast<void*>(this));
+
+        if (ret != kDNSServiceErr_NoError) {
+            D("Got %d from DNSServiceResolve.", ret);
+        } else {
+            Initialize();
+        }
+    }
+
+    const char* ServiceName() {
+        return serviceName_.c_str();
+    }
+
+  private:
+    std::string serviceName_;
+};
+
+static void DNSSD_API register_resolved_mdns_service(DNSServiceRef sdRef,
+                                                     DNSServiceFlags flags,
+                                                     uint32_t interfaceIndex,
+                                                     DNSServiceErrorType errorCode,
+                                                     const char* fullname,
+                                                     const char* hosttarget,
+                                                     uint16_t port,
+                                                     uint16_t /*txtLen*/,
+                                                     const unsigned char* /*txtRecord*/,
+                                                     void* context) {
+    D("Resolved a service.");
+    std::unique_ptr<DiscoveredService> discovered(
+        reinterpret_cast<DiscoveredService*>(context));
+
+    if (errorCode != kDNSServiceErr_NoError) {
+        D("Got error %d resolving service.", errorCode);
+        return;
+    }
+
+
+    auto resolved =
+        new ResolvedService(discovered->ServiceName(),
+                            interfaceIndex, hosttarget, ntohs(port));
+
+    if (! resolved->Initialized()) {
+        delete resolved;
+    }
+
+    if (flags) { /* Only ever equals MoreComing or 0 */
+        discovered.release();
+    }
+}
+
+static void DNSSD_API register_mdns_transport(DNSServiceRef sdRef,
+                                              DNSServiceFlags flags,
+                                              uint32_t interfaceIndex,
+                                              DNSServiceErrorType errorCode,
+                                              const char* serviceName,
+                                              const char* regtype,
+                                              const char* domain,
+                                              void*  /*context*/) {
+    D("Registering a transport.");
+    if (errorCode != kDNSServiceErr_NoError) {
+        D("Got error %d during mDNS browse.", errorCode);
+        DNSServiceRefDeallocate(sdRef);
+        fdevent_destroy(service_ref_fde);
+        return;
+    }
+
+    auto discovered = new DiscoveredService(interfaceIndex, serviceName, regtype, domain);
+    if (!discovered->Initialized()) {
+        delete discovered;
+    }
+}
+
+void init_mdns_transport_discovery_thread(void) {
+    DNSServiceErrorType errorCode = DNSServiceBrowse(&service_ref, 0, 0, kADBServiceType, nullptr,
+                                                     register_mdns_transport, nullptr);
+
+    if (errorCode != kDNSServiceErr_NoError) {
+        D("Got %d initiating mDNS browse.", errorCode);
+        return;
+    }
+
+    fdevent_run_on_main_thread([]() {
+        service_ref_fde =
+            fdevent_create(adb_DNSServiceRefSockFD(service_ref), pump_service_ref, &service_ref);
+        fdevent_set(service_ref_fde, FDE_READ);
+    });
+}
+
+void init_mdns_transport_discovery(void) {
+    std::thread(init_mdns_transport_discovery_thread).detach();
+}
diff --git a/adb/client/usb_libusb.cpp b/adb/client/usb_libusb.cpp
index e7f44c6..10b6090 100644
--- a/adb/client/usb_libusb.cpp
+++ b/adb/client/usb_libusb.cpp
@@ -19,9 +19,11 @@
 #include "sysdeps.h"
 
 #include <stdint.h>
+#include <stdlib.h>
 
 #include <atomic>
 #include <chrono>
+#include <condition_variable>
 #include <memory>
 #include <mutex>
 #include <string>
@@ -32,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>
 
@@ -41,8 +42,6 @@
 #include "transport.h"
 #include "usb.h"
 
-using namespace std::literals;
-
 using android::base::StringPrintf;
 
 // RAII wrappers for libusb.
@@ -222,7 +221,7 @@
 
     // Use size_t for interface_num so <iostream>s don't mangle it.
     size_t interface_num;
-    uint16_t zero_mask;
+    uint16_t zero_mask = 0;
     uint8_t bulk_in = 0, bulk_out = 0;
     size_t packet_size = 0;
     bool found_adb = false;
@@ -333,6 +332,13 @@
             return;
         }
 
+        rc = libusb_set_interface_alt_setting(handle.get(), interface_num, 0);
+        if (rc != 0) {
+            LOG(WARNING) << "failed to set interface alt setting for device '" << device_serial
+                         << "'" << libusb_error_name(rc);
+            return;
+        }
+
         for (uint8_t endpoint : {bulk_in, bulk_out}) {
             rc = libusb_clear_halt(handle.get(), endpoint);
             if (rc != 0) {
@@ -365,9 +371,9 @@
 #endif
     }
 
-    auto result =
-        std::make_unique<usb_handle>(device_address, device_serial, std::move(handle),
-                                     interface_num, bulk_in, bulk_out, zero_mask, packet_size);
+    std::unique_ptr<usb_handle> result(new usb_handle(device_address, device_serial,
+                                                      std::move(handle), interface_num, bulk_in,
+                                                      bulk_out, zero_mask, packet_size));
     usb_handle* usb_handle_raw = result.get();
 
     {
@@ -390,7 +396,7 @@
     // hack around this by inserting a sleep.
     auto thread = std::thread([device]() {
         std::string device_path = get_device_dev_path(device);
-        std::this_thread::sleep_for(1s);
+        std::this_thread::sleep_for(std::chrono::seconds(1));
 
         process_device(device);
         if (--connecting_devices == 0) {
@@ -412,8 +418,13 @@
     if (it != usb_handles.end()) {
         if (!it->second->device_handle) {
             // If the handle is null, we were never able to open the device.
-            unregister_usb_transport(it->second.get());
+
+            // Temporarily release the usb handles mutex to avoid deadlock.
+            std::unique_ptr<usb_handle> handle = std::move(it->second);
             usb_handles.erase(it);
+            lock.unlock();
+            unregister_usb_transport(handle.get());
+            lock.lock();
         } else {
             // Closure of the transport will erase the usb_handle.
         }
@@ -436,8 +447,8 @@
     }
 }
 
-static int hotplug_callback(libusb_context*, libusb_device* device, libusb_hotplug_event event,
-                            void*) {
+static LIBUSB_CALL int hotplug_callback(libusb_context*, libusb_device* device,
+                                        libusb_hotplug_event event, void*) {
     // We're called with the libusb lock taken. Call these on a separate thread outside of this
     // function so that the usb_handle mutex is always taken before the libusb mutex.
     static std::once_flag once;
@@ -481,59 +492,60 @@
     libusb_hotplug_deregister_callback(nullptr, hotplug_handle);
 }
 
+static LIBUSB_CALL void transfer_callback(libusb_transfer* transfer) {
+    transfer_info* info = static_cast<transfer_info*>(transfer->user_data);
+
+    LOG(DEBUG) << info->name << " transfer callback entered";
+
+    // Make sure that the original submitter has made it to the condition_variable wait.
+    std::unique_lock<std::mutex> lock(info->mutex);
+
+    LOG(DEBUG) << info->name << " callback successfully acquired lock";
+
+    if (transfer->status != LIBUSB_TRANSFER_COMPLETED) {
+        LOG(WARNING) << info->name << " transfer failed: " << libusb_error_name(transfer->status);
+        info->Notify();
+        return;
+    }
+
+    // usb_read() can return when receiving some data.
+    if (info->is_bulk_out && transfer->actual_length != transfer->length) {
+        LOG(DEBUG) << info->name << " transfer incomplete, resubmitting";
+        transfer->length -= transfer->actual_length;
+        transfer->buffer += transfer->actual_length;
+        int rc = libusb_submit_transfer(transfer);
+        if (rc != 0) {
+            LOG(WARNING) << "failed to submit " << info->name
+                         << " transfer: " << libusb_error_name(rc);
+            transfer->status = LIBUSB_TRANSFER_ERROR;
+            info->Notify();
+        }
+        return;
+    }
+
+    if (should_perform_zero_transfer(transfer->endpoint, transfer->length, info->zero_mask)) {
+        LOG(DEBUG) << "submitting zero-length write";
+        transfer->length = 0;
+        int rc = libusb_submit_transfer(transfer);
+        if (rc != 0) {
+            LOG(WARNING) << "failed to submit zero-length write: " << libusb_error_name(rc);
+            transfer->status = LIBUSB_TRANSFER_ERROR;
+            info->Notify();
+        }
+        return;
+    }
+
+    LOG(VERBOSE) << info->name << "transfer fully complete";
+    info->Notify();
+}
+
 // Dispatch a libusb transfer, unlock |device_lock|, and then wait for the result.
 static int perform_usb_transfer(usb_handle* h, transfer_info* info,
                                 std::unique_lock<std::mutex> device_lock) {
     libusb_transfer* transfer = info->transfer;
 
     transfer->user_data = info;
-    transfer->callback = [](libusb_transfer* transfer) {
-        transfer_info* info = static_cast<transfer_info*>(transfer->user_data);
-
-        LOG(DEBUG) << info->name << " transfer callback entered";
-
-        // Make sure that the original submitter has made it to the condition_variable wait.
-        std::unique_lock<std::mutex> lock(info->mutex);
-
-        LOG(DEBUG) << info->name << " callback successfully acquired lock";
-
-        if (transfer->status != LIBUSB_TRANSFER_COMPLETED) {
-            LOG(WARNING) << info->name
-                         << " transfer failed: " << libusb_error_name(transfer->status);
-            info->Notify();
-            return;
-        }
-
-        // usb_read() can return when receiving some data.
-        if (info->is_bulk_out && transfer->actual_length != transfer->length) {
-            LOG(DEBUG) << info->name << " transfer incomplete, resubmitting";
-            transfer->length -= transfer->actual_length;
-            transfer->buffer += transfer->actual_length;
-            int rc = libusb_submit_transfer(transfer);
-            if (rc != 0) {
-                LOG(WARNING) << "failed to submit " << info->name
-                             << " transfer: " << libusb_error_name(rc);
-                transfer->status = LIBUSB_TRANSFER_ERROR;
-                info->Notify();
-            }
-            return;
-        }
-
-        if (should_perform_zero_transfer(transfer->endpoint, transfer->length, info->zero_mask)) {
-            LOG(DEBUG) << "submitting zero-length write";
-            transfer->length = 0;
-            int rc = libusb_submit_transfer(transfer);
-            if (rc != 0) {
-                LOG(WARNING) << "failed to submit zero-length write: " << libusb_error_name(rc);
-                transfer->status = LIBUSB_TRANSFER_ERROR;
-                info->Notify();
-            }
-            return;
-        }
-
-        LOG(VERBOSE) << info->name << "transfer fully complete";
-        info->Notify();
-    };
+    transfer->callback = transfer_callback;
 
     LOG(DEBUG) << "locking " << info->name << " transfer_info mutex";
     std::unique_lock<std::mutex> lock(info->mutex);
@@ -577,7 +589,7 @@
 
     int rc = perform_usb_transfer(h, info, std::move(lock));
     LOG(DEBUG) << "usb_write(" << len << ") = " << rc;
-    return rc;
+    return info->transfer->actual_length;
 }
 
 int usb_read(usb_handle* h, void* d, int len) {
diff --git a/adb/client/usb_linux.cpp b/adb/client/usb_linux.cpp
index a7df0ed..869e858 100644
--- a/adb/client/usb_linux.cpp
+++ b/adb/client/usb_linux.cpp
@@ -128,7 +128,7 @@
     if (!bus_dir) return;
 
     dirent* de;
-    while ((de = readdir(bus_dir.get())) != 0) {
+    while ((de = readdir(bus_dir.get())) != nullptr) {
         if (contains_non_digit(de->d_name)) continue;
 
         std::string bus_name = base + "/" + de->d_name;
@@ -253,7 +253,7 @@
                             continue;
                         }
                             /* aproto 01 needs 0 termination */
-                        if (interface->bInterfaceProtocol == 0x01) {
+                        if (interface->bInterfaceProtocol == ADB_PROTOCOL) {
                             max_packet_size = ep1->wMaxPacketSize;
                             zero_mask = ep1->wMaxPacketSize - 1;
                         }
@@ -418,11 +418,11 @@
     if (h->zero_mask && !(len & h->zero_mask)) {
         // If we need 0-markers and our transfer is an even multiple of the packet size,
         // then send a zero marker.
-        return usb_bulk_write(h, _data, 0);
+        return usb_bulk_write(h, _data, 0) == 0 ? n : -1;
     }
 
     D("-- usb_write --");
-    return 0;
+    return n;
 }
 
 int usb_read(usb_handle *h, void *_data, int len)
diff --git a/adb/client/usb_osx.cpp b/adb/client/usb_osx.cpp
index 4e1480f..49baf36 100644
--- a/adb/client/usb_osx.cpp
+++ b/adb/client/usb_osx.cpp
@@ -50,7 +50,7 @@
 {
     UInt8 bulkIn;
     UInt8 bulkOut;
-    IOUSBInterfaceInterface190** interface;
+    IOUSBInterfaceInterface550** interface;
     unsigned int zero_mask;
     size_t max_packet_size;
 
@@ -106,8 +106,8 @@
 }
 
 static void AndroidInterfaceAdded(io_iterator_t iterator);
-static std::unique_ptr<usb_handle> CheckInterface(IOUSBInterfaceInterface190 **iface,
-                                                  UInt16 vendor, UInt16 product);
+static std::unique_ptr<usb_handle> CheckInterface(IOUSBInterfaceInterface550** iface, UInt16 vendor,
+                                                  UInt16 product);
 
 static bool FindUSBDevices() {
     // Create the matching dictionary to find the Android device's adb interface.
@@ -174,7 +174,7 @@
         kr = (*iface)->GetInterfaceClass(iface, &if_class);
         kr = (*iface)->GetInterfaceSubClass(iface, &subclass);
         kr = (*iface)->GetInterfaceProtocol(iface, &protocol);
-        if(if_class != ADB_CLASS || subclass != ADB_SUBCLASS || protocol != ADB_PROTOCOL) {
+        if (!is_adb_interface(if_class, subclass, protocol)) {
             // Ignore non-ADB devices.
             LOG(DEBUG) << "Ignoring interface with incorrect class/subclass/protocol - " << if_class
                        << ", " << subclass << ", " << protocol;
@@ -295,8 +295,8 @@
             continue;
         }
 
-        std::unique_ptr<usb_handle> handle = CheckInterface((IOUSBInterfaceInterface190**)iface,
-                                                            vendor, product);
+        std::unique_ptr<usb_handle> handle =
+            CheckInterface((IOUSBInterfaceInterface550**)iface, vendor, product);
         if (handle == nullptr) {
             LOG(ERROR) << "Could not find device interface";
             (*iface)->Release(iface);
@@ -305,6 +305,7 @@
         handle->devpath = devpath;
         usb_handle* handle_p = handle.get();
         VLOG(USB) << "Add usb device " << serial;
+        LOG(INFO) << "reported max packet size for " << serial << " is " << handle->max_packet_size;
         AddDevice(std::move(handle));
         register_usb_transport(reinterpret_cast<::usb_handle*>(handle_p), serial, devpath.c_str(),
                                1);
@@ -314,7 +315,7 @@
 // Used to clear both the endpoints before starting.
 // When adb quits, we might clear the host endpoint but not the device.
 // So we make sure both sides are clear before starting up.
-static bool ClearPipeStallBothEnds(IOUSBInterfaceInterface190** interface, UInt8 bulkEp) {
+static bool ClearPipeStallBothEnds(IOUSBInterfaceInterface550** interface, UInt8 bulkEp) {
     IOReturn rc = (*interface)->ClearPipeStallBothEnds(interface, bulkEp);
     if (rc != kIOReturnSuccess) {
         LOG(ERROR) << "Could not clear pipe stall both ends: " << std::hex << rc;
@@ -325,9 +326,8 @@
 
 //* TODO: simplify this further since we only register to get ADB interface
 //* subclass+protocol events
-static std::unique_ptr<usb_handle>
-CheckInterface(IOUSBInterfaceInterface190 **interface, UInt16 vendor, UInt16 product)
-{
+static std::unique_ptr<usb_handle> CheckInterface(IOUSBInterfaceInterface550** interface,
+                                                  UInt16 vendor, UInt16 product) {
     std::unique_ptr<usb_handle> handle;
     IOReturn kr;
     UInt8 interfaceNumEndpoints, interfaceClass, interfaceSubClass, interfaceProtocol;
@@ -375,9 +375,14 @@
         UInt8   interval;
         UInt8   number;
         UInt8   direction;
+        UInt8 maxBurst;
+        UInt8 mult;
+        UInt16 bytesPerInterval;
 
-        kr = (*interface)->GetPipeProperties(interface, endpoint, &direction,
-                &number, &transferType, &maxPacketSize, &interval);
+        kr = (*interface)
+                 ->GetPipePropertiesV2(interface, endpoint, &direction, &number, &transferType,
+                                       &maxPacketSize, &interval, &maxBurst, &mult,
+                                       &bytesPerInterval);
         if (kr != kIOReturnSuccess) {
             LOG(ERROR) << "FindDeviceInterface - could not get pipe properties: "
                        << std::hex << kr;
@@ -396,6 +401,13 @@
             if (!ClearPipeStallBothEnds(interface, handle->bulkOut)) goto err_get_pipe_props;
         }
 
+        if (maxBurst != 0)
+            // bMaxBurst is the number of additional packets in the burst.
+            maxPacketSize /= (maxBurst + 1);
+
+        // mult is only relevant for isochronous endpoints.
+        CHECK_EQ(0, mult);
+
         handle->zero_mask = maxPacketSize - 1;
         handle->max_packet_size = maxPacketSize;
     }
@@ -485,8 +497,8 @@
         }
     }
 
-    if (0 == result)
-        return 0;
+    if (!result)
+        return len;
 
     LOG(ERROR) << "usb_write failed with status: " << std::hex << result;
     return -1;
diff --git a/adb/client/usb_windows.cpp b/adb/client/usb_windows.cpp
index 1620e6e..e928377 100644
--- a/adb/client/usb_windows.cpp
+++ b/adb/client/usb_windows.cpp
@@ -18,8 +18,10 @@
 
 #include "sysdeps.h"
 
+// clang-format off
 #include <winsock2.h>  // winsock.h *must* be included before windows.h.
 #include <windows.h>
+// clang-format on
 #include <usb100.h>
 #include <winerror.h>
 
@@ -27,6 +29,7 @@
 #include <stdio.h>
 #include <stdlib.h>
 
+#include <algorithm>
 #include <mutex>
 #include <thread>
 
@@ -38,6 +41,8 @@
 #include "sysdeps/chrono.h"
 #include "transport.h"
 
+namespace native {
+
 /** Structure usb_handle describes our connection to the usb device via
   AdbWinApi.dll. This structure is returned from usb_open() routine and
   is expected in each subsequent call that is accessing the device.
@@ -46,40 +51,31 @@
   rely on AdbWinApi.dll's handle validation and AdbCloseHandle(endpoint)'s
   ability to break a thread out of pipe IO.
 */
-struct usb_handle {
-  /// Previous entry in the list of opened usb handles
-  usb_handle *prev;
+struct usb_handle : public ::usb_handle {
+    /// Handle to USB interface
+    ADBAPIHANDLE adb_interface;
 
-  /// Next entry in the list of opened usb handles
-  usb_handle *next;
+    /// Handle to USB read pipe (endpoint)
+    ADBAPIHANDLE adb_read_pipe;
 
-  /// Handle to USB interface
-  ADBAPIHANDLE  adb_interface;
+    /// Handle to USB write pipe (endpoint)
+    ADBAPIHANDLE adb_write_pipe;
 
-  /// Handle to USB read pipe (endpoint)
-  ADBAPIHANDLE  adb_read_pipe;
+    /// Interface name
+    wchar_t* interface_name;
 
-  /// Handle to USB write pipe (endpoint)
-  ADBAPIHANDLE  adb_write_pipe;
+    /// Maximum packet size.
+    unsigned max_packet_size;
 
-  /// Interface name
-  wchar_t*      interface_name;
-
-  /// Maximum packet size.
-  unsigned max_packet_size;
-
-  /// Mask for determining when to use zero length packets
-  unsigned zero_mask;
+    /// Mask for determining when to use zero length packets
+    unsigned zero_mask;
 };
 
 /// Class ID assigned to the device by androidusb.sys
 static const GUID usb_class_id = ANDROID_USB_CLASS_ID;
 
 /// List of opened usb handles
-static usb_handle handle_list = {
-  .prev = &handle_list,
-  .next = &handle_list,
-};
+static std::vector<usb_handle*>& handle_list = *new std::vector<usb_handle*>();
 
 /// Locker for the list of opened usb handles
 static std::mutex& usb_lock = *new std::mutex();
@@ -118,7 +114,7 @@
 int usb_write(usb_handle* handle, const void* data, int len);
 
 /// Reads data using the opened usb handle
-int usb_read(usb_handle *handle, void* data, int len);
+int usb_read(usb_handle* handle, void* data, int len);
 
 /// Cleans up opened usb handle
 void usb_cleanup_handle(usb_handle* handle);
@@ -130,401 +126,360 @@
 int usb_close(usb_handle* handle);
 
 int known_device_locked(const wchar_t* dev_name) {
-  usb_handle* usb;
-
-  if (NULL != dev_name) {
-    // Iterate through the list looking for the name match.
-    for(usb = handle_list.next; usb != &handle_list; usb = usb->next) {
-      // In Windows names are not case sensetive!
-      if((NULL != usb->interface_name) &&
-         (0 == wcsicmp(usb->interface_name, dev_name))) {
-        return 1;
-      }
+    if (nullptr != dev_name) {
+        // Iterate through the list looking for the name match.
+        for (usb_handle* usb : handle_list) {
+            // In Windows names are not case sensetive!
+            if ((nullptr != usb->interface_name) && (0 == wcsicmp(usb->interface_name, dev_name))) {
+                return 1;
+            }
+        }
     }
-  }
 
-  return 0;
+    return 0;
 }
 
 int known_device(const wchar_t* dev_name) {
-  int ret = 0;
+    int ret = 0;
 
-  if (NULL != dev_name) {
-    std::lock_guard<std::mutex> lock(usb_lock);
-    ret = known_device_locked(dev_name);
-  }
+    if (nullptr != dev_name) {
+        std::lock_guard<std::mutex> lock(usb_lock);
+        ret = known_device_locked(dev_name);
+    }
 
-  return ret;
+    return ret;
 }
 
 int register_new_device(usb_handle* handle) {
-  if (NULL == handle)
-    return 0;
+    if (nullptr == handle) return 0;
 
-  std::lock_guard<std::mutex> lock(usb_lock);
+    std::lock_guard<std::mutex> lock(usb_lock);
 
-  // Check if device is already in the list
-  if (known_device_locked(handle->interface_name)) {
-    return 0;
-  }
+    // Check if device is already in the list
+    if (known_device_locked(handle->interface_name)) {
+        return 0;
+    }
 
-  // Not in the list. Add this handle to the list.
-  handle->next = &handle_list;
-  handle->prev = handle_list.prev;
-  handle->prev->next = handle;
-  handle->next->prev = handle;
+    // Not in the list. Add this handle to the list.
+    handle_list.push_back(handle);
 
-  return 1;
+    return 1;
 }
 
 void device_poll_thread() {
-  adb_thread_setname("Device Poll");
-  D("Created device thread");
+    adb_thread_setname("Device Poll");
+    D("Created device thread");
 
-  while (true) {
-    find_devices();
-    std::this_thread::sleep_for(1s);
-  }
+    while (true) {
+        find_devices();
+        std::this_thread::sleep_for(1s);
+    }
 }
 
-static LRESULT CALLBACK _power_window_proc(HWND hwnd, UINT uMsg, WPARAM wParam,
-                                           LPARAM lParam) {
-  switch (uMsg) {
-  case WM_POWERBROADCAST:
-    switch (wParam) {
-    case PBT_APMRESUMEAUTOMATIC:
-      // Resuming from sleep or hibernation, so kick all existing USB devices
-      // and then allow the device_poll_thread to redetect USB devices from
-      // scratch. If we don't do this, existing USB devices will never respond
-      // to us because they'll be waiting for the connect/auth handshake.
-      D("Received (WM_POWERBROADCAST, PBT_APMRESUMEAUTOMATIC) notification, "
-        "so kicking all USB devices\n");
-      kick_devices();
-      return TRUE;
+static LRESULT CALLBACK _power_window_proc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
+    switch (uMsg) {
+        case WM_POWERBROADCAST:
+            switch (wParam) {
+                case PBT_APMRESUMEAUTOMATIC:
+                    // Resuming from sleep or hibernation, so kick all existing USB devices
+                    // and then allow the device_poll_thread to redetect USB devices from
+                    // scratch. If we don't do this, existing USB devices will never respond
+                    // to us because they'll be waiting for the connect/auth handshake.
+                    D("Received (WM_POWERBROADCAST, PBT_APMRESUMEAUTOMATIC) notification, "
+                      "so kicking all USB devices\n");
+                    kick_devices();
+                    return TRUE;
+            }
     }
-  }
-  return DefWindowProcW(hwnd, uMsg, wParam, lParam);
+    return DefWindowProcW(hwnd, uMsg, wParam, lParam);
 }
 
 static void _power_notification_thread() {
-  // This uses a thread with its own window message pump to get power
-  // notifications. If adb runs from a non-interactive service account, this
-  // might not work (not sure). If that happens to not work, we could use
-  // heavyweight WMI APIs to get power notifications. But for the common case
-  // of a developer's interactive session, a window message pump is more
-  // appropriate.
-  D("Created power notification thread");
-  adb_thread_setname("Power Notifier");
+    // This uses a thread with its own window message pump to get power
+    // notifications. If adb runs from a non-interactive service account, this
+    // might not work (not sure). If that happens to not work, we could use
+    // heavyweight WMI APIs to get power notifications. But for the common case
+    // of a developer's interactive session, a window message pump is more
+    // appropriate.
+    D("Created power notification thread");
+    adb_thread_setname("Power Notifier");
 
-  // Window class names are process specific.
-  static const WCHAR kPowerNotificationWindowClassName[] =
-    L"PowerNotificationWindow";
+    // Window class names are process specific.
+    static const WCHAR kPowerNotificationWindowClassName[] = L"PowerNotificationWindow";
 
-  // Get the HINSTANCE corresponding to the module that _power_window_proc
-  // is in (the main module).
-  const HINSTANCE instance = GetModuleHandleW(NULL);
-  if (!instance) {
-    // This is such a common API call that this should never fail.
-    fatal("GetModuleHandleW failed: %s",
-          android::base::SystemErrorCodeToString(GetLastError()).c_str());
-  }
+    // Get the HINSTANCE corresponding to the module that _power_window_proc
+    // is in (the main module).
+    const HINSTANCE instance = GetModuleHandleW(nullptr);
+    if (!instance) {
+        // This is such a common API call that this should never fail.
+        fatal("GetModuleHandleW failed: %s",
+              android::base::SystemErrorCodeToString(GetLastError()).c_str());
+    }
 
-  WNDCLASSEXW wndclass;
-  memset(&wndclass, 0, sizeof(wndclass));
-  wndclass.cbSize = sizeof(wndclass);
-  wndclass.lpfnWndProc = _power_window_proc;
-  wndclass.hInstance = instance;
-  wndclass.lpszClassName = kPowerNotificationWindowClassName;
-  if (!RegisterClassExW(&wndclass)) {
-    fatal("RegisterClassExW failed: %s",
-          android::base::SystemErrorCodeToString(GetLastError()).c_str());
-  }
+    WNDCLASSEXW wndclass;
+    memset(&wndclass, 0, sizeof(wndclass));
+    wndclass.cbSize = sizeof(wndclass);
+    wndclass.lpfnWndProc = _power_window_proc;
+    wndclass.hInstance = instance;
+    wndclass.lpszClassName = kPowerNotificationWindowClassName;
+    if (!RegisterClassExW(&wndclass)) {
+        fatal("RegisterClassExW failed: %s",
+              android::base::SystemErrorCodeToString(GetLastError()).c_str());
+    }
 
-  if (!CreateWindowExW(WS_EX_NOACTIVATE, kPowerNotificationWindowClassName,
-                       L"ADB Power Notification Window", WS_POPUP, 0, 0, 0, 0,
-                       NULL, NULL, instance, NULL)) {
-    fatal("CreateWindowExW failed: %s",
-          android::base::SystemErrorCodeToString(GetLastError()).c_str());
-  }
+    if (!CreateWindowExW(WS_EX_NOACTIVATE, kPowerNotificationWindowClassName,
+                         L"ADB Power Notification Window", WS_POPUP, 0, 0, 0, 0, nullptr, nullptr,
+                         instance, nullptr)) {
+        fatal("CreateWindowExW failed: %s",
+              android::base::SystemErrorCodeToString(GetLastError()).c_str());
+    }
 
-  MSG msg;
-  while (GetMessageW(&msg, NULL, 0, 0)) {
-    TranslateMessage(&msg);
-    DispatchMessageW(&msg);
-  }
+    MSG msg;
+    while (GetMessageW(&msg, nullptr, 0, 0)) {
+        TranslateMessage(&msg);
+        DispatchMessageW(&msg);
+    }
 
-  // GetMessageW() will return false if a quit message is posted. We don't
-  // do that, but it might be possible for that to occur when logging off or
-  // shutting down. Not a big deal since the whole process will be going away
-  // soon anyway.
-  D("Power notification thread exiting");
+    // GetMessageW() will return false if a quit message is posted. We don't
+    // do that, but it might be possible for that to occur when logging off or
+    // shutting down. Not a big deal since the whole process will be going away
+    // soon anyway.
+    D("Power notification thread exiting");
 }
 
 void usb_init() {
-  std::thread(device_poll_thread).detach();
-  std::thread(_power_notification_thread).detach();
+    std::thread(device_poll_thread).detach();
+    std::thread(_power_notification_thread).detach();
 }
 
 void usb_cleanup() {}
 
 usb_handle* do_usb_open(const wchar_t* interface_name) {
-  unsigned long name_len = 0;
+    unsigned long name_len = 0;
 
-  // Allocate our handle
-  usb_handle* ret = (usb_handle*)calloc(1, sizeof(usb_handle));
-  if (NULL == ret) {
-    D("Could not allocate %u bytes for usb_handle: %s", sizeof(usb_handle),
-      strerror(errno));
-    goto fail;
-  }
+    // Allocate our handle
+    usb_handle* ret = (usb_handle*)calloc(1, sizeof(usb_handle));
+    if (nullptr == ret) {
+        D("Could not allocate %u bytes for usb_handle: %s", sizeof(usb_handle), strerror(errno));
+        goto fail;
+    }
 
-  // Set linkers back to the handle
-  ret->next = ret;
-  ret->prev = ret;
+    // Create interface.
+    ret->adb_interface = AdbCreateInterfaceByName(interface_name);
+    if (nullptr == ret->adb_interface) {
+        D("AdbCreateInterfaceByName failed: %s",
+          android::base::SystemErrorCodeToString(GetLastError()).c_str());
+        goto fail;
+    }
 
-  // Create interface.
-  ret->adb_interface = AdbCreateInterfaceByName(interface_name);
-  if (NULL == ret->adb_interface) {
-    D("AdbCreateInterfaceByName failed: %s",
-      android::base::SystemErrorCodeToString(GetLastError()).c_str());
-    goto fail;
-  }
+    // Open read pipe (endpoint)
+    ret->adb_read_pipe = AdbOpenDefaultBulkReadEndpoint(
+        ret->adb_interface, AdbOpenAccessTypeReadWrite, AdbOpenSharingModeReadWrite);
+    if (nullptr == ret->adb_read_pipe) {
+        D("AdbOpenDefaultBulkReadEndpoint failed: %s",
+          android::base::SystemErrorCodeToString(GetLastError()).c_str());
+        goto fail;
+    }
 
-  // Open read pipe (endpoint)
-  ret->adb_read_pipe =
-    AdbOpenDefaultBulkReadEndpoint(ret->adb_interface,
-                                   AdbOpenAccessTypeReadWrite,
-                                   AdbOpenSharingModeReadWrite);
-  if (NULL == ret->adb_read_pipe) {
-    D("AdbOpenDefaultBulkReadEndpoint failed: %s",
-      android::base::SystemErrorCodeToString(GetLastError()).c_str());
-    goto fail;
-  }
+    // Open write pipe (endpoint)
+    ret->adb_write_pipe = AdbOpenDefaultBulkWriteEndpoint(
+        ret->adb_interface, AdbOpenAccessTypeReadWrite, AdbOpenSharingModeReadWrite);
+    if (nullptr == ret->adb_write_pipe) {
+        D("AdbOpenDefaultBulkWriteEndpoint failed: %s",
+          android::base::SystemErrorCodeToString(GetLastError()).c_str());
+        goto fail;
+    }
 
-  // Open write pipe (endpoint)
-  ret->adb_write_pipe =
-    AdbOpenDefaultBulkWriteEndpoint(ret->adb_interface,
-                                    AdbOpenAccessTypeReadWrite,
-                                    AdbOpenSharingModeReadWrite);
-  if (NULL == ret->adb_write_pipe) {
-    D("AdbOpenDefaultBulkWriteEndpoint failed: %s",
-      android::base::SystemErrorCodeToString(GetLastError()).c_str());
-    goto fail;
-  }
+    // Save interface name
+    // First get expected name length
+    AdbGetInterfaceName(ret->adb_interface, nullptr, &name_len, false);
+    if (0 == name_len) {
+        D("AdbGetInterfaceName returned name length of zero: %s",
+          android::base::SystemErrorCodeToString(GetLastError()).c_str());
+        goto fail;
+    }
 
-  // Save interface name
-  // First get expected name length
-  AdbGetInterfaceName(ret->adb_interface,
-                      NULL,
-                      &name_len,
-                      false);
-  if (0 == name_len) {
-    D("AdbGetInterfaceName returned name length of zero: %s",
-      android::base::SystemErrorCodeToString(GetLastError()).c_str());
-    goto fail;
-  }
+    ret->interface_name = (wchar_t*)malloc(name_len * sizeof(ret->interface_name[0]));
+    if (nullptr == ret->interface_name) {
+        D("Could not allocate %lu characters for interface_name: %s", name_len, strerror(errno));
+        goto fail;
+    }
 
-  ret->interface_name = (wchar_t*)malloc(name_len * sizeof(ret->interface_name[0]));
-  if (NULL == ret->interface_name) {
-    D("Could not allocate %lu characters for interface_name: %s", name_len, strerror(errno));
-    goto fail;
-  }
+    // Now save the name
+    if (!AdbGetInterfaceName(ret->adb_interface, ret->interface_name, &name_len, false)) {
+        D("AdbGetInterfaceName failed: %s",
+          android::base::SystemErrorCodeToString(GetLastError()).c_str());
+        goto fail;
+    }
 
-  // Now save the name
-  if (!AdbGetInterfaceName(ret->adb_interface,
-                           ret->interface_name,
-                           &name_len,
-                           false)) {
-    D("AdbGetInterfaceName failed: %s",
-      android::base::SystemErrorCodeToString(GetLastError()).c_str());
-    goto fail;
-  }
-
-  // We're done at this point
-  return ret;
+    // We're done at this point
+    return ret;
 
 fail:
-  if (NULL != ret) {
-    usb_cleanup_handle(ret);
-    free(ret);
-  }
+    if (nullptr != ret) {
+        usb_cleanup_handle(ret);
+        free(ret);
+    }
 
-  return NULL;
+    return nullptr;
 }
 
 int usb_write(usb_handle* handle, const void* data, int len) {
-  unsigned long time_out = 5000;
-  unsigned long written = 0;
-  int err = 0;
+    unsigned long time_out = 5000;
+    unsigned long written = 0;
+    int err = 0;
 
-  D("usb_write %d", len);
-  if (NULL == handle) {
-    D("usb_write was passed NULL handle");
-    err = EINVAL;
-    goto fail;
-  }
-
-  // Perform write
-  if (!AdbWriteEndpointSync(handle->adb_write_pipe,
-                            (void*)data,
-                            (unsigned long)len,
-                            &written,
-                            time_out)) {
-    D("AdbWriteEndpointSync failed: %s",
-      android::base::SystemErrorCodeToString(GetLastError()).c_str());
-    err = EIO;
-    goto fail;
-  }
-
-  // Make sure that we've written what we were asked to write
-  D("usb_write got: %ld, expected: %d", written, len);
-  if (written != (unsigned long)len) {
-    // If this occurs, this code should be changed to repeatedly call
-    // AdbWriteEndpointSync() until all bytes are written.
-    D("AdbWriteEndpointSync was supposed to write %d, but only wrote %ld",
-      len, written);
-    err = EIO;
-    goto fail;
-  }
-
-  if (handle->zero_mask && (len & handle->zero_mask) == 0) {
-    // Send a zero length packet
-    if (!AdbWriteEndpointSync(handle->adb_write_pipe,
-                              (void*)data,
-                              0,
-                              &written,
-                              time_out)) {
-      D("AdbWriteEndpointSync of zero length packet failed: %s",
-        android::base::SystemErrorCodeToString(GetLastError()).c_str());
-      err = EIO;
-      goto fail;
+    D("usb_write %d", len);
+    if (nullptr == handle) {
+        D("usb_write was passed NULL handle");
+        err = EINVAL;
+        goto fail;
     }
-  }
 
-  return 0;
+    // Perform write
+    if (!AdbWriteEndpointSync(handle->adb_write_pipe, (void*)data, (unsigned long)len, &written,
+                              time_out)) {
+        D("AdbWriteEndpointSync failed: %s",
+          android::base::SystemErrorCodeToString(GetLastError()).c_str());
+        err = EIO;
+        goto fail;
+    }
+
+    // Make sure that we've written what we were asked to write
+    D("usb_write got: %ld, expected: %d", written, len);
+    if (written != (unsigned long)len) {
+        // If this occurs, this code should be changed to repeatedly call
+        // AdbWriteEndpointSync() until all bytes are written.
+        D("AdbWriteEndpointSync was supposed to write %d, but only wrote %ld", len, written);
+        err = EIO;
+        goto fail;
+    }
+
+    if (handle->zero_mask && (len & handle->zero_mask) == 0) {
+        // Send a zero length packet
+        if (!AdbWriteEndpointSync(handle->adb_write_pipe, (void*)data, 0, &written, time_out)) {
+            D("AdbWriteEndpointSync of zero length packet failed: %s",
+              android::base::SystemErrorCodeToString(GetLastError()).c_str());
+            err = EIO;
+            goto fail;
+        }
+    }
+
+    return written;
 
 fail:
-  // Any failure should cause us to kick the device instead of leaving it a
-  // zombie state with potential to hang.
-  if (NULL != handle) {
-    D("Kicking device due to error in usb_write");
-    usb_kick(handle);
-  }
+    // Any failure should cause us to kick the device instead of leaving it a
+    // zombie state with potential to hang.
+    if (nullptr != handle) {
+        D("Kicking device due to error in usb_write");
+        usb_kick(handle);
+    }
 
-  D("usb_write failed");
-  errno = err;
-  return -1;
+    D("usb_write failed");
+    errno = err;
+    return -1;
 }
 
-int usb_read(usb_handle *handle, void* data, int len) {
-  unsigned long time_out = 0;
-  unsigned long read = 0;
-  int err = 0;
-  int orig_len = len;
+int usb_read(usb_handle* handle, void* data, int len) {
+    unsigned long time_out = 0;
+    unsigned long read = 0;
+    int err = 0;
+    int orig_len = len;
 
-  D("usb_read %d", len);
-  if (NULL == handle) {
-    D("usb_read was passed NULL handle");
-    err = EINVAL;
-    goto fail;
-  }
-
-  while (len == orig_len) {
-    if (!AdbReadEndpointSync(handle->adb_read_pipe, data, len, &read, time_out)) {
-      D("AdbReadEndpointSync failed: %s",
-        android::base::SystemErrorCodeToString(GetLastError()).c_str());
-      err = EIO;
-      goto fail;
+    D("usb_read %d", len);
+    if (nullptr == handle) {
+        D("usb_read was passed NULL handle");
+        err = EINVAL;
+        goto fail;
     }
-    D("usb_read got: %ld, expected: %d", read, len);
 
-    data = (char*)data + read;
-    len -= read;
-  }
+    while (len == orig_len) {
+        if (!AdbReadEndpointSync(handle->adb_read_pipe, data, len, &read, time_out)) {
+            D("AdbReadEndpointSync failed: %s",
+              android::base::SystemErrorCodeToString(GetLastError()).c_str());
+            err = EIO;
+            goto fail;
+        }
+        D("usb_read got: %ld, expected: %d", read, len);
 
-  return orig_len - len;
+        data = (char*)data + read;
+        len -= read;
+    }
+
+    return orig_len - len;
 
 fail:
-  // Any failure should cause us to kick the device instead of leaving it a
-  // zombie state with potential to hang.
-  if (NULL != handle) {
-    D("Kicking device due to error in usb_read");
-    usb_kick(handle);
-  }
+    // Any failure should cause us to kick the device instead of leaving it a
+    // zombie state with potential to hang.
+    if (nullptr != handle) {
+        D("Kicking device due to error in usb_read");
+        usb_kick(handle);
+    }
 
-  D("usb_read failed");
-  errno = err;
-  return -1;
+    D("usb_read failed");
+    errno = err;
+    return -1;
 }
 
 // Wrapper around AdbCloseHandle() that logs diagnostics.
 static void _adb_close_handle(ADBAPIHANDLE adb_handle) {
-  if (!AdbCloseHandle(adb_handle)) {
-    D("AdbCloseHandle(%p) failed: %s", adb_handle,
-      android::base::SystemErrorCodeToString(GetLastError()).c_str());
-  }
+    if (!AdbCloseHandle(adb_handle)) {
+        D("AdbCloseHandle(%p) failed: %s", adb_handle,
+          android::base::SystemErrorCodeToString(GetLastError()).c_str());
+    }
 }
 
 void usb_cleanup_handle(usb_handle* handle) {
-  D("usb_cleanup_handle");
-  if (NULL != handle) {
-    if (NULL != handle->interface_name)
-      free(handle->interface_name);
-    // AdbCloseHandle(pipe) will break any threads out of pending IO calls and
-    // wait until the pipe no longer uses the interface. Then we can
-    // AdbCloseHandle() the interface.
-    if (NULL != handle->adb_write_pipe)
-      _adb_close_handle(handle->adb_write_pipe);
-    if (NULL != handle->adb_read_pipe)
-      _adb_close_handle(handle->adb_read_pipe);
-    if (NULL != handle->adb_interface)
-      _adb_close_handle(handle->adb_interface);
+    D("usb_cleanup_handle");
+    if (nullptr != handle) {
+        if (nullptr != handle->interface_name) free(handle->interface_name);
+        // AdbCloseHandle(pipe) will break any threads out of pending IO calls and
+        // wait until the pipe no longer uses the interface. Then we can
+        // AdbCloseHandle() the interface.
+        if (nullptr != handle->adb_write_pipe) _adb_close_handle(handle->adb_write_pipe);
+        if (nullptr != handle->adb_read_pipe) _adb_close_handle(handle->adb_read_pipe);
+        if (nullptr != handle->adb_interface) _adb_close_handle(handle->adb_interface);
 
-    handle->interface_name = NULL;
-    handle->adb_write_pipe = NULL;
-    handle->adb_read_pipe = NULL;
-    handle->adb_interface = NULL;
-  }
+        handle->interface_name = nullptr;
+        handle->adb_write_pipe = nullptr;
+        handle->adb_read_pipe = nullptr;
+        handle->adb_interface = nullptr;
+    }
 }
 
 static void usb_kick_locked(usb_handle* handle) {
-  // The reason the lock must be acquired before calling this function is in
-  // case multiple threads are trying to kick the same device at the same time.
-  usb_cleanup_handle(handle);
+    // The reason the lock must be acquired before calling this function is in
+    // case multiple threads are trying to kick the same device at the same time.
+    usb_cleanup_handle(handle);
 }
 
 void usb_kick(usb_handle* handle) {
-  D("usb_kick");
-  if (NULL != handle) {
-    std::lock_guard<std::mutex> lock(usb_lock);
-    usb_kick_locked(handle);
-  } else {
-    errno = EINVAL;
-  }
+    D("usb_kick");
+    if (nullptr != handle) {
+        std::lock_guard<std::mutex> lock(usb_lock);
+        usb_kick_locked(handle);
+    } else {
+        errno = EINVAL;
+    }
 }
 
 int usb_close(usb_handle* handle) {
-  D("usb_close");
+    D("usb_close");
 
-  if (NULL != handle) {
-    // Remove handle from the list
-    {
-      std::lock_guard<std::mutex> lock(usb_lock);
+    if (nullptr != handle) {
+        // Remove handle from the list
+        {
+            std::lock_guard<std::mutex> lock(usb_lock);
+            handle_list.erase(std::remove(handle_list.begin(), handle_list.end(), handle),
+                              handle_list.end());
+        }
 
-      if ((handle->next != handle) && (handle->prev != handle)) {
-        handle->next->prev = handle->prev;
-        handle->prev->next = handle->next;
-        handle->prev = handle;
-        handle->next = handle;
-      }
+        // Cleanup handle
+        usb_cleanup_handle(handle);
+        free(handle);
     }
 
-    // Cleanup handle
-    usb_cleanup_handle(handle);
-    free(handle);
-  }
-
-  return 0;
+    return 0;
 }
 
 size_t usb_get_max_packet_size(usb_handle* handle) {
@@ -532,131 +487,126 @@
 }
 
 int recognized_device(usb_handle* handle) {
-  if (NULL == handle)
-    return 0;
+    if (nullptr == handle) return 0;
 
-  // Check vendor and product id first
-  USB_DEVICE_DESCRIPTOR device_desc;
+    // Check vendor and product id first
+    USB_DEVICE_DESCRIPTOR device_desc;
 
-  if (!AdbGetUsbDeviceDescriptor(handle->adb_interface,
-                                 &device_desc)) {
-    D("AdbGetUsbDeviceDescriptor failed: %s",
-      android::base::SystemErrorCodeToString(GetLastError()).c_str());
-    return 0;
-  }
+    if (!AdbGetUsbDeviceDescriptor(handle->adb_interface, &device_desc)) {
+        D("AdbGetUsbDeviceDescriptor failed: %s",
+          android::base::SystemErrorCodeToString(GetLastError()).c_str());
+        return 0;
+    }
 
-  // Then check interface properties
-  USB_INTERFACE_DESCRIPTOR interf_desc;
+    // Then check interface properties
+    USB_INTERFACE_DESCRIPTOR interf_desc;
 
-  if (!AdbGetUsbInterfaceDescriptor(handle->adb_interface,
-                                    &interf_desc)) {
-    D("AdbGetUsbInterfaceDescriptor failed: %s",
-      android::base::SystemErrorCodeToString(GetLastError()).c_str());
-    return 0;
-  }
+    if (!AdbGetUsbInterfaceDescriptor(handle->adb_interface, &interf_desc)) {
+        D("AdbGetUsbInterfaceDescriptor failed: %s",
+          android::base::SystemErrorCodeToString(GetLastError()).c_str());
+        return 0;
+    }
 
-  // Must have two endpoints
-  if (2 != interf_desc.bNumEndpoints) {
-    return 0;
-  }
+    // Must have two endpoints
+    if (2 != interf_desc.bNumEndpoints) {
+        return 0;
+    }
 
-  if (is_adb_interface(interf_desc.bInterfaceClass, interf_desc.bInterfaceSubClass,
-                       interf_desc.bInterfaceProtocol)) {
-    if (interf_desc.bInterfaceProtocol == 0x01) {
-      AdbEndpointInformation endpoint_info;
-      // assuming zero is a valid bulk endpoint ID
-      if (AdbGetEndpointInformation(handle->adb_interface, 0, &endpoint_info)) {
+    if (!is_adb_interface(interf_desc.bInterfaceClass, interf_desc.bInterfaceSubClass,
+                          interf_desc.bInterfaceProtocol)) {
+        return 0;
+    }
+
+    AdbEndpointInformation endpoint_info;
+    // assuming zero is a valid bulk endpoint ID
+    if (AdbGetEndpointInformation(handle->adb_interface, 0, &endpoint_info)) {
         handle->max_packet_size = endpoint_info.max_packet_size;
         handle->zero_mask = endpoint_info.max_packet_size - 1;
         D("device zero_mask: 0x%x", handle->zero_mask);
-      } else {
+    } else {
         D("AdbGetEndpointInformation failed: %s",
           android::base::SystemErrorCodeToString(GetLastError()).c_str());
-      }
     }
 
     return 1;
-  }
-
-  return 0;
 }
 
 void find_devices() {
-  usb_handle* handle = NULL;
-  char entry_buffer[2048];
-  AdbInterfaceInfo* next_interface = (AdbInterfaceInfo*)(&entry_buffer[0]);
-  unsigned long entry_buffer_size = sizeof(entry_buffer);
+    usb_handle* handle = nullptr;
+    char entry_buffer[2048];
+    AdbInterfaceInfo* next_interface = (AdbInterfaceInfo*)(&entry_buffer[0]);
+    unsigned long entry_buffer_size = sizeof(entry_buffer);
 
-  // Enumerate all present and active interfaces.
-  ADBAPIHANDLE enum_handle =
-    AdbEnumInterfaces(usb_class_id, true, true, true);
+    // Enumerate all present and active interfaces.
+    ADBAPIHANDLE enum_handle = AdbEnumInterfaces(usb_class_id, true, true, true);
 
-  if (NULL == enum_handle) {
-    D("AdbEnumInterfaces failed: %s",
-      android::base::SystemErrorCodeToString(GetLastError()).c_str());
-    return;
-  }
-
-  while (AdbNextInterface(enum_handle, next_interface, &entry_buffer_size)) {
-    // Lets see if we already have this device in the list
-    if (!known_device(next_interface->device_name)) {
-      // This seems to be a new device. Open it!
-      handle = do_usb_open(next_interface->device_name);
-      if (NULL != handle) {
-        // Lets see if this interface (device) belongs to us
-        if (recognized_device(handle)) {
-          D("adding a new device %ls", next_interface->device_name);
-
-          // We don't request a wchar_t string from AdbGetSerialNumber() because of a bug in
-          // adb_winusb_interface.cpp:CopyMemory(buffer, ser_num->bString, bytes_written) where the
-          // last parameter should be (str_len * sizeof(wchar_t)). The bug reads 2 bytes past the
-          // end of a stack buffer in the best case, and in the unlikely case of a long serial
-          // number, it will read 2 bytes past the end of a heap allocation. This doesn't affect the
-          // resulting string, but we should avoid the bad reads in the first place.
-          char serial_number[512];
-          unsigned long serial_number_len = sizeof(serial_number);
-          if (AdbGetSerialNumber(handle->adb_interface,
-                                serial_number,
-                                &serial_number_len,
-                                true)) {
-            // Lets make sure that we don't duplicate this device
-            if (register_new_device(handle)) {
-              register_usb_transport(handle, serial_number, NULL, 1);
-            } else {
-              D("register_new_device failed for %ls", next_interface->device_name);
-              usb_cleanup_handle(handle);
-              free(handle);
-            }
-          } else {
-            D("cannot get serial number: %s",
-              android::base::SystemErrorCodeToString(GetLastError()).c_str());
-            usb_cleanup_handle(handle);
-            free(handle);
-          }
-        } else {
-          usb_cleanup_handle(handle);
-          free(handle);
-        }
-      }
+    if (nullptr == enum_handle) {
+        D("AdbEnumInterfaces failed: %s",
+          android::base::SystemErrorCodeToString(GetLastError()).c_str());
+        return;
     }
 
-    entry_buffer_size = sizeof(entry_buffer);
-  }
+    while (AdbNextInterface(enum_handle, next_interface, &entry_buffer_size)) {
+        // Lets see if we already have this device in the list
+        if (!known_device(next_interface->device_name)) {
+            // This seems to be a new device. Open it!
+            handle = do_usb_open(next_interface->device_name);
+            if (nullptr != handle) {
+                // Lets see if this interface (device) belongs to us
+                if (recognized_device(handle)) {
+                    D("adding a new device %ls", next_interface->device_name);
 
-  if (GetLastError() != ERROR_NO_MORE_ITEMS) {
-    // Only ERROR_NO_MORE_ITEMS is expected at the end of enumeration.
-    D("AdbNextInterface failed: %s",
-      android::base::SystemErrorCodeToString(GetLastError()).c_str());
-  }
+                    // We don't request a wchar_t string from AdbGetSerialNumber() because of a bug
+                    // in adb_winusb_interface.cpp:CopyMemory(buffer, ser_num->bString,
+                    // bytes_written) where the last parameter should be (str_len *
+                    // sizeof(wchar_t)). The bug reads 2 bytes past the end of a stack buffer in the
+                    // best case, and in the unlikely case of a long serial number, it will read 2
+                    // bytes past the end of a heap allocation. This doesn't affect the resulting
+                    // string, but we should avoid the bad reads in the first place.
+                    char serial_number[512];
+                    unsigned long serial_number_len = sizeof(serial_number);
+                    if (AdbGetSerialNumber(handle->adb_interface, serial_number, &serial_number_len,
+                                           true)) {
+                        // Lets make sure that we don't duplicate this device
+                        if (register_new_device(handle)) {
+                            register_usb_transport(handle, serial_number, nullptr, 1);
+                        } else {
+                            D("register_new_device failed for %ls", next_interface->device_name);
+                            usb_cleanup_handle(handle);
+                            free(handle);
+                        }
+                    } else {
+                        D("cannot get serial number: %s",
+                          android::base::SystemErrorCodeToString(GetLastError()).c_str());
+                        usb_cleanup_handle(handle);
+                        free(handle);
+                    }
+                } else {
+                    usb_cleanup_handle(handle);
+                    free(handle);
+                }
+            }
+        }
 
-  _adb_close_handle(enum_handle);
+        entry_buffer_size = sizeof(entry_buffer);
+    }
+
+    if (GetLastError() != ERROR_NO_MORE_ITEMS) {
+        // Only ERROR_NO_MORE_ITEMS is expected at the end of enumeration.
+        D("AdbNextInterface failed: %s",
+          android::base::SystemErrorCodeToString(GetLastError()).c_str());
+    }
+
+    _adb_close_handle(enum_handle);
 }
 
 static void kick_devices() {
-  // Need to acquire lock to safely walk the list which might be modified
-  // by another thread.
-  std::lock_guard<std::mutex> lock(usb_lock);
-  for (usb_handle* usb = handle_list.next; usb != &handle_list; usb = usb->next) {
-    usb_kick_locked(usb);
-  }
+    // Need to acquire lock to safely walk the list which might be modified
+    // by another thread.
+    std::lock_guard<std::mutex> lock(usb_lock);
+    for (usb_handle* usb : handle_list) {
+        usb_kick_locked(usb);
+    }
 }
+
+}  // namespace native
diff --git a/adb/commandline.cpp b/adb/commandline.cpp
deleted file mode 100644
index c9f1ee9..0000000
--- a/adb/commandline.cpp
+++ /dev/null
@@ -1,2108 +0,0 @@
-/*
- * Copyright (C) 2007 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.
- */
-
-#define TRACE_TAG ADB
-
-#include "sysdeps.h"
-
-#include <assert.h>
-#include <ctype.h>
-#include <errno.h>
-#include <inttypes.h>
-#include <limits.h>
-#include <stdarg.h>
-#include <stdint.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-
-#include <memory>
-#include <string>
-#include <thread>
-#include <vector>
-
-#include <android-base/file.h>
-#include <android-base/logging.h>
-#include <android-base/parseint.h>
-#include <android-base/stringprintf.h>
-#include <android-base/strings.h>
-
-#if !defined(_WIN32)
-#include <signal.h>
-#include <sys/ioctl.h>
-#include <termios.h>
-#include <unistd.h>
-#endif
-
-#include "adb.h"
-#include "adb_auth.h"
-#include "adb_client.h"
-#include "adb_io.h"
-#include "adb_unique_fd.h"
-#include "adb_utils.h"
-#include "bugreport.h"
-#include "commandline.h"
-#include "file_sync_service.h"
-#include "services.h"
-#include "shell_service.h"
-#include "sysdeps/chrono.h"
-
-static int install_app(TransportType t, const char* serial, int argc, const char** argv);
-static int install_multiple_app(TransportType t, const char* serial, int argc, const char** argv);
-static int uninstall_app(TransportType t, const char* serial, int argc, const char** argv);
-static int install_app_legacy(TransportType t, const char* serial, int argc, const char** argv);
-static int uninstall_app_legacy(TransportType t, const char* serial, int argc, const char** argv);
-
-extern int gListenAll;
-
-DefaultStandardStreamsCallback DEFAULT_STANDARD_STREAMS_CALLBACK(nullptr, nullptr);
-
-static std::string product_file(const char* file) {
-    const char* ANDROID_PRODUCT_OUT = getenv("ANDROID_PRODUCT_OUT");
-    if (ANDROID_PRODUCT_OUT == nullptr) {
-        fprintf(stderr, "adb: product directory not specified; set $ANDROID_PRODUCT_OUT\n");
-        exit(1);
-    }
-    return android::base::StringPrintf("%s%s%s", ANDROID_PRODUCT_OUT, OS_PATH_SEPARATOR_STR, file);
-}
-
-static void help() {
-    fprintf(stdout, "%s\n", adb_version().c_str());
-    // clang-format off
-    fprintf(stdout,
-        "global options:\n"
-        " -a         listen on all network interfaces, not just localhost\n"
-        " -d         use USB device (error if multiple devices connected)\n"
-        " -e         use TCP/IP device (error if multiple TCP/IP devices available)\n"
-        " -s SERIAL  use device with given serial (overrides $ANDROID_SERIAL)\n"
-        " -H         name of adb server host [default=localhost]\n"
-        " -P         port of adb server [default=5037]\n"
-        " -L SOCKET  listen on given socket for adb server [default=tcp:localhost:5037]\n"
-        "\n"
-        "general commands:\n"
-        " devices [-l]             list connected devices (-l for long output)\n"
-        " help                     show this help message\n"
-        " version                  show version num\n"
-        "\n"
-        "networking:\n"
-        " connect HOST[:PORT]      connect to a device via TCP/IP [default port=5555]\n"
-        " disconnect [HOST[:PORT]]\n"
-        "     disconnect from given TCP/IP device [default port=5555], or all\n"
-        " forward --list           list all forward socket connections\n"
-        " forward [--no-rebind] LOCAL REMOTE\n"
-        "     forward socket connection using:\n"
-        "       tcp:<port> (<local> may be \"tcp:0\" to pick any open port)\n"
-        "       localabstract:<unix domain socket name>\n"
-        "       localreserved:<unix domain socket name>\n"
-        "       localfilesystem:<unix domain socket name>\n"
-        "       dev:<character device name>\n"
-        "       jdwp:<process pid> (remote only)\n"
-        " forward --remove LOCAL   remove specific forward socket connection\n"
-        " forward --remove-all     remove all forward socket connections\n"
-        " ppp TTY [PARAMETER...]   run PPP over USB\n"
-        " reverse --list           list all reverse socket connections from device\n"
-        " reverse [--no-rebind] REMOTE LOCAL\n"
-        "     reverse socket connection using:\n"
-        "       tcp:<port> (<remote> may be \"tcp:0\" to pick any open port)\n"
-        "       localabstract:<unix domain socket name>\n"
-        "       localreserved:<unix domain socket name>\n"
-        "       localfilesystem:<unix domain socket name>\n"
-        " reverse --remove REMOTE  remove specific reverse socket connection\n"
-        " reverse --remove-all     remove all reverse socket connections from device\n"
-        "\n"
-        "file transfer:\n"
-        " push [--sync] LOCAL... REMOTE\n"
-        "     copy local files/directories to device\n"
-        "     --sync: only push files that are newer on the host than the device\n"
-        " pull [-a] REMOTE... LOCAL\n"
-        "     copy files/dirs from device\n"
-        "     -a: preserve file timestamp and mode\n"
-        " sync [system|vendor|oem|data|all]\n"
-        "     sync a local build from $ANDROID_PRODUCT_OUT to the device (default all)\n"
-        "     -l: list but don't copy\n"
-        "\n"
-        "shell:\n"
-        " shell [-e ESCAPE] [-n] [-Tt] [-x] [COMMAND...]\n"
-        "     run remote shell command (interactive shell if no command given)\n"
-        "     -e: choose escape character, or \"none\"; default '~'\n"
-        "     -n: don't read from stdin\n"
-        "     -T: disable PTY allocation\n"
-        "     -t: force PTY allocation\n"
-        "     -x: disable remote exit codes and stdout/stderr separation\n"
-        " emu COMMAND              run emulator console command\n"
-        "\n"
-        "app installation:\n"
-        " install [-lrtsdg] PACKAGE\n"
-        " install-multiple [-lrtsdpg] PACKAGE...\n"
-        "     push package(s) to the device and install them\n"
-        "     -l: forward lock application\n"
-        "     -r: replace existing application\n"
-        "     -t: allow test packages\n"
-        "     -s: install application on sdcard\n"
-        "     -d: allow version code downgrade (debuggable packages only)\n"
-        "     -p: partial application install (install-multiple only)\n"
-        "     -g: grant all runtime permissions\n"
-        " uninstall [-k] PACKAGE\n"
-        "     remove this app package from the device\n"
-        "     '-k': keep the data and cache directories\n"
-        "\n"
-        "backup/restore:\n"
-        "   to show usage run \"adb shell bu help\"\n"
-        "\n"
-        "debugging:\n"
-        " bugreport [PATH]\n"
-        "     write bugreport to given PATH [default=bugreport.zip];\n"
-        "     if PATH is a directory, the bug report is saved in that directory.\n"
-        "     devices that don't support zipped bug reports output to stdout.\n"
-        " jdwp                     list pids of processes hosting a JDWP transport\n"
-        " logcat                   show device log (logcat --help for more)\n"
-        "\n"
-        "security:\n"
-        " disable-verity           disable dm-verity checking on userdebug builds\n"
-        " enable-verity            re-enable dm-verity checking on userdebug builds\n"
-        " keygen FILE\n"
-        "     generate adb public/private key; private key stored in FILE,\n"
-        "     public key stored in FILE.pub (existing files overwritten)\n"
-        "\n"
-        "scripting:\n"
-        " wait-for[-TRANSPORT]-STATE\n"
-        "     wait for device to be in the given state\n"
-        "     State: device, recovery, sideload, or bootloader\n"
-        "     Transport: usb, local, or any [default=any]\n"
-        " get-state                print offline | bootloader | device\n"
-        " get-serialno             print <serial-number>\n"
-        " get-devpath              print <device-path>\n"
-        " remount\n"
-        "     remount /system, /vendor, and /oem partitions read-write\n"
-        " reboot [bootloader|recovery|sideload|sideload-auto-reboot]\n"
-        "     reboot the device; defaults to booting system image but\n"
-        "     supports bootloader and recovery too. sideload reboots\n"
-        "     into recovery and automatically starts sideload mode,\n"
-        "     sideload-auto-reboot is the same but reboots after sideloading.\n"
-        " sideload OTAPACKAGE      sideload the given full OTA package\n"
-        " root                     restart adbd with root permissions\n"
-        " unroot                   restart adbd without root permissions\n"
-        " usb                      restart adb server listening on USB\n"
-        " tcpip PORT               restart adb server listening on TCP on PORT\n"
-        "\n"
-        "internal debugging:\n"
-        " start-server             ensure that there is a server running\n"
-        " kill-server              kill the server if it is running\n"
-        " reconnect                kick connection from host side to force reconnect\n"
-        " reconnect device         kick connection from device side to force reconnect\n"
-        " reconnect offline        reset offline/unauthorized devices to force reconnect\n"
-        "\n"
-        "environment variables:\n"
-        " $ADB_TRACE\n"
-        "     comma-separated list of debug info to log:\n"
-        "     all,adb,sockets,packets,rwx,usb,sync,sysdeps,transport,jdwp\n"
-        " $ADB_VENDOR_KEYS         colon-separated list of keys (files or directories)\n"
-        " $ANDROID_SERIAL          serial number to connect to (see -s)\n"
-        " $ANDROID_LOG_TAGS        tags to be used by logcat (see logcat --help)\n");
-    // clang-format on
-}
-
-#if defined(_WIN32)
-
-// Implemented in sysdeps_win32.cpp.
-void stdin_raw_init();
-void stdin_raw_restore();
-
-#else
-static termios g_saved_terminal_state;
-
-static void stdin_raw_init() {
-    if (tcgetattr(STDIN_FILENO, &g_saved_terminal_state)) return;
-
-    termios tio;
-    if (tcgetattr(STDIN_FILENO, &tio)) return;
-
-    cfmakeraw(&tio);
-
-    // No timeout but request at least one character per read.
-    tio.c_cc[VTIME] = 0;
-    tio.c_cc[VMIN] = 1;
-
-    tcsetattr(STDIN_FILENO, TCSAFLUSH, &tio);
-}
-
-static void stdin_raw_restore() {
-    tcsetattr(STDIN_FILENO, TCSAFLUSH, &g_saved_terminal_state);
-}
-#endif
-
-// Reads from |fd| and prints received data. If |use_shell_protocol| is true
-// this expects that incoming data will use the shell protocol, in which case
-// stdout/stderr are routed independently and the remote exit code will be
-// returned.
-// if |callback| is non-null, stdout/stderr output will be handled by it.
-int read_and_dump(int fd, bool use_shell_protocol = false,
-                  StandardStreamsCallbackInterface* callback = &DEFAULT_STANDARD_STREAMS_CALLBACK) {
-    int exit_code = 0;
-    if (fd < 0) return exit_code;
-
-    std::unique_ptr<ShellProtocol> protocol;
-    int length = 0;
-
-    char raw_buffer[BUFSIZ];
-    char* buffer_ptr = raw_buffer;
-    if (use_shell_protocol) {
-        protocol.reset(new ShellProtocol(fd));
-        if (!protocol) {
-            LOG(ERROR) << "failed to allocate memory for ShellProtocol object";
-            return 1;
-        }
-        buffer_ptr = protocol->data();
-    }
-
-    while (true) {
-        if (use_shell_protocol) {
-            if (!protocol->Read()) {
-                break;
-            }
-            length = protocol->data_length();
-            switch (protocol->id()) {
-                case ShellProtocol::kIdStdout:
-                    callback->OnStdout(buffer_ptr, length);
-                    break;
-                case ShellProtocol::kIdStderr:
-                    callback->OnStderr(buffer_ptr, length);
-                    break;
-                case ShellProtocol::kIdExit:
-                    exit_code = protocol->data()[0];
-                    continue;
-                default:
-                    continue;
-            }
-            length = protocol->data_length();
-        } else {
-            D("read_and_dump(): pre adb_read(fd=%d)", fd);
-            length = adb_read(fd, raw_buffer, sizeof(raw_buffer));
-            D("read_and_dump(): post adb_read(fd=%d): length=%d", fd, length);
-            if (length <= 0) {
-                break;
-            }
-            callback->OnStdout(buffer_ptr, length);
-        }
-    }
-
-    return callback->Done(exit_code);
-}
-
-static void read_status_line(int fd, char* buf, size_t count)
-{
-    count--;
-    while (count > 0) {
-        int len = adb_read(fd, buf, count);
-        if (len <= 0) {
-            break;
-        }
-
-        buf += len;
-        count -= len;
-    }
-    *buf = '\0';
-}
-
-static void stdinout_raw_prologue(int inFd, int outFd, int& old_stdin_mode, int& old_stdout_mode) {
-    if (inFd == STDIN_FILENO) {
-        stdin_raw_init();
-#ifdef _WIN32
-        old_stdin_mode = _setmode(STDIN_FILENO, _O_BINARY);
-        if (old_stdin_mode == -1) {
-            fatal_errno("could not set stdin to binary");
-        }
-#endif
-    }
-
-#ifdef _WIN32
-    if (outFd == STDOUT_FILENO) {
-        old_stdout_mode = _setmode(STDOUT_FILENO, _O_BINARY);
-        if (old_stdout_mode == -1) {
-            fatal_errno("could not set stdout to binary");
-        }
-    }
-#endif
-}
-
-static void stdinout_raw_epilogue(int inFd, int outFd, int old_stdin_mode, int old_stdout_mode) {
-    if (inFd == STDIN_FILENO) {
-        stdin_raw_restore();
-#ifdef _WIN32
-        if (_setmode(STDIN_FILENO, old_stdin_mode) == -1) {
-            fatal_errno("could not restore stdin mode");
-        }
-#endif
-    }
-
-#ifdef _WIN32
-    if (outFd == STDOUT_FILENO) {
-        if (_setmode(STDOUT_FILENO, old_stdout_mode) == -1) {
-            fatal_errno("could not restore stdout mode");
-        }
-    }
-#endif
-}
-
-static void copy_to_file(int inFd, int outFd) {
-    const size_t BUFSIZE = 32 * 1024;
-    char* buf = (char*) malloc(BUFSIZE);
-    if (buf == nullptr) fatal("couldn't allocate buffer for copy_to_file");
-    int len;
-    long total = 0;
-    int old_stdin_mode = -1;
-    int old_stdout_mode = -1;
-
-    D("copy_to_file(%d -> %d)", inFd, outFd);
-
-    stdinout_raw_prologue(inFd, outFd, old_stdin_mode, old_stdout_mode);
-
-    while (true) {
-        if (inFd == STDIN_FILENO) {
-            len = unix_read(inFd, buf, BUFSIZE);
-        } else {
-            len = adb_read(inFd, buf, BUFSIZE);
-        }
-        if (len == 0) {
-            D("copy_to_file() : read 0 bytes; exiting");
-            break;
-        }
-        if (len < 0) {
-            D("copy_to_file(): read failed: %s", strerror(errno));
-            break;
-        }
-        if (outFd == STDOUT_FILENO) {
-            fwrite(buf, 1, len, stdout);
-            fflush(stdout);
-        } else {
-            adb_write(outFd, buf, len);
-        }
-        total += len;
-    }
-
-    stdinout_raw_epilogue(inFd, outFd, old_stdin_mode, old_stdout_mode);
-
-    D("copy_to_file() finished after %lu bytes", total);
-    free(buf);
-}
-
-static void send_window_size_change(int fd, std::unique_ptr<ShellProtocol>& shell) {
-    // Old devices can't handle window size changes.
-    if (shell == nullptr) return;
-
-#if defined(_WIN32)
-    struct winsize {
-        unsigned short ws_row;
-        unsigned short ws_col;
-        unsigned short ws_xpixel;
-        unsigned short ws_ypixel;
-    };
-#endif
-
-    winsize ws;
-
-#if defined(_WIN32)
-    // If stdout is redirected to a non-console, we won't be able to get the
-    // console size, but that makes sense.
-    const intptr_t intptr_handle = _get_osfhandle(STDOUT_FILENO);
-    if (intptr_handle == -1) return;
-
-    const HANDLE handle = reinterpret_cast<const HANDLE>(intptr_handle);
-
-    CONSOLE_SCREEN_BUFFER_INFO info;
-    memset(&info, 0, sizeof(info));
-    if (!GetConsoleScreenBufferInfo(handle, &info)) return;
-
-    memset(&ws, 0, sizeof(ws));
-    // The number of visible rows, excluding offscreen scroll-back rows which are in info.dwSize.Y.
-    ws.ws_row = info.srWindow.Bottom - info.srWindow.Top + 1;
-    // If the user has disabled "Wrap text output on resize", they can make the screen buffer wider
-    // than the window, in which case we should use the width of the buffer.
-    ws.ws_col = info.dwSize.X;
-#else
-    if (ioctl(fd, TIOCGWINSZ, &ws) == -1) return;
-#endif
-
-    // Send the new window size as human-readable ASCII for debugging convenience.
-    size_t l = snprintf(shell->data(), shell->data_capacity(), "%dx%d,%dx%d",
-                        ws.ws_row, ws.ws_col, ws.ws_xpixel, ws.ws_ypixel);
-    shell->Write(ShellProtocol::kIdWindowSizeChange, l + 1);
-}
-
-// Used to pass multiple values to the stdin read thread.
-struct StdinReadArgs {
-    int stdin_fd, write_fd;
-    bool raw_stdin;
-    std::unique_ptr<ShellProtocol> protocol;
-    char escape_char;
-};
-
-// Loops to read from stdin and push the data to the given FD.
-// The argument should be a pointer to a StdinReadArgs object. This function
-// will take ownership of the object and delete it when finished.
-static void stdin_read_thread_loop(void* x) {
-    std::unique_ptr<StdinReadArgs> args(reinterpret_cast<StdinReadArgs*>(x));
-
-#if !defined(_WIN32)
-    // Mask SIGTTIN in case we're in a backgrounded process.
-    sigset_t sigset;
-    sigemptyset(&sigset);
-    sigaddset(&sigset, SIGTTIN);
-    pthread_sigmask(SIG_BLOCK, &sigset, nullptr);
-#endif
-
-#if defined(_WIN32)
-    // _get_interesting_input_record_uncached() causes unix_read_interruptible()
-    // to return -1 with errno == EINTR if the window size changes.
-#else
-    // Unblock SIGWINCH for this thread, so our read(2) below will be
-    // interrupted if the window size changes.
-    sigset_t mask;
-    sigemptyset(&mask);
-    sigaddset(&mask, SIGWINCH);
-    pthread_sigmask(SIG_UNBLOCK, &mask, nullptr);
-#endif
-
-    // Set up the initial window size.
-    send_window_size_change(args->stdin_fd, args->protocol);
-
-    char raw_buffer[BUFSIZ];
-    char* buffer_ptr = raw_buffer;
-    size_t buffer_size = sizeof(raw_buffer);
-    if (args->protocol != nullptr) {
-        buffer_ptr = args->protocol->data();
-        buffer_size = args->protocol->data_capacity();
-    }
-
-    // If we need to parse escape sequences, make life easy.
-    if (args->raw_stdin && args->escape_char != '\0') {
-        buffer_size = 1;
-    }
-
-    enum EscapeState { kMidFlow, kStartOfLine, kInEscape };
-    EscapeState state = kStartOfLine;
-
-    while (true) {
-        // Use unix_read_interruptible() rather than adb_read() for stdin.
-        D("stdin_read_thread_loop(): pre unix_read_interruptible(fdi=%d,...)", args->stdin_fd);
-        int r = unix_read_interruptible(args->stdin_fd, buffer_ptr,
-                                        buffer_size);
-        if (r == -1 && errno == EINTR) {
-            send_window_size_change(args->stdin_fd, args->protocol);
-            continue;
-        }
-        D("stdin_read_thread_loop(): post unix_read_interruptible(fdi=%d,...)", args->stdin_fd);
-        if (r <= 0) {
-            // Only devices using the shell protocol know to close subprocess
-            // stdin. For older devices we want to just leave the connection
-            // open, otherwise an unpredictable amount of return data could
-            // be lost due to the FD closing before all data has been received.
-            if (args->protocol) {
-                args->protocol->Write(ShellProtocol::kIdCloseStdin, 0);
-            }
-            break;
-        }
-        // If we made stdin raw, check input for escape sequences. In
-        // this situation signals like Ctrl+C are sent remotely rather than
-        // interpreted locally so this provides an emergency out if the remote
-        // process starts ignoring the signal. SSH also does this, see the
-        // "escape characters" section on the ssh man page for more info.
-        if (args->raw_stdin && args->escape_char != '\0') {
-            char ch = buffer_ptr[0];
-            if (ch == args->escape_char) {
-                if (state == kStartOfLine) {
-                    state = kInEscape;
-                    // Swallow the escape character.
-                    continue;
-                } else {
-                    state = kMidFlow;
-                }
-            } else {
-                if (state == kInEscape) {
-                    if (ch == '.') {
-                        fprintf(stderr,"\r\n[ disconnected ]\r\n");
-                        stdin_raw_restore();
-                        exit(0);
-                    } else {
-                        // We swallowed an escape character that wasn't part of
-                        // a valid escape sequence; time to cough it up.
-                        buffer_ptr[0] = args->escape_char;
-                        buffer_ptr[1] = ch;
-                        ++r;
-                    }
-                }
-                state = (ch == '\n' || ch == '\r') ? kStartOfLine : kMidFlow;
-            }
-        }
-        if (args->protocol) {
-            if (!args->protocol->Write(ShellProtocol::kIdStdin, r)) {
-                break;
-            }
-        } else {
-            if (!WriteFdExactly(args->write_fd, buffer_ptr, r)) {
-                break;
-            }
-        }
-    }
-}
-
-// Returns a shell service string with the indicated arguments and command.
-static std::string ShellServiceString(bool use_shell_protocol,
-                                      const std::string& type_arg,
-                                      const std::string& command) {
-    std::vector<std::string> args;
-    if (use_shell_protocol) {
-        args.push_back(kShellServiceArgShellProtocol);
-
-        const char* terminal_type = getenv("TERM");
-        if (terminal_type != nullptr) {
-            args.push_back(std::string("TERM=") + terminal_type);
-        }
-    }
-    if (!type_arg.empty()) {
-        args.push_back(type_arg);
-    }
-
-    // Shell service string can look like: shell[,arg1,arg2,...]:[command].
-    return android::base::StringPrintf("shell%s%s:%s",
-                                       args.empty() ? "" : ",",
-                                       android::base::Join(args, ',').c_str(),
-                                       command.c_str());
-}
-
-// Connects to a shell on the device and read/writes data.
-//
-// Note: currently this function doesn't properly clean up resources; the
-// FD connected to the adb server is never closed and the stdin read thread
-// may never exit.
-//
-// On success returns the remote exit code if |use_shell_protocol| is true,
-// 0 otherwise. On failure returns 1.
-static int RemoteShell(bool use_shell_protocol, const std::string& type_arg,
-                       char escape_char,
-                       const std::string& command) {
-    std::string service_string = ShellServiceString(use_shell_protocol,
-                                                    type_arg, command);
-
-    // Old devices can't handle a service string that's longer than MAX_PAYLOAD_V1.
-    // Use |use_shell_protocol| to determine whether to allow a command longer than that.
-    if (service_string.size() > MAX_PAYLOAD_V1 && !use_shell_protocol) {
-        fprintf(stderr, "error: shell command too long\n");
-        return 1;
-    }
-
-    // Make local stdin raw if the device allocates a PTY, which happens if:
-    //   1. We are explicitly asking for a PTY shell, or
-    //   2. We don't specify shell type and are starting an interactive session.
-    bool raw_stdin = (type_arg == kShellServiceArgPty ||
-                      (type_arg.empty() && command.empty()));
-
-    std::string error;
-    int fd = adb_connect(service_string, &error);
-    if (fd < 0) {
-        fprintf(stderr,"error: %s\n", error.c_str());
-        return 1;
-    }
-
-    StdinReadArgs* args = new StdinReadArgs;
-    if (!args) {
-        LOG(ERROR) << "couldn't allocate StdinReadArgs object";
-        return 1;
-    }
-    args->stdin_fd = STDIN_FILENO;
-    args->write_fd = fd;
-    args->raw_stdin = raw_stdin;
-    args->escape_char = escape_char;
-    if (use_shell_protocol) {
-        args->protocol.reset(new ShellProtocol(args->write_fd));
-    }
-
-    if (raw_stdin) stdin_raw_init();
-
-#if !defined(_WIN32)
-    // Ensure our process is notified if the local window size changes.
-    // We use sigaction(2) to ensure that the SA_RESTART flag is not set,
-    // because the whole reason we're sending signals is to unblock the read(2)!
-    // That also means we don't need to do anything in the signal handler:
-    // the side effect of delivering the signal is all we need.
-    struct sigaction sa;
-    memset(&sa, 0, sizeof(sa));
-    sa.sa_handler = [](int) {};
-    sa.sa_flags = 0;
-    sigaction(SIGWINCH, &sa, nullptr);
-
-    // Now block SIGWINCH in this thread (the main thread) and all threads spawned
-    // from it. The stdin read thread will unblock this signal to ensure that it's
-    // the thread that receives the signal.
-    sigset_t mask;
-    sigemptyset(&mask);
-    sigaddset(&mask, SIGWINCH);
-    pthread_sigmask(SIG_BLOCK, &mask, nullptr);
-#endif
-
-    // TODO: combine read_and_dump with stdin_read_thread to make life simpler?
-    std::thread(stdin_read_thread_loop, args).detach();
-    int exit_code = read_and_dump(fd, use_shell_protocol);
-
-    // TODO: properly exit stdin_read_thread_loop and close |fd|.
-
-    // TODO: we should probably install signal handlers for this.
-    // TODO: can we use atexit? even on Windows?
-    if (raw_stdin) stdin_raw_restore();
-
-    return exit_code;
-}
-
-static int adb_shell(int argc, const char** argv) {
-    FeatureSet features;
-    std::string error;
-    if (!adb_get_feature_set(&features, &error)) {
-        fprintf(stderr, "error: %s\n", error.c_str());
-        return 1;
-    }
-
-    enum PtyAllocationMode { kPtyAuto, kPtyNo, kPtyYes, kPtyDefinitely };
-
-    // Defaults.
-    char escape_char = '~'; // -e
-    bool use_shell_protocol = CanUseFeature(features, kFeatureShell2); // -x
-    PtyAllocationMode tty = use_shell_protocol ? kPtyAuto : kPtyDefinitely; // -t/-T
-
-    // Parse shell-specific command-line options.
-    argv[0] = "adb shell"; // So getopt(3) error messages start "adb shell".
-    optind = 1; // argv[0] is always "shell", so set `optind` appropriately.
-    int opt;
-    while ((opt = getopt(argc, const_cast<char**>(argv), "+e:ntTx")) != -1) {
-        switch (opt) {
-            case 'e':
-                if (!(strlen(optarg) == 1 || strcmp(optarg, "none") == 0)) {
-                    return syntax_error("-e requires a single-character argument or 'none'");
-                }
-                escape_char = (strcmp(optarg, "none") == 0) ? 0 : optarg[0];
-                break;
-            case 'n':
-                close_stdin();
-                break;
-            case 'x':
-                // This option basically asks for historical behavior, so set options that
-                // correspond to the historical defaults. This is slightly weird in that -Tx
-                // is fine (because we'll undo the -T) but -xT isn't, but that does seem to
-                // be our least worst choice...
-                use_shell_protocol = false;
-                tty = kPtyDefinitely;
-                escape_char = '~';
-                break;
-            case 't':
-                // Like ssh, -t arguments are cumulative so that multiple -t's
-                // are needed to force a PTY.
-                tty = (tty >= kPtyYes) ? kPtyDefinitely : kPtyYes;
-                break;
-            case 'T':
-                tty = kPtyNo;
-                break;
-            default:
-                // getopt(3) already printed an error message for us.
-                return 1;
-        }
-    }
-
-    bool is_interactive = (optind == argc);
-
-    std::string shell_type_arg = kShellServiceArgPty;
-    if (tty == kPtyNo) {
-        shell_type_arg = kShellServiceArgRaw;
-    } else if (tty == kPtyAuto) {
-        // If stdin isn't a TTY, default to a raw shell; this lets
-        // things like `adb shell < my_script.sh` work as expected.
-        // Non-interactive shells should also not have a pty.
-        if (!unix_isatty(STDIN_FILENO) || !is_interactive) {
-            shell_type_arg = kShellServiceArgRaw;
-        }
-    } else if (tty == kPtyYes) {
-        // A single -t arg isn't enough to override implicit -T.
-        if (!unix_isatty(STDIN_FILENO)) {
-            fprintf(stderr,
-                    "Remote PTY will not be allocated because stdin is not a terminal.\n"
-                    "Use multiple -t options to force remote PTY allocation.\n");
-            shell_type_arg = kShellServiceArgRaw;
-        }
-    }
-
-    D("shell -e 0x%x t=%d use_shell_protocol=%s shell_type_arg=%s\n",
-      escape_char, tty,
-      use_shell_protocol ? "true" : "false",
-      (shell_type_arg == kShellServiceArgPty) ? "pty" : "raw");
-
-    // Raw mode is only supported when talking to a new device *and* using the shell protocol.
-    if (!use_shell_protocol) {
-        if (shell_type_arg != kShellServiceArgPty) {
-            fprintf(stderr, "error: %s only supports allocating a pty\n",
-                    !CanUseFeature(features, kFeatureShell2) ? "device" : "-x");
-            return 1;
-        } else {
-            // If we're not using the shell protocol, the type argument must be empty.
-            shell_type_arg = "";
-        }
-    }
-
-    std::string command;
-    if (optind < argc) {
-        // We don't escape here, just like ssh(1). http://b/20564385.
-        command = android::base::Join(std::vector<const char*>(argv + optind, argv + argc), ' ');
-    }
-
-    return RemoteShell(use_shell_protocol, shell_type_arg, escape_char, command);
-}
-
-static int adb_sideload_legacy(const char* filename, int in_fd, int size) {
-    std::string error;
-    int out_fd = adb_connect(android::base::StringPrintf("sideload:%d", size), &error);
-    if (out_fd < 0) {
-        fprintf(stderr, "adb: pre-KitKat sideload connection failed: %s\n", error.c_str());
-        return -1;
-    }
-
-    int opt = CHUNK_SIZE;
-    opt = adb_setsockopt(out_fd, SOL_SOCKET, SO_SNDBUF, &opt, sizeof(opt));
-
-    char buf[CHUNK_SIZE];
-    int total = size;
-    while (size > 0) {
-        unsigned xfer = (size > CHUNK_SIZE) ? CHUNK_SIZE : size;
-        if (!ReadFdExactly(in_fd, buf, xfer)) {
-            fprintf(stderr, "adb: failed to read data from %s: %s\n", filename, strerror(errno));
-            adb_close(out_fd);
-            return -1;
-        }
-        if (!WriteFdExactly(out_fd, buf, xfer)) {
-            std::string error;
-            adb_status(out_fd, &error);
-            fprintf(stderr, "adb: failed to write data: %s\n", error.c_str());
-            adb_close(out_fd);
-            return -1;
-        }
-        size -= xfer;
-        printf("sending: '%s' %4d%%    \r", filename, (int)(100LL - ((100LL * size) / (total))));
-        fflush(stdout);
-    }
-    printf("\n");
-
-    if (!adb_status(out_fd, &error)) {
-        fprintf(stderr, "adb: error response: %s\n", error.c_str());
-        adb_close(out_fd);
-        return -1;
-    }
-
-    adb_close(out_fd);
-    return 0;
-}
-
-#define SIDELOAD_HOST_BLOCK_SIZE (CHUNK_SIZE)
-
-/*
- * The sideload-host protocol serves the data in a file (given on the
- * command line) to the client, using a simple protocol:
- *
- * - The connect message includes the total number of bytes in the
- *   file and a block size chosen by us.
- *
- * - The other side sends the desired block number as eight decimal
- *   digits (eg "00000023" for block 23).  Blocks are numbered from
- *   zero.
- *
- * - We send back the data of the requested block.  The last block is
- *   likely to be partial; when the last block is requested we only
- *   send the part of the block that exists, it's not padded up to the
- *   block size.
- *
- * - When the other side sends "DONEDONE" instead of a block number,
- *   we hang up.
- */
-static int adb_sideload_host(const char* filename) {
-    // TODO: use a LinePrinter instead...
-    struct stat sb;
-    if (stat(filename, &sb) == -1) {
-        fprintf(stderr, "adb: failed to stat file %s: %s\n", filename, strerror(errno));
-        return -1;
-    }
-    unique_fd package_fd(adb_open(filename, O_RDONLY));
-    if (package_fd == -1) {
-        fprintf(stderr, "adb: failed to open file %s: %s\n", filename, strerror(errno));
-        return -1;
-    }
-
-    std::string service = android::base::StringPrintf(
-        "sideload-host:%d:%d", static_cast<int>(sb.st_size), SIDELOAD_HOST_BLOCK_SIZE);
-    std::string error;
-    unique_fd device_fd(adb_connect(service, &error));
-    if (device_fd < 0) {
-        // Try falling back to the older (<= K) sideload method. Maybe this
-        // is an older device that doesn't support sideload-host.
-        fprintf(stderr, "adb: sideload connection failed: %s\n", error.c_str());
-        fprintf(stderr, "adb: trying pre-KitKat sideload method...\n");
-        return adb_sideload_legacy(filename, package_fd, static_cast<int>(sb.st_size));
-    }
-
-    int opt = SIDELOAD_HOST_BLOCK_SIZE;
-    adb_setsockopt(device_fd, SOL_SOCKET, SO_SNDBUF, &opt, sizeof(opt));
-
-    char buf[SIDELOAD_HOST_BLOCK_SIZE];
-
-    size_t xfer = 0;
-    int last_percent = -1;
-    while (true) {
-        if (!ReadFdExactly(device_fd, buf, 8)) {
-            fprintf(stderr, "adb: failed to read command: %s\n", strerror(errno));
-            return -1;
-        }
-        buf[8] = '\0';
-
-        if (strcmp("DONEDONE", buf) == 0) {
-            printf("\rTotal xfer: %.2fx%*s\n",
-                   static_cast<double>(xfer) / (sb.st_size ? sb.st_size : 1),
-                   static_cast<int>(strlen(filename) + 10), "");
-            return 0;
-        }
-
-        int block = strtol(buf, NULL, 10);
-
-        size_t offset = block * SIDELOAD_HOST_BLOCK_SIZE;
-        if (offset >= static_cast<size_t>(sb.st_size)) {
-            fprintf(stderr, "adb: failed to read block %d past end\n", block);
-            return -1;
-        }
-
-        size_t to_write = SIDELOAD_HOST_BLOCK_SIZE;
-        if ((offset + SIDELOAD_HOST_BLOCK_SIZE) > static_cast<size_t>(sb.st_size)) {
-            to_write = sb.st_size - offset;
-        }
-
-        if (adb_lseek(package_fd, offset, SEEK_SET) != static_cast<int>(offset)) {
-            fprintf(stderr, "adb: failed to seek to package block: %s\n", strerror(errno));
-            return -1;
-        }
-        if (!ReadFdExactly(package_fd, buf, to_write)) {
-            fprintf(stderr, "adb: failed to read package block: %s\n", strerror(errno));
-            return -1;
-        }
-
-        if (!WriteFdExactly(device_fd, buf, to_write)) {
-            adb_status(device_fd, &error);
-            fprintf(stderr, "adb: failed to write data '%s' *\n", error.c_str());
-            return -1;
-        }
-        xfer += to_write;
-
-        // For normal OTA packages, we expect to transfer every byte
-        // twice, plus a bit of overhead (one read during
-        // verification, one read of each byte for installation, plus
-        // extra access to things like the zip central directory).
-        // This estimate of the completion becomes 100% when we've
-        // transferred ~2.13 (=100/47) times the package size.
-        int percent = static_cast<int>(xfer * 47LL / (sb.st_size ? sb.st_size : 1));
-        if (percent != last_percent) {
-            printf("\rserving: '%s'  (~%d%%)    ", filename, percent);
-            fflush(stdout);
-            last_percent = percent;
-        }
-    }
-}
-
-/**
- * Run ppp in "notty" mode against a resource listed as the first parameter
- * eg:
- *
- * ppp dev:/dev/omap_csmi_tty0 <ppp options>
- *
- */
-static int ppp(int argc, const char** argv) {
-#if defined(_WIN32)
-    fprintf(stderr, "error: adb %s not implemented on Win32\n", argv[0]);
-    return -1;
-#else
-    if (argc < 2) return syntax_error("adb %s <adb service name> [ppp opts]", argv[0]);
-
-    const char* adb_service_name = argv[1];
-    std::string error;
-    int fd = adb_connect(adb_service_name, &error);
-    if (fd < 0) {
-        fprintf(stderr, "adb: could not open adb service %s: %s\n", adb_service_name, error.c_str());
-        return 1;
-    }
-
-    pid_t pid = fork();
-
-    if (pid < 0) {
-        perror("from fork()");
-        return 1;
-    } else if (pid == 0) {
-        int err;
-        int i;
-        const char **ppp_args;
-
-        // copy args
-        ppp_args = (const char **) alloca(sizeof(char *) * argc + 1);
-        ppp_args[0] = "pppd";
-        for (i = 2 ; i < argc ; i++) {
-            //argv[2] and beyond become ppp_args[1] and beyond
-            ppp_args[i - 1] = argv[i];
-        }
-        ppp_args[i-1] = NULL;
-
-        // child side
-
-        dup2(fd, STDIN_FILENO);
-        dup2(fd, STDOUT_FILENO);
-        adb_close(STDERR_FILENO);
-        adb_close(fd);
-
-        err = execvp("pppd", (char * const *)ppp_args);
-
-        if (err < 0) {
-            perror("execing pppd");
-        }
-        exit(-1);
-    } else {
-        // parent side
-
-        adb_close(fd);
-        return 0;
-    }
-#endif /* !defined(_WIN32) */
-}
-
-static bool wait_for_device(const char* service, TransportType t, const char* serial) {
-    std::vector<std::string> components = android::base::Split(service, "-");
-    if (components.size() < 3 || components.size() > 4) {
-        fprintf(stderr, "adb: couldn't parse 'wait-for' command: %s\n", service);
-        return false;
-    }
-
-    // Was the caller vague about what they'd like us to wait for?
-    // If so, check they weren't more specific in their choice of transport type.
-    if (components.size() == 3) {
-        auto it = components.begin() + 2;
-        if (t == kTransportUsb) {
-            components.insert(it, "usb");
-        } else if (t == kTransportLocal) {
-            components.insert(it, "local");
-        } else {
-            components.insert(it, "any");
-        }
-    } else if (components[2] != "any" && components[2] != "local" && components[2] != "usb") {
-        fprintf(stderr, "adb: unknown type %s; expected 'any', 'local', or 'usb'\n",
-                components[2].c_str());
-        return false;
-    }
-
-    if (components[3] != "any" && components[3] != "bootloader" && components[3] != "device" &&
-        components[3] != "recovery" && components[3] != "sideload") {
-        fprintf(stderr,
-                "adb: unknown state %s; "
-                "expected 'any', 'bootloader', 'device', 'recovery', or 'sideload'\n",
-                components[3].c_str());
-        return false;
-    }
-
-    std::string cmd = format_host_command(android::base::Join(components, "-").c_str(), t, serial);
-    return adb_command(cmd);
-}
-
-static bool adb_root(const char* command) {
-    std::string error;
-
-    unique_fd fd(adb_connect(android::base::StringPrintf("%s:", command), &error));
-    if (fd < 0) {
-        fprintf(stderr, "adb: unable to connect for %s: %s\n", command, error.c_str());
-        return false;
-    }
-
-    // Figure out whether we actually did anything.
-    char buf[256];
-    char* cur = buf;
-    ssize_t bytes_left = sizeof(buf);
-    while (bytes_left > 0) {
-        ssize_t bytes_read = adb_read(fd, cur, bytes_left);
-        if (bytes_read == 0) {
-            break;
-        } else if (bytes_read < 0) {
-            fprintf(stderr, "adb: error while reading for %s: %s\n", command, strerror(errno));
-            return false;
-        }
-        cur += bytes_read;
-        bytes_left -= bytes_read;
-    }
-
-    if (bytes_left == 0) {
-        fprintf(stderr, "adb: unexpected output length for %s\n", command);
-        return false;
-    }
-
-    fflush(stdout);
-    WriteFdExactly(STDOUT_FILENO, buf, sizeof(buf) - bytes_left);
-    if (cur != buf && strstr(buf, "restarting") == nullptr) {
-        return true;
-    }
-
-    // Give adbd some time to kill itself and come back up.
-    // We can't use wait-for-device because devices (e.g. adb over network) might not come back.
-    std::this_thread::sleep_for(3s);
-    return true;
-}
-
-int send_shell_command(TransportType transport_type, const char* serial, const std::string& command,
-                       bool disable_shell_protocol, StandardStreamsCallbackInterface* callback) {
-    int fd;
-    bool use_shell_protocol = false;
-
-    while (true) {
-        bool attempt_connection = true;
-
-        // Use shell protocol if it's supported and the caller doesn't explicitly
-        // disable it.
-        if (!disable_shell_protocol) {
-            FeatureSet features;
-            std::string error;
-            if (adb_get_feature_set(&features, &error)) {
-                use_shell_protocol = CanUseFeature(features, kFeatureShell2);
-            } else {
-                // Device was unreachable.
-                attempt_connection = false;
-            }
-        }
-
-        if (attempt_connection) {
-            std::string error;
-            std::string service_string = ShellServiceString(use_shell_protocol, "", command);
-
-            fd = adb_connect(service_string, &error);
-            if (fd >= 0) {
-                break;
-            }
-        }
-
-        fprintf(stderr, "- waiting for device -\n");
-        if (!wait_for_device("wait-for-device", transport_type, serial)) {
-            return 1;
-        }
-    }
-
-    int exit_code = read_and_dump(fd, use_shell_protocol, callback);
-
-    if (adb_close(fd) < 0) {
-        PLOG(ERROR) << "failure closing FD " << fd;
-    }
-
-    return exit_code;
-}
-
-static int logcat(TransportType transport, const char* serial, int argc, const char** argv) {
-    char* log_tags = getenv("ANDROID_LOG_TAGS");
-    std::string quoted = escape_arg(log_tags == nullptr ? "" : log_tags);
-
-    std::string cmd = "export ANDROID_LOG_TAGS=\"" + quoted + "\"; exec logcat";
-
-    if (!strcmp(argv[0], "longcat")) {
-        cmd += " -v long";
-    }
-
-    --argc;
-    ++argv;
-    while (argc-- > 0) {
-        cmd += " " + escape_arg(*argv++);
-    }
-
-    // No need for shell protocol with logcat, always disable for simplicity.
-    return send_shell_command(transport, serial, cmd, true);
-}
-
-static void write_zeros(int bytes, int fd) {
-    int old_stdin_mode = -1;
-    int old_stdout_mode = -1;
-    char* buf = (char*) calloc(1, bytes);
-    if (buf == nullptr) fatal("couldn't allocate buffer for write_zeros");
-
-    D("write_zeros(%d) -> %d", bytes, fd);
-
-    stdinout_raw_prologue(-1, fd, old_stdin_mode, old_stdout_mode);
-
-    if (fd == STDOUT_FILENO) {
-        fwrite(buf, 1, bytes, stdout);
-        fflush(stdout);
-    } else {
-        adb_write(fd, buf, bytes);
-    }
-
-    stdinout_raw_prologue(-1, fd, old_stdin_mode, old_stdout_mode);
-
-    D("write_zeros() finished");
-    free(buf);
-}
-
-static int backup(int argc, const char** argv) {
-    const char* filename = "backup.ab";
-
-    /* find, extract, and use any -f argument */
-    for (int i = 1; i < argc; i++) {
-        if (!strcmp("-f", argv[i])) {
-            if (i == argc - 1) return syntax_error("backup -f passed with no filename");
-            filename = argv[i+1];
-            for (int j = i+2; j <= argc; ) {
-                argv[i++] = argv[j++];
-            }
-            argc -= 2;
-            argv[argc] = NULL;
-        }
-    }
-
-    // Bare "adb backup" or "adb backup -f filename" are not valid invocations ---
-    // a list of packages is required.
-    if (argc < 2) return syntax_error("backup either needs a list of packages or -all/-shared");
-
-    adb_unlink(filename);
-    int outFd = adb_creat(filename, 0640);
-    if (outFd < 0) {
-        fprintf(stderr, "adb: backup unable to create file '%s': %s\n", filename, strerror(errno));
-        return EXIT_FAILURE;
-    }
-
-    std::string cmd = "backup:";
-    --argc;
-    ++argv;
-    while (argc-- > 0) {
-        cmd += " " + escape_arg(*argv++);
-    }
-
-    D("backup. filename=%s cmd=%s", filename, cmd.c_str());
-    std::string error;
-    int fd = adb_connect(cmd, &error);
-    if (fd < 0) {
-        fprintf(stderr, "adb: unable to connect for backup: %s\n", error.c_str());
-        adb_close(outFd);
-        return EXIT_FAILURE;
-    }
-
-    fprintf(stdout, "Now unlock your device and confirm the backup operation...\n");
-    fflush(stdout);
-
-    copy_to_file(fd, outFd);
-
-    adb_close(fd);
-    adb_close(outFd);
-    return EXIT_SUCCESS;
-}
-
-static int restore(int argc, const char** argv) {
-    if (argc != 2) return syntax_error("restore requires an argument");
-
-    const char* filename = argv[1];
-    int tarFd = adb_open(filename, O_RDONLY);
-    if (tarFd < 0) {
-        fprintf(stderr, "adb: unable to open file %s: %s\n", filename, strerror(errno));
-        return -1;
-    }
-
-    std::string error;
-    int fd = adb_connect("restore:", &error);
-    if (fd < 0) {
-        fprintf(stderr, "adb: unable to connect for restore: %s\n", error.c_str());
-        adb_close(tarFd);
-        return -1;
-    }
-
-    fprintf(stdout, "Now unlock your device and confirm the restore operation.\n");
-    fflush(stdout);
-
-    copy_to_file(tarFd, fd);
-
-    // Provide an in-band EOD marker in case the archive file is malformed
-    write_zeros(512*2, fd);
-
-    // Wait until the other side finishes, or it'll get sent SIGHUP.
-    copy_to_file(fd, STDOUT_FILENO);
-
-    adb_close(fd);
-    adb_close(tarFd);
-    return 0;
-}
-
-static void parse_push_pull_args(const char** arg, int narg, std::vector<const char*>* srcs,
-                                 const char** dst, bool* copy_attrs, bool* sync) {
-    *copy_attrs = false;
-
-    srcs->clear();
-    bool ignore_flags = false;
-    while (narg > 0) {
-        if (ignore_flags || *arg[0] != '-') {
-            srcs->push_back(*arg);
-        } else {
-            if (!strcmp(*arg, "-p")) {
-                // Silently ignore for backwards compatibility.
-            } else if (!strcmp(*arg, "-a")) {
-                *copy_attrs = true;
-            } else if (!strcmp(*arg, "--sync")) {
-                if (sync != nullptr) {
-                    *sync = true;
-                }
-            } else if (!strcmp(*arg, "--")) {
-                ignore_flags = true;
-            } else {
-                syntax_error("unrecognized option '%s'", *arg);
-                exit(1);
-            }
-        }
-        ++arg;
-        --narg;
-    }
-
-    if (srcs->size() > 1) {
-        *dst = srcs->back();
-        srcs->pop_back();
-    }
-}
-
-static int adb_connect_command(const std::string& command) {
-    std::string error;
-    int fd = adb_connect(command, &error);
-    if (fd < 0) {
-        fprintf(stderr, "error: %s\n", error.c_str());
-        return 1;
-    }
-    read_and_dump(fd);
-    adb_close(fd);
-    return 0;
-}
-
-static int adb_query_command(const std::string& command) {
-    std::string result;
-    std::string error;
-    if (!adb_query(command, &result, &error)) {
-        fprintf(stderr, "error: %s\n", error.c_str());
-        return 1;
-    }
-    printf("%s\n", result.c_str());
-    return 0;
-}
-
-// Disallow stdin, stdout, and stderr.
-static bool _is_valid_ack_reply_fd(const int ack_reply_fd) {
-#ifdef _WIN32
-    const HANDLE ack_reply_handle = cast_int_to_handle(ack_reply_fd);
-    return (GetStdHandle(STD_INPUT_HANDLE) != ack_reply_handle) &&
-           (GetStdHandle(STD_OUTPUT_HANDLE) != ack_reply_handle) &&
-           (GetStdHandle(STD_ERROR_HANDLE) != ack_reply_handle);
-#else
-    return ack_reply_fd > 2;
-#endif
-}
-
-static bool _use_legacy_install() {
-    FeatureSet features;
-    std::string error;
-    if (!adb_get_feature_set(&features, &error)) {
-        fprintf(stderr, "error: %s\n", error.c_str());
-        return true;
-    }
-    return !CanUseFeature(features, kFeatureCmd);
-}
-
-int adb_commandline(int argc, const char** argv) {
-    int no_daemon = 0;
-    int is_daemon = 0;
-    int is_server = 0;
-    int r;
-    TransportType transport_type = kTransportAny;
-    int ack_reply_fd = -1;
-
-#if !defined(_WIN32)
-    // We'd rather have EPIPE than SIGPIPE.
-    signal(SIGPIPE, SIG_IGN);
-#endif
-
-    const char* server_host_str = nullptr;
-    const char* server_port_str = nullptr;
-    const char* server_socket_str = nullptr;
-
-    // We need to check for -d and -e before we look at $ANDROID_SERIAL.
-    const char* serial = nullptr;
-
-    while (argc > 0) {
-        if (!strcmp(argv[0],"server")) {
-            is_server = 1;
-        } else if (!strcmp(argv[0],"nodaemon")) {
-            no_daemon = 1;
-        } else if (!strcmp(argv[0], "fork-server")) {
-            /* this is a special flag used only when the ADB client launches the ADB Server */
-            is_daemon = 1;
-        } else if (!strcmp(argv[0], "--reply-fd")) {
-            if (argc < 2) return syntax_error("--reply-fd requires an argument");
-            const char* reply_fd_str = argv[1];
-            argc--;
-            argv++;
-            ack_reply_fd = strtol(reply_fd_str, nullptr, 10);
-            if (!_is_valid_ack_reply_fd(ack_reply_fd)) {
-                fprintf(stderr, "adb: invalid reply fd \"%s\"\n", reply_fd_str);
-                return 1;
-            }
-        } else if (argv[0][0]=='-' && argv[0][1]=='s') {
-            if (isdigit(argv[0][2])) {
-                serial = argv[0] + 2;
-            } else {
-                if (argc < 2 || argv[0][2] != '\0') return syntax_error("-s requires an argument");
-                serial = argv[1];
-                argc--;
-                argv++;
-            }
-        } else if (!strcmp(argv[0],"-d")) {
-            transport_type = kTransportUsb;
-        } else if (!strcmp(argv[0],"-e")) {
-            transport_type = kTransportLocal;
-        } else if (!strcmp(argv[0],"-a")) {
-            gListenAll = 1;
-        } else if (!strncmp(argv[0], "-H", 2)) {
-            if (argv[0][2] == '\0') {
-                if (argc < 2) return syntax_error("-H requires an argument");
-                server_host_str = argv[1];
-                argc--;
-                argv++;
-            } else {
-                server_host_str = argv[0] + 2;
-            }
-        } else if (!strncmp(argv[0], "-P", 2)) {
-            if (argv[0][2] == '\0') {
-                if (argc < 2) return syntax_error("-P requires an argument");
-                server_port_str = argv[1];
-                argc--;
-                argv++;
-            } else {
-                server_port_str = argv[0] + 2;
-            }
-        } else if (!strcmp(argv[0], "-L")) {
-            if (argc < 2) return syntax_error("-L requires an argument");
-            server_socket_str = argv[1];
-            argc--;
-            argv++;
-        } else {
-            /* out of recognized modifiers and flags */
-            break;
-        }
-        argc--;
-        argv++;
-    }
-
-    if ((server_host_str || server_port_str) && server_socket_str) {
-        return syntax_error("-L is incompatible with -H or -P");
-    }
-
-    // If -L, -H, or -P are specified, ignore environment variables.
-    // Otherwise, prefer ADB_SERVER_SOCKET over ANDROID_ADB_SERVER_ADDRESS/PORT.
-    if (!server_host_str && !server_port_str && !server_socket_str) {
-        server_socket_str = getenv("ADB_SERVER_SOCKET");
-    }
-
-    if (!server_socket_str) {
-        // tcp:1234 and tcp:localhost:1234 are different with -a, so don't default to localhost
-        server_host_str = server_host_str ? server_host_str : getenv("ANDROID_ADB_SERVER_ADDRESS");
-
-        int server_port = DEFAULT_ADB_PORT;
-        server_port_str = server_port_str ? server_port_str : getenv("ANDROID_ADB_SERVER_PORT");
-        if (server_port_str && strlen(server_port_str) > 0) {
-            if (!android::base::ParseInt(server_port_str, &server_port, 1, 65535)) {
-                fprintf(stderr,
-                        "adb: Env var ANDROID_ADB_SERVER_PORT must be a positive"
-                        " number less than 65535. Got \"%s\"\n",
-                        server_port_str);
-                exit(1);
-            }
-        }
-
-        int rc;
-        char* temp;
-        if (server_host_str) {
-            rc = asprintf(&temp, "tcp:%s:%d", server_host_str, server_port);
-        } else {
-            rc = asprintf(&temp, "tcp:%d", server_port);
-        }
-        if (rc < 0) {
-            fatal("failed to allocate server socket specification");
-        }
-        server_socket_str = temp;
-    }
-
-    adb_set_socket_spec(server_socket_str);
-
-    // If none of -d, -e, or -s were specified, try $ANDROID_SERIAL.
-    if (transport_type == kTransportAny && serial == nullptr) {
-        serial = getenv("ANDROID_SERIAL");
-    }
-
-    adb_set_transport(transport_type, serial);
-
-    if (is_server) {
-        if (no_daemon || is_daemon) {
-            if (is_daemon && (ack_reply_fd == -1)) {
-                fprintf(stderr, "reply fd for adb server to client communication not specified.\n");
-                return 1;
-            }
-            r = adb_server_main(is_daemon, server_socket_str, ack_reply_fd);
-        } else {
-            r = launch_server(server_socket_str);
-        }
-        if (r) {
-            fprintf(stderr,"* could not start server *\n");
-        }
-        return r;
-    }
-
-    if (argc == 0) {
-        help();
-        return 1;
-    }
-
-    /* handle wait-for-* prefix */
-    if (!strncmp(argv[0], "wait-for-", strlen("wait-for-"))) {
-        const char* service = argv[0];
-
-        if (!wait_for_device(service, transport_type, serial)) {
-            return 1;
-        }
-
-        // Allow a command to be run after wait-for-device,
-        // e.g. 'adb wait-for-device shell'.
-        if (argc == 1) {
-            return 0;
-        }
-
-        /* Fall through */
-        argc--;
-        argv++;
-    }
-
-    /* adb_connect() commands */
-    if (!strcmp(argv[0], "devices")) {
-        const char *listopt;
-        if (argc < 2) {
-            listopt = "";
-        } else if (argc == 2 && !strcmp(argv[1], "-l")) {
-            listopt = argv[1];
-        } else {
-            return syntax_error("adb devices [-l]");
-        }
-
-        std::string query = android::base::StringPrintf("host:%s%s", argv[0], listopt);
-        printf("List of devices attached\n");
-        return adb_query_command(query);
-    }
-    else if (!strcmp(argv[0], "connect")) {
-        if (argc != 2) return syntax_error("adb connect <host>[:<port>]");
-
-        std::string query = android::base::StringPrintf("host:connect:%s", argv[1]);
-        return adb_query_command(query);
-    }
-    else if (!strcmp(argv[0], "disconnect")) {
-        if (argc > 2) return syntax_error("adb disconnect [<host>[:<port>]]");
-
-        std::string query = android::base::StringPrintf("host:disconnect:%s",
-                                                        (argc == 2) ? argv[1] : "");
-        return adb_query_command(query);
-    }
-    else if (!strcmp(argv[0], "emu")) {
-        return adb_send_emulator_command(argc, argv, serial);
-    }
-    else if (!strcmp(argv[0], "shell")) {
-        return adb_shell(argc, argv);
-    }
-    else if (!strcmp(argv[0], "exec-in") || !strcmp(argv[0], "exec-out")) {
-        int exec_in = !strcmp(argv[0], "exec-in");
-
-        if (argc < 2) return syntax_error("adb %s command", argv[0]);
-
-        std::string cmd = "exec:";
-        cmd += argv[1];
-        argc -= 2;
-        argv += 2;
-        while (argc-- > 0) {
-            cmd += " " + escape_arg(*argv++);
-        }
-
-        std::string error;
-        int fd = adb_connect(cmd, &error);
-        if (fd < 0) {
-            fprintf(stderr, "error: %s\n", error.c_str());
-            return -1;
-        }
-
-        if (exec_in) {
-            copy_to_file(STDIN_FILENO, fd);
-        } else {
-            copy_to_file(fd, STDOUT_FILENO);
-        }
-
-        adb_close(fd);
-        return 0;
-    }
-    else if (!strcmp(argv[0], "kill-server")) {
-        return adb_kill_server() ? 0 : 1;
-    }
-    else if (!strcmp(argv[0], "sideload")) {
-        if (argc != 2) return syntax_error("sideload requires an argument");
-        if (adb_sideload_host(argv[1])) {
-            return 1;
-        } else {
-            return 0;
-        }
-    }
-    else if (!strcmp(argv[0], "tcpip") && argc > 1) {
-        return adb_connect_command(android::base::StringPrintf("tcpip:%s", argv[1]));
-    }
-    else if (!strcmp(argv[0], "remount") ||
-             !strcmp(argv[0], "reboot") ||
-             !strcmp(argv[0], "reboot-bootloader") ||
-             !strcmp(argv[0], "usb") ||
-             !strcmp(argv[0], "disable-verity") ||
-             !strcmp(argv[0], "enable-verity")) {
-        std::string command;
-        if (!strcmp(argv[0], "reboot-bootloader")) {
-            command = "reboot:bootloader";
-        } else if (argc > 1) {
-            command = android::base::StringPrintf("%s:%s", argv[0], argv[1]);
-        } else {
-            command = android::base::StringPrintf("%s:", argv[0]);
-        }
-        return adb_connect_command(command);
-    } else if (!strcmp(argv[0], "root") || !strcmp(argv[0], "unroot")) {
-        return adb_root(argv[0]) ? 0 : 1;
-    } else if (!strcmp(argv[0], "bugreport")) {
-        Bugreport bugreport;
-        return bugreport.DoIt(transport_type, serial, argc, argv);
-    } else if (!strcmp(argv[0], "forward") || !strcmp(argv[0], "reverse")) {
-        bool reverse = !strcmp(argv[0], "reverse");
-        ++argv;
-        --argc;
-        if (argc < 1) return syntax_error("%s requires an argument", argv[0]);
-
-        // Determine the <host-prefix> for this command.
-        std::string host_prefix;
-        if (reverse) {
-            host_prefix = "reverse";
-        } else {
-            if (serial) {
-                host_prefix = android::base::StringPrintf("host-serial:%s", serial);
-            } else if (transport_type == kTransportUsb) {
-                host_prefix = "host-usb";
-            } else if (transport_type == kTransportLocal) {
-                host_prefix = "host-local";
-            } else {
-                host_prefix = "host";
-            }
-        }
-
-        std::string cmd, error;
-        if (strcmp(argv[0], "--list") == 0) {
-            if (argc != 1) return syntax_error("--list doesn't take any arguments");
-            return adb_query_command(host_prefix + ":list-forward");
-        } else if (strcmp(argv[0], "--remove-all") == 0) {
-            if (argc != 1) return syntax_error("--remove-all doesn't take any arguments");
-            cmd = host_prefix + ":killforward-all";
-        } else if (strcmp(argv[0], "--remove") == 0) {
-            // forward --remove <local>
-            if (argc != 2) return syntax_error("--remove requires an argument");
-            cmd = host_prefix + ":killforward:" + argv[1];
-        } else if (strcmp(argv[0], "--no-rebind") == 0) {
-            // forward --no-rebind <local> <remote>
-            if (argc != 3) return syntax_error("--no-rebind takes two arguments");
-            if (forward_targets_are_valid(argv[1], argv[2], &error)) {
-                cmd = host_prefix + ":forward:norebind:" + argv[1] + ";" + argv[2];
-            }
-        } else {
-            // forward <local> <remote>
-            if (argc != 2) return syntax_error("forward takes two arguments");
-            if (forward_targets_are_valid(argv[0], argv[1], &error)) {
-                cmd = host_prefix + ":forward:" + argv[0] + ";" + argv[1];
-            }
-        }
-
-        if (!error.empty()) {
-            fprintf(stderr, "error: %s\n", error.c_str());
-            return 1;
-        }
-
-        int fd = adb_connect(cmd, &error);
-        if (fd < 0 || !adb_status(fd, &error)) {
-            adb_close(fd);
-            fprintf(stderr, "error: %s\n", error.c_str());
-            return 1;
-        }
-
-        // Server or device may optionally return a resolved TCP port number.
-        std::string resolved_port;
-        if (ReadProtocolString(fd, &resolved_port, &error) && !resolved_port.empty()) {
-            printf("%s\n", resolved_port.c_str());
-        }
-
-        ReadOrderlyShutdown(fd);
-        return 0;
-    }
-    /* do_sync_*() commands */
-    else if (!strcmp(argv[0], "ls")) {
-        if (argc != 2) return syntax_error("ls requires an argument");
-        return do_sync_ls(argv[1]) ? 0 : 1;
-    }
-    else if (!strcmp(argv[0], "push")) {
-        bool copy_attrs = false;
-        bool sync = false;
-        std::vector<const char*> srcs;
-        const char* dst = nullptr;
-
-        parse_push_pull_args(&argv[1], argc - 1, &srcs, &dst, &copy_attrs, &sync);
-        if (srcs.empty() || !dst) return syntax_error("push requires an argument");
-        return do_sync_push(srcs, dst, sync) ? 0 : 1;
-    }
-    else if (!strcmp(argv[0], "pull")) {
-        bool copy_attrs = false;
-        std::vector<const char*> srcs;
-        const char* dst = ".";
-
-        parse_push_pull_args(&argv[1], argc - 1, &srcs, &dst, &copy_attrs, nullptr);
-        if (srcs.empty()) return syntax_error("pull requires an argument");
-        return do_sync_pull(srcs, dst, copy_attrs) ? 0 : 1;
-    }
-    else if (!strcmp(argv[0], "install")) {
-        if (argc < 2) return syntax_error("install requires an argument");
-        if (_use_legacy_install()) {
-            return install_app_legacy(transport_type, serial, argc, argv);
-        }
-        return install_app(transport_type, serial, argc, argv);
-    }
-    else if (!strcmp(argv[0], "install-multiple")) {
-        if (argc < 2) return syntax_error("install-multiple requires an argument");
-        return install_multiple_app(transport_type, serial, argc, argv);
-    }
-    else if (!strcmp(argv[0], "uninstall")) {
-        if (argc < 2) return syntax_error("uninstall requires an argument");
-        if (_use_legacy_install()) {
-            return uninstall_app_legacy(transport_type, serial, argc, argv);
-        }
-        return uninstall_app(transport_type, serial, argc, argv);
-    }
-    else if (!strcmp(argv[0], "sync")) {
-        std::string src;
-        bool list_only = false;
-        if (argc < 2) {
-            // No local path was specified.
-            src = "";
-        } else if (argc >= 2 && strcmp(argv[1], "-l") == 0) {
-            list_only = true;
-            if (argc == 3) {
-                src = argv[2];
-            } else {
-                src = "";
-            }
-        } else if (argc == 2) {
-            // A local path or "android"/"data" arg was specified.
-            src = argv[1];
-        } else {
-            return syntax_error("adb sync [-l] [PARTITION]");
-        }
-
-        if (src == "all") src = "";
-
-        if (src != "" &&
-            src != "system" && src != "data" && src != "vendor" && src != "oem") {
-            return syntax_error("don't know how to sync %s partition", src.c_str());
-        }
-
-        std::string system_src_path = product_file("system");
-        std::string data_src_path = product_file("data");
-        std::string vendor_src_path = product_file("vendor");
-        std::string oem_src_path = product_file("oem");
-
-        bool okay = true;
-        if (okay && (src.empty() || src == "system")) {
-            okay = do_sync_sync(system_src_path, "/system", list_only);
-        }
-        if (okay && (src.empty() || src == "vendor") && directory_exists(vendor_src_path)) {
-            okay = do_sync_sync(vendor_src_path, "/vendor", list_only);
-        }
-        if (okay && (src.empty() || src == "oem") && directory_exists(oem_src_path)) {
-            okay = do_sync_sync(oem_src_path, "/oem", list_only);
-        }
-        if (okay && (src.empty() || src == "data")) {
-            okay = do_sync_sync(data_src_path, "/data", list_only);
-        }
-        return okay ? 0 : 1;
-    }
-    /* passthrough commands */
-    else if (!strcmp(argv[0],"get-state") ||
-        !strcmp(argv[0],"get-serialno") ||
-        !strcmp(argv[0],"get-devpath"))
-    {
-        return adb_query_command(format_host_command(argv[0], transport_type, serial));
-    }
-    /* other commands */
-    else if (!strcmp(argv[0],"logcat") || !strcmp(argv[0],"lolcat") || !strcmp(argv[0],"longcat")) {
-        return logcat(transport_type, serial, argc, argv);
-    }
-    else if (!strcmp(argv[0],"ppp")) {
-        return ppp(argc, argv);
-    }
-    else if (!strcmp(argv[0], "start-server")) {
-        std::string error;
-        const int result = adb_connect("host:start-server", &error);
-        if (result < 0) {
-            fprintf(stderr, "error: %s\n", error.c_str());
-        }
-        return result;
-    }
-    else if (!strcmp(argv[0], "backup")) {
-        return backup(argc, argv);
-    }
-    else if (!strcmp(argv[0], "restore")) {
-        return restore(argc, argv);
-    }
-    else if (!strcmp(argv[0], "keygen")) {
-        if (argc != 2) return syntax_error("keygen requires an argument");
-        // Always print key generation information for keygen command.
-        adb_trace_enable(AUTH);
-        return adb_auth_keygen(argv[1]);
-    }
-    else if (!strcmp(argv[0], "jdwp")) {
-        return adb_connect_command("jdwp");
-    }
-    else if (!strcmp(argv[0], "track-jdwp")) {
-        return adb_connect_command("track-jdwp");
-    }
-    else if (!strcmp(argv[0], "track-devices")) {
-        return adb_connect_command("host:track-devices");
-    }
-
-
-    /* "adb /?" is a common idiom under Windows */
-    else if (!strcmp(argv[0], "--help") || !strcmp(argv[0], "help") || !strcmp(argv[0], "/?")) {
-        help();
-        return 0;
-    }
-    else if (!strcmp(argv[0], "--version") || !strcmp(argv[0], "version")) {
-        fprintf(stdout, "%s", adb_version().c_str());
-        return 0;
-    } else if (!strcmp(argv[0], "features")) {
-        // Only list the features common to both the adb client and the device.
-        FeatureSet features;
-        std::string error;
-        if (!adb_get_feature_set(&features, &error)) {
-            fprintf(stderr, "error: %s\n", error.c_str());
-            return 1;
-        }
-
-        for (const std::string& name : features) {
-            if (CanUseFeature(features, name)) {
-                printf("%s\n", name.c_str());
-            }
-        }
-        return 0;
-    } else if (!strcmp(argv[0], "host-features")) {
-        return adb_query_command("host:host-features");
-    } else if (!strcmp(argv[0], "reconnect")) {
-        if (argc == 1) {
-            return adb_query_command(format_host_command(argv[0], transport_type, serial));
-        } else if (argc == 2) {
-            if (!strcmp(argv[1], "device")) {
-                std::string err;
-                adb_connect("reconnect", &err);
-                return 0;
-            } else if (!strcmp(argv[1], "offline")) {
-                std::string err;
-                return adb_query_command("host:reconnect-offline");
-            } else {
-                return syntax_error("adb reconnect [device|offline]");
-            }
-        }
-    }
-
-    syntax_error("unknown command %s", argv[0]);
-    return 1;
-}
-
-static int uninstall_app(TransportType transport, const char* serial, int argc, const char** argv) {
-    // 'adb uninstall' takes the same arguments as 'cmd package uninstall' on device
-    std::string cmd = "cmd package";
-    while (argc-- > 0) {
-        // deny the '-k' option until the remaining data/cache can be removed with adb/UI
-        if (strcmp(*argv, "-k") == 0) {
-            printf(
-                "The -k option uninstalls the application while retaining the data/cache.\n"
-                "At the moment, there is no way to remove the remaining data.\n"
-                "You will have to reinstall the application with the same signature, and fully uninstall it.\n"
-                "If you truly wish to continue, execute 'adb shell cmd package uninstall -k'.\n");
-            return EXIT_FAILURE;
-        }
-        cmd += " " + escape_arg(*argv++);
-    }
-
-    return send_shell_command(transport, serial, cmd, false);
-}
-
-static int install_app(TransportType transport, const char* serial, int argc, const char** argv) {
-    // The last argument must be the APK file
-    const char* file = argv[argc - 1];
-    if (!android::base::EndsWithIgnoreCase(file, ".apk")) {
-        return syntax_error("filename doesn't end .apk: %s", file);
-    }
-
-    struct stat sb;
-    if (stat(file, &sb) == -1) {
-        fprintf(stderr, "adb: failed to stat %s: %s\n", file, strerror(errno));
-        return 1;
-    }
-
-    int localFd = adb_open(file, O_RDONLY);
-    if (localFd < 0) {
-        fprintf(stderr, "adb: failed to open %s: %s\n", file, strerror(errno));
-        return 1;
-    }
-
-    std::string error;
-    std::string cmd = "exec:cmd package";
-
-    // don't copy the APK name, but, copy the rest of the arguments as-is
-    while (argc-- > 1) {
-        cmd += " " + escape_arg(std::string(*argv++));
-    }
-
-    // add size parameter [required for streaming installs]
-    // do last to override any user specified value
-    cmd += " " + android::base::StringPrintf("-S %" PRIu64, static_cast<uint64_t>(sb.st_size));
-
-    int remoteFd = adb_connect(cmd, &error);
-    if (remoteFd < 0) {
-        fprintf(stderr, "adb: connect error for write: %s\n", error.c_str());
-        adb_close(localFd);
-        return 1;
-    }
-
-    char buf[BUFSIZ];
-    copy_to_file(localFd, remoteFd);
-    read_status_line(remoteFd, buf, sizeof(buf));
-
-    adb_close(localFd);
-    adb_close(remoteFd);
-
-    if (!strncmp("Success", buf, 7)) {
-        fputs(buf, stdout);
-        return 0;
-    }
-    fprintf(stderr, "adb: failed to install %s: %s", file, buf);
-    return 1;
-}
-
-static int install_multiple_app(TransportType transport, const char* serial, int argc,
-                                const char** argv)
-{
-    // Find all APK arguments starting at end.
-    // All other arguments passed through verbatim.
-    int first_apk = -1;
-    uint64_t total_size = 0;
-    for (int i = argc - 1; i >= 0; i--) {
-        const char* file = argv[i];
-
-        if (android::base::EndsWithIgnoreCase(file, ".apk")) {
-            struct stat sb;
-            if (stat(file, &sb) != -1) total_size += sb.st_size;
-            first_apk = i;
-        } else {
-            break;
-        }
-    }
-
-    if (first_apk == -1) return syntax_error("need APK file on command line");
-
-    std::string install_cmd;
-    if (_use_legacy_install()) {
-        install_cmd = "exec:pm";
-    } else {
-        install_cmd = "exec:cmd package";
-    }
-
-    std::string cmd = android::base::StringPrintf("%s install-create -S %" PRIu64, install_cmd.c_str(), total_size);
-    for (int i = 1; i < first_apk; i++) {
-        cmd += " " + escape_arg(argv[i]);
-    }
-
-    // Create install session
-    std::string error;
-    int fd = adb_connect(cmd, &error);
-    if (fd < 0) {
-        fprintf(stderr, "adb: connect error for create: %s\n", error.c_str());
-        return EXIT_FAILURE;
-    }
-    char buf[BUFSIZ];
-    read_status_line(fd, buf, sizeof(buf));
-    adb_close(fd);
-
-    int session_id = -1;
-    if (!strncmp("Success", buf, 7)) {
-        char* start = strrchr(buf, '[');
-        char* end = strrchr(buf, ']');
-        if (start && end) {
-            *end = '\0';
-            session_id = strtol(start + 1, NULL, 10);
-        }
-    }
-    if (session_id < 0) {
-        fprintf(stderr, "adb: failed to create session\n");
-        fputs(buf, stderr);
-        return EXIT_FAILURE;
-    }
-
-    // Valid session, now stream the APKs
-    int success = 1;
-    for (int i = first_apk; i < argc; i++) {
-        const char* file = argv[i];
-        struct stat sb;
-        if (stat(file, &sb) == -1) {
-            fprintf(stderr, "adb: failed to stat %s: %s\n", file, strerror(errno));
-            success = 0;
-            goto finalize_session;
-        }
-
-        std::string cmd = android::base::StringPrintf(
-                "%s install-write -S %" PRIu64 " %d %d_%s -",
-                install_cmd.c_str(), static_cast<uint64_t>(sb.st_size), session_id, i,
-                android::base::Basename(file).c_str());
-
-        int localFd = adb_open(file, O_RDONLY);
-        if (localFd < 0) {
-            fprintf(stderr, "adb: failed to open %s: %s\n", file, strerror(errno));
-            success = 0;
-            goto finalize_session;
-        }
-
-        std::string error;
-        int remoteFd = adb_connect(cmd, &error);
-        if (remoteFd < 0) {
-            fprintf(stderr, "adb: connect error for write: %s\n", error.c_str());
-            adb_close(localFd);
-            success = 0;
-            goto finalize_session;
-        }
-
-        copy_to_file(localFd, remoteFd);
-        read_status_line(remoteFd, buf, sizeof(buf));
-
-        adb_close(localFd);
-        adb_close(remoteFd);
-
-        if (strncmp("Success", buf, 7)) {
-            fprintf(stderr, "adb: failed to write %s\n", file);
-            fputs(buf, stderr);
-            success = 0;
-            goto finalize_session;
-        }
-    }
-
-finalize_session:
-    // Commit session if we streamed everything okay; otherwise abandon
-    std::string service =
-            android::base::StringPrintf("%s install-%s %d",
-                                        install_cmd.c_str(), success ? "commit" : "abandon", session_id);
-    fd = adb_connect(service, &error);
-    if (fd < 0) {
-        fprintf(stderr, "adb: connect error for finalize: %s\n", error.c_str());
-        return EXIT_FAILURE;
-    }
-    read_status_line(fd, buf, sizeof(buf));
-    adb_close(fd);
-
-    if (!strncmp("Success", buf, 7)) {
-        fputs(buf, stdout);
-        return 0;
-    }
-    fprintf(stderr, "adb: failed to finalize session\n");
-    fputs(buf, stderr);
-    return EXIT_FAILURE;
-}
-
-static int pm_command(TransportType transport, const char* serial, int argc, const char** argv) {
-    std::string cmd = "pm";
-
-    while (argc-- > 0) {
-        cmd += " " + escape_arg(*argv++);
-    }
-
-    return send_shell_command(transport, serial, cmd, false);
-}
-
-static int uninstall_app_legacy(TransportType transport, const char* serial, int argc, const char** argv) {
-    /* if the user choose the -k option, we refuse to do it until devices are
-       out with the option to uninstall the remaining data somehow (adb/ui) */
-    int i;
-    for (i = 1; i < argc; i++) {
-        if (!strcmp(argv[i], "-k")) {
-            printf(
-                "The -k option uninstalls the application while retaining the data/cache.\n"
-                "At the moment, there is no way to remove the remaining data.\n"
-                "You will have to reinstall the application with the same signature, and fully uninstall it.\n"
-                "If you truly wish to continue, execute 'adb shell pm uninstall -k'\n.");
-            return EXIT_FAILURE;
-        }
-    }
-
-    /* 'adb uninstall' takes the same arguments as 'pm uninstall' on device */
-    return pm_command(transport, serial, argc, argv);
-}
-
-static int delete_file(TransportType transport, const char* serial, const std::string& filename) {
-    std::string cmd = "rm -f " + escape_arg(filename);
-    return send_shell_command(transport, serial, cmd, false);
-}
-
-static int install_app_legacy(TransportType transport, const char* serial, int argc, const char** argv) {
-    static const char *const DATA_DEST = "/data/local/tmp/%s";
-    static const char *const SD_DEST = "/sdcard/tmp/%s";
-    const char* where = DATA_DEST;
-
-    for (int i = 1; i < argc; i++) {
-        if (!strcmp(argv[i], "-s")) {
-            where = SD_DEST;
-        }
-    }
-
-    // Find last APK argument.
-    // All other arguments passed through verbatim.
-    int last_apk = -1;
-    for (int i = argc - 1; i >= 0; i--) {
-        if (android::base::EndsWithIgnoreCase(argv[i], ".apk")) {
-            last_apk = i;
-            break;
-        }
-    }
-
-    if (last_apk == -1) return syntax_error("need APK file on command line");
-
-    int result = -1;
-    std::vector<const char*> apk_file = {argv[last_apk]};
-    std::string apk_dest = android::base::StringPrintf(
-        where, android::base::Basename(argv[last_apk]).c_str());
-    if (!do_sync_push(apk_file, apk_dest.c_str(), false)) goto cleanup_apk;
-    argv[last_apk] = apk_dest.c_str(); /* destination name, not source location */
-    result = pm_command(transport, serial, argc, argv);
-
-cleanup_apk:
-    delete_file(transport, serial, apk_dest);
-    return result;
-}
diff --git a/adb/commandline.h b/adb/commandline.h
deleted file mode 100644
index 9ba69a3..0000000
--- a/adb/commandline.h
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- * Copyright (C) 2016 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 COMMANDLINE_H
-#define COMMANDLINE_H
-
-#include "adb.h"
-
-// Callback used to handle the standard streams (stdout and stderr) sent by the
-// device's upon receiving a command.
-//
-class StandardStreamsCallbackInterface {
-  public:
-    StandardStreamsCallbackInterface() {
-    }
-    // Handles the stdout output from devices supporting the Shell protocol.
-    virtual void OnStdout(const char* buffer, int length) = 0;
-
-    // Handles the stderr output from devices supporting the Shell protocol.
-    virtual void OnStderr(const char* buffer, int length) = 0;
-
-    // Indicates the communication is finished and returns the appropriate error
-    // code.
-    //
-    // |status| has the status code returning by the underlying communication
-    // channels
-    virtual int Done(int status) = 0;
-
-  protected:
-    static void OnStream(std::string* string, FILE* stream, const char* buffer, int length) {
-        if (string != nullptr) {
-            string->append(buffer, length);
-        } else {
-            fwrite(buffer, 1, length, stream);
-            fflush(stream);
-        }
-    }
-
-  private:
-    DISALLOW_COPY_AND_ASSIGN(StandardStreamsCallbackInterface);
-};
-
-// Default implementation that redirects the streams to the equilavent host
-// stream or to a string
-// passed to the constructor.
-class DefaultStandardStreamsCallback : public StandardStreamsCallbackInterface {
-  public:
-    // If |stdout_str| is non-null, OnStdout will append to it.
-    // If |stderr_str| is non-null, OnStderr will append to it.
-    DefaultStandardStreamsCallback(std::string* stdout_str, std::string* stderr_str)
-        : stdout_str_(stdout_str), stderr_str_(stderr_str) {
-    }
-
-    void OnStdout(const char* buffer, int length) {
-        OnStream(stdout_str_, stdout, buffer, length);
-    }
-
-    void OnStderr(const char* buffer, int length) {
-        OnStream(stderr_str_, stderr, buffer, length);
-    }
-
-    int Done(int status) {
-        return status;
-    }
-
-  private:
-    std::string* stdout_str_;
-    std::string* stderr_str_;
-
-    DISALLOW_COPY_AND_ASSIGN(DefaultStandardStreamsCallback);
-};
-
-// Singleton.
-extern DefaultStandardStreamsCallback DEFAULT_STANDARD_STREAMS_CALLBACK;
-
-int adb_commandline(int argc, const char** argv);
-
-// Connects to the device "shell" service with |command| and prints the
-// resulting output.
-// if |callback| is non-null, stdout/stderr output will be handled by it.
-int send_shell_command(TransportType transport_type, const char* serial, const std::string& command,
-                       bool disable_shell_protocol, StandardStreamsCallbackInterface* callback =
-                                                        &DEFAULT_STANDARD_STREAMS_CALLBACK);
-
-#endif  // COMMANDLINE_H
diff --git a/adb/daemon/auth.cpp b/adb/daemon/auth.cpp
new file mode 100644
index 0000000..180df8f
--- /dev/null
+++ b/adb/daemon/auth.cpp
@@ -0,0 +1,227 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+#define TRACE_TAG AUTH
+
+#include "adb.h"
+#include "adb_auth.h"
+#include "fdevent.h"
+#include "sysdeps.h"
+#include "transport.h"
+
+#include <resolv.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <memory>
+
+#include <android-base/file.h>
+#include <android-base/strings.h>
+#include <crypto_utils/android_pubkey.h>
+#include <openssl/obj_mac.h>
+#include <openssl/rsa.h>
+#include <openssl/sha.h>
+
+static fdevent* listener_fde = nullptr;
+static fdevent* framework_fde = nullptr;
+static int framework_fd = -1;
+
+static void usb_disconnected(void* unused, atransport* t);
+static struct adisconnect usb_disconnect = { usb_disconnected, nullptr};
+static atransport* usb_transport;
+static bool needs_retry = false;
+
+bool auth_required = true;
+
+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) {
+        if (access(path, R_OK) == 0) {
+            LOG(INFO) << "Loading keys from " << path;
+
+            std::string content;
+            if (!android::base::ReadFileToString(path, &content)) {
+                PLOG(ERROR) << "Couldn't read " << path;
+                continue;
+            }
+
+            for (const auto& line : android::base::Split(content, "\n")) {
+                // TODO: do we really have to support both ' ' and '\t'?
+                char* sep = strpbrk(const_cast<char*>(line.c_str()), " \t");
+                if (sep) *sep = '\0';
+
+                // b64_pton requires one additional byte in the target buffer for
+                // decoding to succeed. See http://b/28035006 for details.
+                uint8_t keybuf[ANDROID_PUBKEY_ENCODED_SIZE + 1];
+                if (__b64_pton(line.c_str(), keybuf, sizeof(keybuf)) != ANDROID_PUBKEY_ENCODED_SIZE) {
+                    LOG(ERROR) << "Invalid base64 key " << line.c_str() << " in " << path;
+                    continue;
+                }
+
+                RSA* key = nullptr;
+                if (!android_pubkey_decode(keybuf, ANDROID_PUBKEY_ENCODED_SIZE, &key)) {
+                    LOG(ERROR) << "Failed to parse key " << line.c_str() << " in " << path;
+                    continue;
+                }
+
+                bool verified =
+                    (RSA_verify(NID_sha1, reinterpret_cast<const uint8_t*>(token), token_size,
+                                reinterpret_cast<const uint8_t*>(sig.c_str()), sig.size(),
+                                key) == 1);
+                RSA_free(key);
+                if (verified) return true;
+            }
+        }
+    }
+    return false;
+}
+
+static bool adbd_auth_generate_token(void* token, size_t token_size) {
+    FILE* fp = fopen("/dev/urandom", "re");
+    if (!fp) return false;
+    bool okay = (fread(token, token_size, 1, fp) == 1);
+    fclose(fp);
+    return okay;
+}
+
+static void usb_disconnected(void* unused, atransport* t) {
+    LOG(INFO) << "USB disconnect";
+    usb_transport = nullptr;
+    needs_retry = false;
+}
+
+static void framework_disconnected() {
+    LOG(INFO) << "Framework disconnect";
+    if (framework_fde) {
+        fdevent_destroy(framework_fde);
+        framework_fd = -1;
+    }
+}
+
+static void adbd_auth_event(int fd, unsigned events, void*) {
+    if (events & FDE_READ) {
+        char response[2];
+        int ret = unix_read(fd, response, sizeof(response));
+        if (ret <= 0) {
+            framework_disconnected();
+        } else if (ret == 2 && response[0] == 'O' && response[1] == 'K') {
+            if (usb_transport) {
+                adbd_auth_verified(usb_transport);
+            }
+        }
+    }
+}
+
+void adbd_auth_confirm_key(const char* key, size_t len, atransport* t) {
+    if (!usb_transport) {
+        usb_transport = t;
+        t->AddDisconnect(&usb_disconnect);
+    }
+
+    if (framework_fd < 0) {
+        LOG(ERROR) << "Client not connected";
+        needs_retry = true;
+        return;
+    }
+
+    if (key[len - 1] != '\0') {
+        LOG(ERROR) << "Key must be a null-terminated string";
+        return;
+    }
+
+    char msg[MAX_PAYLOAD_V1];
+    int msg_len = snprintf(msg, sizeof(msg), "PK%s", key);
+    if (msg_len >= static_cast<int>(sizeof(msg))) {
+        LOG(ERROR) << "Key too long (" << msg_len << ")";
+        return;
+    }
+    LOG(DEBUG) << "Sending '" << msg << "'";
+
+    if (unix_write(framework_fd, msg, msg_len) == -1) {
+        PLOG(ERROR) << "Failed to write PK";
+        return;
+    }
+}
+
+static void adbd_auth_listener(int fd, unsigned events, void* data) {
+    int s = adb_socket_accept(fd, nullptr, nullptr);
+    if (s < 0) {
+        PLOG(ERROR) << "Failed to accept";
+        return;
+    }
+
+    if (framework_fd >= 0) {
+        LOG(WARNING) << "adb received framework auth socket connection again";
+        framework_disconnected();
+    }
+
+    framework_fd = s;
+    framework_fde = fdevent_create(framework_fd, adbd_auth_event, nullptr);
+    fdevent_add(framework_fde, FDE_READ);
+
+    if (needs_retry) {
+        needs_retry = false;
+        send_auth_request(usb_transport);
+    }
+}
+
+void adbd_cloexec_auth_socket() {
+    int fd = android_get_control_socket("adbd");
+    if (fd == -1) {
+        PLOG(ERROR) << "Failed to get adbd socket";
+        return;
+    }
+    fcntl(fd, F_SETFD, FD_CLOEXEC);
+}
+
+void adbd_auth_init(void) {
+    int fd = android_get_control_socket("adbd");
+    if (fd == -1) {
+        PLOG(ERROR) << "Failed to get adbd socket";
+        return;
+    }
+
+    if (listen(fd, 4) == -1) {
+        PLOG(ERROR) << "Failed to listen on '" << fd << "'";
+        return;
+    }
+
+    listener_fde = fdevent_create(fd, adbd_auth_listener, nullptr);
+    fdevent_add(listener_fde, FDE_READ);
+}
+
+void send_auth_request(atransport* t) {
+    LOG(INFO) << "Calling send_auth_request...";
+
+    if (!adbd_auth_generate_token(t->token, sizeof(t->token))) {
+        PLOG(ERROR) << "Error generating token";
+        return;
+    }
+
+    apacket* p = get_apacket();
+    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);
+}
+
+void adbd_auth_verified(atransport* t) {
+    LOG(INFO) << "adb client authorized";
+    handle_online(t);
+    send_connect(t);
+}
diff --git a/adb/daemon/file_sync_service.cpp b/adb/daemon/file_sync_service.cpp
new file mode 100644
index 0000000..8c39a20
--- /dev/null
+++ b/adb/daemon/file_sync_service.cpp
@@ -0,0 +1,537 @@
+/*
+ * Copyright (C) 2007 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.
+ */
+
+#define TRACE_TAG SYNC
+
+#include "daemon/file_sync_service.h"
+
+#include "sysdeps.h"
+
+#include <dirent.h>
+#include <errno.h>
+#include <linux/xattr.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/xattr.h>
+#include <unistd.h>
+#include <utime.h>
+
+#include <android-base/file.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+#include <private/android_filesystem_config.h>
+#include <private/android_logger.h>
+#include <selinux/android.h>
+
+#include "adb.h"
+#include "adb_io.h"
+#include "adb_trace.h"
+#include "adb_utils.h"
+#include "file_sync_protocol.h"
+#include "security_log_tags.h"
+#include "sysdeps/errno.h"
+
+using android::base::StringPrintf;
+
+static bool should_use_fs_config(const std::string& path) {
+    // TODO: use fs_config to configure permissions on /data too.
+    return !android::base::StartsWith(path, "/data/");
+}
+
+static bool update_capabilities(const char* path, uint64_t capabilities) {
+    if (capabilities == 0) {
+        // Ensure we clean up in case the capabilities weren't 0 in the past.
+        removexattr(path, XATTR_NAME_CAPS);
+        return true;
+    }
+
+    vfs_cap_data cap_data = {};
+    cap_data.magic_etc = VFS_CAP_REVISION_2 | VFS_CAP_FLAGS_EFFECTIVE;
+    cap_data.data[0].permitted = (capabilities & 0xffffffff);
+    cap_data.data[0].inheritable = 0;
+    cap_data.data[1].permitted = (capabilities >> 32);
+    cap_data.data[1].inheritable = 0;
+    return setxattr(path, XATTR_NAME_CAPS, &cap_data, sizeof(cap_data), 0) != -1;
+}
+
+static bool secure_mkdirs(const std::string& path) {
+    if (path[0] != '/') return false;
+
+    std::vector<std::string> path_components = android::base::Split(path, "/");
+    std::string partial_path;
+    for (const auto& path_component : path_components) {
+        uid_t uid = -1;
+        gid_t gid = -1;
+        unsigned int mode = 0775;
+        uint64_t capabilities = 0;
+
+        if (path_component.empty()) {
+            continue;
+        }
+
+        if (partial_path.empty() || partial_path.back() != OS_PATH_SEPARATOR) {
+            partial_path += OS_PATH_SEPARATOR;
+        }
+        partial_path += path_component;
+
+        if (should_use_fs_config(partial_path)) {
+            fs_config(partial_path.c_str(), 1, nullptr, &uid, &gid, &mode, &capabilities);
+        }
+        if (adb_mkdir(partial_path.c_str(), mode) == -1) {
+            if (errno != EEXIST) {
+                return false;
+            }
+        } else {
+            if (chown(partial_path.c_str(), uid, gid) == -1) return false;
+
+            // Not all filesystems support setting SELinux labels. http://b/23530370.
+            selinux_android_restorecon(partial_path.c_str(), 0);
+
+            if (!update_capabilities(partial_path.c_str(), capabilities)) return false;
+        }
+    }
+    return true;
+}
+
+static bool do_lstat_v1(int s, const char* path) {
+    syncmsg msg = {};
+    msg.stat_v1.id = ID_LSTAT_V1;
+
+    struct stat st = {};
+    lstat(path, &st);
+    msg.stat_v1.mode = st.st_mode;
+    msg.stat_v1.size = st.st_size;
+    msg.stat_v1.time = st.st_mtime;
+    return WriteFdExactly(s, &msg.stat_v1, sizeof(msg.stat_v1));
+}
+
+static bool do_stat_v2(int s, uint32_t id, const char* path) {
+    syncmsg msg = {};
+    msg.stat_v2.id = id;
+
+    decltype(&stat) stat_fn;
+    if (id == ID_STAT_V2) {
+        stat_fn = stat;
+    } else {
+        stat_fn = lstat;
+    }
+
+    struct stat st = {};
+    int rc = stat_fn(path, &st);
+    if (rc == -1) {
+        msg.stat_v2.error = errno_to_wire(errno);
+    } else {
+        msg.stat_v2.dev = st.st_dev;
+        msg.stat_v2.ino = st.st_ino;
+        msg.stat_v2.mode = st.st_mode;
+        msg.stat_v2.nlink = st.st_nlink;
+        msg.stat_v2.uid = st.st_uid;
+        msg.stat_v2.gid = st.st_gid;
+        msg.stat_v2.size = st.st_size;
+        msg.stat_v2.atime = st.st_atime;
+        msg.stat_v2.mtime = st.st_mtime;
+        msg.stat_v2.ctime = st.st_ctime;
+    }
+
+    return WriteFdExactly(s, &msg.stat_v2, sizeof(msg.stat_v2));
+}
+
+static bool do_list(int s, const char* path) {
+    dirent* de;
+
+    syncmsg msg;
+    msg.dent.id = ID_DENT;
+
+    std::unique_ptr<DIR, int(*)(DIR*)> d(opendir(path), closedir);
+    if (!d) goto done;
+
+    while ((de = readdir(d.get()))) {
+        std::string filename(StringPrintf("%s/%s", path, de->d_name));
+
+        struct stat st;
+        if (lstat(filename.c_str(), &st) == 0) {
+            size_t d_name_length = strlen(de->d_name);
+            msg.dent.mode = st.st_mode;
+            msg.dent.size = st.st_size;
+            msg.dent.time = st.st_mtime;
+            msg.dent.namelen = d_name_length;
+
+            if (!WriteFdExactly(s, &msg.dent, sizeof(msg.dent)) ||
+                    !WriteFdExactly(s, de->d_name, d_name_length)) {
+                return false;
+            }
+        }
+    }
+
+done:
+    msg.dent.id = ID_DONE;
+    msg.dent.mode = 0;
+    msg.dent.size = 0;
+    msg.dent.time = 0;
+    msg.dent.namelen = 0;
+    return WriteFdExactly(s, &msg.dent, sizeof(msg.dent));
+}
+
+// Make sure that SendFail from adb_io.cpp isn't accidentally used in this file.
+#pragma GCC poison SendFail
+
+static bool SendSyncFail(int fd, const std::string& reason) {
+    D("sync: failure: %s", reason.c_str());
+
+    syncmsg msg;
+    msg.data.id = ID_FAIL;
+    msg.data.size = reason.size();
+    return WriteFdExactly(fd, &msg.data, sizeof(msg.data)) && WriteFdExactly(fd, reason);
+}
+
+static bool SendSyncFailErrno(int fd, const std::string& reason) {
+    return SendSyncFail(fd, StringPrintf("%s: %s", reason.c_str(), strerror(errno)));
+}
+
+static bool handle_send_file(int s, const char* path, uid_t uid, gid_t gid, uint64_t capabilities,
+                             mode_t mode, std::vector<char>& buffer, bool do_unlink) {
+    syncmsg msg;
+    unsigned int timestamp = 0;
+
+    __android_log_security_bswrite(SEC_TAG_ADB_SEND_FILE, path);
+
+    int fd = adb_open_mode(path, O_WRONLY | O_CREAT | O_EXCL | O_CLOEXEC, mode);
+
+    if (posix_fadvise(fd, 0, 0, POSIX_FADV_SEQUENTIAL | POSIX_FADV_NOREUSE | POSIX_FADV_WILLNEED) <
+        0) {
+        D("[ Failed to fadvise: %d ]", errno);
+    }
+
+    if (fd < 0 && errno == ENOENT) {
+        if (!secure_mkdirs(android::base::Dirname(path))) {
+            SendSyncFailErrno(s, "secure_mkdirs failed");
+            goto fail;
+        }
+        fd = adb_open_mode(path, O_WRONLY | O_CREAT | O_EXCL | O_CLOEXEC, mode);
+    }
+    if (fd < 0 && errno == EEXIST) {
+        fd = adb_open_mode(path, O_WRONLY | O_CLOEXEC, mode);
+    }
+    if (fd < 0) {
+        SendSyncFailErrno(s, "couldn't create file");
+        goto fail;
+    } else {
+        if (fchown(fd, uid, gid) == -1) {
+            SendSyncFailErrno(s, "fchown failed");
+            goto fail;
+        }
+
+        // Not all filesystems support setting SELinux labels. http://b/23530370.
+        selinux_android_restorecon(path, 0);
+
+        // fchown clears the setuid bit - restore it if present.
+        // Ignore the result of calling fchmod. It's not supported
+        // by all filesystems, so we don't check for success. b/12441485
+        fchmod(fd, mode);
+    }
+
+    while (true) {
+        if (!ReadFdExactly(s, &msg.data, sizeof(msg.data))) goto fail;
+
+        if (msg.data.id != ID_DATA) {
+            if (msg.data.id == ID_DONE) {
+                timestamp = msg.data.size;
+                break;
+            }
+            SendSyncFail(s, "invalid data message");
+            goto abort;
+        }
+
+        if (msg.data.size > buffer.size()) {  // TODO: resize buffer?
+            SendSyncFail(s, "oversize data message");
+            goto abort;
+        }
+
+        if (!ReadFdExactly(s, &buffer[0], msg.data.size)) goto abort;
+
+        if (!WriteFdExactly(fd, &buffer[0], msg.data.size)) {
+            SendSyncFailErrno(s, "write failed");
+            goto fail;
+        }
+    }
+
+    adb_close(fd);
+
+    if (!update_capabilities(path, capabilities)) {
+        SendSyncFailErrno(s, "update_capabilities failed");
+        goto fail;
+    }
+
+    utimbuf u;
+    u.actime = timestamp;
+    u.modtime = timestamp;
+    utime(path, &u);
+
+    msg.status.id = ID_OKAY;
+    msg.status.msglen = 0;
+    return WriteFdExactly(s, &msg.status, sizeof(msg.status));
+
+fail:
+    // If there's a problem on the device, we'll send an ID_FAIL message and
+    // close the socket. Unfortunately the kernel will sometimes throw that
+    // data away if the other end keeps writing without reading (which is
+    // the case with old versions of adb). To maintain compatibility, keep
+    // reading and throwing away ID_DATA packets until the other side notices
+    // that we've reported an error.
+    while (true) {
+        if (!ReadFdExactly(s, &msg.data, sizeof(msg.data))) break;
+
+        if (msg.data.id == ID_DONE) {
+            break;
+        } else if (msg.data.id != ID_DATA) {
+            char id[5];
+            memcpy(id, &msg.data.id, sizeof(msg.data.id));
+            id[4] = '\0';
+            D("handle_send_fail received unexpected id '%s' during failure", id);
+            break;
+        }
+
+        if (msg.data.size > buffer.size()) {
+            D("handle_send_fail received oversized packet of length '%u' during failure",
+              msg.data.size);
+            break;
+        }
+
+        if (!ReadFdExactly(s, &buffer[0], msg.data.size)) break;
+    }
+
+abort:
+    if (fd >= 0) adb_close(fd);
+    if (do_unlink) adb_unlink(path);
+    return false;
+}
+
+#if defined(_WIN32)
+extern bool handle_send_link(int s, const std::string& path, std::vector<char>& buffer) __attribute__((error("no symlinks on Windows")));
+#else
+static bool handle_send_link(int s, const std::string& path, std::vector<char>& buffer) {
+    syncmsg msg;
+    unsigned int len;
+    int ret;
+
+    if (!ReadFdExactly(s, &msg.data, sizeof(msg.data))) return false;
+
+    if (msg.data.id != ID_DATA) {
+        SendSyncFail(s, "invalid data message: expected ID_DATA");
+        return false;
+    }
+
+    len = msg.data.size;
+    if (len > buffer.size()) { // TODO: resize buffer?
+        SendSyncFail(s, "oversize data message");
+        return false;
+    }
+    if (!ReadFdExactly(s, &buffer[0], len)) return false;
+
+    ret = symlink(&buffer[0], path.c_str());
+    if (ret && errno == ENOENT) {
+        if (!secure_mkdirs(android::base::Dirname(path))) {
+            SendSyncFailErrno(s, "secure_mkdirs failed");
+            return false;
+        }
+        ret = symlink(&buffer[0], path.c_str());
+    }
+    if (ret) {
+        SendSyncFailErrno(s, "symlink failed");
+        return false;
+    }
+
+    if (!ReadFdExactly(s, &msg.data, sizeof(msg.data))) return false;
+
+    if (msg.data.id == ID_DONE) {
+        msg.status.id = ID_OKAY;
+        msg.status.msglen = 0;
+        if (!WriteFdExactly(s, &msg.status, sizeof(msg.status))) return false;
+    } else {
+        SendSyncFail(s, "invalid data message: expected ID_DONE");
+        return false;
+    }
+
+    return true;
+}
+#endif
+
+static bool do_send(int s, const std::string& spec, std::vector<char>& buffer) {
+    // 'spec' is of the form "/some/path,0755". Break it up.
+    size_t comma = spec.find_last_of(',');
+    if (comma == std::string::npos) {
+        SendSyncFail(s, "missing , in ID_SEND");
+        return false;
+    }
+
+    std::string path = spec.substr(0, comma);
+
+    errno = 0;
+    mode_t mode = strtoul(spec.substr(comma + 1).c_str(), nullptr, 0);
+    if (errno != 0) {
+        SendSyncFail(s, "bad mode");
+        return false;
+    }
+
+    // Don't delete files before copying if they are not "regular" or symlinks.
+    struct stat st;
+    bool do_unlink = (lstat(path.c_str(), &st) == -1) || S_ISREG(st.st_mode) || S_ISLNK(st.st_mode);
+    if (do_unlink) {
+        adb_unlink(path.c_str());
+    }
+
+    if (S_ISLNK(mode)) {
+        return handle_send_link(s, path.c_str(), buffer);
+    }
+
+    // Copy user permission bits to "group" and "other" permissions.
+    mode &= 0777;
+    mode |= ((mode >> 3) & 0070);
+    mode |= ((mode >> 3) & 0007);
+
+    uid_t uid = -1;
+    gid_t gid = -1;
+    uint64_t capabilities = 0;
+    if (should_use_fs_config(path)) {
+        unsigned int broken_api_hack = mode;
+        fs_config(path.c_str(), 0, nullptr, &uid, &gid, &broken_api_hack, &capabilities);
+        mode = broken_api_hack;
+    }
+    return handle_send_file(s, path.c_str(), uid, gid, capabilities, mode, buffer, do_unlink);
+}
+
+static bool do_recv(int s, const char* path, std::vector<char>& buffer) {
+    __android_log_security_bswrite(SEC_TAG_ADB_RECV_FILE, path);
+
+    int fd = adb_open(path, O_RDONLY | O_CLOEXEC);
+    if (fd < 0) {
+        SendSyncFailErrno(s, "open failed");
+        return false;
+    }
+
+    if (posix_fadvise(fd, 0, 0, POSIX_FADV_SEQUENTIAL | POSIX_FADV_NOREUSE) < 0) {
+        D("[ Failed to fadvise: %d ]", errno);
+    }
+
+    syncmsg msg;
+    msg.data.id = ID_DATA;
+    while (true) {
+        int r = adb_read(fd, &buffer[0], buffer.size() - sizeof(msg.data));
+        if (r <= 0) {
+            if (r == 0) break;
+            SendSyncFailErrno(s, "read failed");
+            adb_close(fd);
+            return false;
+        }
+        msg.data.size = r;
+        if (!WriteFdExactly(s, &msg.data, sizeof(msg.data)) || !WriteFdExactly(s, &buffer[0], r)) {
+            adb_close(fd);
+            return false;
+        }
+    }
+
+    adb_close(fd);
+
+    msg.data.id = ID_DONE;
+    msg.data.size = 0;
+    return WriteFdExactly(s, &msg.data, sizeof(msg.data));
+}
+
+static const char* sync_id_to_name(uint32_t id) {
+  switch (id) {
+    case ID_LSTAT_V1:
+      return "lstat_v1";
+    case ID_LSTAT_V2:
+      return "lstat_v2";
+    case ID_STAT_V2:
+      return "stat_v2";
+    case ID_LIST:
+      return "list";
+    case ID_SEND:
+      return "send";
+    case ID_RECV:
+      return "recv";
+    case ID_QUIT:
+        return "quit";
+    default:
+        return "???";
+  }
+}
+
+static bool handle_sync_command(int fd, std::vector<char>& buffer) {
+    D("sync: waiting for request");
+
+    ATRACE_CALL();
+    SyncRequest request;
+    if (!ReadFdExactly(fd, &request, sizeof(request))) {
+        SendSyncFail(fd, "command read failure");
+        return false;
+    }
+    size_t path_length = request.path_length;
+    if (path_length > 1024) {
+        SendSyncFail(fd, "path too long");
+        return false;
+    }
+    char name[1025];
+    if (!ReadFdExactly(fd, name, path_length)) {
+        SendSyncFail(fd, "filename read failure");
+        return false;
+    }
+    name[path_length] = 0;
+
+    std::string id_name = sync_id_to_name(request.id);
+    std::string trace_name = StringPrintf("%s(%s)", id_name.c_str(), name);
+    ATRACE_NAME(trace_name.c_str());
+
+    D("sync: %s('%s')", id_name.c_str(), name);
+    switch (request.id) {
+        case ID_LSTAT_V1:
+            if (!do_lstat_v1(fd, name)) return false;
+            break;
+        case ID_LSTAT_V2:
+        case ID_STAT_V2:
+            if (!do_stat_v2(fd, request.id, name)) return false;
+            break;
+        case ID_LIST:
+            if (!do_list(fd, name)) return false;
+            break;
+        case ID_SEND:
+            if (!do_send(fd, name, buffer)) return false;
+            break;
+        case ID_RECV:
+            if (!do_recv(fd, name, buffer)) return false;
+            break;
+        case ID_QUIT:
+            return false;
+        default:
+            SendSyncFail(fd, StringPrintf("unknown command %08x", request.id));
+            return false;
+    }
+
+    return true;
+}
+
+void file_sync_service(unique_fd fd) {
+    std::vector<char> buffer(SYNC_DATA_MAX);
+
+    while (handle_sync_command(fd.get(), buffer)) {
+    }
+
+    D("sync: done");
+}
diff --git a/adb/daemon/file_sync_service.h b/adb/daemon/file_sync_service.h
new file mode 100644
index 0000000..f300e7b
--- /dev/null
+++ b/adb/daemon/file_sync_service.h
@@ -0,0 +1,21 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include "adb_unique_fd.h"
+
+void file_sync_service(unique_fd fd);
diff --git a/adb/daemon/framebuffer_service.cpp b/adb/daemon/framebuffer_service.cpp
new file mode 100644
index 0000000..8d28c49
--- /dev/null
+++ b/adb/daemon/framebuffer_service.cpp
@@ -0,0 +1,187 @@
+/*
+ * Copyright (C) 2007 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 "framebuffer_service.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <linux/fb.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include "sysdeps.h"
+
+#include "adb.h"
+#include "adb_io.h"
+#include "fdevent.h"
+
+/* TODO:
+** - sync with vsync to avoid tearing
+*/
+/* This version number defines the format of the fbinfo struct.
+   It must match versioning in ddms where this data is consumed. */
+#define DDMS_RAWIMAGE_VERSION 2
+struct fbinfo {
+    unsigned int version;
+    unsigned int bpp;
+    unsigned int colorSpace;
+    unsigned int size;
+    unsigned int width;
+    unsigned int height;
+    unsigned int red_offset;
+    unsigned int red_length;
+    unsigned int blue_offset;
+    unsigned int blue_length;
+    unsigned int green_offset;
+    unsigned int green_length;
+    unsigned int alpha_offset;
+    unsigned int alpha_length;
+} __attribute__((packed));
+
+void framebuffer_service(unique_fd fd) {
+    struct fbinfo fbinfo;
+    unsigned int i, bsize;
+    char buf[640];
+    int fd_screencap;
+    int w, h, f, c;
+    int fds[2];
+    pid_t pid;
+
+    if (pipe2(fds, O_CLOEXEC) < 0) return;
+
+    pid = fork();
+    if (pid < 0) goto done;
+
+    if (pid == 0) {
+        dup2(fds[1], STDOUT_FILENO);
+        adb_close(fds[0]);
+        adb_close(fds[1]);
+        const char* command = "screencap";
+        const char *args[2] = {command, nullptr};
+        execvp(command, (char**)args);
+        exit(1);
+    }
+
+    adb_close(fds[1]);
+    fd_screencap = fds[0];
+
+    /* read w, h, format & color space */
+    if(!ReadFdExactly(fd_screencap, &w, 4)) goto done;
+    if(!ReadFdExactly(fd_screencap, &h, 4)) goto done;
+    if(!ReadFdExactly(fd_screencap, &f, 4)) goto done;
+    if(!ReadFdExactly(fd_screencap, &c, 4)) goto done;
+
+    fbinfo.version = DDMS_RAWIMAGE_VERSION;
+    fbinfo.colorSpace = c;
+    /* see hardware/hardware.h */
+    switch (f) {
+        case 1: /* RGBA_8888 */
+            fbinfo.bpp = 32;
+            fbinfo.size = w * h * 4;
+            fbinfo.width = w;
+            fbinfo.height = h;
+            fbinfo.red_offset = 0;
+            fbinfo.red_length = 8;
+            fbinfo.green_offset = 8;
+            fbinfo.green_length = 8;
+            fbinfo.blue_offset = 16;
+            fbinfo.blue_length = 8;
+            fbinfo.alpha_offset = 24;
+            fbinfo.alpha_length = 8;
+            break;
+        case 2: /* RGBX_8888 */
+            fbinfo.bpp = 32;
+            fbinfo.size = w * h * 4;
+            fbinfo.width = w;
+            fbinfo.height = h;
+            fbinfo.red_offset = 0;
+            fbinfo.red_length = 8;
+            fbinfo.green_offset = 8;
+            fbinfo.green_length = 8;
+            fbinfo.blue_offset = 16;
+            fbinfo.blue_length = 8;
+            fbinfo.alpha_offset = 24;
+            fbinfo.alpha_length = 0;
+            break;
+        case 3: /* RGB_888 */
+            fbinfo.bpp = 24;
+            fbinfo.size = w * h * 3;
+            fbinfo.width = w;
+            fbinfo.height = h;
+            fbinfo.red_offset = 0;
+            fbinfo.red_length = 8;
+            fbinfo.green_offset = 8;
+            fbinfo.green_length = 8;
+            fbinfo.blue_offset = 16;
+            fbinfo.blue_length = 8;
+            fbinfo.alpha_offset = 24;
+            fbinfo.alpha_length = 0;
+            break;
+        case 4: /* RGB_565 */
+            fbinfo.bpp = 16;
+            fbinfo.size = w * h * 2;
+            fbinfo.width = w;
+            fbinfo.height = h;
+            fbinfo.red_offset = 11;
+            fbinfo.red_length = 5;
+            fbinfo.green_offset = 5;
+            fbinfo.green_length = 6;
+            fbinfo.blue_offset = 0;
+            fbinfo.blue_length = 5;
+            fbinfo.alpha_offset = 0;
+            fbinfo.alpha_length = 0;
+            break;
+        case 5: /* BGRA_8888 */
+            fbinfo.bpp = 32;
+            fbinfo.size = w * h * 4;
+            fbinfo.width = w;
+            fbinfo.height = h;
+            fbinfo.red_offset = 16;
+            fbinfo.red_length = 8;
+            fbinfo.green_offset = 8;
+            fbinfo.green_length = 8;
+            fbinfo.blue_offset = 0;
+            fbinfo.blue_length = 8;
+            fbinfo.alpha_offset = 24;
+            fbinfo.alpha_length = 8;
+           break;
+        default:
+            goto done;
+    }
+
+    /* write header */
+    if (!WriteFdExactly(fd.get(), &fbinfo, sizeof(fbinfo))) goto done;
+
+    /* write data */
+    for(i = 0; i < fbinfo.size; i += bsize) {
+      bsize = sizeof(buf);
+      if (i + bsize > fbinfo.size)
+        bsize = fbinfo.size - i;
+      if(!ReadFdExactly(fd_screencap, buf, bsize)) goto done;
+      if (!WriteFdExactly(fd.get(), buf, bsize)) goto done;
+    }
+
+done:
+    adb_close(fds[0]);
+
+    TEMP_FAILURE_RETRY(waitpid(pid, nullptr, 0));
+}
diff --git a/adb/daemon/framebuffer_service.h b/adb/daemon/framebuffer_service.h
new file mode 100644
index 0000000..264da59
--- /dev/null
+++ b/adb/daemon/framebuffer_service.h
@@ -0,0 +1,21 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include "adb_unique_fd.h"
+
+void framebuffer_service(unique_fd fd);
diff --git a/adb/daemon/include/adbd/usb.h b/adb/daemon/include/adbd/usb.h
new file mode 100644
index 0000000..7905d9d
--- /dev/null
+++ b/adb/daemon/include/adbd/usb.h
@@ -0,0 +1,63 @@
+#pragma once
+
+/*
+ * Copyright (C) 2017 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 <atomic>
+#include <condition_variable>
+#include <mutex>
+#include <vector>
+
+#include <asyncio/AsyncIO.h>
+
+struct aio_block {
+    std::vector<struct iocb> iocb;
+    std::vector<struct iocb*> iocbs;
+    std::vector<struct io_event> events;
+    aio_context_t ctx;
+    int num_submitted;
+    int fd;
+};
+
+struct usb_handle {
+    usb_handle() : kicked(false) {
+    }
+
+    std::condition_variable notify;
+    std::mutex lock;
+    std::atomic<bool> kicked;
+    bool open_new_connection = true;
+
+    int (*write)(usb_handle* h, const void* data, int len);
+    int (*read)(usb_handle* h, void* data, int len);
+    void (*kick)(usb_handle* h);
+    void (*close)(usb_handle* h);
+
+    // FunctionFS
+    int control = -1;
+    int bulk_out = -1; /* "out" from the host's perspective => source for adbd */
+    int bulk_in = -1;  /* "in" from the host's perspective => sink for adbd */
+
+    // Access to these blocks is very not thread safe. Have one block for both the
+    // read and write threads.
+    struct aio_block read_aiob;
+    struct aio_block write_aiob;
+
+    bool reads_zero_packets;
+    size_t io_size;
+};
+
+usb_handle *create_usb_handle(unsigned num_bufs, unsigned io_size);
diff --git a/adb/daemon/jdwp_service.cpp b/adb/daemon/jdwp_service.cpp
new file mode 100644
index 0000000..b40faee
--- /dev/null
+++ b/adb/daemon/jdwp_service.cpp
@@ -0,0 +1,588 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+
+#if !ADB_HOST
+
+#define TRACE_TAG JDWP
+
+#include "sysdeps.h"
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <unistd.h>
+
+#include <list>
+#include <memory>
+#include <vector>
+
+#include "adb.h"
+#include "adb_io.h"
+#include "adb_unique_fd.h"
+#include "adb_utils.h"
+
+/* here's how these things work.
+
+   when adbd starts, it creates a unix server socket
+   named @jdwp-control (@ is a shortcut for "first byte is zero"
+   to use the private namespace instead of the file system)
+
+   when a new JDWP daemon thread starts in a new VM process, it creates
+   a connection to @jdwp-control to announce its availability.
+
+
+     JDWP thread                             @jdwp-control
+         |                                         |
+         |------------------------------->         |
+         | hello I'm in process <pid>              |
+         |                                         |
+         |                                         |
+
+    the connection is kept alive. it will be closed automatically if
+    the JDWP process terminates (this allows adbd to detect dead
+    processes).
+
+    adbd thus maintains a list of "active" JDWP processes. it can send
+    its content to clients through the "device:debug-ports" service,
+    or even updates through the "device:track-debug-ports" service.
+
+    when a debugger wants to connect, it simply runs the command
+    equivalent to  "adb forward tcp:<hostport> jdwp:<pid>"
+
+    "jdwp:<pid>" is a new forward destination format used to target
+    a given JDWP process on the device. when sutch a request arrives,
+    adbd does the following:
+
+      - first, it calls socketpair() to create a pair of equivalent
+        sockets.
+
+      - it attaches the first socket in the pair to a local socket
+        which is itself attached to the transport's remote socket:
+
+
+      - it sends the file descriptor of the second socket directly
+        to the JDWP process with the help of sendmsg()
+
+
+     JDWP thread                             @jdwp-control
+         |                                         |
+         |                  <----------------------|
+         |           OK, try this file descriptor  |
+         |                                         |
+         |                                         |
+
+   then, the JDWP thread uses this new socket descriptor as its
+   pass-through connection to the debugger (and receives the
+   JDWP-Handshake message, answers to it, etc...)
+
+   this gives the following graphics:
+                    ____________________________________
+                   |                                    |
+                   |          ADB Server (host)         |
+                   |                                    |
+        Debugger <---> LocalSocket <----> RemoteSocket  |
+                   |                           ^^       |
+                   |___________________________||_______|
+                                               ||
+                                     Transport ||
+           (TCP for emulator - USB for device) ||
+                                               ||
+                    ___________________________||_______
+                   |                           ||       |
+                   |          ADBD  (device)   ||       |
+                   |                           VV       |
+         JDWP <======> LocalSocket <----> RemoteSocket  |
+                   |                                    |
+                   |____________________________________|
+
+    due to the way adb works, this doesn't need a special socket
+    type or fancy handling of socket termination if either the debugger
+    or the JDWP process closes the connection.
+
+    THIS IS THE SIMPLEST IMPLEMENTATION I COULD FIND, IF YOU HAPPEN
+    TO HAVE A BETTER IDEA, LET ME KNOW - Digit
+
+**********************************************************************/
+
+/** JDWP PID List Support Code
+ ** for each JDWP process, we record its pid and its connected socket
+ **/
+
+static void jdwp_process_event(int socket, unsigned events, void* _proc);
+static void jdwp_process_list_updated(void);
+
+struct JdwpProcess;
+static auto& _jdwp_list = *new std::list<std::unique_ptr<JdwpProcess>>();
+
+struct JdwpProcess {
+    explicit JdwpProcess(int socket) {
+        this->socket = socket;
+        this->fde = fdevent_create(socket, jdwp_process_event, this);
+
+        if (!this->fde) {
+            fatal("could not create fdevent for new JDWP process");
+        }
+
+        /* start by waiting for the PID */
+        fdevent_add(this->fde, FDE_READ);
+    }
+
+    ~JdwpProcess() {
+        if (this->socket >= 0) {
+            adb_shutdown(this->socket);
+            this->socket = -1;
+        }
+
+        if (this->fde) {
+            fdevent_destroy(this->fde);
+            this->fde = nullptr;
+        }
+
+        out_fds.clear();
+    }
+
+    void RemoveFromList() {
+        if (this->pid >= 0) {
+            D("removing pid %d from jdwp process list", this->pid);
+        } else {
+            D("removing transient JdwpProcess from list");
+        }
+
+        auto pred = [this](const auto& proc) { return proc.get() == this; };
+        _jdwp_list.remove_if(pred);
+    }
+
+    int32_t pid = -1;
+    int socket = -1;
+    fdevent* fde = nullptr;
+
+    std::vector<unique_fd> out_fds;
+};
+
+static size_t jdwp_process_list(char* buffer, size_t bufferlen) {
+    std::string temp;
+
+    for (auto& proc : _jdwp_list) {
+        /* skip transient connections */
+        if (proc->pid < 0) {
+            continue;
+        }
+
+        std::string next = std::to_string(proc->pid) + "\n";
+        if (temp.length() + next.length() > bufferlen) {
+            D("truncating JDWP process list (max len = %zu)", bufferlen);
+            break;
+        }
+        temp.append(next);
+    }
+
+    memcpy(buffer, temp.data(), temp.length());
+    return temp.length();
+}
+
+static size_t jdwp_process_list_msg(char* buffer, size_t bufferlen) {
+    // Message is length-prefixed with 4 hex digits in ASCII.
+    static constexpr size_t header_len = 4;
+    if (bufferlen < header_len) {
+        fatal("invalid JDWP process list buffer size: %zu", bufferlen);
+    }
+
+    char head[header_len + 1];
+    size_t len = jdwp_process_list(buffer + header_len, bufferlen - header_len);
+    snprintf(head, sizeof head, "%04zx", len);
+    memcpy(buffer, head, header_len);
+    return len + header_len;
+}
+
+static void jdwp_process_event(int socket, unsigned events, void* _proc) {
+    JdwpProcess* proc = reinterpret_cast<JdwpProcess*>(_proc);
+
+    if (events & FDE_READ) {
+        if (proc->pid < 0) {
+            ssize_t rc = TEMP_FAILURE_RETRY(recv(socket, &proc->pid, sizeof(proc->pid), 0));
+            if (rc != sizeof(proc->pid)) {
+                D("failed to read jdwp pid: rc = %zd, errno = %s", rc, strerror(errno));
+                goto CloseProcess;
+            }
+
+            /* all is well, keep reading to detect connection closure */
+            D("Adding pid %d to jdwp process list", proc->pid);
+            jdwp_process_list_updated();
+        } else {
+            /* the pid was read, if we get there it's probably because the connection
+             * was closed (e.g. the JDWP process exited or crashed) */
+            char buf[32];
+
+            while (true) {
+                int len = TEMP_FAILURE_RETRY(recv(socket, buf, sizeof(buf), 0));
+
+                if (len == 0) {
+                    D("terminating JDWP %d connection: EOF", proc->pid);
+                    break;
+                } else if (len < 0) {
+                    if (len < 0 && errno == EAGAIN) {
+                        return;
+                    }
+
+                    D("terminating JDWP %d connection: EOF", proc->pid);
+                    break;
+                } else {
+                    D("ignoring unexpected JDWP %d control socket activity (%d bytes)", proc->pid,
+                      len);
+                }
+            }
+
+            goto CloseProcess;
+        }
+    }
+
+    if (events & FDE_WRITE) {
+        D("trying to send fd to JDWP process (count = %zu)", proc->out_fds.size());
+        if (!proc->out_fds.empty()) {
+            int fd = proc->out_fds.back().get();
+            struct cmsghdr* cmsg;
+            struct msghdr msg;
+            struct iovec iov;
+            char dummy = '!';
+            char buffer[sizeof(struct cmsghdr) + sizeof(int)];
+
+            iov.iov_base = &dummy;
+            iov.iov_len = 1;
+            msg.msg_name = nullptr;
+            msg.msg_namelen = 0;
+            msg.msg_iov = &iov;
+            msg.msg_iovlen = 1;
+            msg.msg_flags = 0;
+            msg.msg_control = buffer;
+            msg.msg_controllen = sizeof(buffer);
+
+            cmsg = CMSG_FIRSTHDR(&msg);
+            cmsg->cmsg_len = msg.msg_controllen;
+            cmsg->cmsg_level = SOL_SOCKET;
+            cmsg->cmsg_type = SCM_RIGHTS;
+            ((int*)CMSG_DATA(cmsg))[0] = fd;
+
+            if (!set_file_block_mode(proc->socket, true)) {
+                VLOG(JDWP) << "failed to set blocking mode for fd " << proc->socket;
+                goto CloseProcess;
+            }
+
+            int ret = TEMP_FAILURE_RETRY(sendmsg(proc->socket, &msg, 0));
+            if (ret < 0) {
+                D("sending new file descriptor to JDWP %d failed: %s", proc->pid, strerror(errno));
+                goto CloseProcess;
+            }
+
+            D("sent file descriptor %d to JDWP process %d", fd, proc->pid);
+
+            proc->out_fds.pop_back();
+
+            if (!set_file_block_mode(proc->socket, false)) {
+                VLOG(JDWP) << "failed to set non-blocking mode for fd " << proc->socket;
+                goto CloseProcess;
+            }
+
+            if (proc->out_fds.empty()) {
+                fdevent_del(proc->fde, FDE_WRITE);
+            }
+        }
+    }
+
+    return;
+
+CloseProcess:
+    proc->RemoveFromList();
+    jdwp_process_list_updated();
+}
+
+unique_fd create_jdwp_connection_fd(int pid) {
+    D("looking for pid %d in JDWP process list", pid);
+
+    for (auto& proc : _jdwp_list) {
+        if (proc->pid == pid) {
+            int fds[2];
+
+            if (adb_socketpair(fds) < 0) {
+                D("%s: socket pair creation failed: %s", __FUNCTION__, strerror(errno));
+                return unique_fd{};
+            }
+            D("socketpair: (%d,%d)", fds[0], fds[1]);
+
+            proc->out_fds.emplace_back(fds[1]);
+            if (proc->out_fds.size() == 1) {
+                fdevent_add(proc->fde, FDE_WRITE);
+            }
+
+            return unique_fd{fds[0]};
+        }
+    }
+    D("search failed !!");
+    return unique_fd{};
+}
+
+/**  VM DEBUG CONTROL SOCKET
+ **
+ **  we do implement a custom asocket to receive the data
+ **/
+
+/* name of the debug control Unix socket */
+#define JDWP_CONTROL_NAME "\0jdwp-control"
+#define JDWP_CONTROL_NAME_LEN (sizeof(JDWP_CONTROL_NAME) - 1)
+
+struct JdwpControl {
+    int listen_socket;
+    fdevent* fde;
+};
+
+static JdwpControl _jdwp_control;
+
+static void jdwp_control_event(int s, unsigned events, void* user);
+
+static int jdwp_control_init(JdwpControl* control, const char* sockname, int socknamelen) {
+    sockaddr_un addr;
+    socklen_t addrlen;
+    int s;
+    int maxpath = sizeof(addr.sun_path);
+    int pathlen = socknamelen;
+
+    if (pathlen >= maxpath) {
+        D("vm debug control socket name too long (%d extra chars)", pathlen + 1 - maxpath);
+        return -1;
+    }
+
+    memset(&addr, 0, sizeof(addr));
+    addr.sun_family = AF_UNIX;
+    memcpy(addr.sun_path, sockname, socknamelen);
+
+    s = socket(AF_UNIX, SOCK_SEQPACKET | SOCK_CLOEXEC, 0);
+    if (s < 0) {
+        D("could not create vm debug control socket. %d: %s", errno, strerror(errno));
+        return -1;
+    }
+
+    addrlen = pathlen + sizeof(addr.sun_family);
+
+    if (bind(s, reinterpret_cast<sockaddr*>(&addr), addrlen) < 0) {
+        D("could not bind vm debug control socket: %d: %s", errno, strerror(errno));
+        adb_close(s);
+        return -1;
+    }
+
+    if (listen(s, 4) < 0) {
+        D("listen failed in jdwp control socket: %d: %s", errno, strerror(errno));
+        adb_close(s);
+        return -1;
+    }
+
+    control->listen_socket = s;
+
+    control->fde = fdevent_create(s, jdwp_control_event, control);
+    if (control->fde == nullptr) {
+        D("could not create fdevent for jdwp control socket");
+        adb_close(s);
+        return -1;
+    }
+
+    /* only wait for incoming connections */
+    fdevent_add(control->fde, FDE_READ);
+
+    D("jdwp control socket started (%d)", control->listen_socket);
+    return 0;
+}
+
+static void jdwp_control_event(int s, unsigned events, void* _control) {
+    JdwpControl* control = (JdwpControl*)_control;
+
+    if (events & FDE_READ) {
+        int s = adb_socket_accept(control->listen_socket, nullptr, nullptr);
+        if (s < 0) {
+            if (errno == ECONNABORTED) {
+                /* oops, the JDWP process died really quick */
+                D("oops, the JDWP process died really quick");
+                return;
+            } else {
+                /* the socket is probably closed ? */
+                D("weird accept() failed on jdwp control socket: %s", strerror(errno));
+                return;
+            }
+        }
+
+        auto proc = std::make_unique<JdwpProcess>(s);
+        if (!proc) {
+            fatal("failed to allocate JdwpProcess");
+        }
+
+        _jdwp_list.emplace_back(std::move(proc));
+    }
+}
+
+/** "jdwp" local service implementation
+ ** this simply returns the list of known JDWP process pids
+ **/
+
+struct JdwpSocket : public asocket {
+    bool pass = false;
+};
+
+static void jdwp_socket_close(asocket* s) {
+    D("LS(%d): closing jdwp socket", s->id);
+
+    if (s->peer) {
+        D("LS(%d) peer->close()ing peer->id=%d peer->fd=%d", s->id, s->peer->id, s->peer->fd);
+        s->peer->peer = nullptr;
+        s->peer->close(s->peer);
+        s->peer = nullptr;
+    }
+
+    remove_socket(s);
+    delete s;
+}
+
+static int jdwp_socket_enqueue(asocket* s, apacket::payload_type) {
+    /* you can't write to this asocket */
+    D("LS(%d): JDWP socket received data?", s->id);
+    s->peer->close(s->peer);
+    return -1;
+}
+
+static void jdwp_socket_ready(asocket* s) {
+    JdwpSocket* jdwp = (JdwpSocket*)s;
+    asocket* peer = jdwp->peer;
+
+    /* on the first call, send the list of pids,
+     * on the second one, close the connection
+     */
+    if (!jdwp->pass) {
+        apacket::payload_type data;
+        data.resize(s->get_max_payload());
+        size_t len = jdwp_process_list(&data[0], data.size());
+        data.resize(len);
+        peer->enqueue(peer, std::move(data));
+        jdwp->pass = true;
+    } else {
+        peer->close(peer);
+    }
+}
+
+asocket* create_jdwp_service_socket(void) {
+    JdwpSocket* s = new JdwpSocket();
+
+    if (!s) {
+        fatal("failed to allocate JdwpSocket");
+    }
+
+    install_local_socket(s);
+
+    s->ready = jdwp_socket_ready;
+    s->enqueue = jdwp_socket_enqueue;
+    s->close = jdwp_socket_close;
+    s->pass = false;
+
+    return s;
+}
+
+/** "track-jdwp" local service implementation
+ ** this periodically sends the list of known JDWP process pids
+ ** to the client...
+ **/
+
+struct JdwpTracker : public asocket {
+    bool need_initial;
+};
+
+static auto& _jdwp_trackers = *new std::vector<std::unique_ptr<JdwpTracker>>();
+
+static void jdwp_process_list_updated(void) {
+    std::string data;
+    data.resize(1024);
+    data.resize(jdwp_process_list_msg(&data[0], data.size()));
+
+    for (auto& t : _jdwp_trackers) {
+        if (t->peer) {
+            // The tracker might not have been connected yet.
+            apacket::payload_type payload(data.begin(), data.end());
+            t->peer->enqueue(t->peer, std::move(payload));
+        }
+    }
+}
+
+static void jdwp_tracker_close(asocket* s) {
+    D("LS(%d): destroying jdwp tracker service", s->id);
+
+    if (s->peer) {
+        D("LS(%d) peer->close()ing peer->id=%d peer->fd=%d", s->id, s->peer->id, s->peer->fd);
+        s->peer->peer = nullptr;
+        s->peer->close(s->peer);
+        s->peer = nullptr;
+    }
+
+    remove_socket(s);
+
+    auto pred = [s](const auto& tracker) { return tracker.get() == s; };
+    _jdwp_trackers.erase(std::remove_if(_jdwp_trackers.begin(), _jdwp_trackers.end(), pred),
+                         _jdwp_trackers.end());
+}
+
+static void jdwp_tracker_ready(asocket* s) {
+    JdwpTracker* t = (JdwpTracker*)s;
+
+    if (t->need_initial) {
+        apacket::payload_type data;
+        data.resize(s->get_max_payload());
+        data.resize(jdwp_process_list_msg(&data[0], data.size()));
+        t->need_initial = false;
+        s->peer->enqueue(s->peer, std::move(data));
+    }
+}
+
+static int jdwp_tracker_enqueue(asocket* s, apacket::payload_type) {
+    /* you can't write to this socket */
+    D("LS(%d): JDWP tracker received data?", s->id);
+    s->peer->close(s->peer);
+    return -1;
+}
+
+asocket* create_jdwp_tracker_service_socket(void) {
+    auto t = std::make_unique<JdwpTracker>();
+    if (!t) {
+        fatal("failed to allocate JdwpTracker");
+    }
+
+    memset(t.get(), 0, sizeof(asocket));
+
+    install_local_socket(t.get());
+    D("LS(%d): created new jdwp tracker service", t->id);
+
+    t->ready = jdwp_tracker_ready;
+    t->enqueue = jdwp_tracker_enqueue;
+    t->close = jdwp_tracker_close;
+    t->need_initial = true;
+
+    asocket* result = t.get();
+
+    _jdwp_trackers.emplace_back(std::move(t));
+
+    return result;
+}
+
+int init_jdwp(void) {
+    return jdwp_control_init(&_jdwp_control, JDWP_CONTROL_NAME, JDWP_CONTROL_NAME_LEN);
+}
+
+#endif /* !ADB_HOST */
diff --git a/adb/daemon/main.cpp b/adb/daemon/main.cpp
index 1c94298..f6f1acc 100644
--- a/adb/daemon/main.cpp
+++ b/adb/daemon/main.cpp
@@ -18,11 +18,14 @@
 
 #include "sysdeps.h"
 
+#include <android/fdsan.h>
 #include <errno.h>
+#include <getopt.h>
+#include <malloc.h>
 #include <signal.h>
 #include <stdio.h>
 #include <stdlib.h>
-#include <getopt.h>
+#include <sys/capability.h>
 #include <sys/prctl.h>
 
 #include <memory>
@@ -36,7 +39,6 @@
 #include <scoped_minijail.h>
 
 #include <private/android_filesystem_config.h>
-#include "debuggerd/handler.h"
 #include "selinux/android.h"
 
 #include "adb.h"
@@ -49,13 +51,13 @@
 
 static const char* root_seclabel = nullptr;
 
-static void drop_capabilities_bounding_set_if_needed(struct minijail *j) {
+static bool should_drop_capabilities_bounding_set() {
 #if defined(ALLOW_ADBD_ROOT)
     if (__android_log_is_debuggable()) {
-        return;
+        return false;
     }
 #endif
-    minijail_capbset_drop(j, CAP_TO_MASK(CAP_SETUID) | CAP_TO_MASK(CAP_SETGID));
+    return true;
 }
 
 static bool should_drop_privileges() {
@@ -116,13 +118,37 @@
     // Don't listen on a port (default 5037) if running in secure mode.
     // Don't run as root if running in secure mode.
     if (should_drop_privileges()) {
-        drop_capabilities_bounding_set_if_needed(jail.get());
+        const bool should_drop_caps = should_drop_capabilities_bounding_set();
+
+        if (should_drop_caps) {
+            minijail_use_caps(jail.get(), CAP_TO_MASK(CAP_SETUID) | CAP_TO_MASK(CAP_SETGID));
+        }
 
         minijail_change_gid(jail.get(), AID_SHELL);
         minijail_change_uid(jail.get(), AID_SHELL);
         // minijail_enter() will abort if any priv-dropping step fails.
         minijail_enter(jail.get());
 
+        // Whenever ambient capabilities are being used, minijail cannot
+        // simultaneously drop the bounding capability set to just
+        // CAP_SETUID|CAP_SETGID while clearing the inheritable, effective,
+        // and permitted sets. So we need to do that in two steps.
+        using ScopedCaps =
+            std::unique_ptr<std::remove_pointer<cap_t>::type, std::function<void(cap_t)>>;
+        ScopedCaps caps(cap_get_proc(), &cap_free);
+        if (cap_clear_flag(caps.get(), CAP_INHERITABLE) == -1) {
+            PLOG(FATAL) << "cap_clear_flag(INHERITABLE) failed";
+        }
+        if (cap_clear_flag(caps.get(), CAP_EFFECTIVE) == -1) {
+            PLOG(FATAL) << "cap_clear_flag(PEMITTED) failed";
+        }
+        if (cap_clear_flag(caps.get(), CAP_PERMITTED) == -1) {
+            PLOG(FATAL) << "cap_clear_flag(PEMITTED) failed";
+        }
+        if (cap_set_proc(caps.get()) != 0) {
+            PLOG(FATAL) << "cap_set_proc() failed";
+        }
+
         D("Local port disabled");
     } else {
         // minijail_enter() will abort if any priv-dropping step fails.
@@ -152,15 +178,21 @@
 
     signal(SIGPIPE, SIG_IGN);
 
+    auto fdsan_level = android_fdsan_get_error_level();
+    if (fdsan_level == ANDROID_FDSAN_ERROR_LEVEL_DISABLED) {
+        android_fdsan_set_error_level(ANDROID_FDSAN_ERROR_LEVEL_WARN_ONCE);
+    }
+
     init_transport_registration();
 
     // We need to call this even if auth isn't enabled because the file
     // 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();
 
@@ -212,6 +244,9 @@
 }
 
 int main(int argc, char** argv) {
+    // Set M_DECAY_TIME so that our allocations aren't immediately purged on free.
+    mallopt(M_DECAY_TIME, 1);
+
     while (true) {
         static struct option opts[] = {
             {"root_seclabel", required_argument, nullptr, 's'},
@@ -233,8 +268,8 @@
             adb_device_banner = optarg;
             break;
         case 'v':
-            printf("Android Debug Bridge Daemon version %d.%d.%d (%s)\n", ADB_VERSION_MAJOR,
-                   ADB_VERSION_MINOR, ADB_SERVER_VERSION, ADB_VERSION);
+            printf("Android Debug Bridge Daemon version %d.%d.%d\n", ADB_VERSION_MAJOR,
+                   ADB_VERSION_MINOR, ADB_SERVER_VERSION);
             return 0;
         default:
             // getopt already prints "adbd: invalid option -- %c" for us.
@@ -244,7 +279,6 @@
 
     close_stdin();
 
-    debuggerd_init(nullptr);
     adb_trace_init(argv);
 
     D("Handling main()");
diff --git a/adb/daemon/remount_service.cpp b/adb/daemon/remount_service.cpp
new file mode 100644
index 0000000..0e79d82
--- /dev/null
+++ b/adb/daemon/remount_service.cpp
@@ -0,0 +1,312 @@
+/*
+ * 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.
+ */
+
+#define TRACE_TAG ADB
+
+#include "sysdeps.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <mntent.h>
+#include <spawn.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mount.h>
+#include <sys/statvfs.h>
+#include <sys/vfs.h>
+#include <unistd.h>
+
+#include <set>
+#include <string>
+#include <vector>
+
+#include <android-base/properties.h>
+#include <bootloader_message/bootloader_message.h>
+#include <cutils/android_reboot.h>
+#include <ext4_utils/ext4_utils.h>
+
+#include "adb.h"
+#include "adb_io.h"
+#include "adb_unique_fd.h"
+#include "adb_utils.h"
+#include "fs_mgr.h"
+#include "set_verity_enable_state_service.h"
+
+// Returns the device used to mount a directory in /proc/mounts.
+static std::string find_proc_mount(const char* dir) {
+    std::unique_ptr<FILE, int(*)(FILE*)> fp(setmntent("/proc/mounts", "r"), endmntent);
+    if (!fp) {
+        return "";
+    }
+
+    mntent* e;
+    while ((e = getmntent(fp.get())) != nullptr) {
+        if (strcmp(dir, e->mnt_dir) == 0) {
+            return e->mnt_fsname;
+        }
+    }
+    return "";
+}
+
+// Returns the device used to mount a directory in the fstab.
+static std::string find_fstab_mount(const char* dir) {
+    std::unique_ptr<fstab, decltype(&fs_mgr_free_fstab)> fstab(fs_mgr_read_fstab_default(),
+                                                               fs_mgr_free_fstab);
+    struct fstab_rec* rec = fs_mgr_get_entry_for_mount_point(fstab.get(), dir);
+    return rec ? rec->blk_device : "";
+}
+
+// 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, bool is_root) {
+    if (is_root) {
+        return find_fstab_mount(dir);
+    } else {
+       return find_proc_mount(dir);
+    }
+}
+
+bool make_block_device_writable(const std::string& dev) {
+    int fd = unix_open(dev.c_str(), O_RDONLY | O_CLOEXEC);
+    if (fd == -1) {
+        return false;
+    }
+
+    int OFF = 0;
+    bool result = (ioctl(fd, BLKROSET, &OFF) != -1);
+    unix_close(fd);
+    return result;
+}
+
+static bool fs_has_shared_blocks(const std::string& mount_point, const std::string& device) {
+    std::string path = mount_point + "/lost+found";
+    struct statfs fs;
+    if (statfs(path.c_str(), &fs) == -1 || fs.f_type != EXT4_SUPER_MAGIC) {
+        return false;
+    }
+    unique_fd fd(unix_open(device.c_str(), O_RDONLY));
+    if (fd < 0) {
+        return false;
+    }
+    struct ext4_super_block sb;
+    if (lseek64(fd, 1024, SEEK_SET) < 0 || unix_read(fd, &sb, sizeof(sb)) < 0) {
+        return false;
+    }
+    struct fs_info info;
+    if (ext4_parse_sb(&sb, &info) < 0) {
+        return false;
+    }
+    return (info.feat_ro_compat & EXT4_FEATURE_RO_COMPAT_SHARED_BLOCKS) != 0;
+}
+
+static bool can_unshare_blocks(int fd, const char* dev) {
+    const char* E2FSCK_BIN = "/system/bin/e2fsck";
+    if (access(E2FSCK_BIN, X_OK)) {
+        WriteFdFmt(fd, "e2fsck is not available, cannot undo deduplication on %s\n", dev);
+        return false;
+    }
+
+    pid_t child;
+    char* env[] = {nullptr};
+    const char* argv[] = {E2FSCK_BIN, "-n", "-E", "unshare_blocks", dev, nullptr};
+    if (posix_spawn(&child, E2FSCK_BIN, nullptr, nullptr, const_cast<char**>(argv), env)) {
+        WriteFdFmt(fd, "failed to e2fsck to check deduplication: %s\n", strerror(errno));
+        return false;
+    }
+    int status = 0;
+    int ret = TEMP_FAILURE_RETRY(waitpid(child, &status, 0));
+    if (ret < 0) {
+        WriteFdFmt(fd, "failed to get e2fsck status: %s\n", strerror(errno));
+        return false;
+    }
+    if (!WIFEXITED(status)) {
+        WriteFdFmt(fd, "e2fsck exited abnormally with status %d\n", status);
+        return false;
+    }
+    int rc = WEXITSTATUS(status);
+    if (rc != 0) {
+        WriteFdFmt(fd,
+                   "%s is deduplicated, and an e2fsck check failed. It might not "
+                   "have enough free-space to be remounted as writable.\n",
+                   dev);
+        return false;
+    }
+    return true;
+}
+
+static unsigned long get_mount_flags(int fd, const char* dir) {
+    struct statvfs st_vfs;
+    if (statvfs(dir, &st_vfs) == -1) {
+        // Even though we could not get the original mount flags, assume that
+        // the mount was originally read-only.
+        WriteFdFmt(fd, "statvfs of the %s mount failed: %s.\n", dir, strerror(errno));
+        return MS_RDONLY;
+    }
+    return st_vfs.f_flag;
+}
+
+static bool remount_partition(int fd, const char* dir) {
+    if (!directory_exists(dir)) {
+        return true;
+    }
+    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 (!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;
+    }
+
+    unsigned long remount_flags = get_mount_flags(fd, dir);
+    remount_flags &= ~MS_RDONLY;
+    remount_flags |= MS_REMOUNT;
+
+    if (mount(dev.c_str(), dir, "none", remount_flags | 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 the %s superblock failed: %s\n", dir, strerror(errno));
+        return false;
+    }
+    return true;
+}
+
+static void reboot_for_remount(int fd, bool need_fsck) {
+    std::string reboot_cmd = "reboot";
+    if (need_fsck) {
+        const std::vector<std::string> options = {"--fsck_unshare_blocks"};
+        std::string err;
+        if (!write_bootloader_message(options, &err)) {
+            WriteFdFmt(fd, "Failed to set bootloader message: %s\n", err.c_str());
+            return;
+        }
+
+        WriteFdExactly(fd,
+                       "The device will now reboot to recovery and attempt "
+                       "un-deduplication.\n");
+        reboot_cmd = "reboot,recovery";
+    }
+
+    sync();
+    android::base::SetProperty(ANDROID_RB_PROPERTY, reboot_cmd.c_str());
+}
+
+void remount_service(unique_fd fd, const std::string& cmd) {
+    bool user_requested_reboot = cmd == "-R";
+
+    if (getuid() != 0) {
+        WriteFdExactly(fd.get(), "Not running as root. Try \"adb root\" first.\n");
+        return;
+    }
+
+    bool system_verified = !(android::base::GetProperty("partition.system.verified", "").empty());
+    bool vendor_verified = !(android::base::GetProperty("partition.vendor.verified", "").empty());
+
+    std::vector<std::string> partitions = {"/odm", "/oem", "/product", "/vendor"};
+    if (android::base::GetBoolProperty("ro.build.system_root_image", false)) {
+        partitions.push_back("/");
+    } else {
+        partitions.push_back("/system");
+    }
+
+    // Find partitions that are deduplicated, and can be un-deduplicated.
+    std::set<std::string> dedup;
+    for (const auto& partition : partitions) {
+        std::string dev = find_mount(partition.c_str(), partition == "/");
+        if (dev.empty() || !fs_has_shared_blocks(partition, dev)) {
+            continue;
+        }
+        if (can_unshare_blocks(fd.get(), dev.c_str())) {
+            dedup.emplace(partition);
+        }
+    }
+
+    bool verity_enabled = (system_verified || vendor_verified);
+
+    // Reboot now if the user requested it (and an operation needs a reboot).
+    if (user_requested_reboot) {
+        if (!dedup.empty() || verity_enabled) {
+            if (verity_enabled) {
+                set_verity_enabled_state_service(unique_fd(dup(fd.get())), false);
+            }
+            reboot_for_remount(fd.get(), !dedup.empty());
+            return;
+        }
+        WriteFdExactly(fd.get(), "No reboot needed, skipping -R.\n");
+    }
+
+    // If we need to disable-verity, but we also need to perform a recovery
+    // fsck for deduplicated partitions, hold off on warning about verity. We
+    // can handle both verity and the recovery fsck in the same reboot cycle.
+    if (verity_enabled && dedup.empty()) {
+        // Allow remount but warn of likely bad effects
+        bool both = system_verified && vendor_verified;
+        WriteFdFmt(fd.get(), "dm_verity is enabled on the %s%s%s partition%s.\n",
+                   system_verified ? "system" : "", both ? " and " : "",
+                   vendor_verified ? "vendor" : "", both ? "s" : "");
+        WriteFdExactly(fd.get(),
+                       "Use \"adb disable-verity\" to disable verity.\n"
+                       "If you do not, remount may succeed, however, you will still "
+                       "not be able to write to these volumes.\n");
+        WriteFdExactly(fd.get(),
+                       "Alternately, use \"adb remount -R\" to disable verity "
+                       "and automatically reboot.\n");
+    }
+
+    bool success = true;
+    for (const auto& partition : partitions) {
+        // Don't try to remount partitions that need an fsck in recovery.
+        if (dedup.count(partition)) {
+            continue;
+        }
+        success &= remount_partition(fd.get(), partition.c_str());
+    }
+
+    if (!dedup.empty()) {
+        WriteFdExactly(fd.get(),
+                       "The following partitions are deduplicated and cannot "
+                       "yet be remounted:\n");
+        for (const std::string& name : dedup) {
+            WriteFdFmt(fd.get(), "  %s\n", name.c_str());
+        }
+
+        WriteFdExactly(fd.get(),
+                       "To reboot and un-deduplicate the listed partitions, "
+                       "please retry with adb remount -R.\n");
+        if (system_verified || vendor_verified) {
+            WriteFdExactly(fd.get(), "Note: verity will be automatically disabled after reboot.\n");
+        }
+        return;
+    }
+
+    if (!success) {
+        WriteFdExactly(fd.get(), "remount failed\n");
+    } else {
+        WriteFdExactly(fd.get(), "remount succeeded\n");
+    }
+}
diff --git a/adb/daemon/remount_service.h b/adb/daemon/remount_service.h
new file mode 100644
index 0000000..e4e2550
--- /dev/null
+++ b/adb/daemon/remount_service.h
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+
+#pragma once
+
+#include <string>
+
+#include "adb_unique_fd.h"
+
+bool make_block_device_writable(const std::string&);
+void remount_service(unique_fd, const std::string&);
diff --git a/adb/daemon/services.cpp b/adb/daemon/services.cpp
new file mode 100644
index 0000000..1f59d64
--- /dev/null
+++ b/adb/daemon/services.cpp
@@ -0,0 +1,259 @@
+/*
+ * 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.
+ */
+
+#define TRACE_TAG SERVICES
+
+#include "sysdeps.h"
+
+#include <errno.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <unistd.h>
+
+#include <thread>
+
+#include <android-base/file.h>
+#include <android-base/parsenetaddress.h>
+#include <android-base/properties.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+#include <android-base/unique_fd.h>
+#include <bootloader_message/bootloader_message.h>
+#include <cutils/android_reboot.h>
+#include <cutils/sockets.h>
+#include <log/log_properties.h>
+
+#include "adb.h"
+#include "adb_io.h"
+#include "adb_unique_fd.h"
+#include "adb_utils.h"
+#include "services.h"
+#include "socket_spec.h"
+#include "sysdeps.h"
+#include "transport.h"
+
+#include "daemon/file_sync_service.h"
+#include "daemon/framebuffer_service.h"
+#include "daemon/remount_service.h"
+#include "daemon/set_verity_enable_state_service.h"
+#include "daemon/shell_service.h"
+
+void restart_root_service(unique_fd fd) {
+    if (getuid() == 0) {
+        WriteFdExactly(fd.get(), "adbd is already running as root\n");
+        return;
+    }
+    if (!__android_log_is_debuggable()) {
+        WriteFdExactly(fd.get(), "adbd cannot run as root in production builds\n");
+        return;
+    }
+
+    android::base::SetProperty("service.adb.root", "1");
+    WriteFdExactly(fd.get(), "restarting adbd as root\n");
+}
+
+void restart_unroot_service(unique_fd fd) {
+    if (getuid() != 0) {
+        WriteFdExactly(fd.get(), "adbd not running as root\n");
+        return;
+    }
+    android::base::SetProperty("service.adb.root", "0");
+    WriteFdExactly(fd.get(), "restarting adbd as non root\n");
+}
+
+void restart_tcp_service(unique_fd fd, int port) {
+    if (port <= 0) {
+        WriteFdFmt(fd.get(), "invalid port %d\n", port);
+        return;
+    }
+
+    android::base::SetProperty("service.adb.tcp.port", android::base::StringPrintf("%d", port));
+    WriteFdFmt(fd.get(), "restarting in TCP mode port: %d\n", port);
+}
+
+void restart_usb_service(unique_fd fd) {
+    android::base::SetProperty("service.adb.tcp.port", "0");
+    WriteFdExactly(fd.get(), "restarting in USB mode\n");
+}
+
+bool reboot_service_impl(unique_fd fd, const std::string& arg) {
+    std::string reboot_arg = arg;
+    bool auto_reboot = false;
+
+    if (reboot_arg == "sideload-auto-reboot") {
+        auto_reboot = true;
+        reboot_arg = "sideload";
+    }
+
+    // It reboots into sideload mode by setting "--sideload" or "--sideload_auto_reboot"
+    // in the command file.
+    if (reboot_arg == "sideload") {
+        if (getuid() != 0) {
+            WriteFdExactly(fd.get(), "'adb root' is required for 'adb reboot sideload'.\n");
+            return false;
+        }
+
+        const std::vector<std::string> options = {auto_reboot ? "--sideload_auto_reboot"
+                                                              : "--sideload"};
+        std::string err;
+        if (!write_bootloader_message(options, &err)) {
+            D("Failed to set bootloader message: %s", err.c_str());
+            return false;
+        }
+
+        reboot_arg = "recovery";
+    }
+
+    sync();
+
+    if (reboot_arg.empty()) reboot_arg = "adb";
+    std::string reboot_string = android::base::StringPrintf("reboot,%s", reboot_arg.c_str());
+    if (!android::base::SetProperty(ANDROID_RB_PROPERTY, reboot_string)) {
+        WriteFdFmt(fd.get(), "reboot (%s) failed\n", reboot_string.c_str());
+        return false;
+    }
+
+    return true;
+}
+
+void reboot_service(unique_fd fd, const std::string& arg) {
+    if (!reboot_service_impl(std::move(fd), arg)) {
+        return;
+    }
+    // Don't return early. Give the reboot command time to take effect
+    // to avoid messing up scripts which do "adb reboot && adb wait-for-device"
+    while (true) {
+        pause();
+    }
+}
+
+void reconnect_service(unique_fd fd, atransport* t) {
+    WriteFdExactly(fd.get(), "done");
+    kick_transport(t);
+}
+
+unique_fd reverse_service(const char* command, atransport* transport) {
+    int s[2];
+    if (adb_socketpair(s)) {
+        PLOG(ERROR) << "cannot create service socket pair.";
+        return unique_fd{};
+    }
+    VLOG(SERVICES) << "service socketpair: " << s[0] << ", " << s[1];
+    if (!handle_forward_request(command, transport, s[1])) {
+        SendFail(s[1], "not a reverse forwarding command");
+    }
+    adb_close(s[1]);
+    return unique_fd{s[0]};
+}
+
+// Shell service string can look like:
+//   shell[,arg1,arg2,...]:[command]
+unique_fd ShellService(const std::string& args, const atransport* transport) {
+    size_t delimiter_index = args.find(':');
+    if (delimiter_index == std::string::npos) {
+        LOG(ERROR) << "No ':' found in shell service arguments: " << args;
+        return unique_fd{};
+    }
+
+    const std::string service_args = args.substr(0, delimiter_index);
+    const std::string command = args.substr(delimiter_index + 1);
+
+    // Defaults:
+    //   PTY for interactive, raw for non-interactive.
+    //   No protocol.
+    //   $TERM set to "dumb".
+    SubprocessType type(command.empty() ? SubprocessType::kPty : SubprocessType::kRaw);
+    SubprocessProtocol protocol = SubprocessProtocol::kNone;
+    std::string terminal_type = "dumb";
+
+    for (const std::string& arg : android::base::Split(service_args, ",")) {
+        if (arg == kShellServiceArgRaw) {
+            type = SubprocessType::kRaw;
+        } else if (arg == kShellServiceArgPty) {
+            type = SubprocessType::kPty;
+        } else if (arg == kShellServiceArgShellProtocol) {
+            protocol = SubprocessProtocol::kShell;
+        } else if (android::base::StartsWith(arg, "TERM=")) {
+            terminal_type = arg.substr(5);
+        } else if (!arg.empty()) {
+            // This is not an error to allow for future expansion.
+            LOG(WARNING) << "Ignoring unknown shell service argument: " << arg;
+        }
+    }
+
+    return StartSubprocess(command.c_str(), terminal_type.c_str(), type, protocol);
+}
+
+unique_fd daemon_service_to_fd(const char* name, atransport* transport) {
+    if (!strncmp("dev:", name, 4)) {
+        return unique_fd{unix_open(name + 4, O_RDWR | O_CLOEXEC)};
+    } else if (!strncmp(name, "framebuffer:", 12)) {
+        return create_service_thread("fb", framebuffer_service);
+    } else if (!strncmp(name, "jdwp:", 5)) {
+        return create_jdwp_connection_fd(atoi(name + 5));
+    } else if (!strncmp(name, "shell", 5)) {
+        return ShellService(name + 5, transport);
+    } else if (!strncmp(name, "exec:", 5)) {
+        return StartSubprocess(name + 5, nullptr, SubprocessType::kRaw, SubprocessProtocol::kNone);
+    } else if (!strncmp(name, "sync:", 5)) {
+        return create_service_thread("sync", file_sync_service);
+    } else if (!strncmp(name, "remount:", 8)) {
+        std::string options(name + strlen("remount:"));
+        return create_service_thread("remount",
+                                     std::bind(remount_service, std::placeholders::_1, options));
+    } else if (!strncmp(name, "reboot:", 7)) {
+        std::string arg(name + strlen("reboot:"));
+        return create_service_thread("reboot",
+                                     std::bind(reboot_service, std::placeholders::_1, arg));
+    } else if (!strncmp(name, "root:", 5)) {
+        return create_service_thread("root", restart_root_service);
+    } else if (!strncmp(name, "unroot:", 7)) {
+        return create_service_thread("unroot", restart_unroot_service);
+    } else if (!strncmp(name, "backup:", 7)) {
+        return StartSubprocess(
+                android::base::StringPrintf("/system/bin/bu backup %s", (name + 7)).c_str(),
+                nullptr, SubprocessType::kRaw, SubprocessProtocol::kNone);
+    } else if (!strncmp(name, "restore:", 8)) {
+        return StartSubprocess("/system/bin/bu restore", nullptr, SubprocessType::kRaw,
+                               SubprocessProtocol::kNone);
+    } else if (!strncmp(name, "tcpip:", 6)) {
+        int port;
+        if (sscanf(name + 6, "%d", &port) != 1) {
+            return unique_fd{};
+        }
+        return create_service_thread("tcp",
+                                     std::bind(restart_tcp_service, std::placeholders::_1, port));
+    } else if (!strncmp(name, "usb:", 4)) {
+        return create_service_thread("usb", restart_usb_service);
+    } else if (!strncmp(name, "reverse:", 8)) {
+        return reverse_service(name + 8, transport);
+    } else if (!strncmp(name, "disable-verity:", 15)) {
+        return create_service_thread("verity-on", std::bind(set_verity_enabled_state_service,
+                                                            std::placeholders::_1, false));
+    } else if (!strncmp(name, "enable-verity:", 15)) {
+        return create_service_thread("verity-off", std::bind(set_verity_enabled_state_service,
+                                                             std::placeholders::_1, true));
+    } else if (!strcmp(name, "reconnect")) {
+        return create_service_thread(
+                "reconnect", std::bind(reconnect_service, std::placeholders::_1, transport));
+    }
+    return unique_fd{};
+}
diff --git a/adb/daemon/set_verity_enable_state_service.cpp b/adb/daemon/set_verity_enable_state_service.cpp
new file mode 100644
index 0000000..2b6ec21
--- /dev/null
+++ b/adb/daemon/set_verity_enable_state_service.cpp
@@ -0,0 +1,200 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+
+#define TRACE_TAG ADB
+
+#include "set_verity_enable_state_service.h"
+#include "sysdeps.h"
+
+#include <fcntl.h>
+#include <inttypes.h>
+#include <libavb_user/libavb_user.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <sys/stat.h>
+
+#include <android-base/properties.h>
+#include <android-base/stringprintf.h>
+#include <log/log_properties.h>
+
+#include "adb.h"
+#include "adb_io.h"
+#include "adb_unique_fd.h"
+#include "fs_mgr.h"
+#include "remount_service.h"
+
+#include "fec/io.h"
+
+struct fstab *fstab;
+
+#ifdef ALLOW_ADBD_DISABLE_VERITY
+static const bool kAllowDisableVerity = true;
+#else
+static const bool kAllowDisableVerity = false;
+#endif
+
+/* Turn verity on/off */
+static bool set_verity_enabled_state(int fd, const char* block_device, const char* mount_point,
+                                     bool enable) {
+    if (!make_block_device_writable(block_device)) {
+        WriteFdFmt(fd, "Could not make block device %s writable (%s).\n",
+                   block_device, strerror(errno));
+        return false;
+    }
+
+    fec::io fh(block_device, O_RDWR);
+
+    if (!fh) {
+        WriteFdFmt(fd, "Could not open block device %s (%s).\n", block_device, strerror(errno));
+        WriteFdFmt(fd, "Maybe run adb root?\n");
+        return false;
+    }
+
+    fec_verity_metadata metadata;
+
+    if (!fh.get_verity_metadata(metadata)) {
+        WriteFdExactly(fd, "Couldn't find verity metadata!\n");
+        return false;
+    }
+
+    if (!enable && metadata.disabled) {
+        WriteFdFmt(fd, "Verity already disabled on %s\n", mount_point);
+        return false;
+    }
+
+    if (enable && !metadata.disabled) {
+        WriteFdFmt(fd, "Verity already enabled on %s\n", mount_point);
+        return false;
+    }
+
+    if (!fh.set_verity_status(enable)) {
+        WriteFdFmt(fd, "Could not set verity %s flag on device %s with error %s\n",
+                   enable ? "enabled" : "disabled",
+                   block_device, strerror(errno));
+        return false;
+    }
+
+    WriteFdFmt(fd, "Verity %s on %s\n", enable ? "enabled" : "disabled", mount_point);
+    return true;
+}
+
+/* Helper function to get A/B suffix, if any. If the device isn't
+ * using A/B the empty string is returned. Otherwise either "_a",
+ * "_b", ... is returned.
+ */
+static std::string get_ab_suffix() {
+    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()) {
+        WriteFdExactly(fd, "Device is locked. Please unlock the device first\n");
+        return false;
+    }
+
+    if (!avb_user_verity_get(ops, ab_suffix.c_str(), &verity_enabled)) {
+        WriteFdExactly(fd, "Error getting verity state. Try adb root first?\n");
+        return false;
+    }
+
+    if ((verity_enabled && enable_verity) || (!verity_enabled && !enable_verity)) {
+        WriteFdFmt(fd, "verity is already %s\n", verity_enabled ? "enabled" : "disabled");
+        return false;
+    }
+
+    if (!avb_user_verity_set(ops, ab_suffix.c_str(), enable_verity)) {
+        WriteFdExactly(fd, "Error setting verity\n");
+        return false;
+    }
+
+    WriteFdFmt(fd, "Successfully %s verity\n", enable_verity ? "enabled" : "disabled");
+    return true;
+}
+
+void set_verity_enabled_state_service(unique_fd fd, bool enable) {
+    bool any_changed = false;
+
+    // Figure out if we're using VB1.0 or VB2.0 (aka AVB) - by
+    // contract, androidboot.vbmeta.digest is set by the bootloader
+    // when using AVB).
+    bool using_avb = !android::base::GetProperty("ro.boot.vbmeta.digest", "").empty();
+
+    // If using AVB, dm-verity is used on any build so we want it to
+    // be possible to disable/enable on any build (except USER). For
+    // VB1.0 dm-verity is only enabled on certain builds.
+    if (!using_avb) {
+        if (!kAllowDisableVerity) {
+            WriteFdFmt(fd.get(), "%s-verity only works for userdebug builds\n",
+                       enable ? "enable" : "disable");
+        }
+
+        if (!android::base::GetBoolProperty("ro.secure", false)) {
+            WriteFdExactly(fd.get(), "verity not enabled - ENG build\n");
+            return;
+        }
+    }
+
+    // Should never be possible to disable dm-verity on a USER build
+    // regardless of using AVB or VB1.0.
+    if (!__android_log_is_debuggable()) {
+        WriteFdExactly(fd.get(), "verity cannot be disabled/enabled - USER build\n");
+        return;
+    }
+
+    if (using_avb) {
+        // Yep, the system is using AVB.
+        AvbOps* ops = avb_ops_user_new();
+        if (ops == nullptr) {
+            WriteFdExactly(fd.get(), "Error getting AVB ops\n");
+            return;
+        }
+        if (set_avb_verity_enabled_state(fd.get(), ops, enable)) {
+            any_changed = true;
+        }
+        avb_ops_user_free(ops);
+    } else {
+        // Not using AVB - assume VB1.0.
+
+        // read all fstab entries at once from all sources
+        fstab = fs_mgr_read_fstab_default();
+        if (!fstab) {
+            WriteFdExactly(fd.get(), "Failed to read fstab\nMaybe run adb root?\n");
+            return;
+        }
+
+        // Loop through entries looking for ones that vold manages.
+        for (int i = 0; i < fstab->num_entries; i++) {
+            if (fs_mgr_is_verified(&fstab->recs[i])) {
+                if (set_verity_enabled_state(fd.get(), fstab->recs[i].blk_device,
+                                             fstab->recs[i].mount_point, enable)) {
+                    any_changed = true;
+                }
+            }
+        }
+    }
+
+    if (any_changed) {
+        WriteFdExactly(fd.get(), "Now reboot your device for settings to take effect\n");
+    }
+}
diff --git a/adb/daemon/set_verity_enable_state_service.h b/adb/daemon/set_verity_enable_state_service.h
new file mode 100644
index 0000000..c1413c8
--- /dev/null
+++ b/adb/daemon/set_verity_enable_state_service.h
@@ -0,0 +1,21 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include "adb_unique_fd.h"
+
+void set_verity_enabled_state_service(unique_fd fd, bool enable);
diff --git a/adb/daemon/shell_service.cpp b/adb/daemon/shell_service.cpp
new file mode 100644
index 0000000..01097ac
--- /dev/null
+++ b/adb/daemon/shell_service.cpp
@@ -0,0 +1,777 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+
+// Functionality for launching and managing shell subprocesses.
+//
+// There are two types of subprocesses, PTY or raw. PTY is typically used for
+// an interactive session, raw for non-interactive. There are also two methods
+// of communication with the subprocess, passing raw data or using a simple
+// protocol to wrap packets. The protocol allows separating stdout/stderr and
+// passing the exit code back, but is not backwards compatible.
+//   ----------------+--------------------------------------
+//   Type  Protocol  |   Exit code?  Separate stdout/stderr?
+//   ----------------+--------------------------------------
+//   PTY   No        |   No          No
+//   Raw   No        |   No          No
+//   PTY   Yes       |   Yes         No
+//   Raw   Yes       |   Yes         Yes
+//   ----------------+--------------------------------------
+//
+// Non-protocol subprocesses work by passing subprocess stdin/out/err through
+// a single pipe which is registered with a local socket in adbd. The local
+// socket uses the fdevent loop to pass raw data between this pipe and the
+// transport, which then passes data back to the adb client. Cleanup is done by
+// waiting in a separate thread for the subprocesses to exit and then signaling
+// a separate fdevent to close out the local socket from the main loop.
+//
+// ------------------+-------------------------+------------------------------
+//   Subprocess      |  adbd subprocess thread |   adbd main fdevent loop
+// ------------------+-------------------------+------------------------------
+//                   |                         |
+//   stdin/out/err <----------------------------->       LocalSocket
+//      |            |                         |
+//      |            |      Block on exit      |
+//      |            |           *             |
+//      v            |           *             |
+//     Exit         --->      Unblock          |
+//                   |           |             |
+//                   |           v             |
+//                   |   Notify shell exit FD --->    Close LocalSocket
+// ------------------+-------------------------+------------------------------
+//
+// The protocol requires the thread to intercept stdin/out/err in order to
+// wrap/unwrap data with shell protocol packets.
+//
+// ------------------+-------------------------+------------------------------
+//   Subprocess      |  adbd subprocess thread |   adbd main fdevent loop
+// ------------------+-------------------------+------------------------------
+//                   |                         |
+//     stdin/out   <--->      Protocol       <--->       LocalSocket
+//     stderr       --->      Protocol        --->       LocalSocket
+//       |           |                         |
+//       v           |                         |
+//      Exit        --->  Exit code protocol  --->       LocalSocket
+//                   |           |             |
+//                   |           v             |
+//                   |   Notify shell exit FD --->    Close LocalSocket
+// ------------------+-------------------------+------------------------------
+//
+// An alternate approach is to put the protocol wrapping/unwrapping in the main
+// fdevent loop, which has the advantage of being able to re-use the existing
+// select() code for handling data streams. However, implementation turned out
+// to be more complex due to partial reads and non-blocking I/O so this model
+// was chosen instead.
+
+#define TRACE_TAG SHELL
+
+#include "sysdeps.h"
+
+#include "shell_service.h"
+
+#include <errno.h>
+#include <paths.h>
+#include <pty.h>
+#include <pwd.h>
+#include <sys/select.h>
+#include <termios.h>
+
+#include <memory>
+#include <string>
+#include <thread>
+#include <unordered_map>
+#include <vector>
+
+#include <android-base/logging.h>
+#include <android-base/properties.h>
+#include <android-base/stringprintf.h>
+#include <private/android_logger.h>
+#include <selinux/android.h>
+
+#include "adb.h"
+#include "adb_io.h"
+#include "adb_trace.h"
+#include "adb_unique_fd.h"
+#include "adb_utils.h"
+#include "security_log_tags.h"
+#include "shell_protocol.h"
+
+namespace {
+
+// Reads from |fd| until close or failure.
+std::string ReadAll(int fd) {
+    char buffer[512];
+    std::string received;
+
+    while (1) {
+        int bytes = adb_read(fd, buffer, sizeof(buffer));
+        if (bytes <= 0) {
+            break;
+        }
+        received.append(buffer, bytes);
+    }
+
+    return received;
+}
+
+// Creates a socketpair and saves the endpoints to |fd1| and |fd2|.
+bool CreateSocketpair(unique_fd* fd1, unique_fd* fd2) {
+    int sockets[2];
+    if (adb_socketpair(sockets) < 0) {
+        PLOG(ERROR) << "cannot create socket pair";
+        return false;
+    }
+    fd1->reset(sockets[0]);
+    fd2->reset(sockets[1]);
+    return true;
+}
+
+class Subprocess {
+  public:
+    Subprocess(const std::string& command, const char* terminal_type,
+               SubprocessType type, SubprocessProtocol protocol);
+    ~Subprocess();
+
+    const std::string& command() const { return command_; }
+
+    int ReleaseLocalSocket() { return local_socket_sfd_.release(); }
+
+    pid_t pid() const { return pid_; }
+
+    // Sets up FDs, forks a subprocess, starts the subprocess manager thread,
+    // and exec's the child. Returns false and sets error on failure.
+    bool ForkAndExec(std::string* _Nonnull error);
+
+    // Start the subprocess manager thread. Consumes the subprocess, regardless of success.
+    // Returns false and sets error on failure.
+    static bool StartThread(std::unique_ptr<Subprocess> subprocess,
+                            std::string* _Nonnull error);
+
+  private:
+    // Opens the file at |pts_name|.
+    int OpenPtyChildFd(const char* pts_name, unique_fd* error_sfd);
+
+    static void ThreadHandler(void* userdata);
+    void PassDataStreams();
+    void WaitForExit();
+
+    unique_fd* SelectLoop(fd_set* master_read_set_ptr,
+                          fd_set* master_write_set_ptr);
+
+    // Input/output stream handlers. Success returns nullptr, failure returns
+    // a pointer to the failed FD.
+    unique_fd* PassInput();
+    unique_fd* PassOutput(unique_fd* sfd, ShellProtocol::Id id);
+
+    const std::string command_;
+    const std::string terminal_type_;
+    bool make_pty_raw_ = false;
+    SubprocessType type_;
+    SubprocessProtocol protocol_;
+    pid_t pid_ = -1;
+    unique_fd local_socket_sfd_;
+
+    // Shell protocol variables.
+    unique_fd stdinout_sfd_, stderr_sfd_, protocol_sfd_;
+    std::unique_ptr<ShellProtocol> input_, output_;
+    size_t input_bytes_left_ = 0;
+
+    DISALLOW_COPY_AND_ASSIGN(Subprocess);
+};
+
+Subprocess::Subprocess(const std::string& command, const char* terminal_type,
+                       SubprocessType type, SubprocessProtocol protocol)
+    : command_(command),
+      terminal_type_(terminal_type ? terminal_type : ""),
+      type_(type),
+      protocol_(protocol) {
+    // If we aren't using the shell protocol we must allocate a PTY to properly close the
+    // subprocess. PTYs automatically send SIGHUP to the slave-side process when the master side
+    // of the PTY closes, which we rely on. If we use a raw pipe, processes that don't read/write,
+    // e.g. screenrecord, will never notice the broken pipe and terminate.
+    // The shell protocol doesn't require a PTY because it's always monitoring the local socket FD
+    // with select() and will send SIGHUP manually to the child process.
+    if (protocol_ == SubprocessProtocol::kNone && type_ == SubprocessType::kRaw) {
+        // Disable PTY input/output processing since the client is expecting raw data.
+        D("Can't create raw subprocess without shell protocol, using PTY in raw mode instead");
+        type_ = SubprocessType::kPty;
+        make_pty_raw_ = true;
+    }
+}
+
+Subprocess::~Subprocess() {
+    WaitForExit();
+}
+
+static std::string GetHostName() {
+    char buf[HOST_NAME_MAX];
+    if (gethostname(buf, sizeof(buf)) != -1 && strcmp(buf, "localhost") != 0) return buf;
+
+    return android::base::GetProperty("ro.product.device", "android");
+}
+
+bool Subprocess::ForkAndExec(std::string* error) {
+    unique_fd child_stdinout_sfd, child_stderr_sfd;
+    unique_fd parent_error_sfd, child_error_sfd;
+    char pts_name[PATH_MAX];
+
+    if (command_.empty()) {
+        __android_log_security_bswrite(SEC_TAG_ADB_SHELL_INTERACTIVE, "");
+    } else {
+        __android_log_security_bswrite(SEC_TAG_ADB_SHELL_CMD, command_.c_str());
+    }
+
+    // Create a socketpair for the fork() child to report any errors back to the parent. Since we
+    // use threads, logging directly from the child might deadlock due to locks held in another
+    // thread during the fork.
+    if (!CreateSocketpair(&parent_error_sfd, &child_error_sfd)) {
+        *error = android::base::StringPrintf(
+            "failed to create pipe for subprocess error reporting: %s", strerror(errno));
+        return false;
+    }
+
+    // Construct the environment for the child before we fork.
+    passwd* pw = getpwuid(getuid());
+    std::unordered_map<std::string, std::string> env;
+    if (environ) {
+        char** current = environ;
+        while (char* env_cstr = *current++) {
+            std::string env_string = env_cstr;
+            char* delimiter = strchr(&env_string[0], '=');
+
+            // Drop any values that don't contain '='.
+            if (delimiter) {
+                *delimiter++ = '\0';
+                env[env_string.c_str()] = delimiter;
+            }
+        }
+    }
+
+    if (pw != nullptr) {
+        env["HOME"] = pw->pw_dir;
+        env["HOSTNAME"] = GetHostName();
+        env["LOGNAME"] = pw->pw_name;
+        env["SHELL"] = pw->pw_shell;
+        env["TMPDIR"] = "/data/local/tmp";
+        env["USER"] = pw->pw_name;
+    }
+
+    if (!terminal_type_.empty()) {
+        env["TERM"] = terminal_type_;
+    }
+
+    std::vector<std::string> joined_env;
+    for (auto it : env) {
+        const char* key = it.first.c_str();
+        const char* value = it.second.c_str();
+        joined_env.push_back(android::base::StringPrintf("%s=%s", key, value));
+    }
+
+    std::vector<const char*> cenv;
+    for (const std::string& str : joined_env) {
+        cenv.push_back(str.c_str());
+    }
+    cenv.push_back(nullptr);
+
+    if (type_ == SubprocessType::kPty) {
+        int fd;
+        pid_ = forkpty(&fd, pts_name, nullptr, nullptr);
+        if (pid_ > 0) {
+          stdinout_sfd_.reset(fd);
+        }
+    } else {
+        if (!CreateSocketpair(&stdinout_sfd_, &child_stdinout_sfd)) {
+            *error = android::base::StringPrintf("failed to create socketpair for stdin/out: %s",
+                                                 strerror(errno));
+            return false;
+        }
+        // Raw subprocess + shell protocol allows for splitting stderr.
+        if (protocol_ == SubprocessProtocol::kShell &&
+                !CreateSocketpair(&stderr_sfd_, &child_stderr_sfd)) {
+            *error = android::base::StringPrintf("failed to create socketpair for stderr: %s",
+                                                 strerror(errno));
+            return false;
+        }
+        pid_ = fork();
+    }
+
+    if (pid_ == -1) {
+        *error = android::base::StringPrintf("fork failed: %s", strerror(errno));
+        return false;
+    }
+
+    if (pid_ == 0) {
+        // Subprocess child.
+        setsid();
+
+        if (type_ == SubprocessType::kPty) {
+            child_stdinout_sfd.reset(OpenPtyChildFd(pts_name, &child_error_sfd));
+        }
+
+        dup2(child_stdinout_sfd, STDIN_FILENO);
+        dup2(child_stdinout_sfd, STDOUT_FILENO);
+        dup2(child_stderr_sfd != -1 ? child_stderr_sfd : child_stdinout_sfd, STDERR_FILENO);
+
+        // exec doesn't trigger destructors, close the FDs manually.
+        stdinout_sfd_.reset(-1);
+        stderr_sfd_.reset(-1);
+        child_stdinout_sfd.reset(-1);
+        child_stderr_sfd.reset(-1);
+        parent_error_sfd.reset(-1);
+        close_on_exec(child_error_sfd);
+
+        // adbd sets SIGPIPE to SIG_IGN to get EPIPE instead, and Linux propagates that to child
+        // 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)));
+        }
+
+#ifdef __ANDROID_RECOVERY__
+        // Special routine for recovery. Switch to shell domain when adbd is
+        // is running with dropped privileged (i.e. not running as root) and
+        // is built for the recovery mode. This is required because recovery
+        // rootfs is not labeled and everything is labeled just as rootfs.
+        char* con = nullptr;
+        if (getcon(&con) == 0) {
+            if (!strcmp(con, "u:r:adbd:s0")) {
+                if (selinux_android_setcon("u:r:shell:s0") < 0) {
+                    LOG(FATAL) << "Could not set SELinux context for subprocess";
+                }
+            }
+            freecon(con);
+        } else {
+            LOG(FATAL) << "Failed to get SELinux context";
+        }
+#endif
+
+        if (command_.empty()) {
+            // 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());
+        }
+        WriteFdExactly(child_error_sfd, "exec '" _PATH_BSHELL "' failed: ");
+        WriteFdExactly(child_error_sfd, strerror(errno));
+        child_error_sfd.reset(-1);
+        _Exit(1);
+    }
+
+    // Subprocess parent.
+    D("subprocess parent: stdin/stdout FD = %d, stderr FD = %d",
+      stdinout_sfd_.get(), stderr_sfd_.get());
+
+    // Wait to make sure the subprocess exec'd without error.
+    child_error_sfd.reset(-1);
+    std::string error_message = ReadAll(parent_error_sfd);
+    if (!error_message.empty()) {
+        *error = error_message;
+        return false;
+    }
+
+    D("subprocess parent: exec completed");
+    if (protocol_ == SubprocessProtocol::kNone) {
+        // No protocol: all streams pass through the stdinout FD and hook
+        // directly into the local socket for raw data transfer.
+        local_socket_sfd_.reset(stdinout_sfd_.release());
+    } else {
+        // Shell protocol: create another socketpair to intercept data.
+        if (!CreateSocketpair(&protocol_sfd_, &local_socket_sfd_)) {
+            *error = android::base::StringPrintf(
+                "failed to create socketpair to intercept data: %s", strerror(errno));
+            kill(pid_, SIGKILL);
+            return false;
+        }
+        D("protocol FD = %d", protocol_sfd_.get());
+
+        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);
+            return false;
+        }
+
+        // Don't let reads/writes to the subprocess block our thread. This isn't
+        // likely but could happen under unusual circumstances, such as if we
+        // write a ton of data to stdin but the subprocess never reads it and
+        // the pipe fills up.
+        for (int fd : {stdinout_sfd_.get(), stderr_sfd_.get()}) {
+            if (fd >= 0) {
+                if (!set_file_block_mode(fd, false)) {
+                    *error = android::base::StringPrintf(
+                        "failed to set non-blocking mode for fd %d", fd);
+                    kill(pid_, SIGKILL);
+                    return false;
+                }
+            }
+        }
+    }
+
+    D("subprocess parent: completed");
+    return true;
+}
+
+bool Subprocess::StartThread(std::unique_ptr<Subprocess> subprocess, std::string* error) {
+    Subprocess* raw = subprocess.release();
+    std::thread(ThreadHandler, raw).detach();
+
+    return true;
+}
+
+int Subprocess::OpenPtyChildFd(const char* pts_name, unique_fd* error_sfd) {
+    int child_fd = adb_open(pts_name, O_RDWR | O_CLOEXEC);
+    if (child_fd == -1) {
+        // Don't use WriteFdFmt; since we're in the fork() child we don't want
+        // to allocate any heap memory to avoid race conditions.
+        const char* messages[] = {"child failed to open pseudo-term slave ",
+                                  pts_name, ": ", strerror(errno)};
+        for (const char* message : messages) {
+            WriteFdExactly(*error_sfd, message);
+        }
+        abort();
+    }
+
+    if (make_pty_raw_) {
+        termios tattr;
+        if (tcgetattr(child_fd, &tattr) == -1) {
+            int saved_errno = errno;
+            WriteFdExactly(*error_sfd, "tcgetattr failed: ");
+            WriteFdExactly(*error_sfd, strerror(saved_errno));
+            abort();
+        }
+
+        cfmakeraw(&tattr);
+        if (tcsetattr(child_fd, TCSADRAIN, &tattr) == -1) {
+            int saved_errno = errno;
+            WriteFdExactly(*error_sfd, "tcsetattr failed: ");
+            WriteFdExactly(*error_sfd, strerror(saved_errno));
+            abort();
+        }
+    }
+
+    return child_fd;
+}
+
+void Subprocess::ThreadHandler(void* userdata) {
+    Subprocess* subprocess = reinterpret_cast<Subprocess*>(userdata);
+
+    adb_thread_setname(android::base::StringPrintf("shell svc %d", subprocess->pid()));
+
+    D("passing data streams for PID %d", subprocess->pid());
+    subprocess->PassDataStreams();
+
+    D("deleting Subprocess for PID %d", subprocess->pid());
+    delete subprocess;
+}
+
+void Subprocess::PassDataStreams() {
+    if (protocol_sfd_ == -1) {
+        return;
+    }
+
+    // Start by trying to read from the protocol FD, stdout, and stderr.
+    fd_set master_read_set, master_write_set;
+    FD_ZERO(&master_read_set);
+    FD_ZERO(&master_write_set);
+    for (unique_fd* sfd : {&protocol_sfd_, &stdinout_sfd_, &stderr_sfd_}) {
+        if (*sfd != -1) {
+            FD_SET(*sfd, &master_read_set);
+        }
+    }
+
+    // Pass data until the protocol FD or both the subprocess pipes die, at
+    // which point we can't pass any more data.
+    while (protocol_sfd_ != -1 && (stdinout_sfd_ != -1 || stderr_sfd_ != -1)) {
+        unique_fd* dead_sfd = SelectLoop(&master_read_set, &master_write_set);
+        if (dead_sfd) {
+            D("closing FD %d", dead_sfd->get());
+            FD_CLR(*dead_sfd, &master_read_set);
+            FD_CLR(*dead_sfd, &master_write_set);
+            if (dead_sfd == &protocol_sfd_) {
+                // Using SIGHUP is a decent general way to indicate that the
+                // controlling process is going away. If specific signals are
+                // needed (e.g. SIGINT), pass those through the shell protocol
+                // and only fall back on this for unexpected closures.
+                D("protocol FD died, sending SIGHUP to pid %d", pid_);
+                kill(pid_, SIGHUP);
+
+                // We also need to close the pipes connected to the child process
+                // so that if it ignores SIGHUP and continues to write data it
+                // won't fill up the pipe and block.
+                stdinout_sfd_.reset();
+                stderr_sfd_.reset();
+            }
+            dead_sfd->reset();
+        }
+    }
+}
+
+namespace {
+
+inline bool ValidAndInSet(const unique_fd& sfd, fd_set* set) {
+    return sfd != -1 && FD_ISSET(sfd, set);
+}
+
+}   // namespace
+
+unique_fd* Subprocess::SelectLoop(fd_set* master_read_set_ptr,
+                                  fd_set* master_write_set_ptr) {
+    fd_set read_set, write_set;
+    int select_n = std::max(std::max(protocol_sfd_, stdinout_sfd_), stderr_sfd_) + 1;
+    unique_fd* dead_sfd = nullptr;
+
+    // Keep calling select() and passing data until an FD closes/errors.
+    while (!dead_sfd) {
+        memcpy(&read_set, master_read_set_ptr, sizeof(read_set));
+        memcpy(&write_set, master_write_set_ptr, sizeof(write_set));
+        if (select(select_n, &read_set, &write_set, nullptr, nullptr) < 0) {
+            if (errno == EINTR) {
+                continue;
+            } else {
+                PLOG(ERROR) << "select failed, closing subprocess pipes";
+                stdinout_sfd_.reset(-1);
+                stderr_sfd_.reset(-1);
+                return nullptr;
+            }
+        }
+
+        // Read stdout, write to protocol FD.
+        if (ValidAndInSet(stdinout_sfd_, &read_set)) {
+            dead_sfd = PassOutput(&stdinout_sfd_, ShellProtocol::kIdStdout);
+        }
+
+        // Read stderr, write to protocol FD.
+        if (!dead_sfd && ValidAndInSet(stderr_sfd_, &read_set)) {
+            dead_sfd = PassOutput(&stderr_sfd_, ShellProtocol::kIdStderr);
+        }
+
+        // Read protocol FD, write to stdin.
+        if (!dead_sfd && ValidAndInSet(protocol_sfd_, &read_set)) {
+            dead_sfd = PassInput();
+            // If we didn't finish writing, block on stdin write.
+            if (input_bytes_left_) {
+                FD_CLR(protocol_sfd_, master_read_set_ptr);
+                FD_SET(stdinout_sfd_, master_write_set_ptr);
+            }
+        }
+
+        // Continue writing to stdin; only happens if a previous write blocked.
+        if (!dead_sfd && ValidAndInSet(stdinout_sfd_, &write_set)) {
+            dead_sfd = PassInput();
+            // If we finished writing, go back to blocking on protocol read.
+            if (!input_bytes_left_) {
+                FD_SET(protocol_sfd_, master_read_set_ptr);
+                FD_CLR(stdinout_sfd_, master_write_set_ptr);
+            }
+        }
+    }  // while (!dead_sfd)
+
+    return dead_sfd;
+}
+
+unique_fd* Subprocess::PassInput() {
+    // Only read a new packet if we've finished writing the last one.
+    if (!input_bytes_left_) {
+        if (!input_->Read()) {
+            // Read() uses ReadFdExactly() which sets errno to 0 on EOF.
+            if (errno != 0) {
+                PLOG(ERROR) << "error reading protocol FD " << protocol_sfd_;
+            }
+            return &protocol_sfd_;
+        }
+
+        if (stdinout_sfd_ != -1) {
+            switch (input_->id()) {
+                case ShellProtocol::kIdWindowSizeChange:
+                    int rows, cols, x_pixels, y_pixels;
+                    if (sscanf(input_->data(), "%dx%d,%dx%d",
+                               &rows, &cols, &x_pixels, &y_pixels) == 4) {
+                        winsize ws;
+                        ws.ws_row = rows;
+                        ws.ws_col = cols;
+                        ws.ws_xpixel = x_pixels;
+                        ws.ws_ypixel = y_pixels;
+                        ioctl(stdinout_sfd_, TIOCSWINSZ, &ws);
+                    }
+                    break;
+                case ShellProtocol::kIdStdin:
+                    input_bytes_left_ = input_->data_length();
+                    break;
+                case ShellProtocol::kIdCloseStdin:
+                    if (type_ == SubprocessType::kRaw) {
+                        if (adb_shutdown(stdinout_sfd_, SHUT_WR) == 0) {
+                            return nullptr;
+                        }
+                        PLOG(ERROR) << "failed to shutdown writes to FD "
+                                    << stdinout_sfd_;
+                        return &stdinout_sfd_;
+                    } else {
+                        // PTYs can't close just input, so rather than close the
+                        // FD and risk losing subprocess output, leave it open.
+                        // This only happens if the client starts a PTY shell
+                        // non-interactively which is rare and unsupported.
+                        // If necessary, the client can manually close the shell
+                        // with `exit` or by killing the adb client process.
+                        D("can't close input for PTY FD %d", stdinout_sfd_.get());
+                    }
+                    break;
+            }
+        }
+    }
+
+    if (input_bytes_left_ > 0) {
+        int index = input_->data_length() - input_bytes_left_;
+        int bytes = adb_write(stdinout_sfd_, input_->data() + index, input_bytes_left_);
+        if (bytes == 0 || (bytes < 0 && errno != EAGAIN)) {
+            if (bytes < 0) {
+                PLOG(ERROR) << "error reading stdin FD " << stdinout_sfd_;
+            }
+            // stdin is done, mark this packet as finished and we'll just start
+            // dumping any further data received from the protocol FD.
+            input_bytes_left_ = 0;
+            return &stdinout_sfd_;
+        } else if (bytes > 0) {
+            input_bytes_left_ -= bytes;
+        }
+    }
+
+    return nullptr;
+}
+
+unique_fd* Subprocess::PassOutput(unique_fd* sfd, ShellProtocol::Id id) {
+    int bytes = adb_read(*sfd, output_->data(), output_->data_capacity());
+    if (bytes == 0 || (bytes < 0 && errno != EAGAIN)) {
+        // read() returns EIO if a PTY closes; don't report this as an error,
+        // it just means the subprocess completed.
+        if (bytes < 0 && !(type_ == SubprocessType::kPty && errno == EIO)) {
+            PLOG(ERROR) << "error reading output FD " << *sfd;
+        }
+        return sfd;
+    }
+
+    if (bytes > 0 && !output_->Write(id, bytes)) {
+        if (errno != 0) {
+            PLOG(ERROR) << "error reading protocol FD " << protocol_sfd_;
+        }
+        return &protocol_sfd_;
+    }
+
+    return nullptr;
+}
+
+void Subprocess::WaitForExit() {
+    int exit_code = 1;
+
+    D("waiting for pid %d", pid_);
+    while (true) {
+        int status;
+        if (pid_ == waitpid(pid_, &status, 0)) {
+            D("post waitpid (pid=%d) status=%04x", pid_, status);
+            if (WIFSIGNALED(status)) {
+                exit_code = 0x80 | WTERMSIG(status);
+                D("subprocess killed by signal %d", WTERMSIG(status));
+                break;
+            } else if (!WIFEXITED(status)) {
+                D("subprocess didn't exit");
+                break;
+            } else if (WEXITSTATUS(status) >= 0) {
+                exit_code = WEXITSTATUS(status);
+                D("subprocess exit code = %d", WEXITSTATUS(status));
+                break;
+            }
+        }
+    }
+
+    // If we have an open protocol FD send an exit packet.
+    if (protocol_sfd_ != -1) {
+        output_->data()[0] = exit_code;
+        if (output_->Write(ShellProtocol::kIdExit, 1)) {
+            D("wrote the exit code packet: %d", exit_code);
+        } else {
+            PLOG(ERROR) << "failed to write the exit code packet";
+        }
+        protocol_sfd_.reset(-1);
+    }
+}
+
+}  // namespace
+
+// Create a pipe containing the error.
+static unique_fd ReportError(SubprocessProtocol protocol, const std::string& message) {
+    unique_fd read, write;
+    if (!Pipe(&read, &write)) {
+        PLOG(ERROR) << "failed to create pipe to report error";
+        return unique_fd{};
+    }
+
+    std::string buf = android::base::StringPrintf("error: %s\n", message.c_str());
+    if (protocol == SubprocessProtocol::kShell) {
+        ShellProtocol::Id id = ShellProtocol::kIdStderr;
+        uint32_t length = buf.length();
+        WriteFdExactly(write.get(), &id, sizeof(id));
+        WriteFdExactly(write.get(), &length, sizeof(length));
+    }
+
+    WriteFdExactly(write.get(), buf.data(), buf.length());
+
+    if (protocol == SubprocessProtocol::kShell) {
+        ShellProtocol::Id id = ShellProtocol::kIdExit;
+        uint32_t length = 1;
+        char exit_code = 126;
+        WriteFdExactly(write.get(), &id, sizeof(id));
+        WriteFdExactly(write.get(), &length, sizeof(length));
+        WriteFdExactly(write.get(), &exit_code, sizeof(exit_code));
+    }
+
+    return read;
+}
+
+unique_fd StartSubprocess(const char* name, const char* terminal_type, SubprocessType type,
+                          SubprocessProtocol protocol) {
+    D("starting %s subprocess (protocol=%s, TERM=%s): '%s'",
+      type == SubprocessType::kRaw ? "raw" : "PTY",
+      protocol == SubprocessProtocol::kNone ? "none" : "shell",
+      terminal_type, name);
+
+    auto subprocess = std::make_unique<Subprocess>(name, terminal_type, type, protocol);
+    if (!subprocess) {
+        LOG(ERROR) << "failed to allocate new subprocess";
+        return ReportError(protocol, "failed to allocate new subprocess");
+    }
+
+    std::string error;
+    if (!subprocess->ForkAndExec(&error)) {
+        LOG(ERROR) << "failed to start subprocess: " << error;
+        return ReportError(protocol, error);
+    }
+
+    unique_fd local_socket(subprocess->ReleaseLocalSocket());
+    D("subprocess creation successful: local_socket_fd=%d, pid=%d", local_socket.get(),
+      subprocess->pid());
+
+    if (!Subprocess::StartThread(std::move(subprocess), &error)) {
+        LOG(ERROR) << "failed to start subprocess management thread: " << error;
+        return ReportError(protocol, error);
+    }
+
+    return local_socket;
+}
diff --git a/adb/daemon/shell_service.h b/adb/daemon/shell_service.h
new file mode 100644
index 0000000..2a48923
--- /dev/null
+++ b/adb/daemon/shell_service.h
@@ -0,0 +1,36 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include "adb_unique_fd.h"
+
+enum class SubprocessType {
+    kPty,
+    kRaw,
+};
+
+enum class SubprocessProtocol {
+    kNone,
+    kShell,
+};
+
+// Forks and starts a new shell subprocess. If |name| is empty an interactive
+// shell is started, otherwise |name| is executed non-interactively.
+//
+// Returns an open FD connected to the subprocess or -1 on failure.
+unique_fd StartSubprocess(const char* name, const char* terminal_type, SubprocessType type,
+                          SubprocessProtocol protocol);
diff --git a/adb/daemon/shell_service_test.cpp b/adb/daemon/shell_service_test.cpp
new file mode 100644
index 0000000..323bcec
--- /dev/null
+++ b/adb/daemon/shell_service_test.cpp
@@ -0,0 +1,267 @@
+/*
+ * Copyright (C) 2015 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 "shell_service.h"
+
+#include <gtest/gtest.h>
+
+#include <signal.h>
+
+#include <string>
+#include <vector>
+
+#include <android-base/strings.h>
+
+#include "adb.h"
+#include "adb_io.h"
+#include "shell_protocol.h"
+#include "sysdeps.h"
+
+class ShellServiceTest : public ::testing::Test {
+  public:
+    static void SetUpTestCase() {
+        // This is normally done in main.cpp.
+        saved_sigpipe_handler_ = signal(SIGPIPE, SIG_IGN);
+
+    }
+
+    static void TearDownTestCase() {
+        signal(SIGPIPE, saved_sigpipe_handler_);
+    }
+
+    // Helpers to start and cleanup a subprocess. Cleanup normally does not
+    // need to be called manually unless multiple subprocesses are run from
+    // a single test.
+    void StartTestSubprocess(const char* command, SubprocessType type,
+                             SubprocessProtocol protocol);
+    void CleanupTestSubprocess();
+
+    virtual void TearDown() override {
+        void CleanupTestSubprocess();
+    }
+
+    static sighandler_t saved_sigpipe_handler_;
+
+    unique_fd subprocess_fd_;
+};
+
+sighandler_t ShellServiceTest::saved_sigpipe_handler_ = nullptr;
+
+void ShellServiceTest::StartTestSubprocess(
+        const char* command, SubprocessType type, SubprocessProtocol protocol) {
+    subprocess_fd_ = StartSubprocess(command, nullptr, type, protocol);
+    ASSERT_TRUE(subprocess_fd_ >= 0);
+}
+
+void ShellServiceTest::CleanupTestSubprocess() {
+}
+
+namespace {
+
+// Reads raw data from |fd| until it closes or errors.
+std::string ReadRaw(int fd) {
+    char buffer[1024];
+    char *cur_ptr = buffer, *end_ptr = buffer + sizeof(buffer);
+
+    while (1) {
+        int bytes = adb_read(fd, cur_ptr, end_ptr - cur_ptr);
+        if (bytes <= 0) {
+            return std::string(buffer, cur_ptr);
+        }
+        cur_ptr += bytes;
+    }
+}
+
+// Reads shell protocol data from |fd| until it closes or errors. Fills
+// |stdout| and |stderr| with their respective data, and returns the exit code
+// read from the protocol or -1 if an exit code packet was not received.
+int ReadShellProtocol(int fd, std::string* stdout, std::string* stderr) {
+    int exit_code = -1;
+    stdout->clear();
+    stderr->clear();
+
+    ShellProtocol* protocol = new ShellProtocol(fd);
+    while (protocol->Read()) {
+        switch (protocol->id()) {
+            case ShellProtocol::kIdStdout:
+                stdout->append(protocol->data(), protocol->data_length());
+                break;
+            case ShellProtocol::kIdStderr:
+                stderr->append(protocol->data(), protocol->data_length());
+                break;
+            case ShellProtocol::kIdExit:
+                EXPECT_EQ(-1, exit_code) << "Multiple exit packets received";
+                EXPECT_EQ(1u, protocol->data_length());
+                exit_code = protocol->data()[0];
+                break;
+            default:
+                ADD_FAILURE() << "Unidentified packet ID: " << protocol->id();
+        }
+    }
+    delete protocol;
+
+    return exit_code;
+}
+
+// Checks if each line in |lines| exists in the same order in |output|. Blank
+// lines in |output| are ignored for simplicity.
+bool ExpectLinesEqual(const std::string& output,
+                      const std::vector<std::string>& lines) {
+    auto output_lines = android::base::Split(output, "\r\n");
+    size_t i = 0;
+
+    for (const std::string& line : lines) {
+        // Skip empty lines in output.
+        while (i < output_lines.size() && output_lines[i].empty()) {
+            ++i;
+        }
+        if (i >= output_lines.size()) {
+            ADD_FAILURE() << "Ran out of output lines";
+            return false;
+        }
+        EXPECT_EQ(line, output_lines[i]);
+        ++i;
+    }
+
+    while (i < output_lines.size() && output_lines[i].empty()) {
+        ++i;
+    }
+    EXPECT_EQ(i, output_lines.size()) << "Found unmatched output lines";
+    return true;
+}
+
+}  // namespace
+
+// Tests a raw subprocess with no protocol.
+TEST_F(ShellServiceTest, RawNoProtocolSubprocess) {
+    // [ -t 0 ] checks if stdin is connected to a terminal.
+    ASSERT_NO_FATAL_FAILURE(StartTestSubprocess(
+            "echo foo; echo bar >&2; [ -t 0 ]; echo $?",
+            SubprocessType::kRaw, SubprocessProtocol::kNone));
+
+    // [ -t 0 ] == 0 means we have a terminal (PTY). Even when requesting a raw subprocess, without
+    // the shell protocol we should always force a PTY to ensure proper cleanup.
+    ExpectLinesEqual(ReadRaw(subprocess_fd_), {"foo", "bar", "0"});
+}
+
+// Tests a PTY subprocess with no protocol.
+TEST_F(ShellServiceTest, PtyNoProtocolSubprocess) {
+    // [ -t 0 ] checks if stdin is connected to a terminal.
+    ASSERT_NO_FATAL_FAILURE(StartTestSubprocess(
+            "echo foo; echo bar >&2; [ -t 0 ]; echo $?",
+            SubprocessType::kPty, SubprocessProtocol::kNone));
+
+    // [ -t 0 ] == 0 means we have a terminal (PTY).
+    ExpectLinesEqual(ReadRaw(subprocess_fd_), {"foo", "bar", "0"});
+}
+
+// Tests a raw subprocess with the shell protocol.
+TEST_F(ShellServiceTest, RawShellProtocolSubprocess) {
+    ASSERT_NO_FATAL_FAILURE(StartTestSubprocess(
+            "echo foo; echo bar >&2; echo baz; exit 24",
+            SubprocessType::kRaw, SubprocessProtocol::kShell));
+
+    std::string stdout, stderr;
+    EXPECT_EQ(24, ReadShellProtocol(subprocess_fd_, &stdout, &stderr));
+    ExpectLinesEqual(stdout, {"foo", "baz"});
+    ExpectLinesEqual(stderr, {"bar"});
+}
+
+// Tests a PTY subprocess with the shell protocol.
+TEST_F(ShellServiceTest, PtyShellProtocolSubprocess) {
+    ASSERT_NO_FATAL_FAILURE(StartTestSubprocess(
+            "echo foo; echo bar >&2; echo baz; exit 50",
+            SubprocessType::kPty, SubprocessProtocol::kShell));
+
+    // PTY always combines stdout and stderr but the shell protocol should
+    // still give us an exit code.
+    std::string stdout, stderr;
+    EXPECT_EQ(50, ReadShellProtocol(subprocess_fd_, &stdout, &stderr));
+    ExpectLinesEqual(stdout, {"foo", "bar", "baz"});
+    ExpectLinesEqual(stderr, {});
+}
+
+// Tests an interactive PTY session.
+TEST_F(ShellServiceTest, InteractivePtySubprocess) {
+    ASSERT_NO_FATAL_FAILURE(StartTestSubprocess(
+            "", SubprocessType::kPty, SubprocessProtocol::kShell));
+
+    // Use variable substitution so echoed input is different from output.
+    const char* commands[] = {"TEST_STR=abc123",
+                              "echo --${TEST_STR}--",
+                              "exit"};
+
+    ShellProtocol* protocol = new ShellProtocol(subprocess_fd_);
+    for (std::string command : commands) {
+        // Interactive shell requires a newline to complete each command.
+        command.push_back('\n');
+        memcpy(protocol->data(), command.data(), command.length());
+        ASSERT_TRUE(protocol->Write(ShellProtocol::kIdStdin, command.length()));
+    }
+    delete protocol;
+
+    std::string stdout, stderr;
+    EXPECT_EQ(0, ReadShellProtocol(subprocess_fd_, &stdout, &stderr));
+    // An unpredictable command prompt makes parsing exact output difficult but
+    // it should at least contain echoed input and the expected output.
+    for (const char* command : commands) {
+        EXPECT_FALSE(stdout.find(command) == std::string::npos);
+    }
+    EXPECT_FALSE(stdout.find("--abc123--") == std::string::npos);
+}
+
+// Tests closing raw subprocess stdin.
+TEST_F(ShellServiceTest, CloseClientStdin) {
+    ASSERT_NO_FATAL_FAILURE(StartTestSubprocess(
+            "cat; echo TEST_DONE",
+            SubprocessType::kRaw, SubprocessProtocol::kShell));
+
+    std::string input = "foo\nbar";
+    ShellProtocol* protocol = new ShellProtocol(subprocess_fd_);
+    memcpy(protocol->data(), input.data(), input.length());
+    ASSERT_TRUE(protocol->Write(ShellProtocol::kIdStdin, input.length()));
+    ASSERT_TRUE(protocol->Write(ShellProtocol::kIdCloseStdin, 0));
+    delete protocol;
+
+    std::string stdout, stderr;
+    EXPECT_EQ(0, ReadShellProtocol(subprocess_fd_, &stdout, &stderr));
+    ExpectLinesEqual(stdout, {"foo", "barTEST_DONE"});
+    ExpectLinesEqual(stderr, {});
+}
+
+// Tests that nothing breaks when the stdin/stdout pipe closes.
+TEST_F(ShellServiceTest, CloseStdinStdoutSubprocess) {
+    ASSERT_NO_FATAL_FAILURE(StartTestSubprocess(
+            "exec 0<&-; exec 1>&-; echo bar >&2",
+            SubprocessType::kRaw, SubprocessProtocol::kShell));
+
+    std::string stdout, stderr;
+    EXPECT_EQ(0, ReadShellProtocol(subprocess_fd_, &stdout, &stderr));
+    ExpectLinesEqual(stdout, {});
+    ExpectLinesEqual(stderr, {"bar"});
+}
+
+// Tests that nothing breaks when the stderr pipe closes.
+TEST_F(ShellServiceTest, CloseStderrSubprocess) {
+    ASSERT_NO_FATAL_FAILURE(StartTestSubprocess(
+            "exec 2>&-; echo foo",
+            SubprocessType::kRaw, SubprocessProtocol::kShell));
+
+    std::string stdout, stderr;
+    EXPECT_EQ(0, ReadShellProtocol(subprocess_fd_, &stdout, &stderr));
+    ExpectLinesEqual(stdout, {"foo"});
+    ExpectLinesEqual(stderr, {});
+}
diff --git a/adb/daemon/usb.cpp b/adb/daemon/usb.cpp
index d3b2f3d..c79d6fc 100644
--- a/adb/daemon/usb.cpp
+++ b/adb/daemon/usb.cpp
@@ -26,6 +26,7 @@
 #include <stdlib.h>
 #include <string.h>
 #include <sys/ioctl.h>
+#include <sys/mman.h>
 #include <sys/types.h>
 #include <unistd.h>
 
@@ -40,7 +41,7 @@
 #include <android-base/properties.h>
 
 #include "adb.h"
-#include "daemon/usb.h"
+#include "adbd/usb.h"
 #include "transport.h"
 
 using namespace std::chrono_literals;
@@ -49,23 +50,14 @@
 #define MAX_PACKET_SIZE_HS 512
 #define MAX_PACKET_SIZE_SS 1024
 
-// Kernels before 3.3 have a 16KiB transfer limit  That limit was replaced
-// with a 16MiB global limit in 3.3, but each URB submitted required a
-// contiguous kernel allocation, so you would get ENOMEM if you tried to
-// send something larger than the biggest available contiguous kernel
-// memory region. Large contiguous allocations could be unreliable
-// on a device kernel that has been running for a while fragmenting its
-// memory so we start with a larger allocation, and shrink the amount if
-// necessary.
 #define USB_FFS_BULK_SIZE 16384
 
+// Number of buffers needed to fit MAX_PAYLOAD, with an extra for ZLPs.
+#define USB_FFS_NUM_BUFS ((4 * MAX_PAYLOAD / USB_FFS_BULK_SIZE) + 1)
+
 #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 {
@@ -234,11 +226,36 @@
     },
 };
 
-bool init_functionfs(struct usb_handle* h) {
+static void aio_block_init(aio_block* aiob, unsigned num_bufs) {
+    aiob->iocb.resize(num_bufs);
+    aiob->iocbs.resize(num_bufs);
+    aiob->events.resize(num_bufs);
+    aiob->num_submitted = 0;
+    for (unsigned i = 0; i < num_bufs; i++) {
+        aiob->iocbs[i] = &aiob->iocb[i];
+    }
+    memset(&aiob->ctx, 0, sizeof(aiob->ctx));
+    if (io_setup(num_bufs, &aiob->ctx)) {
+        D("[ aio: got error on io_setup (%d) ]", errno);
+    }
+}
+
+static int getMaxPacketSize(int ffs_fd) {
+    usb_endpoint_descriptor desc;
+    if (ioctl(ffs_fd, FUNCTIONFS_ENDPOINT_DESC, reinterpret_cast<unsigned long>(&desc))) {
+        D("[ could not get endpoint descriptor! (%d) ]", errno);
+        return MAX_PACKET_SIZE_HS;
+    } else {
+        return desc.wMaxPacketSize;
+    }
+}
+
+static bool init_functionfs(struct usb_handle* h) {
+    LOG(INFO) << "initializing functionfs";
+
     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));
@@ -255,10 +272,10 @@
     v2_descriptor.os_desc = os_desc_compat;
 
     if (h->control < 0) { // might have already done this before
-        D("OPENING %s", USB_FFS_ADB_EP0);
-        h->control = adb_open(USB_FFS_ADB_EP0, O_RDWR);
+        LOG(INFO) << "opening control endpoint " << USB_FFS_ADB_EP0;
+        h->control = adb_open(USB_FFS_ADB_EP0, O_WRONLY);
         if (h->control < 0) {
-            D("[ %s: cannot open control endpoint: errno=%d]", USB_FFS_ADB_EP0, errno);
+            PLOG(ERROR) << "cannot open control endpoint " << USB_FFS_ADB_EP0;
             goto err;
         }
 
@@ -287,41 +304,21 @@
         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) {
-        D("[ %s: cannot open bulk-out ep: errno=%d ]", USB_FFS_ADB_OUT, errno);
+        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) {
-        D("[ %s: cannot open bulk-in ep: errno=%d ]", USB_FFS_ADB_IN, errno);
+        PLOG(ERROR) << "cannot open bulk-in endpoint " << USB_FFS_ADB_IN;
         goto err;
     }
 
-    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;
+    h->read_aiob.fd = h->bulk_out;
+    h->write_aiob.fd = h->bulk_in;
+    h->reads_zero_packets = true;
     return true;
 
 err:
@@ -340,9 +337,7 @@
     return false;
 }
 
-static void usb_ffs_open_thread(void* x) {
-    struct usb_handle* usb = (struct usb_handle*)x;
-
+static void usb_ffs_open_thread(usb_handle *usb) {
     adb_thread_setname("usb ffs open");
 
     while (true) {
@@ -356,13 +351,14 @@
 
         while (true) {
             if (init_functionfs(usb)) {
+                LOG(INFO) << "functionfs successfully initialized";
                 break;
             }
             std::this_thread::sleep_for(1s);
         }
 
-        D("[ usb_thread - registering device ]");
-        register_usb_transport(usb, 0, 0, 1);
+        LOG(INFO) << "registering usb transport";
+        register_usb_transport(usb, nullptr, nullptr, 1);
     }
 
     // never gets here
@@ -373,8 +369,9 @@
     D("about to write (fd=%d, len=%d)", h->bulk_in, len);
 
     const char* buf = static_cast<const char*>(data);
+    int orig_len = len;
     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));
@@ -385,15 +382,16 @@
     }
 
     D("[ done fd=%d ]", h->bulk_in);
-    return 0;
+    return orig_len;
 }
 
 static int usb_ffs_read(usb_handle* h, void* data, int len) {
     D("about to read (fd=%d, len=%d)", h->bulk_out, len);
 
     char* buf = static_cast<char*>(data);
+    int orig_len = len;
     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));
@@ -404,7 +402,74 @@
     }
 
     D("[ done fd=%d ]", h->bulk_out);
-    return 0;
+    return orig_len;
+}
+
+static int usb_ffs_do_aio(usb_handle* h, const void* data, int len, bool read) {
+    aio_block* aiob = read ? &h->read_aiob : &h->write_aiob;
+    bool zero_packet = false;
+
+    int num_bufs = len / h->io_size + (len % h->io_size == 0 ? 0 : 1);
+    const char* cur_data = reinterpret_cast<const char*>(data);
+    int packet_size = getMaxPacketSize(aiob->fd);
+
+    if (posix_madvise(const_cast<void*>(data), len, POSIX_MADV_SEQUENTIAL | POSIX_MADV_WILLNEED) <
+        0) {
+        D("[ Failed to madvise: %d ]", errno);
+    }
+
+    for (int i = 0; i < num_bufs; i++) {
+        int buf_len = std::min(len, static_cast<int>(h->io_size));
+        io_prep(&aiob->iocb[i], aiob->fd, cur_data, buf_len, 0, read);
+
+        len -= buf_len;
+        cur_data += buf_len;
+
+        if (len == 0 && buf_len % packet_size == 0 && read) {
+            // adb does not expect the device to send a zero packet after data transfer,
+            // but the host *does* send a zero packet for the device to read.
+            zero_packet = h->reads_zero_packets;
+        }
+    }
+    if (zero_packet) {
+        io_prep(&aiob->iocb[num_bufs], aiob->fd, reinterpret_cast<const void*>(cur_data),
+                packet_size, 0, read);
+        num_bufs += 1;
+    }
+
+    while (true) {
+        if (TEMP_FAILURE_RETRY(io_submit(aiob->ctx, num_bufs, aiob->iocbs.data())) < num_bufs) {
+            PLOG(ERROR) << "aio: got error submitting " << (read ? "read" : "write");
+            return -1;
+        }
+        if (TEMP_FAILURE_RETRY(io_getevents(aiob->ctx, num_bufs, num_bufs, aiob->events.data(),
+                                            nullptr)) < num_bufs) {
+            PLOG(ERROR) << "aio: got error waiting " << (read ? "read" : "write");
+            return -1;
+        }
+        if (num_bufs == 1 && aiob->events[0].res == -EINTR) {
+            continue;
+        }
+        int ret = 0;
+        for (int i = 0; i < num_bufs; i++) {
+            if (aiob->events[i].res < 0) {
+                errno = -aiob->events[i].res;
+                PLOG(ERROR) << "aio: got error event on " << (read ? "read" : "write")
+                            << " total bufs " << num_bufs;
+                return -1;
+            }
+            ret += aiob->events[i].res;
+        }
+        return ret;
+    }
+}
+
+static int usb_ffs_aio_read(usb_handle* h, void* data, int len) {
+    return usb_ffs_do_aio(h, data, len, true);
+}
+
+static int usb_ffs_aio_write(usb_handle* h, const void* data, int len) {
+    return usb_ffs_do_aio(h, data, len, false);
 }
 
 static void usb_ffs_kick(usb_handle* h) {
@@ -430,9 +495,12 @@
 }
 
 static void usb_ffs_close(usb_handle* h) {
+    LOG(INFO) << "closing functionfs transport";
+
     h->kicked = false;
     adb_close(h->bulk_out);
     adb_close(h->bulk_in);
+
     // Notify usb_adb_open_thread to open a new connection.
     h->lock.lock();
     h->open_new_connection = true;
@@ -440,24 +508,32 @@
     h->notify.notify_one();
 }
 
-static void usb_ffs_init() {
-    D("[ usb_init - using FunctionFS ]");
-
+usb_handle *create_usb_handle(unsigned num_bufs, unsigned io_size) {
     usb_handle* h = new usb_handle();
 
-    h->write = usb_ffs_write;
-    h->read = usb_ffs_read;
+    if (android::base::GetBoolProperty("sys.usb.ffs.aio_compat", false)) {
+        // Devices on older kernels (< 3.18) will not have aio support for ffs
+        // unless backported. Fall back on the non-aio functions instead.
+        h->write = usb_ffs_write;
+        h->read = usb_ffs_read;
+    } else {
+        h->write = usb_ffs_aio_write;
+        h->read = usb_ffs_aio_read;
+        aio_block_init(&h->read_aiob, num_bufs);
+        aio_block_init(&h->write_aiob, num_bufs);
+    }
+    h->io_size = io_size;
     h->kick = usb_ffs_kick;
     h->close = usb_ffs_close;
-
-    D("[ usb_init - starting thread ]");
-    std::thread(usb_ffs_open_thread, h).detach();
+    return h;
 }
 
 void usb_init() {
+    D("[ usb_init - using FunctionFS ]");
     dummy_fd = adb_open("/dev/null", O_WRONLY);
     CHECK_NE(dummy_fd, -1);
-    usb_ffs_init();
+
+    std::thread(usb_ffs_open_thread, create_usb_handle(USB_FFS_NUM_BUFS, USB_FFS_BULK_SIZE)).detach();
 }
 
 int usb_write(usb_handle* h, const void* data, int len) {
diff --git a/adb/daemon/usb.h b/adb/daemon/usb.h
deleted file mode 100644
index 55b5995..0000000
--- a/adb/daemon/usb.h
+++ /dev/null
@@ -1,45 +0,0 @@
-#pragma once
-
-/*
- * Copyright (C) 2017 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 <atomic>
-#include <condition_variable>
-#include <mutex>
-
-struct usb_handle {
-    usb_handle() : kicked(false) {
-    }
-
-    std::condition_variable notify;
-    std::mutex lock;
-    std::atomic<bool> kicked;
-    bool open_new_connection = true;
-
-    int (*write)(usb_handle* h, const void* data, int len);
-    int (*read)(usb_handle* h, void* data, int len);
-    void (*kick)(usb_handle* h);
-    void (*close)(usb_handle* h);
-
-    // FunctionFS
-    int control = -1;
-    int bulk_out = -1; /* "out" from the host's perspective => source for adbd */
-    int bulk_in = -1;  /* "in" from the host's perspective => sink for adbd */
-
-    int max_rw;
-};
-
-bool init_functionfs(struct usb_handle* h);
diff --git a/adb/diagnose_usb.cpp b/adb/diagnose_usb.cpp
deleted file mode 100644
index 9f721bf..0000000
--- a/adb/diagnose_usb.cpp
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * Copyright (C) 2015 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 "diagnose_usb.h"
-
-#include <errno.h>
-#include <unistd.h>
-
-#include <string>
-
-#include <android-base/stringprintf.h>
-
-#if defined(__linux__)
-#include <grp.h>
-#include <pwd.h>
-#endif
-
-static const char kPermissionsHelpUrl[] = "http://developer.android.com/tools/device.html";
-
-// 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__)
-    errno = 0;
-    group* plugdev_group = getgrnam("plugdev");
-
-    if (plugdev_group == nullptr) {
-        if (errno != 0) {
-            perror("failed to read plugdev group info");
-        }
-        // We can't give any generally useful advice here, just let the caller print the help URL.
-        return "";
-    }
-
-    // getgroups(2) indicates that the GNU group_member(3) may not check the egid so we check it
-    // additionally just to be sure.
-    if (group_member(plugdev_group->gr_gid) || getegid() == plugdev_group->gr_gid) {
-        // The user is in plugdev so the problem is likely with the udev rules.
-        return "user in plugdev group; are your udev rules wrong?";
-    }
-    passwd* pwd = getpwuid(getuid());
-    return android::base::StringPrintf("user %s is not in the plugdev group",
-                                       pwd ? pwd->pw_name : "?");
-#else
-    return "";
-#endif
-}
-
-// Short help text must be a single line, and will look something like:
-//
-//   no permissions (reason); see [URL]
-std::string UsbNoPermissionsShortHelpText() {
-    std::string help_text = "no permissions";
-
-    std::string problem(GetUdevProblem());
-    if (!problem.empty()) help_text += " (" + problem + ")";
-
-    return android::base::StringPrintf("%s; see [%s]", help_text.c_str(), kPermissionsHelpUrl);
-}
-
-// Long help text can span multiple lines but doesn't currently provide more detailed information:
-//
-//   insufficient permissions for device: reason
-//   See [URL] for more information
-std::string UsbNoPermissionsLongHelpText() {
-    std::string header = "insufficient permissions for device";
-
-    std::string problem(GetUdevProblem());
-    if (!problem.empty()) header += ": " + problem;
-
-    return android::base::StringPrintf("%s\nSee [%s] for more information", header.c_str(),
-                                       kPermissionsHelpUrl);
-}
diff --git a/adb/fdevent.cpp b/adb/fdevent.cpp
index b28de4b..dee87bd 100644
--- a/adb/fdevent.cpp
+++ b/adb/fdevent.cpp
@@ -21,11 +21,14 @@
 #include "fdevent.h"
 
 #include <fcntl.h>
+#include <inttypes.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 +38,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
 
@@ -61,7 +58,7 @@
 
   explicit PollNode(fdevent* fde) : fde(fde) {
       memset(&pollfd, 0, sizeof(pollfd));
-      pollfd.fd = fde->fd;
+      pollfd.fd = fde->fd.get();
 
 #if defined(__linux__)
       // Always enable POLLRDHUP, so the host server can take action when some clients disconnect.
@@ -77,21 +74,24 @@
 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 uint64_t fdevent_id;
+
+static bool run_needs_flush = false;
 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) {
@@ -114,37 +114,24 @@
     if (fde->state & FDE_ERROR) {
         state += "E";
     }
-    if (fde->state & FDE_DONT_CLOSE) {
-        state += "D";
-    }
-    return android::base::StringPrintf("(fdevent %d %s)", fde->fd, state.c_str());
-}
-
-fdevent* fdevent_create(int fd, fd_func func, void* arg) {
-    check_main_thread();
-    fdevent *fde = (fdevent*) malloc(sizeof(fdevent));
-    if(fde == 0) return 0;
-    fdevent_install(fde, fd, func, arg);
-    fde->state |= FDE_CREATED;
-    return fde;
-}
-
-void fdevent_destroy(fdevent* fde) {
-    check_main_thread();
-    if(fde == 0) return;
-    if(!(fde->state & FDE_CREATED)) {
-        LOG(FATAL) << "destroying fde not created by fdevent_create(): " << dump_fde(fde);
-    }
-    fdevent_remove(fde);
-    free(fde);
+    return android::base::StringPrintf("(fdevent %" PRIu64 ": fd %d %s)", fde->id, fde->fd.get(),
+                                       state.c_str());
 }
 
 void fdevent_install(fdevent* fde, int fd, fd_func func, void* arg) {
     check_main_thread();
     CHECK_GE(fd, 0);
     memset(fde, 0, sizeof(fdevent));
+}
+
+fdevent* fdevent_create(int fd, fd_func func, void* arg) {
+    check_main_thread();
+    CHECK_GE(fd, 0);
+
+    fdevent* fde = new fdevent();
+    fde->id = fdevent_id++;
     fde->state = FDE_ACTIVE;
-    fde->fd = fd;
+    fde->fd.reset(fd);
     fde->func = func;
     fde->arg = arg;
     if (!set_file_block_mode(fd, false)) {
@@ -153,30 +140,35 @@
         // to handle it.
         LOG(ERROR) << "failed to set non-blocking mode for fd " << fd;
     }
-    auto pair = g_poll_node_map.emplace(fde->fd, PollNode(fde));
+    auto pair = g_poll_node_map.emplace(fde->fd.get(), PollNode(fde));
     CHECK(pair.second) << "install existing fd " << fd;
-    D("fdevent_install %s", dump_fde(fde).c_str());
+
+    fde->state |= FDE_CREATED;
+    return fde;
 }
 
-void fdevent_remove(fdevent* fde) {
+void fdevent_destroy(fdevent* fde) {
     check_main_thread();
-    D("fdevent_remove %s", dump_fde(fde).c_str());
+    if (fde == nullptr) return;
+    if (!(fde->state & FDE_CREATED)) {
+        LOG(FATAL) << "destroying fde not created by fdevent_create(): " << dump_fde(fde);
+    }
+
     if (fde->state & FDE_ACTIVE) {
-        g_poll_node_map.erase(fde->fd);
+        g_poll_node_map.erase(fde->fd.get());
         if (fde->state & FDE_PENDING) {
             g_pending_list.remove(fde);
         }
-        if (!(fde->state & FDE_DONT_CLOSE)) {
-            adb_close(fde->fd);
-            fde->fd = -1;
-        }
+        fde->fd.reset();
         fde->state = 0;
         fde->events = 0;
     }
+
+    delete fde;
 }
 
 static void fdevent_update(fdevent* fde, unsigned events) {
-    auto it = g_poll_node_map.find(fde->fd);
+    auto it = g_poll_node_map.find(fde->fd.get());
     CHECK(it != g_poll_node_map.end());
     PollNode& node = it->second;
     if (events & FDE_READ) {
@@ -275,7 +267,7 @@
             auto it = g_poll_node_map.find(pollfd.fd);
             CHECK(it != g_poll_node_map.end());
             fdevent* fde = it->second.fde;
-            CHECK_EQ(fde->fd, pollfd.fd);
+            CHECK_EQ(fde->fd.get(), pollfd.fd);
             fde->events |= events;
             D("%s got events %x", dump_fde(fde).c_str(), events);
             fde->state |= FDE_PENDING;
@@ -290,82 +282,26 @@
     CHECK(fde->state & FDE_PENDING);
     fde->state &= (~FDE_PENDING);
     D("fdevent_call_fdfunc %s", dump_fde(fde).c_str());
-    fde->func(fde->fd, events, fde->arg);
+    fde->func(fde->fd.get(), 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 +313,28 @@
         PLOG(FATAL) << "failed to empty run queue notify fd";
     }
 
-    std::lock_guard<std::mutex> lock(run_queue_mutex);
-    fdevent_run_flush();
+    // Mark that we need to flush, and then run it at the end of fdevent_loop.
+    run_needs_flush = true;
 }
 
 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 +346,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 +359,6 @@
 
 void fdevent_loop() {
     set_main_thread();
-#if !ADB_HOST
-    fdevent_subproc_setup();
-#endif // !ADB_HOST
     fdevent_run_setup();
 
     while (true) {
@@ -431,6 +375,11 @@
             g_pending_list.pop_front();
             fdevent_call_fdfunc(fde);
         }
+
+        if (run_needs_flush) {
+            fdevent_run_flush();
+            run_needs_flush = false;
+        }
     }
 }
 
diff --git a/adb/fdevent.h b/adb/fdevent.h
index 896400a..d501b86 100644
--- a/adb/fdevent.h
+++ b/adb/fdevent.h
@@ -22,28 +22,26 @@
 
 #include <functional>
 
+#include "adb_unique_fd.h"
+
 /* events that may be observed */
 #define FDE_READ              0x0001
 #define FDE_WRITE             0x0002
 #define FDE_ERROR             0x0004
 
-/* features that may be set (via the events set/add/del interface) */
-#define FDE_DONT_CLOSE        0x0080
-
 typedef void (*fd_func)(int fd, unsigned events, void *userdata);
 
 struct fdevent {
-    fdevent *next;
-    fdevent *prev;
+    uint64_t id;
 
-    int fd;
-    int force_eof;
+    unique_fd fd;
+    int force_eof = 0;
 
-    uint16_t state;
-    uint16_t events;
+    uint16_t state = 0;
+    uint16_t events = 0;
 
-    fd_func func;
-    void *arg;
+    fd_func func = nullptr;
+    void* arg = nullptr;
 };
 
 /* Allocate and initialize a new fdevent object
@@ -57,15 +55,6 @@
 */
 void fdevent_destroy(fdevent *fde);
 
-/* Initialize an fdevent object that was externally allocated
-*/
-void fdevent_install(fdevent *fde, int fd, fd_func func, void *arg);
-
-/* Uninitialize an fdevent object that was initialized by
-** fdevent_install()
-*/
-void fdevent_remove(fdevent *item);
-
 /* Change which events should cause notifications
 */
 void fdevent_set(fdevent *fde, unsigned events);
diff --git a/adb/fdevent_test.cpp b/adb/fdevent_test.cpp
index 86e0209..0cb2439 100644
--- a/adb/fdevent_test.cpp
+++ b/adb/fdevent_test.cpp
@@ -26,18 +26,19 @@
 
 #include "adb_io.h"
 #include "fdevent_test.h"
+#include "sysdeps/memory.h"
 
 class FdHandler {
   public:
     FdHandler(int read_fd, int write_fd) : read_fd_(read_fd), write_fd_(write_fd) {
-        fdevent_install(&read_fde_, read_fd_, FdEventCallback, this);
-        fdevent_add(&read_fde_, FDE_READ);
-        fdevent_install(&write_fde_, write_fd_, FdEventCallback, this);
+        read_fde_ = fdevent_create(read_fd_, FdEventCallback, this);
+        fdevent_add(read_fde_, FDE_READ);
+        write_fde_ = fdevent_create(write_fd_, FdEventCallback, this);
     }
 
     ~FdHandler() {
-        fdevent_remove(&read_fde_);
-        fdevent_remove(&write_fde_);
+        fdevent_destroy(read_fde_);
+        fdevent_destroy(write_fde_);
     }
 
   private:
@@ -49,7 +50,7 @@
             char c;
             ASSERT_EQ(1, adb_read(fd, &c, 1));
             handler->queue_.push(c);
-            fdevent_add(&handler->write_fde_, FDE_WRITE);
+            fdevent_add(handler->write_fde_, FDE_WRITE);
         }
         if (events & FDE_WRITE) {
             ASSERT_EQ(fd, handler->write_fd_);
@@ -58,7 +59,7 @@
             handler->queue_.pop();
             ASSERT_EQ(1, adb_write(fd, &c, 1));
             if (handler->queue_.empty()) {
-              fdevent_del(&handler->write_fde_, FDE_WRITE);
+                fdevent_del(handler->write_fde_, FDE_WRITE);
             }
         }
     }
@@ -66,8 +67,8 @@
   private:
     const int read_fd_;
     const int write_fd_;
-    fdevent read_fde_;
-    fdevent write_fde_;
+    fdevent* read_fde_;
+    fdevent* write_fde_;
     std::queue<char> queue_;
 };
 
@@ -79,30 +80,7 @@
 
 TEST_F(FdeventTest, fdevent_terminate) {
     PrepareThread();
-
-    std::thread thread(fdevent_loop);
-    TerminateThread(thread);
-}
-
-static void FdEventThreadFunc(ThreadArg* arg) {
-    std::vector<int> read_fds;
-    std::vector<int> write_fds;
-
-    read_fds.push_back(arg->first_read_fd);
-    for (size_t i = 0; i < arg->middle_pipe_count; ++i) {
-        int fds[2];
-        ASSERT_EQ(0, adb_socketpair(fds));
-        read_fds.push_back(fds[0]);
-        write_fds.push_back(fds[1]);
-    }
-    write_fds.push_back(arg->last_write_fd);
-
-    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])));
-    }
-
-    fdevent_loop();
+    TerminateThread();
 }
 
 TEST_F(FdeventTest, smoke) {
@@ -121,7 +99,26 @@
     int reader = fd_pair2[0];
 
     PrepareThread();
-    std::thread thread(FdEventThreadFunc, &thread_arg);
+
+    std::vector<std::unique_ptr<FdHandler>> fd_handlers;
+    fdevent_run_on_main_thread([&thread_arg, &fd_handlers]() {
+        std::vector<int> read_fds;
+        std::vector<int> write_fds;
+
+        read_fds.push_back(thread_arg.first_read_fd);
+        for (size_t i = 0; i < thread_arg.middle_pipe_count; ++i) {
+            int fds[2];
+            ASSERT_EQ(0, adb_socketpair(fds));
+            read_fds.push_back(fds[0]);
+            write_fds.push_back(fds[1]);
+        }
+        write_fds.push_back(thread_arg.last_write_fd);
+
+        for (size_t i = 0; i < read_fds.size(); ++i) {
+            fd_handlers.push_back(std::make_unique<FdHandler>(read_fds[i], write_fds[i]));
+        }
+    });
+    WaitForFdeventLoop();
 
     for (size_t i = 0; i < MESSAGE_LOOP_COUNT; ++i) {
         std::string read_buffer = MESSAGE;
@@ -131,21 +128,24 @@
         ASSERT_EQ(read_buffer, write_buffer);
     }
 
-    TerminateThread(thread);
+    fdevent_run_on_main_thread([&fd_handlers]() { fd_handlers.clear(); });
+    WaitForFdeventLoop();
+
+    TerminateThread();
     ASSERT_EQ(0, adb_close(writer));
     ASSERT_EQ(0, adb_close(reader));
 }
 
 struct InvalidFdArg {
-    fdevent fde;
+    fdevent* fde;
     unsigned expected_events;
     size_t* happened_event_count;
 };
 
-static void InvalidFdEventCallback(int fd, unsigned events, void* userdata) {
+static void InvalidFdEventCallback(int, unsigned events, void* userdata) {
     InvalidFdArg* arg = reinterpret_cast<InvalidFdArg*>(userdata);
     ASSERT_EQ(arg->expected_events, events);
-    fdevent_remove(&arg->fde);
+    fdevent_destroy(arg->fde);
     if (++*(arg->happened_event_count) == 2) {
         fdevent_terminate_loop();
     }
@@ -157,15 +157,15 @@
     InvalidFdArg read_arg;
     read_arg.expected_events = FDE_READ | FDE_ERROR;
     read_arg.happened_event_count = &happened_event_count;
-    fdevent_install(&read_arg.fde, INVALID_READ_FD, InvalidFdEventCallback, &read_arg);
-    fdevent_add(&read_arg.fde, FDE_READ);
+    read_arg.fde = fdevent_create(INVALID_READ_FD, InvalidFdEventCallback, &read_arg);
+    fdevent_add(read_arg.fde, FDE_READ);
 
     const int INVALID_WRITE_FD = std::numeric_limits<int>::max();
     InvalidFdArg write_arg;
     write_arg.expected_events = FDE_READ | FDE_ERROR;
     write_arg.happened_event_count = &happened_event_count;
-    fdevent_install(&write_arg.fde, INVALID_WRITE_FD, InvalidFdEventCallback, &write_arg);
-    fdevent_add(&write_arg.fde, FDE_WRITE);
+    write_arg.fde = fdevent_create(INVALID_WRITE_FD, InvalidFdEventCallback, &write_arg);
+    fdevent_add(write_arg.fde, FDE_WRITE);
     fdevent_loop();
 }
 
@@ -178,16 +178,46 @@
     std::vector<int> vec;
 
     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);
         });
     }
 
-    TerminateThread(thread);
+    TerminateThread();
+
+    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();
+    fdevent_run_on_main_thread(make_appender(&vec, 0));
+    TerminateThread();
 
     ASSERT_EQ(100u, vec.size());
     for (int i = 0; i < 100; ++i) {
diff --git a/adb/fdevent_test.h b/adb/fdevent_test.h
index 5ca49ac..5a417e0 100644
--- a/adb/fdevent_test.h
+++ b/adb/fdevent_test.h
@@ -16,10 +16,31 @@
 
 #include <gtest/gtest.h>
 
+#include <condition_variable>
+#include <mutex>
 #include <thread>
 
 #include "socket.h"
 #include "sysdeps.h"
+#include "sysdeps/chrono.h"
+
+static void WaitForFdeventLoop() {
+    // Sleep for a bit to make sure that network events have propagated.
+    std::this_thread::sleep_for(100ms);
+
+    // fdevent_run_on_main_thread has a guaranteed ordering, and is guaranteed to happen after
+    // socket events, so as soon as our function is called, we know that we've processed all
+    // previous events.
+    std::mutex mutex;
+    std::condition_variable cv;
+    std::unique_lock<std::mutex> lock(mutex);
+    fdevent_run_on_main_thread([&]() {
+        mutex.lock();
+        mutex.unlock();
+        cv.notify_one();
+    });
+    cv.wait(lock);
+}
 
 class FdeventTest : public ::testing::Test {
   protected:
@@ -49,22 +70,22 @@
         }
         dummy_socket->ready(dummy_socket);
         dummy = dummy_fds[0];
+
+        thread_ = std::thread([]() { fdevent_loop(); });
+        WaitForFdeventLoop();
     }
 
     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) {
+    void TerminateThread() {
         fdevent_terminate_loop();
         ASSERT_TRUE(WriteFdExactly(dummy, "", 1));
-        thread.join();
+        thread_.join();
         ASSERT_EQ(0, adb_close(dummy));
     }
+
+    std::thread thread_;
 };
diff --git a/adb/file_sync_client.cpp b/adb/file_sync_client.cpp
deleted file mode 100644
index 2576fb1..0000000
--- a/adb/file_sync_client.cpp
+++ /dev/null
@@ -1,1267 +0,0 @@
-/*
- * Copyright (C) 2007 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 <dirent.h>
-#include <inttypes.h>
-#include <limits.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <sys/stat.h>
-#include <sys/time.h>
-#include <sys/types.h>
-#include <time.h>
-#include <unistd.h>
-#include <utime.h>
-
-#include <chrono>
-#include <functional>
-#include <memory>
-#include <sstream>
-#include <string>
-#include <vector>
-
-#include "sysdeps.h"
-
-#include "adb.h"
-#include "adb_client.h"
-#include "adb_io.h"
-#include "adb_utils.h"
-#include "file_sync_service.h"
-#include "line_printer.h"
-#include "sysdeps/errno.h"
-#include "sysdeps/stat.h"
-
-#include <android-base/file.h>
-#include <android-base/strings.h>
-#include <android-base/stringprintf.h>
-
-struct syncsendbuf {
-    unsigned id;
-    unsigned size;
-    char data[SYNC_DATA_MAX];
-};
-
-static void ensure_trailing_separators(std::string& local_path, std::string& remote_path) {
-    if (!adb_is_separator(local_path.back())) {
-        local_path.push_back(OS_PATH_SEPARATOR);
-    }
-    if (remote_path.back() != '/') {
-        remote_path.push_back('/');
-    }
-}
-
-static bool should_pull_file(mode_t mode) {
-    return S_ISREG(mode) || S_ISBLK(mode) || S_ISCHR(mode);
-}
-
-static bool should_push_file(mode_t mode) {
-    return S_ISREG(mode) || S_ISLNK(mode);
-}
-
-struct copyinfo {
-    std::string lpath;
-    std::string rpath;
-    int64_t time = 0;
-    uint32_t mode;
-    uint64_t size = 0;
-    bool skip = false;
-
-    copyinfo(const std::string& local_path,
-             const std::string& remote_path,
-             const std::string& name,
-             unsigned int mode)
-            : lpath(local_path), rpath(remote_path), mode(mode) {
-        ensure_trailing_separators(lpath, rpath);
-        lpath.append(name);
-        rpath.append(name);
-        if (S_ISDIR(mode)) {
-            ensure_trailing_separators(lpath, rpath);
-        }
-    }
-};
-
-enum class TransferDirection {
-    push,
-    pull,
-};
-
-struct TransferLedger {
-    std::chrono::steady_clock::time_point start_time;
-    uint64_t files_transferred;
-    uint64_t files_skipped;
-    uint64_t bytes_transferred;
-    uint64_t bytes_expected;
-    bool expect_multiple_files;
-
-    TransferLedger() {
-        Reset();
-    }
-
-    bool operator==(const TransferLedger& other) const {
-        return files_transferred == other.files_transferred &&
-               files_skipped == other.files_skipped && bytes_transferred == other.bytes_transferred;
-    }
-
-    bool operator!=(const TransferLedger& other) const {
-        return !(*this == other);
-    }
-
-    void Reset() {
-        start_time = std::chrono::steady_clock::now();
-        files_transferred = 0;
-        files_skipped = 0;
-        bytes_transferred = 0;
-        bytes_expected = 0;
-    }
-
-    std::string TransferRate() {
-        if (bytes_transferred == 0) return "";
-
-        std::chrono::duration<double> duration;
-        duration = std::chrono::steady_clock::now() - start_time;
-
-        double s = duration.count();
-        if (s == 0) {
-            return "";
-        }
-        double rate = (static_cast<double>(bytes_transferred) / s) / (1024 * 1024);
-        return android::base::StringPrintf(" %.1f MB/s (%" PRIu64 " bytes in %.3fs)", rate,
-                                           bytes_transferred, s);
-    }
-
-    void ReportProgress(LinePrinter& lp, const std::string& file, uint64_t file_copied_bytes,
-                        uint64_t file_total_bytes) {
-        char overall_percentage_str[5] = "?";
-        if (bytes_expected != 0 && bytes_transferred <= bytes_expected) {
-            int overall_percentage = static_cast<int>(bytes_transferred * 100 / bytes_expected);
-            // If we're pulling symbolic links, we'll pull the target of the link rather than
-            // just create a local link, and that will cause us to go over 100%.
-            if (overall_percentage <= 100) {
-                snprintf(overall_percentage_str, sizeof(overall_percentage_str), "%d%%",
-                         overall_percentage);
-            }
-        }
-
-        std::string output;
-        if (file_copied_bytes > file_total_bytes || file_total_bytes == 0) {
-            // This case can happen if we're racing against something that wrote to the file
-            // between our stat and our read, or if we're reading a magic file that lies about
-            // its size. Just show how much we've copied.
-            output = android::base::StringPrintf("[%4s] %s: %" PRId64 "/?", overall_percentage_str,
-                                                 file.c_str(), file_copied_bytes);
-        } else {
-            // If we're transferring multiple files, we want to know how far through the current
-            // file we are, as well as the overall percentage.
-            if (expect_multiple_files) {
-                int file_percentage = static_cast<int>(file_copied_bytes * 100 / file_total_bytes);
-                output = android::base::StringPrintf("[%4s] %s: %d%%", overall_percentage_str,
-                                                     file.c_str(), file_percentage);
-            } else {
-                output =
-                    android::base::StringPrintf("[%4s] %s", overall_percentage_str, file.c_str());
-            }
-        }
-        lp.Print(output, LinePrinter::LineType::INFO);
-    }
-
-    void ReportTransferRate(LinePrinter& lp, const std::string& name, TransferDirection direction) {
-        const char* direction_str = (direction == TransferDirection::push) ? "pushed" : "pulled";
-        std::stringstream ss;
-        if (!name.empty()) {
-            ss << name << ": ";
-        }
-        ss << files_transferred << " file" << ((files_transferred == 1) ? "" : "s") << " "
-           << direction_str << ".";
-        if (files_skipped > 0) {
-            ss << " " << files_skipped << " file" << ((files_skipped == 1) ? "" : "s")
-               << " skipped.";
-        }
-        ss << TransferRate();
-
-        lp.Print(ss.str(), LinePrinter::LineType::INFO);
-        lp.KeepInfoLine();
-    }
-};
-
-class SyncConnection {
-  public:
-    SyncConnection() : expect_done_(false) {
-        max = SYNC_DATA_MAX; // TODO: decide at runtime.
-
-        std::string error;
-        FeatureSet features;
-        if (!adb_get_feature_set(&features, &error)) {
-            fd = -1;
-            Error("failed to get feature set: %s", error.c_str());
-        } else {
-            have_stat_v2_ = CanUseFeature(features, kFeatureStat2);
-            fd = adb_connect("sync:", &error);
-            if (fd < 0) {
-                Error("connect failed: %s", error.c_str());
-            }
-        }
-    }
-
-    ~SyncConnection() {
-        if (!IsValid()) return;
-
-        if (SendQuit()) {
-            // We sent a quit command, so the server should be doing orderly
-            // shutdown soon. But if we encountered an error while we were using
-            // the connection, the server might still be sending data (before
-            // doing orderly shutdown), in which case we won't wait for all of
-            // the data nor the coming orderly shutdown. In the common success
-            // case, this will wait for the server to do orderly shutdown.
-            ReadOrderlyShutdown(fd);
-        }
-        adb_close(fd);
-
-        line_printer_.KeepInfoLine();
-    }
-
-    bool IsValid() { return fd >= 0; }
-
-    bool ReceivedError(const char* from, const char* to) {
-        adb_pollfd pfd = {.fd = fd, .events = POLLIN};
-        int rc = adb_poll(&pfd, 1, 0);
-        if (rc < 0) {
-            Error("failed to poll: %s", strerror(errno));
-            return true;
-        }
-        return rc != 0;
-    }
-
-    void NewTransfer() {
-        current_ledger_.Reset();
-    }
-
-    void RecordBytesTransferred(size_t bytes) {
-        current_ledger_.bytes_transferred += bytes;
-        global_ledger_.bytes_transferred += bytes;
-    }
-
-    void RecordFilesTransferred(size_t files) {
-        current_ledger_.files_transferred += files;
-        global_ledger_.files_transferred += files;
-    }
-
-    void RecordFilesSkipped(size_t files) {
-        current_ledger_.files_skipped += files;
-        global_ledger_.files_skipped += files;
-    }
-
-    void ReportProgress(const std::string& file, uint64_t file_copied_bytes,
-                        uint64_t file_total_bytes) {
-        current_ledger_.ReportProgress(line_printer_, file, file_copied_bytes, file_total_bytes);
-    }
-
-    void ReportTransferRate(const std::string& file, TransferDirection direction) {
-        current_ledger_.ReportTransferRate(line_printer_, file, direction);
-    }
-
-    void ReportOverallTransferRate(TransferDirection direction) {
-        if (current_ledger_ != global_ledger_) {
-            global_ledger_.ReportTransferRate(line_printer_, "", direction);
-        }
-    }
-
-    bool SendRequest(int id, const char* path_and_mode) {
-        size_t path_length = strlen(path_and_mode);
-        if (path_length > 1024) {
-            Error("SendRequest failed: path too long: %zu", path_length);
-            errno = ENAMETOOLONG;
-            return false;
-        }
-
-        // Sending header and payload in a single write makes a noticeable
-        // difference to "adb sync" performance.
-        std::vector<char> buf(sizeof(SyncRequest) + path_length);
-        SyncRequest* req = reinterpret_cast<SyncRequest*>(&buf[0]);
-        req->id = id;
-        req->path_length = path_length;
-        char* data = reinterpret_cast<char*>(req + 1);
-        memcpy(data, path_and_mode, path_length);
-
-        return WriteFdExactly(fd, &buf[0], buf.size());
-    }
-
-    bool SendStat(const char* path_and_mode) {
-        if (!have_stat_v2_) {
-            errno = ENOTSUP;
-            return false;
-        }
-        return SendRequest(ID_STAT_V2, path_and_mode);
-    }
-
-    bool SendLstat(const char* path_and_mode) {
-        if (have_stat_v2_) {
-            return SendRequest(ID_LSTAT_V2, path_and_mode);
-        } else {
-            return SendRequest(ID_LSTAT_V1, path_and_mode);
-        }
-    }
-
-    bool FinishStat(struct stat* st) {
-        syncmsg msg;
-
-        memset(st, 0, sizeof(*st));
-        if (have_stat_v2_) {
-            if (!ReadFdExactly(fd, &msg.stat_v2, sizeof(msg.stat_v2))) {
-                fatal_errno("protocol fault: failed to read stat response");
-            }
-
-            if (msg.stat_v2.id != ID_LSTAT_V2 && msg.stat_v2.id != ID_STAT_V2) {
-                fatal_errno("protocol fault: stat response has wrong message id: %" PRIx32,
-                            msg.stat_v2.id);
-            }
-
-            if (msg.stat_v2.error != 0) {
-                errno = errno_from_wire(msg.stat_v2.error);
-                return false;
-            }
-
-            st->st_dev = msg.stat_v2.dev;
-            st->st_ino = msg.stat_v2.ino;
-            st->st_mode = msg.stat_v2.mode;
-            st->st_nlink = msg.stat_v2.nlink;
-            st->st_uid = msg.stat_v2.uid;
-            st->st_gid = msg.stat_v2.gid;
-            st->st_size = msg.stat_v2.size;
-            st->st_atime = msg.stat_v2.atime;
-            st->st_mtime = msg.stat_v2.mtime;
-            st->st_ctime = msg.stat_v2.ctime;
-            return true;
-        } else {
-            if (!ReadFdExactly(fd, &msg.stat_v1, sizeof(msg.stat_v1))) {
-                fatal_errno("protocol fault: failed to read stat response");
-            }
-
-            if (msg.stat_v1.id != ID_LSTAT_V1) {
-                fatal_errno("protocol fault: stat response has wrong message id: %" PRIx32,
-                            msg.stat_v1.id);
-            }
-
-            if (msg.stat_v1.mode == 0 && msg.stat_v1.size == 0 && msg.stat_v1.time == 0) {
-                // There's no way for us to know what the error was.
-                errno = ENOPROTOOPT;
-                return false;
-            }
-
-            st->st_mode = msg.stat_v1.mode;
-            st->st_size = msg.stat_v1.size;
-            st->st_ctime = msg.stat_v1.time;
-            st->st_mtime = msg.stat_v1.time;
-        }
-
-        return true;
-    }
-
-    // Sending header, payload, and footer in a single write makes a huge
-    // difference to "adb sync" performance.
-    bool SendSmallFile(const char* path_and_mode,
-                       const char* lpath, const char* rpath,
-                       unsigned mtime,
-                       const char* data, size_t data_length) {
-        size_t path_length = strlen(path_and_mode);
-        if (path_length > 1024) {
-            Error("SendSmallFile failed: path too long: %zu", path_length);
-            errno = ENAMETOOLONG;
-            return false;
-        }
-
-        std::vector<char> buf(sizeof(SyncRequest) + path_length +
-                              sizeof(SyncRequest) + data_length +
-                              sizeof(SyncRequest));
-        char* p = &buf[0];
-
-        SyncRequest* req_send = reinterpret_cast<SyncRequest*>(p);
-        req_send->id = ID_SEND;
-        req_send->path_length = path_length;
-        p += sizeof(SyncRequest);
-        memcpy(p, path_and_mode, path_length);
-        p += path_length;
-
-        SyncRequest* req_data = reinterpret_cast<SyncRequest*>(p);
-        req_data->id = ID_DATA;
-        req_data->path_length = data_length;
-        p += sizeof(SyncRequest);
-        memcpy(p, data, data_length);
-        p += data_length;
-
-        SyncRequest* req_done = reinterpret_cast<SyncRequest*>(p);
-        req_done->id = ID_DONE;
-        req_done->path_length = mtime;
-        p += sizeof(SyncRequest);
-
-        WriteOrDie(lpath, rpath, &buf[0], (p - &buf[0]));
-        expect_done_ = true;
-
-        // RecordFilesTransferred gets called in CopyDone.
-        RecordBytesTransferred(data_length);
-        ReportProgress(rpath, data_length, data_length);
-        return true;
-    }
-
-    bool SendLargeFile(const char* path_and_mode,
-                       const char* lpath, const char* rpath,
-                       unsigned mtime) {
-        if (!SendRequest(ID_SEND, path_and_mode)) {
-            Error("failed to send ID_SEND message '%s': %s", path_and_mode, strerror(errno));
-            return false;
-        }
-
-        struct stat st;
-        if (stat(lpath, &st) == -1) {
-            Error("cannot stat '%s': %s", lpath, strerror(errno));
-            return false;
-        }
-
-        uint64_t total_size = st.st_size;
-        uint64_t bytes_copied = 0;
-
-        int lfd = adb_open(lpath, O_RDONLY);
-        if (lfd < 0) {
-            Error("opening '%s' locally failed: %s", lpath, strerror(errno));
-            return false;
-        }
-
-        syncsendbuf sbuf;
-        sbuf.id = ID_DATA;
-        while (true) {
-            int bytes_read = adb_read(lfd, sbuf.data, max);
-            if (bytes_read == -1) {
-                Error("reading '%s' locally failed: %s", lpath, strerror(errno));
-                adb_close(lfd);
-                return false;
-            } else if (bytes_read == 0) {
-                break;
-            }
-
-            sbuf.size = bytes_read;
-            WriteOrDie(lpath, rpath, &sbuf, sizeof(SyncRequest) + bytes_read);
-
-            RecordBytesTransferred(bytes_read);
-            bytes_copied += bytes_read;
-
-            // Check to see if we've received an error from the other side.
-            if (ReceivedError(lpath, rpath)) {
-                break;
-            }
-
-            ReportProgress(rpath, bytes_copied, total_size);
-        }
-
-        adb_close(lfd);
-
-        syncmsg msg;
-        msg.data.id = ID_DONE;
-        msg.data.size = mtime;
-        expect_done_ = true;
-
-        // RecordFilesTransferred gets called in CopyDone.
-        return WriteOrDie(lpath, rpath, &msg.data, sizeof(msg.data));
-    }
-
-    bool CopyDone(const char* from, const char* to) {
-        syncmsg msg;
-        if (!ReadFdExactly(fd, &msg.status, sizeof(msg.status))) {
-            Error("failed to copy '%s' to '%s': couldn't read from device", from, to);
-            return false;
-        }
-        if (msg.status.id == ID_OKAY) {
-            if (expect_done_) {
-                expect_done_ = false;
-                RecordFilesTransferred(1);
-                return true;
-            } else {
-                Error("failed to copy '%s' to '%s': received premature success", from, to);
-                return true;
-            }
-        }
-        if (msg.status.id != ID_FAIL) {
-            Error("failed to copy '%s' to '%s': unknown reason %d", from, to, msg.status.id);
-            return false;
-        }
-        return ReportCopyFailure(from, to, msg);
-    }
-
-    bool ReportCopyFailure(const char* from, const char* to, const syncmsg& msg) {
-        std::vector<char> buf(msg.status.msglen + 1);
-        if (!ReadFdExactly(fd, &buf[0], msg.status.msglen)) {
-            Error("failed to copy '%s' to '%s'; failed to read reason (!): %s",
-                  from, to, strerror(errno));
-            return false;
-        }
-        buf[msg.status.msglen] = 0;
-        Error("failed to copy '%s' to '%s': remote %s", from, to, &buf[0]);
-        return false;
-    }
-
-
-    void Printf(const char* fmt, ...) __attribute__((__format__(ADB_FORMAT_ARCHETYPE, 2, 3))) {
-        std::string s;
-
-        va_list ap;
-        va_start(ap, fmt);
-        android::base::StringAppendV(&s, fmt, ap);
-        va_end(ap);
-
-        line_printer_.Print(s, LinePrinter::INFO);
-    }
-
-    void Println(const char* fmt, ...) __attribute__((__format__(ADB_FORMAT_ARCHETYPE, 2, 3))) {
-        std::string s;
-
-        va_list ap;
-        va_start(ap, fmt);
-        android::base::StringAppendV(&s, fmt, ap);
-        va_end(ap);
-
-        line_printer_.Print(s, LinePrinter::INFO);
-        line_printer_.KeepInfoLine();
-    }
-
-    void Error(const char* fmt, ...) __attribute__((__format__(ADB_FORMAT_ARCHETYPE, 2, 3))) {
-        std::string s = "adb: error: ";
-
-        va_list ap;
-        va_start(ap, fmt);
-        android::base::StringAppendV(&s, fmt, ap);
-        va_end(ap);
-
-        line_printer_.Print(s, LinePrinter::ERROR);
-    }
-
-    void Warning(const char* fmt, ...) __attribute__((__format__(ADB_FORMAT_ARCHETYPE, 2, 3))) {
-        std::string s = "adb: warning: ";
-
-        va_list ap;
-        va_start(ap, fmt);
-        android::base::StringAppendV(&s, fmt, ap);
-        va_end(ap);
-
-        line_printer_.Print(s, LinePrinter::WARNING);
-    }
-
-    void ComputeExpectedTotalBytes(const std::vector<copyinfo>& file_list) {
-        current_ledger_.bytes_expected = 0;
-        for (const copyinfo& ci : file_list) {
-            // Unfortunately, this doesn't work for symbolic links, because we'll copy the
-            // target of the link rather than just creating a link. (But ci.size is the link size.)
-            if (!ci.skip) current_ledger_.bytes_expected += ci.size;
-        }
-        current_ledger_.expect_multiple_files = true;
-    }
-
-    void SetExpectedTotalBytes(uint64_t expected_total_bytes) {
-        current_ledger_.bytes_expected = expected_total_bytes;
-        current_ledger_.expect_multiple_files = false;
-    }
-
-    // TODO: add a char[max] buffer here, to replace syncsendbuf...
-    int fd;
-    size_t max;
-
-  private:
-    bool expect_done_;
-    bool have_stat_v2_;
-
-    TransferLedger global_ledger_;
-    TransferLedger current_ledger_;
-    LinePrinter line_printer_;
-
-    bool SendQuit() {
-        return SendRequest(ID_QUIT, ""); // TODO: add a SendResponse?
-    }
-
-    bool WriteOrDie(const char* from, const char* to, const void* data, size_t data_length) {
-        if (!WriteFdExactly(fd, data, data_length)) {
-            if (errno == ECONNRESET) {
-                // Assume adbd told us why it was closing the connection, and
-                // try to read failure reason from adbd.
-                syncmsg msg;
-                if (!ReadFdExactly(fd, &msg.status, sizeof(msg.status))) {
-                    Error("failed to copy '%s' to '%s': no response: %s", from, to, strerror(errno));
-                } else if (msg.status.id != ID_FAIL) {
-                    Error("failed to copy '%s' to '%s': not ID_FAIL: %d", from, to, msg.status.id);
-                } else {
-                    ReportCopyFailure(from, to, msg);
-                }
-            } else {
-                Error("%zu-byte write failed: %s", data_length, strerror(errno));
-            }
-            _exit(1);
-        }
-        return true;
-    }
-};
-
-typedef void (sync_ls_cb)(unsigned mode, unsigned size, unsigned time, const char* name);
-
-static bool sync_ls(SyncConnection& sc, const char* path,
-                    const std::function<sync_ls_cb>& func) {
-    if (!sc.SendRequest(ID_LIST, path)) return false;
-
-    while (true) {
-        syncmsg msg;
-        if (!ReadFdExactly(sc.fd, &msg.dent, sizeof(msg.dent))) return false;
-
-        if (msg.dent.id == ID_DONE) return true;
-        if (msg.dent.id != ID_DENT) return false;
-
-        size_t len = msg.dent.namelen;
-        if (len > 256) return false; // TODO: resize buffer? continue?
-
-        char buf[257];
-        if (!ReadFdExactly(sc.fd, buf, len)) return false;
-        buf[len] = 0;
-
-        func(msg.dent.mode, msg.dent.size, msg.dent.time, buf);
-    }
-}
-
-static bool sync_stat(SyncConnection& sc, const char* path, struct stat* st) {
-    return sc.SendStat(path) && sc.FinishStat(st);
-}
-
-static bool sync_lstat(SyncConnection& sc, const char* path, struct stat* st) {
-    return sc.SendLstat(path) && sc.FinishStat(st);
-}
-
-static bool sync_stat_fallback(SyncConnection& sc, const char* path, struct stat* st) {
-    if (sync_stat(sc, path, st)) {
-        return true;
-    }
-
-    if (errno != ENOTSUP) {
-        return false;
-    }
-
-    // Try to emulate the parts we can when talking to older adbds.
-    bool lstat_result = sync_lstat(sc, path, st);
-    if (!lstat_result) {
-        return false;
-    }
-
-    if (S_ISLNK(st->st_mode)) {
-        // If the target is a symlink, figure out whether it's a file or a directory.
-        // Also, zero out the st_size field, since no one actually cares what the path length is.
-        st->st_size = 0;
-        std::string dir_path = path;
-        dir_path.push_back('/');
-        struct stat tmp_st;
-
-        st->st_mode &= ~S_IFMT;
-        if (sync_lstat(sc, dir_path.c_str(), &tmp_st)) {
-            st->st_mode |= S_IFDIR;
-        } else {
-            st->st_mode |= S_IFREG;
-        }
-    }
-    return true;
-}
-
-static bool sync_send(SyncConnection& sc, const char* lpath, const char* rpath, unsigned mtime,
-                      mode_t mode, bool sync) {
-    std::string path_and_mode = android::base::StringPrintf("%s,%d", rpath, mode);
-
-    if (sync) {
-        struct stat st;
-        if (sync_lstat(sc, rpath, &st)) {
-            // For links, we cannot update the atime/mtime.
-            if ((S_ISREG(mode & st.st_mode) && st.st_mtime == static_cast<time_t>(mtime)) ||
-                (S_ISLNK(mode & st.st_mode) && st.st_mtime >= static_cast<time_t>(mtime))) {
-                sc.RecordFilesSkipped(1);
-                return true;
-            }
-        }
-    }
-
-    if (S_ISLNK(mode)) {
-#if !defined(_WIN32)
-        char buf[PATH_MAX];
-        ssize_t data_length = readlink(lpath, buf, PATH_MAX - 1);
-        if (data_length == -1) {
-            sc.Error("readlink '%s' failed: %s", lpath, strerror(errno));
-            return false;
-        }
-        buf[data_length++] = '\0';
-
-        if (!sc.SendSmallFile(path_and_mode.c_str(), lpath, rpath, mtime, buf, data_length)) {
-            return false;
-        }
-        return sc.CopyDone(lpath, rpath);
-#endif
-    }
-
-    struct stat st;
-    if (stat(lpath, &st) == -1) {
-        sc.Error("failed to stat local file '%s': %s", lpath, strerror(errno));
-        return false;
-    }
-    if (st.st_size < SYNC_DATA_MAX) {
-        std::string data;
-        if (!android::base::ReadFileToString(lpath, &data, true)) {
-            sc.Error("failed to read all of '%s': %s", lpath, strerror(errno));
-            return false;
-        }
-        if (!sc.SendSmallFile(path_and_mode.c_str(), lpath, rpath, mtime,
-                              data.data(), data.size())) {
-            return false;
-        }
-    } else {
-        if (!sc.SendLargeFile(path_and_mode.c_str(), lpath, rpath, mtime)) {
-            return false;
-        }
-    }
-    return sc.CopyDone(lpath, rpath);
-}
-
-static bool sync_recv(SyncConnection& sc, const char* rpath, const char* lpath,
-                      const char* name, uint64_t expected_size) {
-    if (!sc.SendRequest(ID_RECV, rpath)) return false;
-
-    adb_unlink(lpath);
-    int lfd = adb_creat(lpath, 0644);
-    if (lfd < 0) {
-        sc.Error("cannot create '%s': %s", lpath, strerror(errno));
-        return false;
-    }
-
-    uint64_t bytes_copied = 0;
-    while (true) {
-        syncmsg msg;
-        if (!ReadFdExactly(sc.fd, &msg.data, sizeof(msg.data))) {
-            adb_close(lfd);
-            adb_unlink(lpath);
-            return false;
-        }
-
-        if (msg.data.id == ID_DONE) break;
-
-        if (msg.data.id != ID_DATA) {
-            adb_close(lfd);
-            adb_unlink(lpath);
-            sc.ReportCopyFailure(rpath, lpath, msg);
-            return false;
-        }
-
-        if (msg.data.size > sc.max) {
-            sc.Error("msg.data.size too large: %u (max %zu)", msg.data.size, sc.max);
-            adb_close(lfd);
-            adb_unlink(lpath);
-            return false;
-        }
-
-        char buffer[SYNC_DATA_MAX];
-        if (!ReadFdExactly(sc.fd, buffer, msg.data.size)) {
-            adb_close(lfd);
-            adb_unlink(lpath);
-            return false;
-        }
-
-        if (!WriteFdExactly(lfd, buffer, msg.data.size)) {
-            sc.Error("cannot write '%s': %s", lpath, strerror(errno));
-            adb_close(lfd);
-            adb_unlink(lpath);
-            return false;
-        }
-
-        bytes_copied += msg.data.size;
-
-        sc.RecordBytesTransferred(msg.data.size);
-        sc.ReportProgress(name != nullptr ? name : rpath, bytes_copied, expected_size);
-    }
-
-    sc.RecordFilesTransferred(1);
-    adb_close(lfd);
-    return true;
-}
-
-bool do_sync_ls(const char* path) {
-    SyncConnection sc;
-    if (!sc.IsValid()) return false;
-
-    return sync_ls(sc, path, [](unsigned mode, unsigned size, unsigned time,
-                                const char* name) {
-        printf("%08x %08x %08x %s\n", mode, size, time, name);
-    });
-}
-
-static bool IsDotOrDotDot(const char* name) {
-    return name[0] == '.' && (name[1] == '\0' || (name[1] == '.' && name[2] == '\0'));
-}
-
-static bool local_build_list(SyncConnection& sc, std::vector<copyinfo>* file_list,
-                             const std::string& lpath,
-                             const std::string& rpath) {
-    std::vector<copyinfo> dirlist;
-    std::unique_ptr<DIR, int (*)(DIR*)> dir(opendir(lpath.c_str()), closedir);
-    if (!dir) {
-        sc.Error("cannot open '%s': %s", lpath.c_str(), strerror(errno));
-        return false;
-    }
-
-    bool empty_dir = true;
-    dirent* de;
-    while ((de = readdir(dir.get()))) {
-        if (IsDotOrDotDot(de->d_name)) {
-            continue;
-        }
-
-        empty_dir = false;
-        std::string stat_path = lpath + de->d_name;
-
-        struct stat st;
-        if (lstat(stat_path.c_str(), &st) == -1) {
-            sc.Error("cannot lstat '%s': %s", stat_path.c_str(),
-                     strerror(errno));
-            continue;
-        }
-
-        copyinfo ci(lpath, rpath, de->d_name, st.st_mode);
-        if (S_ISDIR(st.st_mode)) {
-            dirlist.push_back(ci);
-        } else {
-            if (!should_push_file(st.st_mode)) {
-                sc.Warning("skipping special file '%s' (mode = 0o%o)", lpath.c_str(), st.st_mode);
-                ci.skip = true;
-            }
-            ci.time = st.st_mtime;
-            ci.size = st.st_size;
-            file_list->push_back(ci);
-        }
-    }
-
-    // Close this directory and recurse.
-    dir.reset();
-
-    // Add the current directory to the list if it was empty, to ensure that
-    // it gets created.
-    if (empty_dir) {
-        // TODO(b/25566053): Make pushing empty directories work.
-        // TODO(b/25457350): We don't preserve permissions on directories.
-        sc.Warning("skipping empty directory '%s'", lpath.c_str());
-        copyinfo ci(android::base::Dirname(lpath), android::base::Dirname(rpath),
-                    android::base::Basename(lpath), S_IFDIR);
-        ci.skip = true;
-        file_list->push_back(ci);
-        return true;
-    }
-
-    for (const copyinfo& ci : dirlist) {
-        local_build_list(sc, file_list, ci.lpath, ci.rpath);
-    }
-
-    return true;
-}
-
-static bool copy_local_dir_remote(SyncConnection& sc, std::string lpath,
-                                  std::string rpath, bool check_timestamps,
-                                  bool list_only) {
-    sc.NewTransfer();
-
-    // Make sure that both directory paths end in a slash.
-    // Both paths are known to be nonempty, so we don't need to check.
-    ensure_trailing_separators(lpath, rpath);
-
-    // Recursively build the list of files to copy.
-    std::vector<copyinfo> file_list;
-    int skipped = 0;
-    if (!local_build_list(sc, &file_list, lpath, rpath)) {
-        return false;
-    }
-
-    if (check_timestamps) {
-        for (const copyinfo& ci : file_list) {
-            if (!sc.SendLstat(ci.rpath.c_str())) {
-                sc.Error("failed to send lstat");
-                return false;
-            }
-        }
-        for (copyinfo& ci : file_list) {
-            struct stat st;
-            if (sc.FinishStat(&st)) {
-                if (st.st_size == static_cast<off_t>(ci.size)) {
-                    // For links, we cannot update the atime/mtime.
-                    if ((S_ISREG(ci.mode & st.st_mode) && st.st_mtime == ci.time) ||
-                        (S_ISLNK(ci.mode & st.st_mode) && st.st_mtime >= ci.time)) {
-                        ci.skip = true;
-                    }
-                }
-            }
-        }
-    }
-
-    sc.ComputeExpectedTotalBytes(file_list);
-
-    for (const copyinfo& ci : file_list) {
-        if (!ci.skip) {
-            if (list_only) {
-                sc.Println("would push: %s -> %s", ci.lpath.c_str(), ci.rpath.c_str());
-            } else {
-                if (!sync_send(sc, ci.lpath.c_str(), ci.rpath.c_str(), ci.time, ci.mode, false)) {
-                    return false;
-                }
-            }
-        } else {
-            skipped++;
-        }
-    }
-
-    sc.RecordFilesSkipped(skipped);
-    sc.ReportTransferRate(lpath, TransferDirection::push);
-    return true;
-}
-
-bool do_sync_push(const std::vector<const char*>& srcs, const char* dst, bool sync) {
-    SyncConnection sc;
-    if (!sc.IsValid()) return false;
-
-    bool success = true;
-    bool dst_exists;
-    bool dst_isdir;
-
-    struct stat st;
-    if (sync_stat_fallback(sc, dst, &st)) {
-        dst_exists = true;
-        dst_isdir = S_ISDIR(st.st_mode);
-    } else {
-        if (errno == ENOENT || errno == ENOPROTOOPT) {
-            dst_exists = false;
-            dst_isdir = false;
-        } else {
-            sc.Error("stat failed when trying to push to %s: %s", dst, strerror(errno));
-            return false;
-        }
-    }
-
-    if (!dst_isdir) {
-        if (srcs.size() > 1) {
-            sc.Error("target '%s' is not a directory", dst);
-            return false;
-        } else {
-            size_t dst_len = strlen(dst);
-
-            // A path that ends with a slash doesn't have to be a directory if
-            // it doesn't exist yet.
-            if (dst[dst_len - 1] == '/' && dst_exists) {
-                sc.Error("failed to access '%s': Not a directory", dst);
-                return false;
-            }
-        }
-    }
-
-    for (const char* src_path : srcs) {
-        const char* dst_path = dst;
-        struct stat st;
-        if (stat(src_path, &st) == -1) {
-            sc.Error("cannot stat '%s': %s", src_path, strerror(errno));
-            success = false;
-            continue;
-        }
-
-        if (S_ISDIR(st.st_mode)) {
-            std::string dst_dir = dst;
-
-            // If the destination path existed originally, the source directory
-            // should be copied as a child of the destination.
-            if (dst_exists) {
-                if (!dst_isdir) {
-                    sc.Error("target '%s' is not a directory", dst);
-                    return false;
-                }
-                // dst is a POSIX path, so we don't want to use the sysdeps
-                // helpers here.
-                if (dst_dir.back() != '/') {
-                    dst_dir.push_back('/');
-                }
-                dst_dir.append(android::base::Basename(src_path));
-            }
-
-            success &= copy_local_dir_remote(sc, src_path, dst_dir.c_str(), sync, false);
-            continue;
-        } else if (!should_push_file(st.st_mode)) {
-            sc.Warning("skipping special file '%s' (mode = 0o%o)", src_path, st.st_mode);
-            continue;
-        }
-
-        std::string path_holder;
-        if (dst_isdir) {
-            // If we're copying a local file to a remote directory,
-            // we really want to copy to remote_dir + "/" + local_filename.
-            path_holder = dst_path;
-            if (path_holder.back() != '/') {
-                path_holder.push_back('/');
-            }
-            path_holder += android::base::Basename(src_path);
-            dst_path = path_holder.c_str();
-        }
-
-        sc.NewTransfer();
-        sc.SetExpectedTotalBytes(st.st_size);
-        success &= sync_send(sc, src_path, dst_path, st.st_mtime, st.st_mode, sync);
-        sc.ReportTransferRate(src_path, TransferDirection::push);
-    }
-
-    sc.ReportOverallTransferRate(TransferDirection::push);
-    return success;
-}
-
-static bool remote_build_list(SyncConnection& sc, std::vector<copyinfo>* file_list,
-                              const std::string& rpath, const std::string& lpath) {
-    std::vector<copyinfo> dirlist;
-    std::vector<copyinfo> linklist;
-
-    // Add an entry for the current directory to ensure it gets created before pulling its contents.
-    copyinfo ci(android::base::Dirname(lpath), android::base::Dirname(rpath),
-                android::base::Basename(lpath), S_IFDIR);
-    file_list->push_back(ci);
-
-    // Put the files/dirs in rpath on the lists.
-    auto callback = [&](unsigned mode, unsigned size, unsigned time, const char* name) {
-        if (IsDotOrDotDot(name)) {
-            return;
-        }
-
-        copyinfo ci(lpath, rpath, name, mode);
-        if (S_ISDIR(mode)) {
-            dirlist.push_back(ci);
-        } else if (S_ISLNK(mode)) {
-            linklist.push_back(ci);
-        } else {
-            if (!should_pull_file(ci.mode)) {
-                sc.Warning("skipping special file '%s' (mode = 0o%o)", ci.rpath.c_str(), ci.mode);
-                ci.skip = true;
-            }
-            ci.time = time;
-            ci.size = size;
-            file_list->push_back(ci);
-        }
-    };
-
-    if (!sync_ls(sc, rpath.c_str(), callback)) {
-        return false;
-    }
-
-    // Check each symlink we found to see whether it's a file or directory.
-    for (copyinfo& link_ci : linklist) {
-        struct stat st;
-        if (!sync_stat_fallback(sc, link_ci.rpath.c_str(), &st)) {
-            sc.Warning("stat failed for path %s: %s", link_ci.rpath.c_str(), strerror(errno));
-            continue;
-        }
-
-        if (S_ISDIR(st.st_mode)) {
-            dirlist.emplace_back(std::move(link_ci));
-        } else {
-            file_list->emplace_back(std::move(link_ci));
-        }
-    }
-
-    // Recurse into each directory we found.
-    while (!dirlist.empty()) {
-        copyinfo current = dirlist.back();
-        dirlist.pop_back();
-        if (!remote_build_list(sc, file_list, current.rpath, current.lpath)) {
-            return false;
-        }
-    }
-
-    return true;
-}
-
-static int set_time_and_mode(const std::string& lpath, time_t time,
-                             unsigned int mode) {
-    struct utimbuf times = { time, time };
-    int r1 = utime(lpath.c_str(), &times);
-
-    /* use umask for permissions */
-    mode_t mask = umask(0000);
-    umask(mask);
-    int r2 = chmod(lpath.c_str(), mode & ~mask);
-
-    return r1 ? r1 : r2;
-}
-
-static bool copy_remote_dir_local(SyncConnection& sc, std::string rpath,
-                                  std::string lpath, bool copy_attrs) {
-    sc.NewTransfer();
-
-    // Make sure that both directory paths end in a slash.
-    // Both paths are known to be nonempty, so we don't need to check.
-    ensure_trailing_separators(lpath, rpath);
-
-    // Recursively build the list of files to copy.
-    sc.Printf("pull: building file list...");
-    std::vector<copyinfo> file_list;
-    if (!remote_build_list(sc, &file_list, rpath.c_str(), lpath.c_str())) {
-        return false;
-    }
-
-    sc.ComputeExpectedTotalBytes(file_list);
-
-    int skipped = 0;
-    for (const copyinfo &ci : file_list) {
-        if (!ci.skip) {
-            if (S_ISDIR(ci.mode)) {
-                // Entry is for an empty directory, create it and continue.
-                // TODO(b/25457350): We don't preserve permissions on directories.
-                if (!mkdirs(ci.lpath))  {
-                    sc.Error("failed to create directory '%s': %s",
-                             ci.lpath.c_str(), strerror(errno));
-                    return false;
-                }
-                continue;
-            }
-
-            if (!sync_recv(sc, ci.rpath.c_str(), ci.lpath.c_str(), nullptr, ci.size)) {
-                return false;
-            }
-
-            if (copy_attrs && set_time_and_mode(ci.lpath, ci.time, ci.mode)) {
-                return false;
-            }
-        } else {
-            skipped++;
-        }
-    }
-
-    sc.RecordFilesSkipped(skipped);
-    sc.ReportTransferRate(rpath, TransferDirection::pull);
-    return true;
-}
-
-bool do_sync_pull(const std::vector<const char*>& srcs, const char* dst,
-                  bool copy_attrs, const char* name) {
-    SyncConnection sc;
-    if (!sc.IsValid()) return false;
-
-    bool success = true;
-    struct stat st;
-    bool dst_exists = true;
-
-    if (stat(dst, &st) == -1) {
-        dst_exists = false;
-
-        // If we're only pulling one path, the destination path might point to
-        // a path that doesn't exist yet.
-        if (srcs.size() == 1 && errno == ENOENT) {
-            // However, its parent must exist.
-            struct stat parent_st;
-            if (stat(android::base::Dirname(dst).c_str(), &parent_st) == -1) {
-                sc.Error("cannot create file/directory '%s': %s", dst, strerror(errno));
-                return false;
-            }
-        } else {
-            sc.Error("failed to access '%s': %s", dst, strerror(errno));
-            return false;
-        }
-    }
-
-    bool dst_isdir = dst_exists && S_ISDIR(st.st_mode);
-    if (!dst_isdir) {
-        if (srcs.size() > 1) {
-            sc.Error("target '%s' is not a directory", dst);
-            return false;
-        } else {
-            size_t dst_len = strlen(dst);
-
-            // A path that ends with a slash doesn't have to be a directory if
-            // it doesn't exist yet.
-            if (adb_is_separator(dst[dst_len - 1]) && dst_exists) {
-                sc.Error("failed to access '%s': Not a directory", dst);
-                return false;
-            }
-        }
-    }
-
-    for (const char* src_path : srcs) {
-        const char* dst_path = dst;
-        struct stat src_st;
-        if (!sync_stat_fallback(sc, src_path, &src_st)) {
-            if (errno == ENOPROTOOPT) {
-                sc.Error("remote object '%s' does not exist", src_path);
-            } else {
-                sc.Error("failed to stat remote object '%s': %s", src_path, strerror(errno));
-            }
-
-            success = false;
-            continue;
-        }
-
-        bool src_isdir = S_ISDIR(src_st.st_mode);
-        if (src_isdir) {
-            std::string dst_dir = dst;
-
-            // If the destination path existed originally, the source directory
-            // should be copied as a child of the destination.
-            if (dst_exists) {
-                if (!dst_isdir) {
-                    sc.Error("target '%s' is not a directory", dst);
-                    return false;
-                }
-                if (!adb_is_separator(dst_dir.back())) {
-                    dst_dir.push_back(OS_PATH_SEPARATOR);
-                }
-                dst_dir.append(android::base::Basename(src_path));
-            }
-
-            success &= copy_remote_dir_local(sc, src_path, dst_dir.c_str(), copy_attrs);
-            continue;
-        } else if (!should_pull_file(src_st.st_mode)) {
-            sc.Warning("skipping special file '%s' (mode = 0o%o)", src_path, src_st.st_mode);
-            continue;
-        }
-
-        std::string path_holder;
-        if (dst_isdir) {
-            // If we're copying a remote file to a local directory, we
-            // really want to copy to local_dir + OS_PATH_SEPARATOR +
-            // basename(remote).
-            path_holder = android::base::StringPrintf("%s%c%s", dst_path, OS_PATH_SEPARATOR,
-                                                      android::base::Basename(src_path).c_str());
-            dst_path = path_holder.c_str();
-        }
-
-        sc.NewTransfer();
-        sc.SetExpectedTotalBytes(src_st.st_size);
-        if (!sync_recv(sc, src_path, dst_path, name, src_st.st_size)) {
-            success = false;
-            continue;
-        }
-
-        if (copy_attrs && set_time_and_mode(dst_path, src_st.st_mtime, src_st.st_mode) != 0) {
-            success = false;
-            continue;
-        }
-        sc.ReportTransferRate(src_path, TransferDirection::pull);
-    }
-
-    sc.ReportOverallTransferRate(TransferDirection::pull);
-    return success;
-}
-
-bool do_sync_sync(const std::string& lpath, const std::string& rpath, bool list_only) {
-    SyncConnection sc;
-    if (!sc.IsValid()) return false;
-
-    bool success = copy_local_dir_remote(sc, lpath, rpath, true, list_only);
-    if (!list_only) {
-        sc.ReportOverallTransferRate(TransferDirection::push);
-    }
-    return success;
-}
diff --git a/adb/file_sync_protocol.h b/adb/file_sync_protocol.h
new file mode 100644
index 0000000..108639a
--- /dev/null
+++ b/adb/file_sync_protocol.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2007 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.
+ */
+
+#pragma once
+
+#define MKID(a, b, c, d) ((a) | ((b) << 8) | ((c) << 16) | ((d) << 24))
+
+#define ID_LSTAT_V1 MKID('S', 'T', 'A', 'T')
+#define ID_STAT_V2 MKID('S', 'T', 'A', '2')
+#define ID_LSTAT_V2 MKID('L', 'S', 'T', '2')
+#define ID_LIST MKID('L', 'I', 'S', 'T')
+#define ID_SEND MKID('S', 'E', 'N', 'D')
+#define ID_RECV MKID('R', 'E', 'C', 'V')
+#define ID_DENT MKID('D', 'E', 'N', 'T')
+#define ID_DONE MKID('D', 'O', 'N', 'E')
+#define ID_DATA MKID('D', 'A', 'T', 'A')
+#define ID_OKAY MKID('O', 'K', 'A', 'Y')
+#define ID_FAIL MKID('F', 'A', 'I', 'L')
+#define ID_QUIT MKID('Q', 'U', 'I', 'T')
+
+struct SyncRequest {
+    uint32_t id;           // ID_STAT, et cetera.
+    uint32_t path_length;  // <= 1024
+    // Followed by 'path_length' bytes of path (not NUL-terminated).
+} __attribute__((packed));
+
+union syncmsg {
+    struct __attribute__((packed)) {
+        uint32_t id;
+        uint32_t mode;
+        uint32_t size;
+        uint32_t time;
+    } stat_v1;
+    struct __attribute__((packed)) {
+        uint32_t id;
+        uint32_t error;
+        uint64_t dev;
+        uint64_t ino;
+        uint32_t mode;
+        uint32_t nlink;
+        uint32_t uid;
+        uint32_t gid;
+        uint64_t size;
+        int64_t atime;
+        int64_t mtime;
+        int64_t ctime;
+    } stat_v2;
+    struct __attribute__((packed)) {
+        uint32_t id;
+        uint32_t mode;
+        uint32_t size;
+        uint32_t time;
+        uint32_t namelen;
+    } dent;
+    struct __attribute__((packed)) {
+        uint32_t id;
+        uint32_t size;
+    } data;
+    struct __attribute__((packed)) {
+        uint32_t id;
+        uint32_t msglen;
+    } status;
+};
+
+#define SYNC_DATA_MAX (64 * 1024)
diff --git a/adb/file_sync_service.cpp b/adb/file_sync_service.cpp
deleted file mode 100644
index 2acf661..0000000
--- a/adb/file_sync_service.cpp
+++ /dev/null
@@ -1,522 +0,0 @@
-/*
- * Copyright (C) 2007 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.
- */
-
-#define TRACE_TAG SYNC
-
-#include "sysdeps.h"
-#include "file_sync_service.h"
-
-#include <dirent.h>
-#include <errno.h>
-#include <linux/xattr.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <sys/xattr.h>
-#include <unistd.h>
-#include <utime.h>
-
-#include <android-base/file.h>
-#include <android-base/stringprintf.h>
-#include <android-base/strings.h>
-#include <private/android_filesystem_config.h>
-#include <private/android_logger.h>
-#include <selinux/android.h>
-
-#include "adb.h"
-#include "adb_io.h"
-#include "adb_trace.h"
-#include "adb_utils.h"
-#include "security_log_tags.h"
-#include "sysdeps/errno.h"
-
-using android::base::StringPrintf;
-
-static bool should_use_fs_config(const std::string& path) {
-    // TODO: use fs_config to configure permissions on /data.
-    return android::base::StartsWith(path, "/system/") ||
-           android::base::StartsWith(path, "/vendor/") ||
-           android::base::StartsWith(path, "/oem/");
-}
-
-static bool update_capabilities(const char* path, uint64_t capabilities) {
-    if (capabilities == 0) {
-        // Ensure we clean up in case the capabilities weren't 0 in the past.
-        removexattr(path, XATTR_NAME_CAPS);
-        return true;
-    }
-
-    vfs_cap_data cap_data = {};
-    cap_data.magic_etc = VFS_CAP_REVISION | VFS_CAP_FLAGS_EFFECTIVE;
-    cap_data.data[0].permitted = (capabilities & 0xffffffff);
-    cap_data.data[0].inheritable = 0;
-    cap_data.data[1].permitted = (capabilities >> 32);
-    cap_data.data[1].inheritable = 0;
-    return setxattr(path, XATTR_NAME_CAPS, &cap_data, sizeof(cap_data), 0) != -1;
-}
-
-static bool secure_mkdirs(const std::string& path) {
-    uid_t uid = -1;
-    gid_t gid = -1;
-    unsigned int mode = 0775;
-    uint64_t capabilities = 0;
-
-    if (path[0] != '/') return false;
-
-    std::vector<std::string> path_components = android::base::Split(path, "/");
-    std::string partial_path;
-    for (const auto& path_component : path_components) {
-        if (partial_path.back() != OS_PATH_SEPARATOR) partial_path += OS_PATH_SEPARATOR;
-        partial_path += path_component;
-
-        if (should_use_fs_config(partial_path)) {
-            fs_config(partial_path.c_str(), 1, nullptr, &uid, &gid, &mode, &capabilities);
-        }
-        if (adb_mkdir(partial_path.c_str(), mode) == -1) {
-            if (errno != EEXIST) {
-                return false;
-            }
-        } else {
-            if (chown(partial_path.c_str(), uid, gid) == -1) return false;
-
-            // Not all filesystems support setting SELinux labels. http://b/23530370.
-            selinux_android_restorecon(partial_path.c_str(), 0);
-
-            if (!update_capabilities(partial_path.c_str(), capabilities)) return false;
-        }
-    }
-    return true;
-}
-
-static bool do_lstat_v1(int s, const char* path) {
-    syncmsg msg = {};
-    msg.stat_v1.id = ID_LSTAT_V1;
-
-    struct stat st = {};
-    lstat(path, &st);
-    msg.stat_v1.mode = st.st_mode;
-    msg.stat_v1.size = st.st_size;
-    msg.stat_v1.time = st.st_mtime;
-    return WriteFdExactly(s, &msg.stat_v1, sizeof(msg.stat_v1));
-}
-
-static bool do_stat_v2(int s, uint32_t id, const char* path) {
-    syncmsg msg = {};
-    msg.stat_v2.id = id;
-
-    decltype(&stat) stat_fn;
-    if (id == ID_STAT_V2) {
-        stat_fn = stat;
-    } else {
-        stat_fn = lstat;
-    }
-
-    struct stat st = {};
-    int rc = stat_fn(path, &st);
-    if (rc == -1) {
-        msg.stat_v2.error = errno_to_wire(errno);
-    } else {
-        msg.stat_v2.dev = st.st_dev;
-        msg.stat_v2.ino = st.st_ino;
-        msg.stat_v2.mode = st.st_mode;
-        msg.stat_v2.nlink = st.st_nlink;
-        msg.stat_v2.uid = st.st_uid;
-        msg.stat_v2.gid = st.st_gid;
-        msg.stat_v2.size = st.st_size;
-        msg.stat_v2.atime = st.st_atime;
-        msg.stat_v2.mtime = st.st_mtime;
-        msg.stat_v2.ctime = st.st_ctime;
-    }
-
-    return WriteFdExactly(s, &msg.stat_v2, sizeof(msg.stat_v2));
-}
-
-static bool do_list(int s, const char* path) {
-    dirent* de;
-
-    syncmsg msg;
-    msg.dent.id = ID_DENT;
-
-    std::unique_ptr<DIR, int(*)(DIR*)> d(opendir(path), closedir);
-    if (!d) goto done;
-
-    while ((de = readdir(d.get()))) {
-        std::string filename(StringPrintf("%s/%s", path, de->d_name));
-
-        struct stat st;
-        if (lstat(filename.c_str(), &st) == 0) {
-            size_t d_name_length = strlen(de->d_name);
-            msg.dent.mode = st.st_mode;
-            msg.dent.size = st.st_size;
-            msg.dent.time = st.st_mtime;
-            msg.dent.namelen = d_name_length;
-
-            if (!WriteFdExactly(s, &msg.dent, sizeof(msg.dent)) ||
-                    !WriteFdExactly(s, de->d_name, d_name_length)) {
-                return false;
-            }
-        }
-    }
-
-done:
-    msg.dent.id = ID_DONE;
-    msg.dent.mode = 0;
-    msg.dent.size = 0;
-    msg.dent.time = 0;
-    msg.dent.namelen = 0;
-    return WriteFdExactly(s, &msg.dent, sizeof(msg.dent));
-}
-
-// Make sure that SendFail from adb_io.cpp isn't accidentally used in this file.
-#pragma GCC poison SendFail
-
-static bool SendSyncFail(int fd, const std::string& reason) {
-    D("sync: failure: %s", reason.c_str());
-
-    syncmsg msg;
-    msg.data.id = ID_FAIL;
-    msg.data.size = reason.size();
-    return WriteFdExactly(fd, &msg.data, sizeof(msg.data)) && WriteFdExactly(fd, reason);
-}
-
-static bool SendSyncFailErrno(int fd, const std::string& reason) {
-    return SendSyncFail(fd, StringPrintf("%s: %s", reason.c_str(), strerror(errno)));
-}
-
-static bool handle_send_file(int s, const char* path, uid_t uid, gid_t gid, uint64_t capabilities,
-                             mode_t mode, std::vector<char>& buffer, bool do_unlink) {
-    syncmsg msg;
-    unsigned int timestamp = 0;
-
-    __android_log_security_bswrite(SEC_TAG_ADB_SEND_FILE, path);
-
-    int fd = adb_open_mode(path, O_WRONLY | O_CREAT | O_EXCL | O_CLOEXEC, mode);
-    if (fd < 0 && errno == ENOENT) {
-        if (!secure_mkdirs(android::base::Dirname(path))) {
-            SendSyncFailErrno(s, "secure_mkdirs failed");
-            goto fail;
-        }
-        fd = adb_open_mode(path, O_WRONLY | O_CREAT | O_EXCL | O_CLOEXEC, mode);
-    }
-    if (fd < 0 && errno == EEXIST) {
-        fd = adb_open_mode(path, O_WRONLY | O_CLOEXEC, mode);
-    }
-    if (fd < 0) {
-        SendSyncFailErrno(s, "couldn't create file");
-        goto fail;
-    } else {
-        if (fchown(fd, uid, gid) == -1) {
-            SendSyncFailErrno(s, "fchown failed");
-            goto fail;
-        }
-
-        // Not all filesystems support setting SELinux labels. http://b/23530370.
-        selinux_android_restorecon(path, 0);
-
-        // fchown clears the setuid bit - restore it if present.
-        // Ignore the result of calling fchmod. It's not supported
-        // by all filesystems, so we don't check for success. b/12441485
-        fchmod(fd, mode);
-    }
-
-    while (true) {
-        if (!ReadFdExactly(s, &msg.data, sizeof(msg.data))) goto fail;
-
-        if (msg.data.id != ID_DATA) {
-            if (msg.data.id == ID_DONE) {
-                timestamp = msg.data.size;
-                break;
-            }
-            SendSyncFail(s, "invalid data message");
-            goto abort;
-        }
-
-        if (msg.data.size > buffer.size()) {  // TODO: resize buffer?
-            SendSyncFail(s, "oversize data message");
-            goto abort;
-        }
-
-        if (!ReadFdExactly(s, &buffer[0], msg.data.size)) goto abort;
-
-        if (!WriteFdExactly(fd, &buffer[0], msg.data.size)) {
-            SendSyncFailErrno(s, "write failed");
-            goto fail;
-        }
-    }
-
-    adb_close(fd);
-
-    if (!update_capabilities(path, capabilities)) {
-        SendSyncFailErrno(s, "update_capabilities failed");
-        goto fail;
-    }
-
-    utimbuf u;
-    u.actime = timestamp;
-    u.modtime = timestamp;
-    utime(path, &u);
-
-    msg.status.id = ID_OKAY;
-    msg.status.msglen = 0;
-    return WriteFdExactly(s, &msg.status, sizeof(msg.status));
-
-fail:
-    // If there's a problem on the device, we'll send an ID_FAIL message and
-    // close the socket. Unfortunately the kernel will sometimes throw that
-    // data away if the other end keeps writing without reading (which is
-    // the case with old versions of adb). To maintain compatibility, keep
-    // reading and throwing away ID_DATA packets until the other side notices
-    // that we've reported an error.
-    while (true) {
-        if (!ReadFdExactly(s, &msg.data, sizeof(msg.data))) goto fail;
-
-        if (msg.data.id == ID_DONE) {
-            goto abort;
-        } else if (msg.data.id != ID_DATA) {
-            char id[5];
-            memcpy(id, &msg.data.id, sizeof(msg.data.id));
-            id[4] = '\0';
-            D("handle_send_fail received unexpected id '%s' during failure", id);
-            goto abort;
-        }
-
-        if (msg.data.size > buffer.size()) {
-            D("handle_send_fail received oversized packet of length '%u' during failure",
-              msg.data.size);
-            goto abort;
-        }
-
-        if (!ReadFdExactly(s, &buffer[0], msg.data.size)) goto abort;
-    }
-
-abort:
-    if (fd >= 0) adb_close(fd);
-    if (do_unlink) adb_unlink(path);
-    return false;
-}
-
-#if defined(_WIN32)
-extern bool handle_send_link(int s, const std::string& path, std::vector<char>& buffer) __attribute__((error("no symlinks on Windows")));
-#else
-static bool handle_send_link(int s, const std::string& path, std::vector<char>& buffer) {
-    syncmsg msg;
-    unsigned int len;
-    int ret;
-
-    if (!ReadFdExactly(s, &msg.data, sizeof(msg.data))) return false;
-
-    if (msg.data.id != ID_DATA) {
-        SendSyncFail(s, "invalid data message: expected ID_DATA");
-        return false;
-    }
-
-    len = msg.data.size;
-    if (len > buffer.size()) { // TODO: resize buffer?
-        SendSyncFail(s, "oversize data message");
-        return false;
-    }
-    if (!ReadFdExactly(s, &buffer[0], len)) return false;
-
-    ret = symlink(&buffer[0], path.c_str());
-    if (ret && errno == ENOENT) {
-        if (!secure_mkdirs(android::base::Dirname(path))) {
-            SendSyncFailErrno(s, "secure_mkdirs failed");
-            return false;
-        }
-        ret = symlink(&buffer[0], path.c_str());
-    }
-    if (ret) {
-        SendSyncFailErrno(s, "symlink failed");
-        return false;
-    }
-
-    if (!ReadFdExactly(s, &msg.data, sizeof(msg.data))) return false;
-
-    if (msg.data.id == ID_DONE) {
-        msg.status.id = ID_OKAY;
-        msg.status.msglen = 0;
-        if (!WriteFdExactly(s, &msg.status, sizeof(msg.status))) return false;
-    } else {
-        SendSyncFail(s, "invalid data message: expected ID_DONE");
-        return false;
-    }
-
-    return true;
-}
-#endif
-
-static bool do_send(int s, const std::string& spec, std::vector<char>& buffer) {
-    // 'spec' is of the form "/some/path,0755". Break it up.
-    size_t comma = spec.find_last_of(',');
-    if (comma == std::string::npos) {
-        SendSyncFail(s, "missing , in ID_SEND");
-        return false;
-    }
-
-    std::string path = spec.substr(0, comma);
-
-    errno = 0;
-    mode_t mode = strtoul(spec.substr(comma + 1).c_str(), nullptr, 0);
-    if (errno != 0) {
-        SendSyncFail(s, "bad mode");
-        return false;
-    }
-
-    // Don't delete files before copying if they are not "regular" or symlinks.
-    struct stat st;
-    bool do_unlink = (lstat(path.c_str(), &st) == -1) || S_ISREG(st.st_mode) || S_ISLNK(st.st_mode);
-    if (do_unlink) {
-        adb_unlink(path.c_str());
-    }
-
-    if (S_ISLNK(mode)) {
-        return handle_send_link(s, path.c_str(), buffer);
-    }
-
-    // Copy user permission bits to "group" and "other" permissions.
-    mode &= 0777;
-    mode |= ((mode >> 3) & 0070);
-    mode |= ((mode >> 3) & 0007);
-
-    uid_t uid = -1;
-    gid_t gid = -1;
-    uint64_t capabilities = 0;
-    if (should_use_fs_config(path)) {
-        unsigned int broken_api_hack = mode;
-        fs_config(path.c_str(), 0, nullptr, &uid, &gid, &broken_api_hack, &capabilities);
-        mode = broken_api_hack;
-    }
-    return handle_send_file(s, path.c_str(), uid, gid, capabilities, mode, buffer, do_unlink);
-}
-
-static bool do_recv(int s, const char* path, std::vector<char>& buffer) {
-    __android_log_security_bswrite(SEC_TAG_ADB_RECV_FILE, path);
-
-    int fd = adb_open(path, O_RDONLY | O_CLOEXEC);
-    if (fd < 0) {
-        SendSyncFailErrno(s, "open failed");
-        return false;
-    }
-
-    syncmsg msg;
-    msg.data.id = ID_DATA;
-    while (true) {
-        int r = adb_read(fd, &buffer[0], buffer.size());
-        if (r <= 0) {
-            if (r == 0) break;
-            SendSyncFailErrno(s, "read failed");
-            adb_close(fd);
-            return false;
-        }
-        msg.data.size = r;
-        if (!WriteFdExactly(s, &msg.data, sizeof(msg.data)) || !WriteFdExactly(s, &buffer[0], r)) {
-            adb_close(fd);
-            return false;
-        }
-    }
-
-    adb_close(fd);
-
-    msg.data.id = ID_DONE;
-    msg.data.size = 0;
-    return WriteFdExactly(s, &msg.data, sizeof(msg.data));
-}
-
-static const char* sync_id_to_name(uint32_t id) {
-  switch (id) {
-    case ID_LSTAT_V1:
-      return "lstat_v1";
-    case ID_LSTAT_V2:
-      return "lstat_v2";
-    case ID_STAT_V2:
-      return "stat_v2";
-    case ID_LIST:
-      return "list";
-    case ID_SEND:
-      return "send";
-    case ID_RECV:
-      return "recv";
-    case ID_QUIT:
-        return "quit";
-    default:
-        return "???";
-  }
-}
-
-static bool handle_sync_command(int fd, std::vector<char>& buffer) {
-    D("sync: waiting for request");
-
-    ATRACE_CALL();
-    SyncRequest request;
-    if (!ReadFdExactly(fd, &request, sizeof(request))) {
-        SendSyncFail(fd, "command read failure");
-        return false;
-    }
-    size_t path_length = request.path_length;
-    if (path_length > 1024) {
-        SendSyncFail(fd, "path too long");
-        return false;
-    }
-    char name[1025];
-    if (!ReadFdExactly(fd, name, path_length)) {
-        SendSyncFail(fd, "filename read failure");
-        return false;
-    }
-    name[path_length] = 0;
-
-    std::string id_name = sync_id_to_name(request.id);
-    std::string trace_name = StringPrintf("%s(%s)", id_name.c_str(), name);
-    ATRACE_NAME(trace_name.c_str());
-
-    D("sync: %s('%s')", id_name.c_str(), name);
-    switch (request.id) {
-        case ID_LSTAT_V1:
-            if (!do_lstat_v1(fd, name)) return false;
-            break;
-        case ID_LSTAT_V2:
-        case ID_STAT_V2:
-            if (!do_stat_v2(fd, request.id, name)) return false;
-            break;
-        case ID_LIST:
-            if (!do_list(fd, name)) return false;
-            break;
-        case ID_SEND:
-            if (!do_send(fd, name, buffer)) return false;
-            break;
-        case ID_RECV:
-            if (!do_recv(fd, name, buffer)) return false;
-            break;
-        case ID_QUIT:
-            return false;
-        default:
-            SendSyncFail(fd, StringPrintf("unknown command %08x", request.id));
-            return false;
-    }
-
-    return true;
-}
-
-void file_sync_service(int fd, void*) {
-    std::vector<char> buffer(SYNC_DATA_MAX);
-
-    while (handle_sync_command(fd, buffer)) {
-    }
-
-    D("sync: done");
-    adb_close(fd);
-}
diff --git a/adb/file_sync_service.h b/adb/file_sync_service.h
deleted file mode 100644
index 6606efd..0000000
--- a/adb/file_sync_service.h
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * Copyright (C) 2007 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 _FILE_SYNC_SERVICE_H_
-#define _FILE_SYNC_SERVICE_H_
-
-#include <string>
-#include <vector>
-
-#define MKID(a,b,c,d) ((a) | ((b) << 8) | ((c) << 16) | ((d) << 24))
-
-#define ID_LSTAT_V1 MKID('S','T','A','T')
-#define ID_STAT_V2 MKID('S','T','A','2')
-#define ID_LSTAT_V2 MKID('L','S','T','2')
-#define ID_LIST MKID('L','I','S','T')
-#define ID_SEND MKID('S','E','N','D')
-#define ID_RECV MKID('R','E','C','V')
-#define ID_DENT MKID('D','E','N','T')
-#define ID_DONE MKID('D','O','N','E')
-#define ID_DATA MKID('D','A','T','A')
-#define ID_OKAY MKID('O','K','A','Y')
-#define ID_FAIL MKID('F','A','I','L')
-#define ID_QUIT MKID('Q','U','I','T')
-
-struct SyncRequest {
-    uint32_t id;  // ID_STAT, et cetera.
-    uint32_t path_length;  // <= 1024
-    // Followed by 'path_length' bytes of path (not NUL-terminated).
-} __attribute__((packed)) ;
-
-union syncmsg {
-    struct __attribute__((packed)) {
-        uint32_t id;
-        uint32_t mode;
-        uint32_t size;
-        uint32_t time;
-    } stat_v1;
-    struct __attribute__((packed)) {
-        uint32_t id;
-        uint32_t error;
-        uint64_t dev;
-        uint64_t ino;
-        uint32_t mode;
-        uint32_t nlink;
-        uint32_t uid;
-        uint32_t gid;
-        uint64_t size;
-        int64_t atime;
-        int64_t mtime;
-        int64_t ctime;
-    } stat_v2;
-    struct __attribute__((packed)) {
-        uint32_t id;
-        uint32_t mode;
-        uint32_t size;
-        uint32_t time;
-        uint32_t namelen;
-    } dent;
-    struct __attribute__((packed)) {
-        uint32_t id;
-        uint32_t size;
-    } data;
-    struct __attribute__((packed)) {
-        uint32_t id;
-        uint32_t msglen;
-    } status;
-};
-
-void file_sync_service(int fd, void* cookie);
-bool do_sync_ls(const char* path);
-bool do_sync_push(const std::vector<const char*>& srcs, const char* dst, bool sync);
-bool do_sync_pull(const std::vector<const char*>& srcs, const char* dst,
-                  bool copy_attrs, const char* name=nullptr);
-
-bool do_sync_sync(const std::string& lpath, const std::string& rpath, bool list_only);
-
-#define SYNC_DATA_MAX (64*1024)
-
-#endif
diff --git a/adb/framebuffer_service.cpp b/adb/framebuffer_service.cpp
deleted file mode 100644
index 7baad8b..0000000
--- a/adb/framebuffer_service.cpp
+++ /dev/null
@@ -1,185 +0,0 @@
-/*
- * Copyright (C) 2007 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 <errno.h>
-#include <fcntl.h>
-#include <linux/fb.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/ioctl.h>
-#include <sys/mman.h>
-#include <sys/types.h>
-#include <sys/wait.h>
-#include <unistd.h>
-
-#include "sysdeps.h"
-
-#include "adb.h"
-#include "adb_io.h"
-#include "fdevent.h"
-
-/* TODO:
-** - sync with vsync to avoid tearing
-*/
-/* This version number defines the format of the fbinfo struct.
-   It must match versioning in ddms where this data is consumed. */
-#define DDMS_RAWIMAGE_VERSION 1
-struct fbinfo {
-    unsigned int version;
-    unsigned int bpp;
-    unsigned int size;
-    unsigned int width;
-    unsigned int height;
-    unsigned int red_offset;
-    unsigned int red_length;
-    unsigned int blue_offset;
-    unsigned int blue_length;
-    unsigned int green_offset;
-    unsigned int green_length;
-    unsigned int alpha_offset;
-    unsigned int alpha_length;
-} __attribute__((packed));
-
-void framebuffer_service(int fd, void *cookie)
-{
-    struct fbinfo fbinfo;
-    unsigned int i, bsize;
-    char buf[640];
-    int fd_screencap;
-    int w, h, f;
-    int fds[2];
-    pid_t pid;
-
-    if (pipe2(fds, O_CLOEXEC) < 0) goto pipefail;
-
-    pid = fork();
-    if (pid < 0) goto done;
-
-    if (pid == 0) {
-        dup2(fds[1], STDOUT_FILENO);
-        adb_close(fds[0]);
-        adb_close(fds[1]);
-        const char* command = "screencap";
-        const char *args[2] = {command, NULL};
-        execvp(command, (char**)args);
-        exit(1);
-    }
-
-    adb_close(fds[1]);
-    fd_screencap = fds[0];
-
-    /* read w, h & format */
-    if(!ReadFdExactly(fd_screencap, &w, 4)) goto done;
-    if(!ReadFdExactly(fd_screencap, &h, 4)) goto done;
-    if(!ReadFdExactly(fd_screencap, &f, 4)) goto done;
-
-    fbinfo.version = DDMS_RAWIMAGE_VERSION;
-    /* see hardware/hardware.h */
-    switch (f) {
-        case 1: /* RGBA_8888 */
-            fbinfo.bpp = 32;
-            fbinfo.size = w * h * 4;
-            fbinfo.width = w;
-            fbinfo.height = h;
-            fbinfo.red_offset = 0;
-            fbinfo.red_length = 8;
-            fbinfo.green_offset = 8;
-            fbinfo.green_length = 8;
-            fbinfo.blue_offset = 16;
-            fbinfo.blue_length = 8;
-            fbinfo.alpha_offset = 24;
-            fbinfo.alpha_length = 8;
-            break;
-        case 2: /* RGBX_8888 */
-            fbinfo.bpp = 32;
-            fbinfo.size = w * h * 4;
-            fbinfo.width = w;
-            fbinfo.height = h;
-            fbinfo.red_offset = 0;
-            fbinfo.red_length = 8;
-            fbinfo.green_offset = 8;
-            fbinfo.green_length = 8;
-            fbinfo.blue_offset = 16;
-            fbinfo.blue_length = 8;
-            fbinfo.alpha_offset = 24;
-            fbinfo.alpha_length = 0;
-            break;
-        case 3: /* RGB_888 */
-            fbinfo.bpp = 24;
-            fbinfo.size = w * h * 3;
-            fbinfo.width = w;
-            fbinfo.height = h;
-            fbinfo.red_offset = 0;
-            fbinfo.red_length = 8;
-            fbinfo.green_offset = 8;
-            fbinfo.green_length = 8;
-            fbinfo.blue_offset = 16;
-            fbinfo.blue_length = 8;
-            fbinfo.alpha_offset = 24;
-            fbinfo.alpha_length = 0;
-            break;
-        case 4: /* RGB_565 */
-            fbinfo.bpp = 16;
-            fbinfo.size = w * h * 2;
-            fbinfo.width = w;
-            fbinfo.height = h;
-            fbinfo.red_offset = 11;
-            fbinfo.red_length = 5;
-            fbinfo.green_offset = 5;
-            fbinfo.green_length = 6;
-            fbinfo.blue_offset = 0;
-            fbinfo.blue_length = 5;
-            fbinfo.alpha_offset = 0;
-            fbinfo.alpha_length = 0;
-            break;
-        case 5: /* BGRA_8888 */
-            fbinfo.bpp = 32;
-            fbinfo.size = w * h * 4;
-            fbinfo.width = w;
-            fbinfo.height = h;
-            fbinfo.red_offset = 16;
-            fbinfo.red_length = 8;
-            fbinfo.green_offset = 8;
-            fbinfo.green_length = 8;
-            fbinfo.blue_offset = 0;
-            fbinfo.blue_length = 8;
-            fbinfo.alpha_offset = 24;
-            fbinfo.alpha_length = 8;
-           break;
-        default:
-            goto done;
-    }
-
-    /* write header */
-    if(!WriteFdExactly(fd, &fbinfo, sizeof(fbinfo))) goto done;
-
-    /* write data */
-    for(i = 0; i < fbinfo.size; i += bsize) {
-      bsize = sizeof(buf);
-      if (i + bsize > fbinfo.size)
-        bsize = fbinfo.size - i;
-      if(!ReadFdExactly(fd_screencap, buf, bsize)) goto done;
-      if(!WriteFdExactly(fd, buf, bsize)) goto done;
-    }
-
-done:
-    adb_close(fds[0]);
-
-    TEMP_FAILURE_RETRY(waitpid(pid, NULL, 0));
-pipefail:
-    adb_close(fd);
-}
diff --git a/adb/jdwp_service.cpp b/adb/jdwp_service.cpp
deleted file mode 100644
index f0dff06..0000000
--- a/adb/jdwp_service.cpp
+++ /dev/null
@@ -1,605 +0,0 @@
-/*
- * Copyright (C) 2015 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.
- */
-
-#if !ADB_HOST
-
-#define TRACE_TAG JDWP
-
-#include "sysdeps.h"
-
-#include <errno.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/socket.h>
-#include <sys/un.h>
-#include <unistd.h>
-
-#include <list>
-#include <memory>
-#include <vector>
-
-#include "adb.h"
-#include "adb_io.h"
-#include "adb_unique_fd.h"
-#include "adb_utils.h"
-
-/* here's how these things work.
-
-   when adbd starts, it creates a unix server socket
-   named @jdwp-control (@ is a shortcut for "first byte is zero"
-   to use the private namespace instead of the file system)
-
-   when a new JDWP daemon thread starts in a new VM process, it creates
-   a connection to @jdwp-control to announce its availability.
-
-
-     JDWP thread                             @jdwp-control
-         |                                         |
-         |------------------------------->         |
-         | hello I'm in process <pid>              |
-         |                                         |
-         |                                         |
-
-    the connection is kept alive. it will be closed automatically if
-    the JDWP process terminates (this allows adbd to detect dead
-    processes).
-
-    adbd thus maintains a list of "active" JDWP processes. it can send
-    its content to clients through the "device:debug-ports" service,
-    or even updates through the "device:track-debug-ports" service.
-
-    when a debugger wants to connect, it simply runs the command
-    equivalent to  "adb forward tcp:<hostport> jdwp:<pid>"
-
-    "jdwp:<pid>" is a new forward destination format used to target
-    a given JDWP process on the device. when sutch a request arrives,
-    adbd does the following:
-
-      - first, it calls socketpair() to create a pair of equivalent
-        sockets.
-
-      - it attaches the first socket in the pair to a local socket
-        which is itself attached to the transport's remote socket:
-
-
-      - it sends the file descriptor of the second socket directly
-        to the JDWP process with the help of sendmsg()
-
-
-     JDWP thread                             @jdwp-control
-         |                                         |
-         |                  <----------------------|
-         |           OK, try this file descriptor  |
-         |                                         |
-         |                                         |
-
-   then, the JDWP thread uses this new socket descriptor as its
-   pass-through connection to the debugger (and receives the
-   JDWP-Handshake message, answers to it, etc...)
-
-   this gives the following graphics:
-                    ____________________________________
-                   |                                    |
-                   |          ADB Server (host)         |
-                   |                                    |
-        Debugger <---> LocalSocket <----> RemoteSocket  |
-                   |                           ^^       |
-                   |___________________________||_______|
-                                               ||
-                                     Transport ||
-           (TCP for emulator - USB for device) ||
-                                               ||
-                    ___________________________||_______
-                   |                           ||       |
-                   |          ADBD  (device)   ||       |
-                   |                           VV       |
-         JDWP <======> LocalSocket <----> RemoteSocket  |
-                   |                                    |
-                   |____________________________________|
-
-    due to the way adb works, this doesn't need a special socket
-    type or fancy handling of socket termination if either the debugger
-    or the JDWP process closes the connection.
-
-    THIS IS THE SIMPLEST IMPLEMENTATION I COULD FIND, IF YOU HAPPEN
-    TO HAVE A BETTER IDEA, LET ME KNOW - Digit
-
-**********************************************************************/
-
-/** JDWP PID List Support Code
- ** for each JDWP process, we record its pid and its connected socket
- **/
-
-// PIDs are transmitted as 4 hex digits in ascii.
-static constexpr size_t PID_LEN = 4;
-
-static void jdwp_process_event(int socket, unsigned events, void* _proc);
-static void jdwp_process_list_updated(void);
-
-struct JdwpProcess;
-static std::list<std::unique_ptr<JdwpProcess>> _jdwp_list;
-
-struct JdwpProcess {
-    explicit JdwpProcess(int socket) {
-        this->socket = socket;
-        this->fde = fdevent_create(socket, jdwp_process_event, this);
-
-        if (!this->fde) {
-            fatal("could not create fdevent for new JDWP process");
-        }
-
-        this->fde->state |= FDE_DONT_CLOSE;
-
-        /* start by waiting for the PID */
-        fdevent_add(this->fde, FDE_READ);
-    }
-
-    ~JdwpProcess() {
-        if (this->socket >= 0) {
-            adb_shutdown(this->socket);
-            adb_close(this->socket);
-            this->socket = -1;
-        }
-
-        if (this->fde) {
-            fdevent_destroy(this->fde);
-            this->fde = nullptr;
-        }
-
-        out_fds.clear();
-    }
-
-    void RemoveFromList() {
-        if (this->pid >= 0) {
-            D("removing pid %d from jdwp process list", this->pid);
-        } else {
-            D("removing transient JdwpProcess from list");
-        }
-
-        auto pred = [this](const auto& proc) { return proc.get() == this; };
-        _jdwp_list.remove_if(pred);
-    }
-
-    int pid = -1;
-    int socket = -1;
-    fdevent* fde = nullptr;
-
-    std::vector<unique_fd> out_fds;
-};
-
-static size_t jdwp_process_list(char* buffer, size_t bufferlen) {
-    std::string temp;
-
-    for (auto& proc : _jdwp_list) {
-        /* skip transient connections */
-        if (proc->pid < 0) {
-            continue;
-        }
-
-        std::string next = std::to_string(proc->pid) + "\n";
-        if (temp.length() + next.length() > bufferlen) {
-            D("truncating JDWP process list (max len = %zu)", bufferlen);
-            break;
-        }
-        temp.append(next);
-    }
-
-    memcpy(buffer, temp.data(), temp.length());
-    return temp.length();
-}
-
-static size_t jdwp_process_list_msg(char* buffer, size_t bufferlen) {
-    // Message is length-prefixed with 4 hex digits in ASCII.
-    static constexpr size_t header_len = 4;
-    if (bufferlen < header_len) {
-        fatal("invalid JDWP process list buffer size: %zu", bufferlen);
-    }
-
-    char head[header_len + 1];
-    size_t len = jdwp_process_list(buffer + header_len, bufferlen - header_len);
-    snprintf(head, sizeof head, "%04zx", len);
-    memcpy(buffer, head, header_len);
-    return len + header_len;
-}
-
-static void jdwp_process_event(int socket, unsigned events, void* _proc) {
-    JdwpProcess* proc = reinterpret_cast<JdwpProcess*>(_proc);
-
-    if (events & FDE_READ) {
-        if (proc->pid < 0) {
-            /* read the PID as a 4-hexchar string */
-            char buf[PID_LEN + 1];
-            ssize_t rc = TEMP_FAILURE_RETRY(recv(socket, buf, PID_LEN, 0));
-            if (rc != PID_LEN) {
-                D("failed to read jdwp pid: %s", strerror(errno));
-                goto CloseProcess;
-            }
-            buf[PID_LEN] = '\0';
-
-            if (sscanf(buf, "%04x", &proc->pid) != 1) {
-                D("could not decode JDWP %p PID number: '%s'", proc, buf);
-                goto CloseProcess;
-            }
-
-            /* all is well, keep reading to detect connection closure */
-            D("Adding pid %d to jdwp process list", proc->pid);
-            jdwp_process_list_updated();
-        } else {
-            /* the pid was read, if we get there it's probably because the connection
-             * was closed (e.g. the JDWP process exited or crashed) */
-            char buf[32];
-
-            while (true) {
-                int len = TEMP_FAILURE_RETRY(recv(socket, buf, sizeof(buf), 0));
-
-                if (len == 0) {
-                    D("terminating JDWP %d connection: EOF", proc->pid);
-                    break;
-                } else if (len < 0) {
-                    if (len < 0 && errno == EAGAIN) {
-                        return;
-                    }
-
-                    D("terminating JDWP %d connection: EOF", proc->pid);
-                    break;
-                } else {
-                    D("ignoring unexpected JDWP %d control socket activity (%d bytes)", proc->pid,
-                      len);
-                }
-            }
-
-            goto CloseProcess;
-        }
-    }
-
-    if (events & FDE_WRITE) {
-        D("trying to send fd to JDWP process (count = %zu)", proc->out_fds.size());
-        if (!proc->out_fds.empty()) {
-            int fd = proc->out_fds.back().get();
-            struct cmsghdr* cmsg;
-            struct msghdr msg;
-            struct iovec iov;
-            char dummy = '!';
-            char buffer[sizeof(struct cmsghdr) + sizeof(int)];
-
-            iov.iov_base = &dummy;
-            iov.iov_len = 1;
-            msg.msg_name = NULL;
-            msg.msg_namelen = 0;
-            msg.msg_iov = &iov;
-            msg.msg_iovlen = 1;
-            msg.msg_flags = 0;
-            msg.msg_control = buffer;
-            msg.msg_controllen = sizeof(buffer);
-
-            cmsg = CMSG_FIRSTHDR(&msg);
-            cmsg->cmsg_len = msg.msg_controllen;
-            cmsg->cmsg_level = SOL_SOCKET;
-            cmsg->cmsg_type = SCM_RIGHTS;
-            ((int*)CMSG_DATA(cmsg))[0] = fd;
-
-            if (!set_file_block_mode(proc->socket, true)) {
-                VLOG(JDWP) << "failed to set blocking mode for fd " << proc->socket;
-                goto CloseProcess;
-            }
-
-            int ret = TEMP_FAILURE_RETRY(sendmsg(proc->socket, &msg, 0));
-            if (ret < 0) {
-                D("sending new file descriptor to JDWP %d failed: %s", proc->pid, strerror(errno));
-                goto CloseProcess;
-            }
-
-            adb_close(fd);
-
-            D("sent file descriptor %d to JDWP process %d", fd, proc->pid);
-
-            proc->out_fds.pop_back();
-
-            if (!set_file_block_mode(proc->socket, false)) {
-                VLOG(JDWP) << "failed to set non-blocking mode for fd " << proc->socket;
-                goto CloseProcess;
-            }
-
-            if (proc->out_fds.empty()) {
-                fdevent_del(proc->fde, FDE_WRITE);
-            }
-        }
-    }
-
-    return;
-
-CloseProcess:
-    proc->RemoveFromList();
-    jdwp_process_list_updated();
-}
-
-int create_jdwp_connection_fd(int pid) {
-    D("looking for pid %d in JDWP process list", pid);
-
-    for (auto& proc : _jdwp_list) {
-        if (proc->pid == pid) {
-            int fds[2];
-
-            if (adb_socketpair(fds) < 0) {
-                D("%s: socket pair creation failed: %s", __FUNCTION__, strerror(errno));
-                return -1;
-            }
-            D("socketpair: (%d,%d)", fds[0], fds[1]);
-
-            proc->out_fds.emplace_back(fds[1]);
-            if (proc->out_fds.size() == 1) {
-                fdevent_add(proc->fde, FDE_WRITE);
-            }
-
-            return fds[0];
-        }
-    }
-    D("search failed !!");
-    return -1;
-}
-
-/**  VM DEBUG CONTROL SOCKET
- **
- **  we do implement a custom asocket to receive the data
- **/
-
-/* name of the debug control Unix socket */
-#define JDWP_CONTROL_NAME "\0jdwp-control"
-#define JDWP_CONTROL_NAME_LEN (sizeof(JDWP_CONTROL_NAME) - 1)
-
-struct JdwpControl {
-    int listen_socket;
-    fdevent* fde;
-};
-
-static JdwpControl _jdwp_control;
-
-static void jdwp_control_event(int s, unsigned events, void* user);
-
-static int jdwp_control_init(JdwpControl* control, const char* sockname, int socknamelen) {
-    sockaddr_un addr;
-    socklen_t addrlen;
-    int s;
-    int maxpath = sizeof(addr.sun_path);
-    int pathlen = socknamelen;
-
-    if (pathlen >= maxpath) {
-        D("vm debug control socket name too long (%d extra chars)", pathlen + 1 - maxpath);
-        return -1;
-    }
-
-    memset(&addr, 0, sizeof(addr));
-    addr.sun_family = AF_UNIX;
-    memcpy(addr.sun_path, sockname, socknamelen);
-
-    s = socket(AF_UNIX, SOCK_SEQPACKET | SOCK_CLOEXEC, 0);
-    if (s < 0) {
-        D("could not create vm debug control socket. %d: %s", errno, strerror(errno));
-        return -1;
-    }
-
-    addrlen = pathlen + sizeof(addr.sun_family);
-
-    if (bind(s, reinterpret_cast<sockaddr*>(&addr), addrlen) < 0) {
-        D("could not bind vm debug control socket: %d: %s", errno, strerror(errno));
-        adb_close(s);
-        return -1;
-    }
-
-    if (listen(s, 4) < 0) {
-        D("listen failed in jdwp control socket: %d: %s", errno, strerror(errno));
-        adb_close(s);
-        return -1;
-    }
-
-    control->listen_socket = s;
-
-    control->fde = fdevent_create(s, jdwp_control_event, control);
-    if (control->fde == NULL) {
-        D("could not create fdevent for jdwp control socket");
-        adb_close(s);
-        return -1;
-    }
-
-    /* only wait for incoming connections */
-    fdevent_add(control->fde, FDE_READ);
-
-    D("jdwp control socket started (%d)", control->listen_socket);
-    return 0;
-}
-
-static void jdwp_control_event(int s, unsigned events, void* _control) {
-    JdwpControl* control = (JdwpControl*)_control;
-
-    if (events & FDE_READ) {
-        int s = adb_socket_accept(control->listen_socket, nullptr, nullptr);
-        if (s < 0) {
-            if (errno == ECONNABORTED) {
-                /* oops, the JDWP process died really quick */
-                D("oops, the JDWP process died really quick");
-                return;
-            } else {
-                /* the socket is probably closed ? */
-                D("weird accept() failed on jdwp control socket: %s", strerror(errno));
-                return;
-            }
-        }
-
-        auto proc = std::make_unique<JdwpProcess>(s);
-        if (!proc) {
-            fatal("failed to allocate JdwpProcess");
-        }
-
-        _jdwp_list.emplace_back(std::move(proc));
-    }
-}
-
-/** "jdwp" local service implementation
- ** this simply returns the list of known JDWP process pids
- **/
-
-struct JdwpSocket : public asocket {
-    bool pass;
-};
-
-static void jdwp_socket_close(asocket* s) {
-    D("LS(%d): closing jdwp socket", s->id);
-
-    if (s->peer) {
-        D("LS(%d) peer->close()ing peer->id=%d peer->fd=%d", s->id, s->peer->id, s->peer->fd);
-        s->peer->peer = nullptr;
-        s->peer->close(s->peer);
-        s->peer = nullptr;
-    }
-
-    remove_socket(s);
-    free(s);
-}
-
-static int jdwp_socket_enqueue(asocket* s, apacket* p) {
-    /* you can't write to this asocket */
-    D("LS(%d): JDWP socket received data?", s->id);
-    put_apacket(p);
-    s->peer->close(s->peer);
-    return -1;
-}
-
-static void jdwp_socket_ready(asocket* s) {
-    JdwpSocket* jdwp = (JdwpSocket*)s;
-    asocket* peer = jdwp->peer;
-
-    /* on the first call, send the list of pids,
-     * on the second one, close the connection
-     */
-    if (!jdwp->pass) {
-        apacket* p = get_apacket();
-        p->len = jdwp_process_list((char*)p->data, s->get_max_payload());
-        peer->enqueue(peer, p);
-        jdwp->pass = true;
-    } else {
-        peer->close(peer);
-    }
-}
-
-asocket* create_jdwp_service_socket(void) {
-    JdwpSocket* s = reinterpret_cast<JdwpSocket*>(calloc(sizeof(*s), 1));
-
-    if (!s) {
-        fatal("failed to allocate JdwpSocket");
-    }
-
-    install_local_socket(s);
-
-    s->ready = jdwp_socket_ready;
-    s->enqueue = jdwp_socket_enqueue;
-    s->close = jdwp_socket_close;
-    s->pass = false;
-
-    return s;
-}
-
-/** "track-jdwp" local service implementation
- ** this periodically sends the list of known JDWP process pids
- ** to the client...
- **/
-
-struct JdwpTracker : public asocket {
-    bool need_initial;
-};
-
-static std::vector<std::unique_ptr<JdwpTracker>> _jdwp_trackers;
-
-static void jdwp_process_list_updated(void) {
-    char buffer[1024];
-    int len = jdwp_process_list_msg(buffer, sizeof(buffer));
-
-    for (auto& t : _jdwp_trackers) {
-        apacket* p = get_apacket();
-        memcpy(p->data, buffer, len);
-        p->len = len;
-
-        if (t->peer) {
-            // The tracker might not have been connected yet.
-            t->peer->enqueue(t->peer, p);
-        }
-    }
-}
-
-static void jdwp_tracker_close(asocket* s) {
-    D("LS(%d): destroying jdwp tracker service", s->id);
-
-    if (s->peer) {
-        D("LS(%d) peer->close()ing peer->id=%d peer->fd=%d", s->id, s->peer->id, s->peer->fd);
-        s->peer->peer = nullptr;
-        s->peer->close(s->peer);
-        s->peer = nullptr;
-    }
-
-    remove_socket(s);
-
-    auto pred = [s](const auto& tracker) { return tracker.get() == s; };
-    _jdwp_trackers.erase(std::remove_if(_jdwp_trackers.begin(), _jdwp_trackers.end(), pred),
-                         _jdwp_trackers.end());
-}
-
-static void jdwp_tracker_ready(asocket* s) {
-    JdwpTracker* t = (JdwpTracker*)s;
-
-    if (t->need_initial) {
-        apacket* p = get_apacket();
-        t->need_initial = false;
-        p->len = jdwp_process_list_msg((char*)p->data, s->get_max_payload());
-        s->peer->enqueue(s->peer, p);
-    }
-}
-
-static int jdwp_tracker_enqueue(asocket* s, apacket* p) {
-    /* you can't write to this socket */
-    D("LS(%d): JDWP tracker received data?", s->id);
-    put_apacket(p);
-    s->peer->close(s->peer);
-    return -1;
-}
-
-asocket* create_jdwp_tracker_service_socket(void) {
-    auto t = std::make_unique<JdwpTracker>();
-    if (!t) {
-        fatal("failed to allocate JdwpTracker");
-    }
-
-    memset(t.get(), 0, sizeof(asocket));
-
-    install_local_socket(t.get());
-    D("LS(%d): created new jdwp tracker service", t->id);
-
-    t->ready = jdwp_tracker_ready;
-    t->enqueue = jdwp_tracker_enqueue;
-    t->close = jdwp_tracker_close;
-    t->need_initial = true;
-
-    asocket* result = t.get();
-
-    _jdwp_trackers.emplace_back(std::move(t));
-
-    return result;
-}
-
-int init_jdwp(void) {
-    return jdwp_control_init(&_jdwp_control, JDWP_CONTROL_NAME, JDWP_CONTROL_NAME_LEN);
-}
-
-#endif /* !ADB_HOST */
diff --git a/adb/line_printer.cpp b/adb/line_printer.cpp
deleted file mode 100644
index 64d10b6..0000000
--- a/adb/line_printer.cpp
+++ /dev/null
@@ -1,128 +0,0 @@
-// Copyright 2013 Google Inc. All Rights Reserved.
-//
-// 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 "line_printer.h"
-
-#include <stdio.h>
-#include <stdlib.h>
-#ifdef _WIN32
-#include <windows.h>
-#else
-#include <unistd.h>
-#include <sys/ioctl.h>
-#include <termios.h>
-#include <sys/time.h>
-#endif
-
-// Make sure printf is really adb_printf which works for UTF-8 on Windows.
-#include <sysdeps.h>
-
-// Stuff from ninja's util.h that's needed below.
-#include <vector>
-using namespace std;
-string ElideMiddle(const string& str, size_t width) {
-  const int kMargin = 3;  // Space for "...".
-  string result = str;
-  if (result.size() + kMargin > width) {
-    size_t elide_size = (width - kMargin) / 2;
-    result = result.substr(0, elide_size)
-      + "..."
-      + result.substr(result.size() - elide_size, elide_size);
-  }
-  return result;
-}
-
-LinePrinter::LinePrinter() : have_blank_line_(true) {
-#ifndef _WIN32
-  const char* term = getenv("TERM");
-  smart_terminal_ = unix_isatty(1) && term && string(term) != "dumb";
-#else
-  // Disable output buffer.  It'd be nice to use line buffering but
-  // MSDN says: "For some systems, [_IOLBF] provides line
-  // buffering. However, for Win32, the behavior is the same as _IOFBF
-  // - Full Buffering."
-  setvbuf(stdout, NULL, _IONBF, 0);
-  console_ = GetStdHandle(STD_OUTPUT_HANDLE);
-  CONSOLE_SCREEN_BUFFER_INFO csbi;
-  smart_terminal_ = GetConsoleScreenBufferInfo(console_, &csbi);
-#endif
-}
-
-static void Out(const std::string& s) {
-  // Avoid printf and C strings, since the actual output might contain null
-  // bytes like UTF-16 does (yuck).
-  fwrite(s.data(), 1, s.size(), stdout);
-}
-
-void LinePrinter::Print(string to_print, LineType type) {
-  if (!smart_terminal_) {
-    Out(to_print + "\n");
-    return;
-  }
-
-  // Print over previous line, if any.
-  // On Windows, calling a C library function writing to stdout also handles
-  // pausing the executable when the "Pause" key or Ctrl-S is pressed.
-  printf("\r");
-
-  if (type == INFO) {
-#ifdef _WIN32
-    CONSOLE_SCREEN_BUFFER_INFO csbi;
-    GetConsoleScreenBufferInfo(console_, &csbi);
-
-    // TODO: std::wstring to_print_wide; if (!android::base::UTF8ToWide(to_print, &to_print_wide)...
-    // TODO: wstring ElideMiddle.
-    to_print = ElideMiddle(to_print, static_cast<size_t>(csbi.dwSize.X));
-    // We don't want to have the cursor spamming back and forth, so instead of
-    // printf use WriteConsoleOutput which updates the contents of the buffer,
-    // but doesn't move the cursor position.
-    COORD buf_size = { csbi.dwSize.X, 1 };
-    COORD zero_zero = { 0, 0 };
-    SMALL_RECT target = {
-      csbi.dwCursorPosition.X, csbi.dwCursorPosition.Y,
-      static_cast<SHORT>(csbi.dwCursorPosition.X + csbi.dwSize.X - 1),
-      csbi.dwCursorPosition.Y
-    };
-    vector<CHAR_INFO> char_data(csbi.dwSize.X);
-    for (size_t i = 0; i < static_cast<size_t>(csbi.dwSize.X); ++i) {
-      // TODO: UnicodeChar instead of AsciiChar, to_print_wide[i].
-      char_data[i].Char.AsciiChar = i < to_print.size() ? to_print[i] : ' ';
-      char_data[i].Attributes = csbi.wAttributes;
-    }
-    // TODO: WriteConsoleOutputW.
-    WriteConsoleOutput(console_, &char_data[0], buf_size, zero_zero, &target);
-#else
-    // Limit output to width of the terminal if provided so we don't cause
-    // line-wrapping.
-    winsize size;
-    if ((ioctl(0, TIOCGWINSZ, &size) == 0) && size.ws_col) {
-      to_print = ElideMiddle(to_print, size.ws_col);
-    }
-    Out(to_print);
-    printf("\x1B[K");  // Clear to end of line.
-    fflush(stdout);
-#endif
-
-    have_blank_line_ = false;
-  } else {
-    Out(to_print);
-    Out("\n");
-    have_blank_line_ = true;
-  }
-}
-
-void LinePrinter::KeepInfoLine() {
-  if (!have_blank_line_) Out("\n");
-  have_blank_line_ = true;
-}
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/remount_service.cpp b/adb/remount_service.cpp
deleted file mode 100644
index 32ed28f..0000000
--- a/adb/remount_service.cpp
+++ /dev/null
@@ -1,142 +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.
- */
-
-#define TRACE_TAG ADB
-
-#include "sysdeps.h"
-
-#include <errno.h>
-#include <fcntl.h>
-#include <mntent.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/mount.h>
-#include <unistd.h>
-
-#include <string>
-
-#include <android-base/properties.h>
-
-#include "adb.h"
-#include "adb_io.h"
-#include "adb_utils.h"
-#include "fs_mgr.h"
-
-// Returns the device used to mount a directory in /proc/mounts.
-static std::string find_proc_mount(const char* dir) {
-    std::unique_ptr<FILE, int(*)(FILE*)> fp(setmntent("/proc/mounts", "r"), endmntent);
-    if (!fp) {
-        return "";
-    }
-
-    mntent* e;
-    while ((e = getmntent(fp.get())) != nullptr) {
-        if (strcmp(dir, e->mnt_dir) == 0) {
-            return e->mnt_fsname;
-        }
-    }
-    return "";
-}
-
-// Returns the device used to mount a directory in the fstab.
-static std::string find_fstab_mount(const char* dir) {
-    std::unique_ptr<fstab, decltype(&fs_mgr_free_fstab)> fstab(fs_mgr_read_fstab_default(),
-                                                               fs_mgr_free_fstab);
-    struct fstab_rec* rec = fs_mgr_get_entry_for_mount_point(fstab.get(), dir);
-    return rec ? rec->blk_device : "";
-}
-
-// 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);
-    } else {
-       return find_proc_mount(dir);
-    }
-}
-
-bool make_block_device_writable(const std::string& dev) {
-    int fd = unix_open(dev.c_str(), O_RDONLY | O_CLOEXEC);
-    if (fd == -1) {
-        return false;
-    }
-
-    int OFF = 0;
-    bool result = (ioctl(fd, BLKROSET, &OFF) != -1);
-    unix_close(fd);
-    return result;
-}
-
-static bool remount_partition(int fd, const char* dir) {
-    if (!directory_exists(dir)) {
-        return true;
-    }
-    std::string dev = find_mount(dir);
-    if (dev.empty()) {
-        return true;
-    }
-    if (!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, nullptr) == -1) {
-        WriteFdFmt(fd, "remount of %s failed: %s\n", dir, strerror(errno));
-        return false;
-    }
-    return true;
-}
-
-void remount_service(int fd, void* cookie) {
-    if (getuid() != 0) {
-        WriteFdExactly(fd, "Not running as root. Try \"adb root\" first.\n");
-        adb_close(fd);
-        return;
-    }
-
-    bool system_verified = !(android::base::GetProperty("partition.system.verified", "").empty());
-    bool vendor_verified = !(android::base::GetProperty("partition.vendor.verified", "").empty());
-
-    if (system_verified || vendor_verified) {
-        // Allow remount but warn of likely bad effects
-        bool both = system_verified && vendor_verified;
-        WriteFdFmt(fd,
-                   "dm_verity is enabled on the %s%s%s partition%s.\n",
-                   system_verified ? "system" : "",
-                   both ? " and " : "",
-                   vendor_verified ? "vendor" : "",
-                   both ? "s" : "");
-        WriteFdExactly(fd,
-                       "Use \"adb disable-verity\" to disable verity.\n"
-                       "If you do not, remount may succeed, however, you will still "
-                       "not be able to write to these volumes.\n");
-    }
-
-    bool success = true;
-    if (android::base::GetBoolProperty("ro.build.system_root_image", false)) {
-        success &= remount_partition(fd, "/");
-    } else {
-        success &= remount_partition(fd, "/system");
-    }
-    success &= remount_partition(fd, "/vendor");
-    success &= remount_partition(fd, "/oem");
-
-    WriteFdExactly(fd, success ? "remount succeeded\n" : "remount failed\n");
-
-    adb_close(fd);
-}
diff --git a/adb/remount_service.h b/adb/remount_service.h
deleted file mode 100644
index 7bda1be..0000000
--- a/adb/remount_service.h
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * Copyright (C) 2015 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 _REMOUNT_SERVICE_H_
-#define _REMOUNT_SERVICE_H_
-
-#include <string>
-
-bool make_block_device_writable(const std::string&);
-void remount_service(int, void*);
-
-#endif
diff --git a/adb/services.cpp b/adb/services.cpp
index 9605e6e..4b033bd 100644
--- a/adb/services.cpp
+++ b/adb/services.cpp
@@ -24,228 +24,40 @@
 #include <stdlib.h>
 #include <string.h>
 
-#ifndef _WIN32
-#include <netdb.h>
-#include <netinet/in.h>
-#include <sys/ioctl.h>
-#include <unistd.h>
-#endif
-
 #include <thread>
-
-#include <android-base/file.h>
-#include <android-base/parsenetaddress.h>
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
 #include <cutils/sockets.h>
 
-#if !ADB_HOST
-#include <android-base/properties.h>
-#include <bootloader_message/bootloader_message.h>
-#include <cutils/android_reboot.h>
-#include <log/log_properties.h>
-#endif
-
 #include "adb.h"
 #include "adb_io.h"
+#include "adb_unique_fd.h"
 #include "adb_utils.h"
-#include "file_sync_service.h"
-#include "remount_service.h"
 #include "services.h"
-#include "shell_service.h"
 #include "socket_spec.h"
 #include "sysdeps.h"
 #include "transport.h"
 
-struct stinfo {
-    void (*func)(int fd, void *cookie);
-    int fd;
-    void *cookie;
-};
+namespace {
 
-static void service_bootstrap_func(void* x) {
-    stinfo* sti = reinterpret_cast<stinfo*>(x);
-    adb_thread_setname(android::base::StringPrintf("service %d", sti->fd));
-    sti->func(sti->fd, sti->cookie);
-    free(sti);
+void service_bootstrap_func(std::string service_name, std::function<void(unique_fd)> func,
+                            unique_fd fd) {
+    adb_thread_setname(android::base::StringPrintf("%s svc %d", service_name.c_str(), fd.get()));
+    func(std::move(fd));
 }
 
-#if !ADB_HOST
+}  // namespace
 
-void restart_root_service(int fd, void *cookie) {
-    if (getuid() == 0) {
-        WriteFdExactly(fd, "adbd is already running as root\n");
-        adb_close(fd);
-    } else {
-        if (!__android_log_is_debuggable()) {
-            WriteFdExactly(fd, "adbd cannot run as root in production builds\n");
-            adb_close(fd);
-            return;
-        }
-
-        android::base::SetProperty("service.adb.root", "1");
-        WriteFdExactly(fd, "restarting adbd as root\n");
-        adb_close(fd);
-    }
-}
-
-void restart_unroot_service(int fd, void *cookie) {
-    if (getuid() != 0) {
-        WriteFdExactly(fd, "adbd not running as root\n");
-        adb_close(fd);
-    } else {
-        android::base::SetProperty("service.adb.root", "0");
-        WriteFdExactly(fd, "restarting adbd as non root\n");
-        adb_close(fd);
-    }
-}
-
-void restart_tcp_service(int fd, void *cookie) {
-    int port = (int) (uintptr_t) cookie;
-    if (port <= 0) {
-        WriteFdFmt(fd, "invalid port %d\n", port);
-        adb_close(fd);
-        return;
-    }
-
-    android::base::SetProperty("service.adb.tcp.port", android::base::StringPrintf("%d", port));
-    WriteFdFmt(fd, "restarting in TCP mode port: %d\n", port);
-    adb_close(fd);
-}
-
-void restart_usb_service(int fd, void *cookie) {
-    android::base::SetProperty("service.adb.tcp.port", "0");
-    WriteFdExactly(fd, "restarting in USB mode\n");
-    adb_close(fd);
-}
-
-static bool reboot_service_impl(int fd, const char* arg) {
-    const char* reboot_arg = arg;
-    bool auto_reboot = false;
-
-    if (strcmp(reboot_arg, "sideload-auto-reboot") == 0) {
-        auto_reboot = true;
-        reboot_arg = "sideload";
-    }
-
-    // It reboots into sideload mode by setting "--sideload" or "--sideload_auto_reboot"
-    // in the command file.
-    if (strcmp(reboot_arg, "sideload") == 0) {
-        if (getuid() != 0) {
-            WriteFdExactly(fd, "'adb root' is required for 'adb reboot sideload'.\n");
-            return false;
-        }
-
-        const std::vector<std::string> options = {
-            auto_reboot ? "--sideload_auto_reboot" : "--sideload"
-        };
-        std::string err;
-        if (!write_bootloader_message(options, &err)) {
-            D("Failed to set bootloader message: %s", err.c_str());
-            return false;
-        }
-
-        reboot_arg = "recovery";
-    }
-
-    sync();
-
-    std::string reboot_string = android::base::StringPrintf("reboot,%s", reboot_arg);
-    if (!android::base::SetProperty(ANDROID_RB_PROPERTY, reboot_string)) {
-        WriteFdFmt(fd, "reboot (%s) failed\n", reboot_string.c_str());
-        return false;
-    }
-
-    return true;
-}
-
-void reboot_service(int fd, void* arg)
-{
-    if (reboot_service_impl(fd, static_cast<const char*>(arg))) {
-        // Don't return early. Give the reboot command time to take effect
-        // to avoid messing up scripts which do "adb reboot && adb wait-for-device"
-        while (true) {
-            pause();
-        }
-    }
-
-    free(arg);
-    adb_close(fd);
-}
-
-static void reconnect_service(int fd, void* arg) {
-    WriteFdExactly(fd, "done");
-    adb_close(fd);
-    atransport* t = static_cast<atransport*>(arg);
-    kick_transport(t);
-}
-
-int reverse_service(const char* command) {
-    int s[2];
-    if (adb_socketpair(s)) {
-        PLOG(ERROR) << "cannot create service socket pair.";
-        return -1;
-    }
-    VLOG(SERVICES) << "service socketpair: " << s[0] << ", " << s[1];
-    if (handle_forward_request(command, kTransportAny, nullptr, s[1]) < 0) {
-        SendFail(s[1], "not a reverse forwarding command");
-    }
-    adb_close(s[1]);
-    return s[0];
-}
-
-// Shell service string can look like:
-//   shell[,arg1,arg2,...]:[command]
-static int ShellService(const std::string& args, const atransport* transport) {
-    size_t delimiter_index = args.find(':');
-    if (delimiter_index == std::string::npos) {
-        LOG(ERROR) << "No ':' found in shell service arguments: " << args;
-        return -1;
-    }
-
-    const std::string service_args = args.substr(0, delimiter_index);
-    const std::string command = args.substr(delimiter_index + 1);
-
-    // Defaults:
-    //   PTY for interactive, raw for non-interactive.
-    //   No protocol.
-    //   $TERM set to "dumb".
-    SubprocessType type(command.empty() ? SubprocessType::kPty
-                                        : SubprocessType::kRaw);
-    SubprocessProtocol protocol = SubprocessProtocol::kNone;
-    std::string terminal_type = "dumb";
-
-    for (const std::string& arg : android::base::Split(service_args, ",")) {
-        if (arg == kShellServiceArgRaw) {
-            type = SubprocessType::kRaw;
-        } else if (arg == kShellServiceArgPty) {
-            type = SubprocessType::kPty;
-        } else if (arg == kShellServiceArgShellProtocol) {
-            protocol = SubprocessProtocol::kShell;
-        } else if (android::base::StartsWith(arg, "TERM=")) {
-            terminal_type = arg.substr(5);
-        } else if (!arg.empty()) {
-            // This is not an error to allow for future expansion.
-            LOG(WARNING) << "Ignoring unknown shell service argument: " << arg;
-        }
-    }
-
-    return StartSubprocess(command.c_str(), terminal_type.c_str(), type, protocol);
-}
-
-#endif  // !ADB_HOST
-
-static int create_service_thread(void (*func)(int, void *), void *cookie)
-{
+unique_fd create_service_thread(const char* service_name, std::function<void(unique_fd)> func) {
     int s[2];
     if (adb_socketpair(s)) {
         printf("cannot create service socket pair\n");
-        return -1;
+        return unique_fd();
     }
     D("socketpair: (%d,%d)", s[0], s[1]);
 
 #if !ADB_HOST
-    if (func == &file_sync_service) {
+    if (strcmp(service_name, "sync") == 0) {
         // Set file sync service socket to maximum size
         int max_buf = LINUX_MAX_SOCKET_SIZE;
         adb_setsockopt(s[0], SOL_SOCKET, SO_SNDBUF, &max_buf, sizeof(max_buf));
@@ -253,21 +65,13 @@
     }
 #endif // !ADB_HOST
 
-    stinfo* sti = reinterpret_cast<stinfo*>(malloc(sizeof(stinfo)));
-    if (sti == nullptr) {
-        fatal("cannot allocate stinfo");
-    }
-    sti->func = func;
-    sti->cookie = cookie;
-    sti->fd = s[1];
-
-    std::thread(service_bootstrap_func, sti).detach();
+    std::thread(service_bootstrap_func, service_name, func, unique_fd(s[1])).detach();
 
     D("service thread started, %d:%d",s[0], s[1]);
-    return s[0];
+    return unique_fd(s[0]);
 }
 
-int service_to_fd(const char* name, const atransport* transport) {
+int service_to_fd(const char* name, atransport* transport) {
     int ret = -1;
 
     if (is_socket_spec(name)) {
@@ -276,54 +80,12 @@
         if (ret < 0) {
             LOG(ERROR) << "failed to connect to socket '" << name << "': " << error;
         }
+    } else {
 #if !ADB_HOST
-    } else if(!strncmp("dev:", name, 4)) {
-        ret = unix_open(name + 4, O_RDWR | O_CLOEXEC);
-    } else if(!strncmp(name, "framebuffer:", 12)) {
-        ret = create_service_thread(framebuffer_service, 0);
-    } else if (!strncmp(name, "jdwp:", 5)) {
-        ret = create_jdwp_connection_fd(atoi(name+5));
-    } else if(!strncmp(name, "shell", 5)) {
-        ret = ShellService(name + 5, transport);
-    } else if(!strncmp(name, "exec:", 5)) {
-        ret = StartSubprocess(name + 5, nullptr, SubprocessType::kRaw, SubprocessProtocol::kNone);
-    } else if(!strncmp(name, "sync:", 5)) {
-        ret = create_service_thread(file_sync_service, NULL);
-    } else if(!strncmp(name, "remount:", 8)) {
-        ret = create_service_thread(remount_service, NULL);
-    } else if(!strncmp(name, "reboot:", 7)) {
-        void* arg = strdup(name + 7);
-        if (arg == NULL) return -1;
-        ret = create_service_thread(reboot_service, arg);
-    } else if(!strncmp(name, "root:", 5)) {
-        ret = create_service_thread(restart_root_service, NULL);
-    } else if(!strncmp(name, "unroot:", 7)) {
-        ret = create_service_thread(restart_unroot_service, NULL);
-    } else if(!strncmp(name, "backup:", 7)) {
-        ret = StartSubprocess(android::base::StringPrintf("/system/bin/bu backup %s",
-                                                          (name + 7)).c_str(),
-                              nullptr, SubprocessType::kRaw, SubprocessProtocol::kNone);
-    } else if(!strncmp(name, "restore:", 8)) {
-        ret = StartSubprocess("/system/bin/bu restore", nullptr, SubprocessType::kRaw,
-                              SubprocessProtocol::kNone);
-    } else if(!strncmp(name, "tcpip:", 6)) {
-        int port;
-        if (sscanf(name + 6, "%d", &port) != 1) {
-            return -1;
-        }
-        ret = create_service_thread(restart_tcp_service, (void *) (uintptr_t) port);
-    } else if(!strncmp(name, "usb:", 4)) {
-        ret = create_service_thread(restart_usb_service, NULL);
-    } else if (!strncmp(name, "reverse:", 8)) {
-        ret = reverse_service(name + 8);
-    } else if(!strncmp(name, "disable-verity:", 15)) {
-        ret = create_service_thread(set_verity_enabled_state_service, (void*)0);
-    } else if(!strncmp(name, "enable-verity:", 15)) {
-        ret = create_service_thread(set_verity_enabled_state_service, (void*)1);
-    } else if (!strcmp(name, "reconnect")) {
-        ret = create_service_thread(reconnect_service, const_cast<atransport*>(transport));
+        ret = daemon_service_to_fd(name, transport).release();
 #endif
     }
+
     if (ret >= 0) {
         close_on_exec(ret);
     }
@@ -334,6 +96,7 @@
 struct state_info {
     TransportType transport_type;
     std::string serial;
+    TransportId transport_id;
     ConnectionState state;
 };
 
@@ -345,8 +108,9 @@
     while (true) {
         bool is_ambiguous = false;
         std::string error = "unknown error";
-        const char* serial = sinfo->serial.length() ? sinfo->serial.c_str() : NULL;
-        atransport* t = acquire_one_transport(sinfo->transport_type, serial, &is_ambiguous, &error);
+        const char* serial = sinfo->serial.length() ? sinfo->serial.c_str() : nullptr;
+        atransport* t = acquire_one_transport(sinfo->transport_type, serial, sinfo->transport_id,
+                                              &is_ambiguous, &error);
         if (t != nullptr && (sinfo->state == kCsAny || sinfo->state == t->GetConnectionState())) {
             SendOkay(fd);
             break;
@@ -381,8 +145,8 @@
         return;
     }
 
-    int console_port = strtol(pieces[0].c_str(), NULL, 0);
-    int adb_port = strtol(pieces[1].c_str(), NULL, 0);
+    int console_port = strtol(pieces[0].c_str(), nullptr, 0);
+    int adb_port = strtol(pieces[1].c_str(), nullptr, 0);
     if (console_port <= 0 || adb_port <= 0) {
         *response = android::base::StringPrintf("Invalid port numbers: %s", port_spec.c_str());
         return;
@@ -401,14 +165,6 @@
         return;
     }
 
-    // Check if more emulators can be registered. Similar unproblematic
-    // race condition as above.
-    int candidate_slot = get_available_local_transport_index();
-    if (candidate_slot < 0) {
-        *response = "Cannot accept more emulators";
-        return;
-    }
-
     // Preconditions met, try to connect to the emulator.
     std::string error;
     if (!local_connect_arbitrary_ports(console_port, adb_port, &error)) {
@@ -420,36 +176,36 @@
     }
 }
 
-static void connect_service(int fd, void* data) {
-    char* host = reinterpret_cast<char*>(data);
+static void connect_service(unique_fd fd, std::string host) {
     std::string response;
-    if (!strncmp(host, "emu:", 4)) {
-        connect_emulator(host + 4, &response);
+    if (!strncmp(host.c_str(), "emu:", 4)) {
+        connect_emulator(host.c_str() + 4, &response);
     } else {
-        connect_device(host, &response);
+        connect_device(host.c_str(), &response);
     }
-    free(host);
 
     // Send response for emulator and device
-    SendProtocolString(fd, response);
-    adb_close(fd);
+    SendProtocolString(fd.get(), response);
 }
 #endif
 
 #if ADB_HOST
-asocket* host_service_to_socket(const char* name, const char* serial) {
+asocket* host_service_to_socket(const char* name, const char* serial, TransportId transport_id) {
     if (!strcmp(name,"track-devices")) {
-        return create_device_tracker();
+        return create_device_tracker(false);
+    } else if (!strcmp(name, "track-devices-l")) {
+        return create_device_tracker(true);
     } else if (android::base::StartsWith(name, "wait-for-")) {
         name += strlen("wait-for-");
 
-        std::unique_ptr<state_info> sinfo(new state_info);
+        std::unique_ptr<state_info> sinfo = std::make_unique<state_info>();
         if (sinfo == nullptr) {
             fprintf(stderr, "couldn't allocate state_info: %s", strerror(errno));
             return nullptr;
         }
 
         if (serial) sinfo->serial = serial;
+        sinfo->transport_id = transport_id;
 
         if (android::base::StartsWith(name, "local")) {
             name += strlen("local");
@@ -478,13 +234,20 @@
             return nullptr;
         }
 
-        int fd = create_service_thread(wait_for_state, sinfo.release());
+        int fd = create_service_thread(
+                         "wait", std::bind(wait_for_state, std::placeholders::_1, sinfo.get()))
+                         .release();
+        if (fd != -1) {
+            sinfo.release();
+        }
         return create_local_socket(fd);
     } else if (!strncmp(name, "connect:", 8)) {
-        char* host = strdup(name + 8);
-        int fd = create_service_thread(connect_service, host);
+        std::string host(name + strlen("connect:"));
+        int fd = create_service_thread("connect",
+                                       std::bind(connect_service, std::placeholders::_1, host))
+                         .release();
         return create_local_socket(fd);
     }
-    return NULL;
+    return nullptr;
 }
 #endif /* ADB_HOST */
diff --git a/adb/services.h b/adb/services.h
index 0428ca4..0ce25ba 100644
--- a/adb/services.h
+++ b/adb/services.h
@@ -17,8 +17,11 @@
 #ifndef SERVICES_H_
 #define SERVICES_H_
 
+#include "adb_unique_fd.h"
+
 constexpr char kShellServiceArgRaw[] = "raw";
 constexpr char kShellServiceArgPty[] = "pty";
 constexpr char kShellServiceArgShellProtocol[] = "v2";
 
+unique_fd create_service_thread(const char* service_name, std::function<void(unique_fd)> func);
 #endif  // SERVICES_H_
diff --git a/adb/set_verity_enable_state_service.cpp b/adb/set_verity_enable_state_service.cpp
deleted file mode 100644
index 253d14a..0000000
--- a/adb/set_verity_enable_state_service.cpp
+++ /dev/null
@@ -1,205 +0,0 @@
-/*
- * Copyright (C) 2014 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.
- */
-
-#define TRACE_TAG ADB
-
-#include "sysdeps.h"
-
-#include <fcntl.h>
-#include <inttypes.h>
-#include <libavb_user/libavb_user.h>
-#include <stdarg.h>
-#include <stdio.h>
-#include <sys/stat.h>
-
-#include "android-base/properties.h"
-#include "android-base/stringprintf.h"
-#include <log/log_properties.h>
-
-#include "adb.h"
-#include "adb_io.h"
-#include "adb_unique_fd.h"
-#include "fs_mgr.h"
-#include "remount_service.h"
-
-#include "fec/io.h"
-
-struct fstab *fstab;
-
-#ifdef ALLOW_ADBD_DISABLE_VERITY
-static const bool kAllowDisableVerity = true;
-#else
-static const bool kAllowDisableVerity = false;
-#endif
-
-/* Turn verity on/off */
-static bool set_verity_enabled_state(int fd, const char* block_device, const char* mount_point,
-                                     bool enable) {
-    if (!make_block_device_writable(block_device)) {
-        WriteFdFmt(fd, "Could not make block device %s writable (%s).\n",
-                   block_device, strerror(errno));
-        return false;
-    }
-
-    fec::io fh(block_device, O_RDWR);
-
-    if (!fh) {
-        WriteFdFmt(fd, "Could not open block device %s (%s).\n", block_device, strerror(errno));
-        WriteFdFmt(fd, "Maybe run adb root?\n");
-        return false;
-    }
-
-    fec_verity_metadata metadata;
-
-    if (!fh.get_verity_metadata(metadata)) {
-        WriteFdFmt(fd, "Couldn't find verity metadata!\n");
-        return false;
-    }
-
-    if (!enable && metadata.disabled) {
-        WriteFdFmt(fd, "Verity already disabled on %s\n", mount_point);
-        return false;
-    }
-
-    if (enable && !metadata.disabled) {
-        WriteFdFmt(fd, "Verity already enabled on %s\n", mount_point);
-        return false;
-    }
-
-    if (!fh.set_verity_status(enable)) {
-        WriteFdFmt(fd, "Could not set verity %s flag on device %s with error %s\n",
-                   enable ? "enabled" : "disabled",
-                   block_device, strerror(errno));
-        return false;
-    }
-
-    WriteFdFmt(fd, "Verity %s on %s\n", enable ? "enabled" : "disabled", mount_point);
-    return true;
-}
-
-/* Helper function to get A/B suffix, if any. If the device isn't
- * using A/B the empty string is returned. Otherwise either "_a",
- * "_b", ... is returned.
- *
- * Note that since sometime in O androidboot.slot_suffix is deprecated
- * and androidboot.slot should be used instead. Since bootloaders may
- * be out of sync with the OS, we check both and for extra safety
- * prepend a leading underscore if there isn't one already.
- */
-static std::string get_ab_suffix() {
-    std::string ab_suffix = android::base::GetProperty("ro.boot.slot_suffix", "");
-    if (ab_suffix == "") {
-        ab_suffix = android::base::GetProperty("ro.boot.slot", "");
-    }
-    if (ab_suffix.size() > 0 && ab_suffix[0] != '_') {
-        ab_suffix = std::string("_") + ab_suffix;
-    }
-    return ab_suffix;
-}
-
-/* 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 (!avb_user_verity_get(ops, ab_suffix.c_str(), &verity_enabled)) {
-        WriteFdFmt(fd, "Error getting verity state\n");
-        return false;
-    }
-
-    if ((verity_enabled && enable_verity) || (!verity_enabled && !enable_verity)) {
-        WriteFdFmt(fd, "verity is already %s\n", verity_enabled ? "enabled" : "disabled");
-        return false;
-    }
-
-    if (!avb_user_verity_set(ops, ab_suffix.c_str(), enable_verity)) {
-        WriteFdFmt(fd, "Error setting verity\n");
-        return false;
-    }
-
-    WriteFdFmt(fd, "Successfully %s verity\n", enable_verity ? "enabled" : "disabled");
-    return true;
-}
-
-void set_verity_enabled_state_service(int fd, void* cookie) {
-    unique_fd closer(fd);
-    bool any_changed = false;
-
-    bool enable = (cookie != NULL);
-
-    // Figure out if we're using VB1.0 or VB2.0 (aka AVB) - by
-    // contract, androidboot.vbmeta.digest is set by the bootloader
-    // when using AVB).
-    bool using_avb = !android::base::GetProperty("ro.boot.vbmeta.digest", "").empty();
-
-    // If using AVB, dm-verity is used on any build so we want it to
-    // be possible to disable/enable on any build (except USER). For
-    // VB1.0 dm-verity is only enabled on certain builds.
-    if (!using_avb) {
-        if (!kAllowDisableVerity) {
-            WriteFdFmt(fd, "%s-verity only works for userdebug builds\n",
-                       enable ? "enable" : "disable");
-        }
-
-        if (!android::base::GetBoolProperty("ro.secure", false)) {
-            WriteFdFmt(fd, "verity not enabled - ENG build\n");
-            return;
-        }
-    }
-
-    // Should never be possible to disable dm-verity on a USER build
-    // regardless of using AVB or VB1.0.
-    if (!__android_log_is_debuggable()) {
-        WriteFdFmt(fd, "verity cannot be disabled/enabled - USER build\n");
-        return;
-    }
-
-    if (using_avb) {
-        // Yep, the system is using AVB.
-        AvbOps* ops = avb_ops_user_new();
-        if (ops == nullptr) {
-            WriteFdFmt(fd, "Error getting AVB ops\n");
-            return;
-        }
-        if (set_avb_verity_enabled_state(fd, ops, enable)) {
-            any_changed = true;
-        }
-        avb_ops_user_free(ops);
-    } else {
-        // Not using AVB - assume VB1.0.
-
-        // read all fstab entries at once from all sources
-        fstab = fs_mgr_read_fstab_default();
-        if (!fstab) {
-            WriteFdFmt(fd, "Failed to read fstab\nMaybe run adb root?\n");
-            return;
-        }
-
-        // Loop through entries looking for ones that vold manages.
-        for (int i = 0; i < fstab->num_entries; i++) {
-            if (fs_mgr_is_verified(&fstab->recs[i])) {
-                if (set_verity_enabled_state(fd, fstab->recs[i].blk_device,
-                                             fstab->recs[i].mount_point, enable)) {
-                    any_changed = true;
-                }
-            }
-        }
-    }
-
-    if (any_changed) {
-        WriteFdFmt(fd, "Now reboot your device for settings to take effect\n");
-    }
-}
diff --git a/adb/shell_protocol.h b/adb/shell_protocol.h
new file mode 100644
index 0000000..2c82689
--- /dev/null
+++ b/adb/shell_protocol.h
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+
+#pragma once
+
+#include <stdint.h>
+
+#include <android-base/macros.h>
+
+#include "adb.h"
+
+// Class to send and receive shell protocol packets.
+//
+// To keep things simple and predictable, reads and writes block until an entire
+// packet is complete.
+//
+// Example: read raw data from |fd| and send it in a packet.
+//   ShellProtocol* p = new ShellProtocol(protocol_fd);
+//   int len = adb_read(stdout_fd, p->data(), p->data_capacity());
+//   packet->WritePacket(ShellProtocol::kIdStdout, len);
+//
+// Example: read a packet and print it to |stdout|.
+//   ShellProtocol* p = new ShellProtocol(protocol_fd);
+//   if (p->ReadPacket() && p->id() == kIdStdout) {
+//       fwrite(p->data(), 1, p->data_length(), stdout);
+//   }
+class ShellProtocol {
+  public:
+    // This is an unscoped enum to make it easier to compare against raw bytes.
+    enum Id : uint8_t {
+        kIdStdin = 0,
+        kIdStdout = 1,
+        kIdStderr = 2,
+        kIdExit = 3,
+
+        // Close subprocess stdin if possible.
+        kIdCloseStdin = 4,
+
+        // Window size change (an ASCII version of struct winsize).
+        kIdWindowSizeChange = 5,
+
+        // Indicates an invalid or unknown packet.
+        kIdInvalid = 255,
+    };
+
+    // ShellPackets will probably be too large to allocate on the stack so they
+    // should be dynamically allocated on the heap instead.
+    //
+    // |fd| is an open file descriptor to be used to send or receive packets.
+    explicit ShellProtocol(int fd);
+    virtual ~ShellProtocol();
+
+    // Returns a pointer to the data buffer.
+    const char* data() const { return buffer_ + kHeaderSize; }
+    char* data() { return buffer_ + kHeaderSize; }
+
+    // Returns the total capacity of the data buffer.
+    size_t data_capacity() const { return buffer_end_ - data(); }
+
+    // Reads a packet from the FD.
+    //
+    // If a packet is too big to fit in the buffer then Read() will split the
+    // packet across multiple calls. For example, reading a 50-byte packet into
+    // a 20-byte buffer would read 20 bytes, 20 bytes, then 10 bytes.
+    //
+    // Returns false if the FD closed or errored.
+    bool Read();
+
+    // Returns the ID of the packet in the buffer.
+    int id() const { return buffer_[0]; }
+
+    // Returns the number of bytes that have been read into the data buffer.
+    size_t data_length() const { return data_length_; }
+
+    // Writes the packet currently in the buffer to the FD.
+    //
+    // Returns false if the FD closed or errored.
+    bool Write(Id id, size_t length);
+
+  private:
+    // Packets support 4-byte lengths.
+    typedef uint32_t length_t;
+
+    enum {
+        // It's OK if MAX_PAYLOAD doesn't match on the sending and receiving
+        // end, reading will split larger packets into multiple smaller ones.
+        kBufferSize = MAX_PAYLOAD,
+
+        // Header is 1 byte ID + 4 bytes length.
+        kHeaderSize = sizeof(Id) + sizeof(length_t)
+    };
+
+    int fd_;
+    char buffer_[kBufferSize];
+    size_t data_length_ = 0, bytes_left_ = 0;
+
+    // We need to be able to modify this value for testing purposes, but it
+    // will stay constant during actual program use.
+    char* buffer_end_ = buffer_ + sizeof(buffer_);
+
+    friend class ShellProtocolTest;
+
+    DISALLOW_COPY_AND_ASSIGN(ShellProtocol);
+};
diff --git a/adb/shell_service.cpp b/adb/shell_service.cpp
deleted file mode 100644
index ee821f8..0000000
--- a/adb/shell_service.cpp
+++ /dev/null
@@ -1,756 +0,0 @@
-/*
- * Copyright (C) 2015 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.
- */
-
-// Functionality for launching and managing shell subprocesses.
-//
-// There are two types of subprocesses, PTY or raw. PTY is typically used for
-// an interactive session, raw for non-interactive. There are also two methods
-// of communication with the subprocess, passing raw data or using a simple
-// protocol to wrap packets. The protocol allows separating stdout/stderr and
-// passing the exit code back, but is not backwards compatible.
-//   ----------------+--------------------------------------
-//   Type  Protocol  |   Exit code?  Separate stdout/stderr?
-//   ----------------+--------------------------------------
-//   PTY   No        |   No          No
-//   Raw   No        |   No          No
-//   PTY   Yes       |   Yes         No
-//   Raw   Yes       |   Yes         Yes
-//   ----------------+--------------------------------------
-//
-// Non-protocol subprocesses work by passing subprocess stdin/out/err through
-// a single pipe which is registered with a local socket in adbd. The local
-// socket uses the fdevent loop to pass raw data between this pipe and the
-// transport, which then passes data back to the adb client. Cleanup is done by
-// waiting in a separate thread for the subprocesses to exit and then signaling
-// a separate fdevent to close out the local socket from the main loop.
-//
-// ------------------+-------------------------+------------------------------
-//   Subprocess      |  adbd subprocess thread |   adbd main fdevent loop
-// ------------------+-------------------------+------------------------------
-//                   |                         |
-//   stdin/out/err <----------------------------->       LocalSocket
-//      |            |                         |
-//      |            |      Block on exit      |
-//      |            |           *             |
-//      v            |           *             |
-//     Exit         --->      Unblock          |
-//                   |           |             |
-//                   |           v             |
-//                   |   Notify shell exit FD --->    Close LocalSocket
-// ------------------+-------------------------+------------------------------
-//
-// The protocol requires the thread to intercept stdin/out/err in order to
-// wrap/unwrap data with shell protocol packets.
-//
-// ------------------+-------------------------+------------------------------
-//   Subprocess      |  adbd subprocess thread |   adbd main fdevent loop
-// ------------------+-------------------------+------------------------------
-//                   |                         |
-//     stdin/out   <--->      Protocol       <--->       LocalSocket
-//     stderr       --->      Protocol        --->       LocalSocket
-//       |           |                         |
-//       v           |                         |
-//      Exit        --->  Exit code protocol  --->       LocalSocket
-//                   |           |             |
-//                   |           v             |
-//                   |   Notify shell exit FD --->    Close LocalSocket
-// ------------------+-------------------------+------------------------------
-//
-// An alternate approach is to put the protocol wrapping/unwrapping in the main
-// fdevent loop, which has the advantage of being able to re-use the existing
-// select() code for handling data streams. However, implementation turned out
-// to be more complex due to partial reads and non-blocking I/O so this model
-// was chosen instead.
-
-#define TRACE_TAG SHELL
-
-#include "sysdeps.h"
-
-#include "shell_service.h"
-
-#include <errno.h>
-#include <paths.h>
-#include <pty.h>
-#include <pwd.h>
-#include <sys/select.h>
-#include <termios.h>
-
-#include <memory>
-#include <string>
-#include <thread>
-#include <unordered_map>
-#include <vector>
-
-#include <android-base/logging.h>
-#include <android-base/stringprintf.h>
-#include <private/android_logger.h>
-
-#include "adb.h"
-#include "adb_io.h"
-#include "adb_trace.h"
-#include "adb_unique_fd.h"
-#include "adb_utils.h"
-#include "security_log_tags.h"
-
-namespace {
-
-// Reads from |fd| until close or failure.
-std::string ReadAll(int fd) {
-    char buffer[512];
-    std::string received;
-
-    while (1) {
-        int bytes = adb_read(fd, buffer, sizeof(buffer));
-        if (bytes <= 0) {
-            break;
-        }
-        received.append(buffer, bytes);
-    }
-
-    return received;
-}
-
-// Creates a socketpair and saves the endpoints to |fd1| and |fd2|.
-bool CreateSocketpair(unique_fd* fd1, unique_fd* fd2) {
-    int sockets[2];
-    if (adb_socketpair(sockets) < 0) {
-        PLOG(ERROR) << "cannot create socket pair";
-        return false;
-    }
-    fd1->reset(sockets[0]);
-    fd2->reset(sockets[1]);
-    return true;
-}
-
-class Subprocess {
-  public:
-    Subprocess(const std::string& command, const char* terminal_type,
-               SubprocessType type, SubprocessProtocol protocol);
-    ~Subprocess();
-
-    const std::string& command() const { return command_; }
-
-    int ReleaseLocalSocket() { return local_socket_sfd_.release(); }
-
-    pid_t pid() const { return pid_; }
-
-    // Sets up FDs, forks a subprocess, starts the subprocess manager thread,
-    // and exec's the child. Returns false and sets error on failure.
-    bool ForkAndExec(std::string* _Nonnull error);
-
-    // Start the subprocess manager thread. Consumes the subprocess, regardless of success.
-    // Returns false and sets error on failure.
-    static bool StartThread(std::unique_ptr<Subprocess> subprocess,
-                            std::string* _Nonnull error);
-
-  private:
-    // Opens the file at |pts_name|.
-    int OpenPtyChildFd(const char* pts_name, unique_fd* error_sfd);
-
-    static void ThreadHandler(void* userdata);
-    void PassDataStreams();
-    void WaitForExit();
-
-    unique_fd* SelectLoop(fd_set* master_read_set_ptr,
-                          fd_set* master_write_set_ptr);
-
-    // Input/output stream handlers. Success returns nullptr, failure returns
-    // a pointer to the failed FD.
-    unique_fd* PassInput();
-    unique_fd* PassOutput(unique_fd* sfd, ShellProtocol::Id id);
-
-    const std::string command_;
-    const std::string terminal_type_;
-    bool make_pty_raw_ = false;
-    SubprocessType type_;
-    SubprocessProtocol protocol_;
-    pid_t pid_ = -1;
-    unique_fd local_socket_sfd_;
-
-    // Shell protocol variables.
-    unique_fd stdinout_sfd_, stderr_sfd_, protocol_sfd_;
-    std::unique_ptr<ShellProtocol> input_, output_;
-    size_t input_bytes_left_ = 0;
-
-    DISALLOW_COPY_AND_ASSIGN(Subprocess);
-};
-
-Subprocess::Subprocess(const std::string& command, const char* terminal_type,
-                       SubprocessType type, SubprocessProtocol protocol)
-    : command_(command),
-      terminal_type_(terminal_type ? terminal_type : ""),
-      type_(type),
-      protocol_(protocol) {
-    // If we aren't using the shell protocol we must allocate a PTY to properly close the
-    // subprocess. PTYs automatically send SIGHUP to the slave-side process when the master side
-    // of the PTY closes, which we rely on. If we use a raw pipe, processes that don't read/write,
-    // e.g. screenrecord, will never notice the broken pipe and terminate.
-    // The shell protocol doesn't require a PTY because it's always monitoring the local socket FD
-    // with select() and will send SIGHUP manually to the child process.
-    if (protocol_ == SubprocessProtocol::kNone && type_ == SubprocessType::kRaw) {
-        // Disable PTY input/output processing since the client is expecting raw data.
-        D("Can't create raw subprocess without shell protocol, using PTY in raw mode instead");
-        type_ = SubprocessType::kPty;
-        make_pty_raw_ = true;
-    }
-}
-
-Subprocess::~Subprocess() {
-    WaitForExit();
-}
-
-bool Subprocess::ForkAndExec(std::string* error) {
-    unique_fd child_stdinout_sfd, child_stderr_sfd;
-    unique_fd parent_error_sfd, child_error_sfd;
-    char pts_name[PATH_MAX];
-
-    if (command_.empty()) {
-        __android_log_security_bswrite(SEC_TAG_ADB_SHELL_INTERACTIVE, "");
-    } else {
-        __android_log_security_bswrite(SEC_TAG_ADB_SHELL_CMD, command_.c_str());
-    }
-
-    // Create a socketpair for the fork() child to report any errors back to the parent. Since we
-    // use threads, logging directly from the child might deadlock due to locks held in another
-    // thread during the fork.
-    if (!CreateSocketpair(&parent_error_sfd, &child_error_sfd)) {
-        *error = android::base::StringPrintf(
-            "failed to create pipe for subprocess error reporting: %s", strerror(errno));
-        return false;
-    }
-
-    // Construct the environment for the child before we fork.
-    passwd* pw = getpwuid(getuid());
-    std::unordered_map<std::string, std::string> env;
-    if (environ) {
-        char** current = environ;
-        while (char* env_cstr = *current++) {
-            std::string env_string = env_cstr;
-            char* delimiter = strchr(&env_string[0], '=');
-
-            // Drop any values that don't contain '='.
-            if (delimiter) {
-                *delimiter++ = '\0';
-                env[env_string.c_str()] = delimiter;
-            }
-        }
-    }
-
-    if (pw != nullptr) {
-        // TODO: $HOSTNAME? Normally bash automatically sets that, but mksh doesn't.
-        env["HOME"] = pw->pw_dir;
-        env["LOGNAME"] = pw->pw_name;
-        env["USER"] = pw->pw_name;
-        env["SHELL"] = pw->pw_shell;
-    }
-
-    if (!terminal_type_.empty()) {
-        env["TERM"] = terminal_type_;
-    }
-
-    std::vector<std::string> joined_env;
-    for (auto it : env) {
-        const char* key = it.first.c_str();
-        const char* value = it.second.c_str();
-        joined_env.push_back(android::base::StringPrintf("%s=%s", key, value));
-    }
-
-    std::vector<const char*> cenv;
-    for (const std::string& str : joined_env) {
-        cenv.push_back(str.c_str());
-    }
-    cenv.push_back(nullptr);
-
-    if (type_ == SubprocessType::kPty) {
-        int fd;
-        pid_ = forkpty(&fd, pts_name, nullptr, nullptr);
-        if (pid_ > 0) {
-          stdinout_sfd_.reset(fd);
-        }
-    } else {
-        if (!CreateSocketpair(&stdinout_sfd_, &child_stdinout_sfd)) {
-            *error = android::base::StringPrintf("failed to create socketpair for stdin/out: %s",
-                                                 strerror(errno));
-            return false;
-        }
-        // Raw subprocess + shell protocol allows for splitting stderr.
-        if (protocol_ == SubprocessProtocol::kShell &&
-                !CreateSocketpair(&stderr_sfd_, &child_stderr_sfd)) {
-            *error = android::base::StringPrintf("failed to create socketpair for stderr: %s",
-                                                 strerror(errno));
-            return false;
-        }
-        pid_ = fork();
-    }
-
-    if (pid_ == -1) {
-        *error = android::base::StringPrintf("fork failed: %s", strerror(errno));
-        return false;
-    }
-
-    if (pid_ == 0) {
-        // Subprocess child.
-        setsid();
-
-        if (type_ == SubprocessType::kPty) {
-            child_stdinout_sfd.reset(OpenPtyChildFd(pts_name, &child_error_sfd));
-        }
-
-        dup2(child_stdinout_sfd, STDIN_FILENO);
-        dup2(child_stdinout_sfd, STDOUT_FILENO);
-        dup2(child_stderr_sfd != -1 ? child_stderr_sfd : child_stdinout_sfd, STDERR_FILENO);
-
-        // exec doesn't trigger destructors, close the FDs manually.
-        stdinout_sfd_.reset(-1);
-        stderr_sfd_.reset(-1);
-        child_stdinout_sfd.reset(-1);
-        child_stderr_sfd.reset(-1);
-        parent_error_sfd.reset(-1);
-        close_on_exec(child_error_sfd);
-
-        // adbd sets SIGPIPE to SIG_IGN to get EPIPE instead, and Linux propagates that to child
-        // processes, so we need to manually reset back to SIG_DFL here (http://b/35209888).
-        signal(SIGPIPE, SIG_DFL);
-
-        if (command_.empty()) {
-            execle(_PATH_BSHELL, _PATH_BSHELL, "-", nullptr, cenv.data());
-        } else {
-            execle(_PATH_BSHELL, _PATH_BSHELL, "-c", command_.c_str(), nullptr, cenv.data());
-        }
-        WriteFdExactly(child_error_sfd, "exec '" _PATH_BSHELL "' failed: ");
-        WriteFdExactly(child_error_sfd, strerror(errno));
-        child_error_sfd.reset(-1);
-        _Exit(1);
-    }
-
-    // Subprocess parent.
-    D("subprocess parent: stdin/stdout FD = %d, stderr FD = %d",
-      stdinout_sfd_.get(), stderr_sfd_.get());
-
-    // Wait to make sure the subprocess exec'd without error.
-    child_error_sfd.reset(-1);
-    std::string error_message = ReadAll(parent_error_sfd);
-    if (!error_message.empty()) {
-        *error = error_message;
-        return false;
-    }
-
-    D("subprocess parent: exec completed");
-    if (protocol_ == SubprocessProtocol::kNone) {
-        // No protocol: all streams pass through the stdinout FD and hook
-        // directly into the local socket for raw data transfer.
-        local_socket_sfd_.reset(stdinout_sfd_.release());
-    } else {
-        // Shell protocol: create another socketpair to intercept data.
-        if (!CreateSocketpair(&protocol_sfd_, &local_socket_sfd_)) {
-            *error = android::base::StringPrintf(
-                "failed to create socketpair to intercept data: %s", strerror(errno));
-            kill(pid_, SIGKILL);
-            return false;
-        }
-        D("protocol FD = %d", protocol_sfd_.get());
-
-        input_.reset(new ShellProtocol(protocol_sfd_));
-        output_.reset(new ShellProtocol(protocol_sfd_));
-        if (!input_ || !output_) {
-            *error = "failed to allocate shell protocol objects";
-            kill(pid_, SIGKILL);
-            return false;
-        }
-
-        // Don't let reads/writes to the subprocess block our thread. This isn't
-        // likely but could happen under unusual circumstances, such as if we
-        // write a ton of data to stdin but the subprocess never reads it and
-        // the pipe fills up.
-        for (int fd : {stdinout_sfd_.get(), stderr_sfd_.get()}) {
-            if (fd >= 0) {
-                if (!set_file_block_mode(fd, false)) {
-                    *error = android::base::StringPrintf(
-                        "failed to set non-blocking mode for fd %d", fd);
-                    kill(pid_, SIGKILL);
-                    return false;
-                }
-            }
-        }
-    }
-
-    D("subprocess parent: completed");
-    return true;
-}
-
-bool Subprocess::StartThread(std::unique_ptr<Subprocess> subprocess, std::string* error) {
-    Subprocess* raw = subprocess.release();
-    std::thread(ThreadHandler, raw).detach();
-
-    return true;
-}
-
-int Subprocess::OpenPtyChildFd(const char* pts_name, unique_fd* error_sfd) {
-    int child_fd = adb_open(pts_name, O_RDWR | O_CLOEXEC);
-    if (child_fd == -1) {
-        // Don't use WriteFdFmt; since we're in the fork() child we don't want
-        // to allocate any heap memory to avoid race conditions.
-        const char* messages[] = {"child failed to open pseudo-term slave ",
-                                  pts_name, ": ", strerror(errno)};
-        for (const char* message : messages) {
-            WriteFdExactly(*error_sfd, message);
-        }
-        abort();
-    }
-
-    if (make_pty_raw_) {
-        termios tattr;
-        if (tcgetattr(child_fd, &tattr) == -1) {
-            int saved_errno = errno;
-            WriteFdExactly(*error_sfd, "tcgetattr failed: ");
-            WriteFdExactly(*error_sfd, strerror(saved_errno));
-            abort();
-        }
-
-        cfmakeraw(&tattr);
-        if (tcsetattr(child_fd, TCSADRAIN, &tattr) == -1) {
-            int saved_errno = errno;
-            WriteFdExactly(*error_sfd, "tcsetattr failed: ");
-            WriteFdExactly(*error_sfd, strerror(saved_errno));
-            abort();
-        }
-    }
-
-    return child_fd;
-}
-
-void Subprocess::ThreadHandler(void* userdata) {
-    Subprocess* subprocess = reinterpret_cast<Subprocess*>(userdata);
-
-    adb_thread_setname(android::base::StringPrintf(
-            "shell srvc %d", subprocess->pid()));
-
-    D("passing data streams for PID %d", subprocess->pid());
-    subprocess->PassDataStreams();
-
-    D("deleting Subprocess for PID %d", subprocess->pid());
-    delete subprocess;
-}
-
-void Subprocess::PassDataStreams() {
-    if (protocol_sfd_ == -1) {
-        return;
-    }
-
-    // Start by trying to read from the protocol FD, stdout, and stderr.
-    fd_set master_read_set, master_write_set;
-    FD_ZERO(&master_read_set);
-    FD_ZERO(&master_write_set);
-    for (unique_fd* sfd : {&protocol_sfd_, &stdinout_sfd_, &stderr_sfd_}) {
-        if (*sfd != -1) {
-            FD_SET(*sfd, &master_read_set);
-        }
-    }
-
-    // Pass data until the protocol FD or both the subprocess pipes die, at
-    // which point we can't pass any more data.
-    while (protocol_sfd_ != -1 && (stdinout_sfd_ != -1 || stderr_sfd_ != -1)) {
-        unique_fd* dead_sfd = SelectLoop(&master_read_set, &master_write_set);
-        if (dead_sfd) {
-            D("closing FD %d", dead_sfd->get());
-            FD_CLR(*dead_sfd, &master_read_set);
-            FD_CLR(*dead_sfd, &master_write_set);
-            if (dead_sfd == &protocol_sfd_) {
-                // Using SIGHUP is a decent general way to indicate that the
-                // controlling process is going away. If specific signals are
-                // needed (e.g. SIGINT), pass those through the shell protocol
-                // and only fall back on this for unexpected closures.
-                D("protocol FD died, sending SIGHUP to pid %d", pid_);
-                kill(pid_, SIGHUP);
-
-                // We also need to close the pipes connected to the child process
-                // so that if it ignores SIGHUP and continues to write data it
-                // won't fill up the pipe and block.
-                stdinout_sfd_.reset();
-                stderr_sfd_.reset();
-            }
-            dead_sfd->reset();
-        }
-    }
-}
-
-namespace {
-
-inline bool ValidAndInSet(const unique_fd& sfd, fd_set* set) {
-    return sfd != -1 && FD_ISSET(sfd, set);
-}
-
-}   // namespace
-
-unique_fd* Subprocess::SelectLoop(fd_set* master_read_set_ptr,
-                                  fd_set* master_write_set_ptr) {
-    fd_set read_set, write_set;
-    int select_n = std::max(std::max(protocol_sfd_, stdinout_sfd_), stderr_sfd_) + 1;
-    unique_fd* dead_sfd = nullptr;
-
-    // Keep calling select() and passing data until an FD closes/errors.
-    while (!dead_sfd) {
-        memcpy(&read_set, master_read_set_ptr, sizeof(read_set));
-        memcpy(&write_set, master_write_set_ptr, sizeof(write_set));
-        if (select(select_n, &read_set, &write_set, nullptr, nullptr) < 0) {
-            if (errno == EINTR) {
-                continue;
-            } else {
-                PLOG(ERROR) << "select failed, closing subprocess pipes";
-                stdinout_sfd_.reset(-1);
-                stderr_sfd_.reset(-1);
-                return nullptr;
-            }
-        }
-
-        // Read stdout, write to protocol FD.
-        if (ValidAndInSet(stdinout_sfd_, &read_set)) {
-            dead_sfd = PassOutput(&stdinout_sfd_, ShellProtocol::kIdStdout);
-        }
-
-        // Read stderr, write to protocol FD.
-        if (!dead_sfd && ValidAndInSet(stderr_sfd_, &read_set)) {
-            dead_sfd = PassOutput(&stderr_sfd_, ShellProtocol::kIdStderr);
-        }
-
-        // Read protocol FD, write to stdin.
-        if (!dead_sfd && ValidAndInSet(protocol_sfd_, &read_set)) {
-            dead_sfd = PassInput();
-            // If we didn't finish writing, block on stdin write.
-            if (input_bytes_left_) {
-                FD_CLR(protocol_sfd_, master_read_set_ptr);
-                FD_SET(stdinout_sfd_, master_write_set_ptr);
-            }
-        }
-
-        // Continue writing to stdin; only happens if a previous write blocked.
-        if (!dead_sfd && ValidAndInSet(stdinout_sfd_, &write_set)) {
-            dead_sfd = PassInput();
-            // If we finished writing, go back to blocking on protocol read.
-            if (!input_bytes_left_) {
-                FD_SET(protocol_sfd_, master_read_set_ptr);
-                FD_CLR(stdinout_sfd_, master_write_set_ptr);
-            }
-        }
-    }  // while (!dead_sfd)
-
-    return dead_sfd;
-}
-
-unique_fd* Subprocess::PassInput() {
-    // Only read a new packet if we've finished writing the last one.
-    if (!input_bytes_left_) {
-        if (!input_->Read()) {
-            // Read() uses ReadFdExactly() which sets errno to 0 on EOF.
-            if (errno != 0) {
-                PLOG(ERROR) << "error reading protocol FD " << protocol_sfd_;
-            }
-            return &protocol_sfd_;
-        }
-
-        if (stdinout_sfd_ != -1) {
-            switch (input_->id()) {
-                case ShellProtocol::kIdWindowSizeChange:
-                    int rows, cols, x_pixels, y_pixels;
-                    if (sscanf(input_->data(), "%dx%d,%dx%d",
-                               &rows, &cols, &x_pixels, &y_pixels) == 4) {
-                        winsize ws;
-                        ws.ws_row = rows;
-                        ws.ws_col = cols;
-                        ws.ws_xpixel = x_pixels;
-                        ws.ws_ypixel = y_pixels;
-                        ioctl(stdinout_sfd_, TIOCSWINSZ, &ws);
-                    }
-                    break;
-                case ShellProtocol::kIdStdin:
-                    input_bytes_left_ = input_->data_length();
-                    break;
-                case ShellProtocol::kIdCloseStdin:
-                    if (type_ == SubprocessType::kRaw) {
-                        if (adb_shutdown(stdinout_sfd_, SHUT_WR) == 0) {
-                            return nullptr;
-                        }
-                        PLOG(ERROR) << "failed to shutdown writes to FD "
-                                    << stdinout_sfd_;
-                        return &stdinout_sfd_;
-                    } else {
-                        // PTYs can't close just input, so rather than close the
-                        // FD and risk losing subprocess output, leave it open.
-                        // This only happens if the client starts a PTY shell
-                        // non-interactively which is rare and unsupported.
-                        // If necessary, the client can manually close the shell
-                        // with `exit` or by killing the adb client process.
-                        D("can't close input for PTY FD %d", stdinout_sfd_.get());
-                    }
-                    break;
-            }
-        }
-    }
-
-    if (input_bytes_left_ > 0) {
-        int index = input_->data_length() - input_bytes_left_;
-        int bytes = adb_write(stdinout_sfd_, input_->data() + index, input_bytes_left_);
-        if (bytes == 0 || (bytes < 0 && errno != EAGAIN)) {
-            if (bytes < 0) {
-                PLOG(ERROR) << "error reading stdin FD " << stdinout_sfd_;
-            }
-            // stdin is done, mark this packet as finished and we'll just start
-            // dumping any further data received from the protocol FD.
-            input_bytes_left_ = 0;
-            return &stdinout_sfd_;
-        } else if (bytes > 0) {
-            input_bytes_left_ -= bytes;
-        }
-    }
-
-    return nullptr;
-}
-
-unique_fd* Subprocess::PassOutput(unique_fd* sfd, ShellProtocol::Id id) {
-    int bytes = adb_read(*sfd, output_->data(), output_->data_capacity());
-    if (bytes == 0 || (bytes < 0 && errno != EAGAIN)) {
-        // read() returns EIO if a PTY closes; don't report this as an error,
-        // it just means the subprocess completed.
-        if (bytes < 0 && !(type_ == SubprocessType::kPty && errno == EIO)) {
-            PLOG(ERROR) << "error reading output FD " << *sfd;
-        }
-        return sfd;
-    }
-
-    if (bytes > 0 && !output_->Write(id, bytes)) {
-        if (errno != 0) {
-            PLOG(ERROR) << "error reading protocol FD " << protocol_sfd_;
-        }
-        return &protocol_sfd_;
-    }
-
-    return nullptr;
-}
-
-void Subprocess::WaitForExit() {
-    int exit_code = 1;
-
-    D("waiting for pid %d", pid_);
-    while (true) {
-        int status;
-        if (pid_ == waitpid(pid_, &status, 0)) {
-            D("post waitpid (pid=%d) status=%04x", pid_, status);
-            if (WIFSIGNALED(status)) {
-                exit_code = 0x80 | WTERMSIG(status);
-                D("subprocess killed by signal %d", WTERMSIG(status));
-                break;
-            } else if (!WIFEXITED(status)) {
-                D("subprocess didn't exit");
-                break;
-            } else if (WEXITSTATUS(status) >= 0) {
-                exit_code = WEXITSTATUS(status);
-                D("subprocess exit code = %d", WEXITSTATUS(status));
-                break;
-            }
-        }
-    }
-
-    // If we have an open protocol FD send an exit packet.
-    if (protocol_sfd_ != -1) {
-        output_->data()[0] = exit_code;
-        if (output_->Write(ShellProtocol::kIdExit, 1)) {
-            D("wrote the exit code packet: %d", exit_code);
-        } else {
-            PLOG(ERROR) << "failed to write the exit code packet";
-        }
-        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
-
-// Create a pipe containing the error.
-static int ReportError(SubprocessProtocol protocol, const std::string& message) {
-    int pipefd[2];
-    if (pipe(pipefd) != 0) {
-        LOG(ERROR) << "failed to create pipe to report error";
-        return -1;
-    }
-
-    std::string buf = android::base::StringPrintf("error: %s\n", message.c_str());
-    if (protocol == SubprocessProtocol::kShell) {
-        ShellProtocol::Id id = ShellProtocol::kIdStderr;
-        uint32_t length = buf.length();
-        WriteFdExactly(pipefd[1], &id, sizeof(id));
-        WriteFdExactly(pipefd[1], &length, sizeof(length));
-    }
-
-    WriteFdExactly(pipefd[1], buf.data(), buf.length());
-
-    if (protocol == SubprocessProtocol::kShell) {
-        ShellProtocol::Id id = ShellProtocol::kIdExit;
-        uint32_t length = 1;
-        char exit_code = 126;
-        WriteFdExactly(pipefd[1], &id, sizeof(id));
-        WriteFdExactly(pipefd[1], &length, sizeof(length));
-        WriteFdExactly(pipefd[1], &exit_code, sizeof(exit_code));
-    }
-
-    adb_close(pipefd[1]);
-    return pipefd[0];
-}
-
-int StartSubprocess(const char* name, const char* terminal_type,
-                    SubprocessType type, SubprocessProtocol protocol) {
-    D("starting %s subprocess (protocol=%s, TERM=%s): '%s'",
-      type == SubprocessType::kRaw ? "raw" : "PTY",
-      protocol == SubprocessProtocol::kNone ? "none" : "shell",
-      terminal_type, name);
-
-    auto subprocess = std::make_unique<Subprocess>(name, terminal_type, type, protocol);
-    if (!subprocess) {
-        LOG(ERROR) << "failed to allocate new subprocess";
-        return ReportError(protocol, "failed to allocate new subprocess");
-    }
-
-    std::string error;
-    if (!subprocess->ForkAndExec(&error)) {
-        LOG(ERROR) << "failed to start subprocess: " << error;
-        return ReportError(protocol, error);
-    }
-
-    unique_fd local_socket(subprocess->ReleaseLocalSocket());
-    D("subprocess creation successful: local_socket_fd=%d, pid=%d", local_socket.get(),
-      subprocess->pid());
-
-    if (!Subprocess::StartThread(std::move(subprocess), &error)) {
-        LOG(ERROR) << "failed to start subprocess management thread: " << error;
-        return ReportError(protocol, error);
-    }
-
-    return local_socket.release();
-}
diff --git a/adb/shell_service.h b/adb/shell_service.h
deleted file mode 100644
index e3d676a..0000000
--- a/adb/shell_service.h
+++ /dev/null
@@ -1,149 +0,0 @@
-/*
- * Copyright (C) 2015 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.
- */
-
-// This file contains classes and functionality to launch shell subprocesses
-// in adbd and communicate between those subprocesses and the adb client.
-//
-// The main features exposed here are:
-//   1. A ShellPacket class to wrap data in a simple protocol. Both adbd and
-//      the adb client use this class to transmit data between them.
-//   2. Functions to launch a subprocess on the adbd side.
-
-#ifndef SHELL_SERVICE_H_
-#define SHELL_SERVICE_H_
-
-#include <stdint.h>
-
-#include <android-base/macros.h>
-
-#include "adb.h"
-
-// Class to send and receive shell protocol packets.
-//
-// To keep things simple and predictable, reads and writes block until an entire
-// packet is complete.
-//
-// Example: read raw data from |fd| and send it in a packet.
-//   ShellProtocol* p = new ShellProtocol(protocol_fd);
-//   int len = adb_read(stdout_fd, p->data(), p->data_capacity());
-//   packet->WritePacket(ShellProtocol::kIdStdout, len);
-//
-// Example: read a packet and print it to |stdout|.
-//   ShellProtocol* p = new ShellProtocol(protocol_fd);
-//   if (p->ReadPacket() && p->id() == kIdStdout) {
-//       fwrite(p->data(), 1, p->data_length(), stdout);
-//   }
-class ShellProtocol {
-  public:
-    // This is an unscoped enum to make it easier to compare against raw bytes.
-    enum Id : uint8_t {
-        kIdStdin = 0,
-        kIdStdout = 1,
-        kIdStderr = 2,
-        kIdExit = 3,
-
-        // Close subprocess stdin if possible.
-        kIdCloseStdin = 4,
-
-        // Window size change (an ASCII version of struct winsize).
-        kIdWindowSizeChange = 5,
-
-        // Indicates an invalid or unknown packet.
-        kIdInvalid = 255,
-    };
-
-    // ShellPackets will probably be too large to allocate on the stack so they
-    // should be dynamically allocated on the heap instead.
-    //
-    // |fd| is an open file descriptor to be used to send or receive packets.
-    explicit ShellProtocol(int fd);
-    virtual ~ShellProtocol();
-
-    // Returns a pointer to the data buffer.
-    const char* data() const { return buffer_ + kHeaderSize; }
-    char* data() { return buffer_ + kHeaderSize; }
-
-    // Returns the total capacity of the data buffer.
-    size_t data_capacity() const { return buffer_end_ - data(); }
-
-    // Reads a packet from the FD.
-    //
-    // If a packet is too big to fit in the buffer then Read() will split the
-    // packet across multiple calls. For example, reading a 50-byte packet into
-    // a 20-byte buffer would read 20 bytes, 20 bytes, then 10 bytes.
-    //
-    // Returns false if the FD closed or errored.
-    bool Read();
-
-    // Returns the ID of the packet in the buffer.
-    int id() const { return buffer_[0]; }
-
-    // Returns the number of bytes that have been read into the data buffer.
-    size_t data_length() const { return data_length_; }
-
-    // Writes the packet currently in the buffer to the FD.
-    //
-    // Returns false if the FD closed or errored.
-    bool Write(Id id, size_t length);
-
-  private:
-    // Packets support 4-byte lengths.
-    typedef uint32_t length_t;
-
-    enum {
-        // It's OK if MAX_PAYLOAD doesn't match on the sending and receiving
-        // end, reading will split larger packets into multiple smaller ones.
-        kBufferSize = MAX_PAYLOAD,
-
-        // Header is 1 byte ID + 4 bytes length.
-        kHeaderSize = sizeof(Id) + sizeof(length_t)
-    };
-
-    int fd_;
-    char buffer_[kBufferSize];
-    size_t data_length_ = 0, bytes_left_ = 0;
-
-    // We need to be able to modify this value for testing purposes, but it
-    // will stay constant during actual program use.
-    char* buffer_end_ = buffer_ + sizeof(buffer_);
-
-    friend class ShellProtocolTest;
-
-    DISALLOW_COPY_AND_ASSIGN(ShellProtocol);
-};
-
-#if !ADB_HOST
-
-enum class SubprocessType {
-    kPty,
-    kRaw,
-};
-
-enum class SubprocessProtocol {
-    kNone,
-    kShell,
-};
-
-// Forks and starts a new shell subprocess. If |name| is empty an interactive
-// shell is started, otherwise |name| is executed non-interactively.
-//
-// Returns an open FD connected to the subprocess or -1 on failure.
-int StartSubprocess(const char* name, const char* terminal_type,
-                    SubprocessType type, SubprocessProtocol protocol);
-
-#endif  // !ADB_HOST
-
-#endif  // SHELL_SERVICE_H_
diff --git a/adb/shell_service_protocol.cpp b/adb/shell_service_protocol.cpp
index 623629c..13b66ec 100644
--- a/adb/shell_service_protocol.cpp
+++ b/adb/shell_service_protocol.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#include "shell_service.h"
+#include "shell_protocol.h"
 
 #include <string.h>
 
diff --git a/adb/shell_service_protocol_test.cpp b/adb/shell_service_protocol_test.cpp
index b0fa3ed..a10b5c0 100644
--- a/adb/shell_service_protocol_test.cpp
+++ b/adb/shell_service_protocol_test.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#include "shell_service.h"
+#include "shell_protocol.h"
 
 #include <gtest/gtest.h>
 
diff --git a/adb/shell_service_test.cpp b/adb/shell_service_test.cpp
deleted file mode 100644
index 839284e..0000000
--- a/adb/shell_service_test.cpp
+++ /dev/null
@@ -1,290 +0,0 @@
-/*
- * Copyright (C) 2015 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 "shell_service.h"
-
-#include <gtest/gtest.h>
-
-#include <signal.h>
-
-#include <string>
-#include <vector>
-
-#include <android-base/strings.h>
-
-#include "adb.h"
-#include "adb_io.h"
-#include "sysdeps.h"
-
-class ShellServiceTest : public ::testing::Test {
-  public:
-    static void SetUpTestCase() {
-        // This is normally done in main.cpp.
-        saved_sigpipe_handler_ = signal(SIGPIPE, SIG_IGN);
-
-    }
-
-    static void TearDownTestCase() {
-        signal(SIGPIPE, saved_sigpipe_handler_);
-    }
-
-    // Helpers to start and cleanup a subprocess. Cleanup normally does not
-    // need to be called manually unless multiple subprocesses are run from
-    // a single test.
-    void StartTestSubprocess(const char* command, SubprocessType type,
-                             SubprocessProtocol protocol);
-    void CleanupTestSubprocess();
-
-    virtual void TearDown() override {
-        void CleanupTestSubprocess();
-    }
-
-    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_;
-    }
-}
-
-namespace {
-
-// Reads raw data from |fd| until it closes or errors.
-std::string ReadRaw(int fd) {
-    char buffer[1024];
-    char *cur_ptr = buffer, *end_ptr = buffer + sizeof(buffer);
-
-    while (1) {
-        int bytes = adb_read(fd, cur_ptr, end_ptr - cur_ptr);
-        if (bytes <= 0) {
-            return std::string(buffer, cur_ptr);
-        }
-        cur_ptr += bytes;
-    }
-}
-
-// Reads shell protocol data from |fd| until it closes or errors. Fills
-// |stdout| and |stderr| with their respective data, and returns the exit code
-// read from the protocol or -1 if an exit code packet was not received.
-int ReadShellProtocol(int fd, std::string* stdout, std::string* stderr) {
-    int exit_code = -1;
-    stdout->clear();
-    stderr->clear();
-
-    ShellProtocol* protocol = new ShellProtocol(fd);
-    while (protocol->Read()) {
-        switch (protocol->id()) {
-            case ShellProtocol::kIdStdout:
-                stdout->append(protocol->data(), protocol->data_length());
-                break;
-            case ShellProtocol::kIdStderr:
-                stderr->append(protocol->data(), protocol->data_length());
-                break;
-            case ShellProtocol::kIdExit:
-                EXPECT_EQ(-1, exit_code) << "Multiple exit packets received";
-                EXPECT_EQ(1u, protocol->data_length());
-                exit_code = protocol->data()[0];
-                break;
-            default:
-                ADD_FAILURE() << "Unidentified packet ID: " << protocol->id();
-        }
-    }
-    delete protocol;
-
-    return exit_code;
-}
-
-// Checks if each line in |lines| exists in the same order in |output|. Blank
-// lines in |output| are ignored for simplicity.
-bool ExpectLinesEqual(const std::string& output,
-                      const std::vector<std::string>& lines) {
-    auto output_lines = android::base::Split(output, "\r\n");
-    size_t i = 0;
-
-    for (const std::string& line : lines) {
-        // Skip empty lines in output.
-        while (i < output_lines.size() && output_lines[i].empty()) {
-            ++i;
-        }
-        if (i >= output_lines.size()) {
-            ADD_FAILURE() << "Ran out of output lines";
-            return false;
-        }
-        EXPECT_EQ(line, output_lines[i]);
-        ++i;
-    }
-
-    while (i < output_lines.size() && output_lines[i].empty()) {
-        ++i;
-    }
-    EXPECT_EQ(i, output_lines.size()) << "Found unmatched output lines";
-    return true;
-}
-
-}  // namespace
-
-// Tests a raw subprocess with no protocol.
-TEST_F(ShellServiceTest, RawNoProtocolSubprocess) {
-    // [ -t 0 ] checks if stdin is connected to a terminal.
-    ASSERT_NO_FATAL_FAILURE(StartTestSubprocess(
-            "echo foo; echo bar >&2; [ -t 0 ]; echo $?",
-            SubprocessType::kRaw, SubprocessProtocol::kNone));
-
-    // [ -t 0 ] == 0 means we have a terminal (PTY). Even when requesting a raw subprocess, without
-    // the shell protocol we should always force a PTY to ensure proper cleanup.
-    ExpectLinesEqual(ReadRaw(subprocess_fd_), {"foo", "bar", "0"});
-}
-
-// Tests a PTY subprocess with no protocol.
-TEST_F(ShellServiceTest, PtyNoProtocolSubprocess) {
-    // [ -t 0 ] checks if stdin is connected to a terminal.
-    ASSERT_NO_FATAL_FAILURE(StartTestSubprocess(
-            "echo foo; echo bar >&2; [ -t 0 ]; echo $?",
-            SubprocessType::kPty, SubprocessProtocol::kNone));
-
-    // [ -t 0 ] == 0 means we have a terminal (PTY).
-    ExpectLinesEqual(ReadRaw(subprocess_fd_), {"foo", "bar", "0"});
-}
-
-// Tests a raw subprocess with the shell protocol.
-TEST_F(ShellServiceTest, RawShellProtocolSubprocess) {
-    ASSERT_NO_FATAL_FAILURE(StartTestSubprocess(
-            "echo foo; echo bar >&2; echo baz; exit 24",
-            SubprocessType::kRaw, SubprocessProtocol::kShell));
-
-    std::string stdout, stderr;
-    EXPECT_EQ(24, ReadShellProtocol(subprocess_fd_, &stdout, &stderr));
-    ExpectLinesEqual(stdout, {"foo", "baz"});
-    ExpectLinesEqual(stderr, {"bar"});
-}
-
-// Tests a PTY subprocess with the shell protocol.
-TEST_F(ShellServiceTest, PtyShellProtocolSubprocess) {
-    ASSERT_NO_FATAL_FAILURE(StartTestSubprocess(
-            "echo foo; echo bar >&2; echo baz; exit 50",
-            SubprocessType::kPty, SubprocessProtocol::kShell));
-
-    // PTY always combines stdout and stderr but the shell protocol should
-    // still give us an exit code.
-    std::string stdout, stderr;
-    EXPECT_EQ(50, ReadShellProtocol(subprocess_fd_, &stdout, &stderr));
-    ExpectLinesEqual(stdout, {"foo", "bar", "baz"});
-    ExpectLinesEqual(stderr, {});
-}
-
-// Tests an interactive PTY session.
-TEST_F(ShellServiceTest, InteractivePtySubprocess) {
-    ASSERT_NO_FATAL_FAILURE(StartTestSubprocess(
-            "", SubprocessType::kPty, SubprocessProtocol::kShell));
-
-    // Use variable substitution so echoed input is different from output.
-    const char* commands[] = {"TEST_STR=abc123",
-                              "echo --${TEST_STR}--",
-                              "exit"};
-
-    ShellProtocol* protocol = new ShellProtocol(subprocess_fd_);
-    for (std::string command : commands) {
-        // Interactive shell requires a newline to complete each command.
-        command.push_back('\n');
-        memcpy(protocol->data(), command.data(), command.length());
-        ASSERT_TRUE(protocol->Write(ShellProtocol::kIdStdin, command.length()));
-    }
-    delete protocol;
-
-    std::string stdout, stderr;
-    EXPECT_EQ(0, ReadShellProtocol(subprocess_fd_, &stdout, &stderr));
-    // An unpredictable command prompt makes parsing exact output difficult but
-    // it should at least contain echoed input and the expected output.
-    for (const char* command : commands) {
-        EXPECT_FALSE(stdout.find(command) == std::string::npos);
-    }
-    EXPECT_FALSE(stdout.find("--abc123--") == std::string::npos);
-}
-
-// Tests closing raw subprocess stdin.
-TEST_F(ShellServiceTest, CloseClientStdin) {
-    ASSERT_NO_FATAL_FAILURE(StartTestSubprocess(
-            "cat; echo TEST_DONE",
-            SubprocessType::kRaw, SubprocessProtocol::kShell));
-
-    std::string input = "foo\nbar";
-    ShellProtocol* protocol = new ShellProtocol(subprocess_fd_);
-    memcpy(protocol->data(), input.data(), input.length());
-    ASSERT_TRUE(protocol->Write(ShellProtocol::kIdStdin, input.length()));
-    ASSERT_TRUE(protocol->Write(ShellProtocol::kIdCloseStdin, 0));
-    delete protocol;
-
-    std::string stdout, stderr;
-    EXPECT_EQ(0, ReadShellProtocol(subprocess_fd_, &stdout, &stderr));
-    ExpectLinesEqual(stdout, {"foo", "barTEST_DONE"});
-    ExpectLinesEqual(stderr, {});
-}
-
-// Tests that nothing breaks when the stdin/stdout pipe closes.
-TEST_F(ShellServiceTest, CloseStdinStdoutSubprocess) {
-    ASSERT_NO_FATAL_FAILURE(StartTestSubprocess(
-            "exec 0<&-; exec 1>&-; echo bar >&2",
-            SubprocessType::kRaw, SubprocessProtocol::kShell));
-
-    std::string stdout, stderr;
-    EXPECT_EQ(0, ReadShellProtocol(subprocess_fd_, &stdout, &stderr));
-    ExpectLinesEqual(stdout, {});
-    ExpectLinesEqual(stderr, {"bar"});
-}
-
-// Tests that nothing breaks when the stderr pipe closes.
-TEST_F(ShellServiceTest, CloseStderrSubprocess) {
-    ASSERT_NO_FATAL_FAILURE(StartTestSubprocess(
-            "exec 2>&-; echo foo",
-            SubprocessType::kRaw, SubprocessProtocol::kShell));
-
-    std::string stdout, stderr;
-    EXPECT_EQ(0, ReadShellProtocol(subprocess_fd_, &stdout, &stderr));
-    ExpectLinesEqual(stdout, {"foo"});
-    ExpectLinesEqual(stderr, {});
-}
diff --git a/adb/socket.h b/adb/socket.h
index 4acdf4a..0905aab 100644
--- a/adb/socket.h
+++ b/adb/socket.h
@@ -19,84 +19,80 @@
 
 #include <stddef.h>
 
-#include "fdevent.h"
+#include <deque>
+#include <memory>
+#include <string>
 
-struct apacket;
+#include "fdevent.h"
+#include "types.h"
+
 class atransport;
 
 /* An asocket represents one half of a connection between a local and
-** remote entity.  A local asocket is bound to a file descriptor.  A
-** remote asocket is bound to the protocol engine.
-*/
+ * remote entity.  A local asocket is bound to a file descriptor.  A
+ * remote asocket is bound to the protocol engine.
+ */
 struct asocket {
-        /* chain pointers for the local/remote list of
-        ** asockets that this asocket lives in
-        */
-    asocket *next;
-    asocket *prev;
+    /* the unique identifier for this asocket
+     */
+    unsigned id = 0;
 
-        /* the unique identifier for this asocket
-        */
-    unsigned id;
-
-        /* flag: set when the socket's peer has closed
-        ** but packets are still queued for delivery
-        */
-    int    closing;
+    /* flag: set when the socket's peer has closed
+     * but packets are still queued for delivery
+     */
+    int closing = 0;
 
     // flag: set when the socket failed to write, so the socket will not wait to
     // write packets and close directly.
-    bool has_write_error;
+    bool has_write_error = 0;
 
-        /* flag: quit adbd when both ends close the
-        ** local service socket
-        */
-    int    exit_on_close;
+    /* flag: quit adbd when both ends close the
+     * local service socket
+     */
+    int exit_on_close = 0;
 
-        /* the asocket we are connected to
-        */
+    // the asocket we are connected to
+    asocket* peer = nullptr;
 
-    asocket *peer;
+    /* For local asockets, the fde is used to bind
+     * us to our fd event system.  For remote asockets
+     * these fields are not used.
+     */
+    fdevent* fde = nullptr;
+    int fd = -1;
 
-        /* For local asockets, the fde is used to bind
-        ** us to our fd event system.  For remote asockets
-        ** these fields are not used.
-        */
-    fdevent fde;
-    int fd;
+    // queue of data waiting to be written
+    IOVector packet_queue;
 
-        /* queue of apackets waiting to be written
-        */
-    apacket *pkt_first;
-    apacket *pkt_last;
+    std::string smart_socket_data;
 
-        /* enqueue is called by our peer when it has data
-        ** for us.  It should return 0 if we can accept more
-        ** data or 1 if not.  If we return 1, we must call
-        ** peer->ready() when we once again are ready to
-        ** receive data.
-        */
-    int (*enqueue)(asocket *s, apacket *pkt);
+    /* enqueue is called by our peer when it has data
+     * for us.  It should return 0 if we can accept more
+     * data or 1 if not.  If we return 1, we must call
+     * peer->ready() when we once again are ready to
+     * receive data.
+     */
+    int (*enqueue)(asocket* s, apacket::payload_type data) = nullptr;
 
-        /* ready is called by the peer when it is ready for
-        ** us to send data via enqueue again
-        */
-    void (*ready)(asocket *s);
+    /* ready is called by the peer when it is ready for
+     * us to send data via enqueue again
+     */
+    void (*ready)(asocket* s) = nullptr;
 
-        /* shutdown is called by the peer before it goes away.
-        ** the socket should not do any further calls on its peer.
-        ** Always followed by a call to close. Optional, i.e. can be NULL.
-        */
-    void (*shutdown)(asocket *s);
+    /* shutdown is called by the peer before it goes away.
+     * the socket should not do any further calls on its peer.
+     * Always followed by a call to close. Optional, i.e. can be NULL.
+     */
+    void (*shutdown)(asocket* s) = nullptr;
 
-        /* close is called by the peer when it has gone away.
-        ** we are not allowed to make any further calls on the
-        ** peer once our close method is called.
-        */
-    void (*close)(asocket *s);
+    /* close is called by the peer when it has gone away.
+     * we are not allowed to make any further calls on the
+     * peer once our close method is called.
+     */
+    void (*close)(asocket* s) = nullptr;
 
-        /* A socket is bound to atransport */
-    atransport *transport;
+    /* A socket is bound to atransport */
+    atransport* transport = nullptr;
 
     size_t get_max_payload() const;
 };
@@ -107,8 +103,7 @@
 void close_all_sockets(atransport *t);
 
 asocket *create_local_socket(int fd);
-asocket *create_local_service_socket(const char* destination,
-                                     const atransport* transport);
+asocket* create_local_service_socket(const char* destination, atransport* transport);
 
 asocket *create_remote_socket(unsigned id, atransport *t);
 void connect_to_remote(asocket *s, const char *destination);
diff --git a/adb/socket_spec.cpp b/adb/socket_spec.cpp
index 14eb16b..eb4df97 100644
--- a/adb/socket_spec.cpp
+++ b/adb/socket_spec.cpp
@@ -118,7 +118,7 @@
 bool is_socket_spec(const std::string& spec) {
     for (const auto& it : kLocalSocketTypes) {
         std::string prefix = it.first + ":";
-        if (StartsWith(spec, prefix.c_str())) {
+        if (StartsWith(spec, prefix)) {
             return true;
         }
     }
@@ -128,7 +128,7 @@
 bool is_local_socket_spec(const std::string& spec) {
     for (const auto& it : kLocalSocketTypes) {
         std::string prefix = it.first + ":";
-        if (StartsWith(spec, prefix.c_str())) {
+        if (StartsWith(spec, prefix)) {
             return true;
         }
     }
@@ -170,7 +170,7 @@
 
     for (const auto& it : kLocalSocketTypes) {
         std::string prefix = it.first + ":";
-        if (StartsWith(spec, prefix.c_str())) {
+        if (StartsWith(spec, prefix)) {
             if (!it.second.available) {
                 *error = StringPrintf("socket type %s is unavailable on this platform",
                                       it.first.c_str());
@@ -213,7 +213,7 @@
 
     for (const auto& it : kLocalSocketTypes) {
         std::string prefix = it.first + ":";
-        if (StartsWith(spec, prefix.c_str())) {
+        if (StartsWith(spec, prefix)) {
             if (!it.second.available) {
                 *error = StringPrintf("attempted to listen on unavailable socket type: '%s'",
                                       spec.c_str());
diff --git a/adb/socket_test.cpp b/adb/socket_test.cpp
index f7c66db..04214a2 100644
--- a/adb/socket_test.cpp
+++ b/adb/socket_test.cpp
@@ -42,8 +42,6 @@
 
 class LocalSocketTest : public FdeventTest {};
 
-constexpr auto SLEEP_FOR_FDEVENT = 100ms;
-
 TEST_F(LocalSocketTest, smoke) {
     // Join two socketpairs with a chain of intermediate socketpairs.
     int first[2];
@@ -84,7 +82,6 @@
     connect(prev_tail, end);
 
     PrepareThread();
-    std::thread thread(fdevent_loop);
 
     for (size_t i = 0; i < MESSAGE_LOOP_COUNT; ++i) {
         std::string read_buffer = MESSAGE;
@@ -98,9 +95,9 @@
     ASSERT_EQ(0, adb_close(last[1]));
 
     // Wait until the local sockets are closed.
-    std::this_thread::sleep_for(SLEEP_FOR_FDEVENT);
+    WaitForFdeventLoop();
     ASSERT_EQ(GetAdditionalLocalSocketCount(), fdevent_installed_count());
-    TerminateThread(thread);
+    TerminateThread();
 }
 
 struct CloseWithPacketArg {
@@ -109,28 +106,39 @@
     int cause_close_fd;
 };
 
-static void CloseWithPacketThreadFunc(CloseWithPacketArg* arg) {
-    asocket* s = create_local_socket(arg->socket_fd);
-    ASSERT_TRUE(s != nullptr);
-    arg->bytes_written = 0;
-    while (true) {
-        apacket* p = get_apacket();
-        p->len = sizeof(p->data);
-        arg->bytes_written += p->len;
-        int ret = s->enqueue(s, p);
-        if (ret == 1) {
-            // The writer has one packet waiting to send.
-            break;
+static void CreateCloser(CloseWithPacketArg* arg) {
+    fdevent_run_on_main_thread([arg]() {
+        asocket* s = create_local_socket(arg->socket_fd);
+        ASSERT_TRUE(s != nullptr);
+        arg->bytes_written = 0;
+
+        // On platforms that implement sockets via underlying sockets (e.g. Wine),
+        // a socket can appear to be full, and then become available for writes
+        // again without read being called on the other end. Loop and sleep after
+        // each write to give the underlying implementation time to flush.
+        bool socket_filled = false;
+        for (int i = 0; i < 128; ++i) {
+            apacket::payload_type data;
+            data.resize(MAX_PAYLOAD);
+            arg->bytes_written += data.size();
+            int ret = s->enqueue(s, std::move(data));
+            if (ret == 1) {
+                socket_filled = true;
+                break;
+            }
+            ASSERT_NE(-1, ret);
+
+            std::this_thread::sleep_for(250ms);
         }
-    }
+        ASSERT_TRUE(socket_filled);
 
-    asocket* cause_close_s = create_local_socket(arg->cause_close_fd);
-    ASSERT_TRUE(cause_close_s != nullptr);
-    cause_close_s->peer = s;
-    s->peer = cause_close_s;
-    cause_close_s->ready(cause_close_s);
-
-    fdevent_loop();
+        asocket* cause_close_s = create_local_socket(arg->cause_close_fd);
+        ASSERT_TRUE(cause_close_s != nullptr);
+        cause_close_s->peer = s;
+        s->peer = cause_close_s;
+        cause_close_s->ready(cause_close_s);
+    });
+    WaitForFdeventLoop();
 }
 
 // This test checks if we can close local socket in the following situation:
@@ -147,16 +155,17 @@
     arg.cause_close_fd = cause_close_fd[1];
 
     PrepareThread();
-    std::thread thread(CloseWithPacketThreadFunc, &arg);
-    // Wait until the fdevent_loop() starts.
-    std::this_thread::sleep_for(SLEEP_FOR_FDEVENT);
+    CreateCloser(&arg);
+
     ASSERT_EQ(0, adb_close(cause_close_fd[0]));
-    std::this_thread::sleep_for(SLEEP_FOR_FDEVENT);
+
+    WaitForFdeventLoop();
     EXPECT_EQ(1u + GetAdditionalLocalSocketCount(), fdevent_installed_count());
     ASSERT_EQ(0, adb_close(socket_fd[0]));
-    std::this_thread::sleep_for(SLEEP_FOR_FDEVENT);
+
+    WaitForFdeventLoop();
     ASSERT_EQ(GetAdditionalLocalSocketCount(), fdevent_installed_count());
-    TerminateThread(thread);
+    TerminateThread();
 }
 
 // This test checks if we can read packets from a closing local socket.
@@ -170,11 +179,12 @@
     arg.cause_close_fd = cause_close_fd[1];
 
     PrepareThread();
-    std::thread thread(CloseWithPacketThreadFunc, &arg);
-    // Wait until the fdevent_loop() starts.
-    std::this_thread::sleep_for(SLEEP_FOR_FDEVENT);
+    CreateCloser(&arg);
+
+    WaitForFdeventLoop();
     ASSERT_EQ(0, adb_close(cause_close_fd[0]));
-    std::this_thread::sleep_for(SLEEP_FOR_FDEVENT);
+
+    WaitForFdeventLoop();
     EXPECT_EQ(1u + GetAdditionalLocalSocketCount(), fdevent_installed_count());
 
     // Verify if we can read successfully.
@@ -183,9 +193,9 @@
     ASSERT_EQ(true, ReadFdExactly(socket_fd[0], buf.data(), buf.size()));
     ASSERT_EQ(0, adb_close(socket_fd[0]));
 
-    std::this_thread::sleep_for(SLEEP_FOR_FDEVENT);
+    WaitForFdeventLoop();
     ASSERT_EQ(GetAdditionalLocalSocketCount(), fdevent_installed_count());
-    TerminateThread(thread);
+    TerminateThread();
 }
 
 // This test checks if we can close local socket in the following situation:
@@ -202,15 +212,52 @@
     arg.cause_close_fd = cause_close_fd[1];
 
     PrepareThread();
-    std::thread thread(CloseWithPacketThreadFunc, &arg);
-    // Wait until the fdevent_loop() starts.
-    std::this_thread::sleep_for(SLEEP_FOR_FDEVENT);
+    CreateCloser(&arg);
+
+    WaitForFdeventLoop();
     EXPECT_EQ(2u + GetAdditionalLocalSocketCount(), fdevent_installed_count());
     ASSERT_EQ(0, adb_close(socket_fd[0]));
 
-    std::this_thread::sleep_for(SLEEP_FOR_FDEVENT);
+    WaitForFdeventLoop();
     ASSERT_EQ(GetAdditionalLocalSocketCount(), fdevent_installed_count());
-    TerminateThread(thread);
+    TerminateThread();
+}
+
+// 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();
+
+    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]);
+
+    WaitForFdeventLoop();
+    ASSERT_EQ(GetAdditionalLocalSocketCount(), fdevent_installed_count());
+    TerminateThread();
 }
 
 #if defined(__linux__)
@@ -219,21 +266,10 @@
     std::string error;
     int fd = network_loopback_client(5038, SOCK_STREAM, &error);
     ASSERT_GE(fd, 0) << error;
-    std::this_thread::sleep_for(200ms);
+    std::this_thread::sleep_for(1s);
     ASSERT_EQ(0, adb_close(fd));
 }
 
-struct CloseRdHupSocketArg {
-    int socket_fd;
-};
-
-static void CloseRdHupSocketThreadFunc(CloseRdHupSocketArg* arg) {
-    asocket* s = create_local_socket(arg->socket_fd);
-    ASSERT_TRUE(s != nullptr);
-
-    fdevent_loop();
-}
-
 // This test checks if we can close sockets in CLOSE_WAIT state.
 TEST_F(LocalSocketTest, close_socket_in_CLOSE_WAIT_state) {
     std::string error;
@@ -244,22 +280,23 @@
 
     int accept_fd = adb_socket_accept(listen_fd, nullptr, nullptr);
     ASSERT_GE(accept_fd, 0);
-    CloseRdHupSocketArg arg;
-    arg.socket_fd = accept_fd;
 
     PrepareThread();
-    std::thread thread(CloseRdHupSocketThreadFunc, &arg);
 
-    // Wait until the fdevent_loop() starts.
-    std::this_thread::sleep_for(SLEEP_FOR_FDEVENT);
+    fdevent_run_on_main_thread([accept_fd]() {
+        asocket* s = create_local_socket(accept_fd);
+        ASSERT_TRUE(s != nullptr);
+    });
+
+    WaitForFdeventLoop();
     EXPECT_EQ(1u + GetAdditionalLocalSocketCount(), fdevent_installed_count());
 
     // Wait until the client closes its socket.
     client_thread.join();
 
-    std::this_thread::sleep_for(SLEEP_FOR_FDEVENT);
+    WaitForFdeventLoop();
     ASSERT_EQ(GetAdditionalLocalSocketCount(), fdevent_installed_count());
-    TerminateThread(thread);
+    TerminateThread();
 }
 
 #endif  // defined(__linux__)
diff --git a/adb/sockets.cpp b/adb/sockets.cpp
index e0143c6..69b5180 100644
--- a/adb/sockets.cpp
+++ b/adb/sockets.cpp
@@ -38,31 +38,27 @@
 #include "adb.h"
 #include "adb_io.h"
 #include "transport.h"
+#include "types.h"
 
 static std::recursive_mutex& local_socket_list_lock = *new std::recursive_mutex();
 static unsigned local_socket_next_id = 1;
 
-static asocket local_socket_list = {
-    .next = &local_socket_list, .prev = &local_socket_list,
-};
+static auto& local_socket_list = *new std::vector<asocket*>();
 
 /* the the list of currently closing local sockets.
 ** these have no peer anymore, but still packets to
 ** write to their fd.
 */
-static asocket local_socket_closing_list = {
-    .next = &local_socket_closing_list, .prev = &local_socket_closing_list,
-};
+static auto& local_socket_closing_list = *new std::vector<asocket*>();
 
 // Parse the global list of sockets to find one with id |local_id|.
 // If |peer_id| is not 0, also check that it is connected to a peer
 // with id |peer_id|. Returns an asocket handle on success, NULL on failure.
 asocket* find_local_socket(unsigned local_id, unsigned peer_id) {
-    asocket* s;
-    asocket* result = NULL;
+    asocket* result = nullptr;
 
     std::lock_guard<std::recursive_mutex> lock(local_socket_list_lock);
-    for (s = local_socket_list.next; s != &local_socket_list; s = s->next) {
+    for (asocket* s : local_socket_list) {
         if (s->id != local_id) {
             continue;
         }
@@ -75,13 +71,6 @@
     return result;
 }
 
-static void insert_local_socket(asocket* s, asocket* list) {
-    s->next = list;
-    s->prev = s->next->prev;
-    s->prev->next = s;
-    s->next->prev = s;
-}
-
 void install_local_socket(asocket* s) {
     std::lock_guard<std::recursive_mutex> lock(local_socket_list_lock);
 
@@ -92,29 +81,24 @@
         fatal("local socket id overflow");
     }
 
-    insert_local_socket(s, &local_socket_list);
+    local_socket_list.push_back(s);
 }
 
 void remove_socket(asocket* s) {
-    // socket_list_lock should already be held
-    if (s->prev && s->next) {
-        s->prev->next = s->next;
-        s->next->prev = s->prev;
-        s->next = 0;
-        s->prev = 0;
-        s->id = 0;
+    std::lock_guard<std::recursive_mutex> lock(local_socket_list_lock);
+    for (auto list : { &local_socket_list, &local_socket_closing_list }) {
+        list->erase(std::remove_if(list->begin(), list->end(), [s](asocket* x) { return x == s; }),
+                    list->end());
     }
 }
 
 void close_all_sockets(atransport* t) {
-    asocket* s;
-
     /* this is a little gross, but since s->close() *will* modify
     ** the list out from under you, your options are limited.
     */
     std::lock_guard<std::recursive_mutex> lock(local_socket_list_lock);
 restart:
-    for (s = local_socket_list.next; s != &local_socket_list; s = s->next) {
+    for (asocket* s : local_socket_list) {
         if (s->transport == t || (s->peer && s->peer->transport == t)) {
             s->close(s);
             goto restart;
@@ -122,86 +106,150 @@
     }
 }
 
-static int local_socket_enqueue(asocket* s, apacket* p) {
-    D("LS(%d): enqueue %zu", s->id, p->len);
+enum class SocketFlushResult {
+    Destroyed,
+    TryAgain,
+    Completed,
+};
 
-    p->ptr = p->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->pkt_first) {
-        goto enqueue;
+static SocketFlushResult local_socket_flush_incoming(asocket* s) {
+    if (!s->packet_queue.empty()) {
+        std::vector<adb_iovec> iov = s->packet_queue.iovecs();
+        ssize_t rc = adb_writev(s->fd, iov.data(), iov.size());
+        if (rc > 0 && static_cast<size_t>(rc) == s->packet_queue.size()) {
+            s->packet_queue.clear();
+        } else if (rc > 0) {
+            // TODO: Implement a faster drop_front?
+            s->packet_queue.take_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;
+        } else {
+            // 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;
+        }
     }
 
-    /* write as much as we can, until we
-    ** would block or there is an error/eof
-    */
-    while (p->len > 0) {
-        int r = adb_write(s->fd, p->ptr, p->len);
-        if (r > 0) {
-            p->len -= r;
-            p->ptr += r;
+    // 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();
+    apacket::payload_type 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;
         }
-        if ((r == 0) || (errno != EAGAIN)) {
-            D("LS(%d): not ready, errno=%d: %s", s->id, errno, strerror(errno));
-            put_apacket(p);
-            s->has_write_error = true;
-            s->close(s);
-            return 1; /* not ready (error) */
-        } else {
-            break;
+
+        /* 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);
         }
     }
 
-    if (p->len == 0) {
-        put_apacket(p);
-        return 0; /* ready for more data */
+    // 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;
     }
 
-enqueue:
-    p->next = 0;
-    if (s->pkt_first) {
-        s->pkt_last->next = p;
-    } else {
-        s->pkt_first = p;
+    return true;
+}
+
+static int local_socket_enqueue(asocket* s, apacket::payload_type data) {
+    D("LS(%d): enqueue %zu", s->id, data.size());
+
+    s->packet_queue.append(std::move(data));
+    switch (local_socket_flush_incoming(s)) {
+        case SocketFlushResult::Destroyed:
+            return -1;
+
+        case SocketFlushResult::TryAgain:
+            return 1;
+
+        case SocketFlushResult::Completed:
+            return 0;
     }
-    s->pkt_last = p;
 
-    /* make sure we are notified when we can drain the queue */
-    fdevent_add(&s->fde, FDE_WRITE);
-
-    return 1; /* not ready (backlog) */
+    return !s->packet_queue.empty();
 }
 
 static void local_socket_ready(asocket* s) {
     /* far side is ready for data, pay attention to
        readable events */
-    fdevent_add(&s->fde, FDE_READ);
+    fdevent_add(s->fde, FDE_READ);
 }
 
 // be sure to hold the socket list lock when calling this
 static void local_socket_destroy(asocket* s) {
-    apacket *p, *n;
     int exit_on_close = s->exit_on_close;
 
-    D("LS(%d): destroying fde.fd=%d", s->id, s->fde.fd);
+    D("LS(%d): destroying fde.fd=%d", s->id, s->fd);
 
     /* IMPORTANT: the remove closes the fd
     ** that belongs to this socket
     */
-    fdevent_remove(&s->fde);
+    fdevent_destroy(s->fde);
 
-    /* dispose of any unwritten data */
-    for (p = s->pkt_first; p; p = n) {
-        D("LS(%d): discarding %zu bytes", s->id, p->len);
-        n = p->next;
-        put_apacket(p);
-    }
     remove_socket(s);
-    free(s);
+    delete s;
 
     if (exit_on_close) {
         D("local_socket_destroy: exiting");
@@ -229,7 +277,7 @@
     /* If we are already closing, or if there are no
     ** pending packets, destroy immediately
     */
-    if (s->closing || s->has_write_error || s->pkt_first == NULL) {
+    if (s->closing || s->has_write_error || s->packet_queue.empty()) {
         int id = s->id;
         local_socket_destroy(s);
         D("LS(%d): closed", id);
@@ -240,11 +288,11 @@
     */
     D("LS(%d): closing", s->id);
     s->closing = 1;
-    fdevent_del(&s->fde, FDE_READ);
+    fdevent_del(s->fde, FDE_READ);
     remove_socket(s);
     D("LS(%d): put on socket_closing_list fd=%d", s->id, s->fd);
-    insert_local_socket(s, &local_socket_closing_list);
-    CHECK_EQ(FDE_WRITE, s->fde.state & FDE_WRITE);
+    local_socket_closing_list.push_back(s);
+    CHECK_EQ(FDE_WRITE, s->fde->state & FDE_WRITE);
 }
 
 static void local_socket_event_func(int fd, unsigned ev, void* _s) {
@@ -255,119 +303,21 @@
     ** in order to simplify the code.
     */
     if (ev & FDE_WRITE) {
-        apacket* p;
-        while ((p = s->pkt_first) != nullptr) {
-            while (p->len > 0) {
-                int r = adb_write(fd, p->ptr, p->len);
-                if (r == -1) {
-                    /* returning here is ok because FDE_READ will
-                    ** be processed in the next iteration loop
-                    */
-                    if (errno == EAGAIN) {
-                        return;
-                    }
-                } else if (r > 0) {
-                    p->ptr += r;
-                    p->len -= r;
-                    continue;
-                }
-
-                D(" closing after write because r=%d and errno is %d", r, errno);
-                s->has_write_error = true;
-                s->close(s);
+        switch (local_socket_flush_incoming(s)) {
+            case SocketFlushResult::Destroyed:
                 return;
-            }
 
-            if (p->len == 0) {
-                s->pkt_first = p->next;
-                if (s->pkt_first == 0) {
-                    s->pkt_last = 0;
-                }
-                put_apacket(p);
-            }
+            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) {
-        apacket* p = get_apacket();
-        char* x = p->data;
-        const size_t max_payload = s->get_max_payload();
-        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 == 0)) {
-            put_apacket(p);
-        } else {
-            p->len = 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, p);
-            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;
         }
     }
@@ -383,23 +333,20 @@
 }
 
 asocket* create_local_socket(int fd) {
-    asocket* s = reinterpret_cast<asocket*>(calloc(1, sizeof(asocket)));
-    if (s == NULL) {
-        fatal("cannot allocate socket");
-    }
+    asocket* s = new asocket();
     s->fd = fd;
     s->enqueue = local_socket_enqueue;
     s->ready = local_socket_ready;
-    s->shutdown = NULL;
+    s->shutdown = nullptr;
     s->close = local_socket_close;
     install_local_socket(s);
 
-    fdevent_install(&s->fde, fd, local_socket_event_func, s);
+    s->fde = fdevent_create(fd, local_socket_event_func, s);
     D("LS(%d): created (fd=%d)", s->id, s->fd);
     return s;
 }
 
-asocket* create_local_service_socket(const char* name, const atransport* transport) {
+asocket* create_local_service_socket(const char* name, atransport* transport) {
 #if !ADB_HOST
     if (!strcmp(name, "jdwp")) {
         return create_jdwp_service_socket();
@@ -430,12 +377,13 @@
 }
 
 #if ADB_HOST
-static asocket* create_host_service_socket(const char* name, const char* serial) {
+static asocket* create_host_service_socket(const char* name, const char* serial,
+                                           TransportId transport_id) {
     asocket* s;
 
-    s = host_service_to_socket(name, serial);
+    s = host_service_to_socket(name, serial, transport_id);
 
-    if (s != NULL) {
+    if (s != nullptr) {
         D("LS(%d) bound to '%s'", s->id, name);
         return s;
     }
@@ -444,12 +392,22 @@
 }
 #endif /* ADB_HOST */
 
-static int remote_socket_enqueue(asocket* s, apacket* p) {
+static int remote_socket_enqueue(asocket* s, apacket::payload_type data) {
     D("entered remote_socket_enqueue RS(%d) WRITE fd=%d peer.fd=%d", s->id, s->fd, s->peer->fd);
+    apacket* p = get_apacket();
+
     p->msg.command = A_WRTE;
     p->msg.arg0 = s->peer->id;
     p->msg.arg1 = s->id;
-    p->msg.data_length = p->len;
+
+    if (data.size() > MAX_PAYLOAD) {
+        put_apacket(p);
+        return -1;
+    }
+
+    p->payload = std::move(data);
+    p->msg.data_length = p->payload.size();
+
     send_packet(p, s->transport);
     return 1;
 }
@@ -477,14 +435,14 @@
 
 static void remote_socket_close(asocket* s) {
     if (s->peer) {
-        s->peer->peer = 0;
+        s->peer->peer = nullptr;
         D("RS(%d) peer->close()ing peer->id=%d peer->fd=%d", s->id, s->peer->id, s->peer->fd);
         s->peer->close(s->peer);
     }
     D("entered remote_socket_close RS(%d) CLOSE fd=%d peer->fd=%d", s->id, s->fd,
       s->peer ? s->peer->fd : -1);
     D("RS(%d): closed", s->id);
-    free(s);
+    delete s;
 }
 
 // Create a remote socket to exchange packets with a remote service through transport
@@ -495,11 +453,7 @@
     if (id == 0) {
         fatal("invalid remote socket id (0)");
     }
-    asocket* s = reinterpret_cast<asocket*>(calloc(1, sizeof(asocket)));
-
-    if (s == NULL) {
-        fatal("cannot allocate socket");
-    }
+    asocket* s = new asocket();
     s->id = id;
     s->enqueue = remote_socket_enqueue;
     s->ready = remote_socket_ready;
@@ -514,17 +468,19 @@
 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.assign(destination, destination + strlen(destination) + 1);
+    p->msg.data_length = p->payload.size();
+
+    if (p->msg.data_length > s->get_max_payload()) {
+        fatal("destination oversized");
+    }
+
     send_packet(p, s->transport);
 }
 
@@ -532,7 +488,7 @@
    send the go-ahead message when they connect */
 static void local_socket_ready_notify(asocket* s) {
     s->ready = local_socket_ready;
-    s->shutdown = NULL;
+    s->shutdown = nullptr;
     s->close = local_socket_close;
     SendOkay(s->fd);
     s->ready(s);
@@ -543,13 +499,13 @@
    connected (to avoid closing them without a status message) */
 static void local_socket_close_notify(asocket* s) {
     s->ready = local_socket_ready;
-    s->shutdown = NULL;
+    s->shutdown = nullptr;
     s->close = local_socket_close;
     SendFail(s->fd, "closed");
     s->close(s);
 }
 
-static unsigned unhex(char* s, int len) {
+static unsigned unhex(const char* s, int len) {
     unsigned n = 0, c;
 
     while (len-- > 0) {
@@ -653,57 +609,47 @@
 
 #endif  // ADB_HOST
 
-static int smart_socket_enqueue(asocket* s, apacket* p) {
-    unsigned len;
+static int smart_socket_enqueue(asocket* s, apacket::payload_type data) {
 #if ADB_HOST
     char* service = nullptr;
     char* serial = nullptr;
+    TransportId transport_id = 0;
     TransportType type = kTransportAny;
 #endif
 
-    D("SS(%d): enqueue %zu", s->id, p->len);
+    D("SS(%d): enqueue %zu", s->id, data.size());
 
-    if (s->pkt_first == 0) {
-        s->pkt_first = p;
-        s->pkt_last = p;
+    if (s->smart_socket_data.empty()) {
+        // TODO: Make this an IOVector?
+        s->smart_socket_data.assign(data.begin(), data.end());
     } else {
-        if ((s->pkt_first->len + p->len) > s->get_max_payload()) {
-            D("SS(%d): overflow", s->id);
-            put_apacket(p);
-            goto fail;
-        }
-
-        memcpy(s->pkt_first->data + s->pkt_first->len, p->data, p->len);
-        s->pkt_first->len += p->len;
-        put_apacket(p);
-
-        p = s->pkt_first;
+        std::copy(data.begin(), data.end(), std::back_inserter(s->smart_socket_data));
     }
 
     /* don't bother if we can't decode the length */
-    if (p->len < 4) {
+    if (s->smart_socket_data.size() < 4) {
         return 0;
     }
 
-    len = unhex(p->data, 4);
-    if ((len < 1) || (len > MAX_PAYLOAD)) {
-        D("SS(%d): bad size (%d)", s->id, len);
+    uint32_t len = unhex(s->smart_socket_data.data(), 4);
+    if (len == 0 || len > MAX_PAYLOAD) {
+        D("SS(%d): bad size (%u)", s->id, len);
         goto fail;
     }
 
-    D("SS(%d): len is %d", s->id, len);
+    D("SS(%d): len is %u", s->id, len);
     /* can't do anything until we have the full header */
-    if ((len + 4) > p->len) {
-        D("SS(%d): waiting for %zu more bytes", s->id, len + 4 - p->len);
+    if ((len + 4) > s->smart_socket_data.size()) {
+        D("SS(%d): waiting for %zu more bytes", s->id, len + 4 - s->smart_socket_data.size());
         return 0;
     }
 
-    p->data[len + 4] = 0;
+    s->smart_socket_data[len + 4] = 0;
 
-    D("SS(%d): '%s'", s->id, (char*)(p->data + 4));
+    D("SS(%d): '%s'", s->id, (char*)(s->smart_socket_data.data() + 4));
 
 #if ADB_HOST
-    service = (char*)p->data + 4;
+    service = &s->smart_socket_data[4];
     if (!strncmp(service, "host-serial:", strlen("host-serial:"))) {
         char* serial_end;
         service += strlen("host-serial:");
@@ -715,6 +661,14 @@
             serial = service;
             service = serial_end + 1;
         }
+    } else if (!strncmp(service, "host-transport-id:", strlen("host-transport-id:"))) {
+        service += strlen("host-transport-id:");
+        transport_id = strtoll(service, &service, 10);
+
+        if (*service != ':') {
+            return -1;
+        }
+        service++;
     } else if (!strncmp(service, "host-usb:", strlen("host-usb:"))) {
         type = kTransportUsb;
         service += strlen("host-usb:");
@@ -736,14 +690,14 @@
         ** the OKAY or FAIL message and all we have to do
         ** is clean up.
         */
-        if (handle_host_request(service, type, serial, s->peer->fd, s) == 0) {
+        if (handle_host_request(service, type, serial, transport_id, s->peer->fd, s) == 0) {
             /* XXX fail message? */
             D("SS(%d): handled host service '%s'", s->id, service);
             goto fail;
         }
         if (!strncmp(service, "transport", strlen("transport"))) {
             D("SS(%d): okay transport", s->id);
-            p->len = 0;
+            s->smart_socket_data.clear();
             return 0;
         }
 
@@ -751,8 +705,8 @@
         ** if no such service exists, we'll fail out
         ** and tear down here.
         */
-        s2 = create_host_service_socket(service, serial);
-        if (s2 == 0) {
+        s2 = create_host_service_socket(service, serial, transport_id);
+        if (s2 == nullptr) {
             D("SS(%d): couldn't create host service '%s'", s->id, service);
             SendFail(s->peer->fd, "unknown host service");
             goto fail;
@@ -772,7 +726,7 @@
         s->peer->close = local_socket_close;
         s->peer->peer = s2;
         s2->peer = s->peer;
-        s->peer = 0;
+        s->peer = nullptr;
         D("SS(%d): okay", s->id);
         s->close(s);
 
@@ -783,7 +737,7 @@
 #else /* !ADB_HOST */
     if (s->transport == nullptr) {
         std::string error_msg = "unknown failure";
-        s->transport = acquire_one_transport(kTransportAny, nullptr, nullptr, &error_msg);
+        s->transport = acquire_one_transport(kTransportAny, nullptr, 0, nullptr, &error_msg);
         if (s->transport == nullptr) {
             SendFail(s->peer->fd, error_msg);
             goto fail;
@@ -794,7 +748,7 @@
     if (!s->transport) {
         SendFail(s->peer->fd, "device offline (no transport)");
         goto fail;
-    } else if (s->transport->GetConnectionState() == kCsOffline) {
+    } else if (!ConnectionStateIsOnline(s->transport->GetConnectionState())) {
         /* if there's no remote we fail the connection
          ** right here and terminate it
          */
@@ -810,12 +764,12 @@
     s->peer->ready = local_socket_ready_notify;
     s->peer->shutdown = nullptr;
     s->peer->close = local_socket_close_notify;
-    s->peer->peer = 0;
+    s->peer->peer = nullptr;
     /* give him our transport and upref it */
     s->peer->transport = s->transport;
 
-    connect_to_remote(s->peer, (char*)(p->data + 4));
-    s->peer = 0;
+    connect_to_remote(s->peer, s->smart_socket_data.data() + 4);
+    s->peer = nullptr;
     s->close(s);
     return 1;
 
@@ -834,24 +788,20 @@
 
 static void smart_socket_close(asocket* s) {
     D("SS(%d): closed", s->id);
-    if (s->pkt_first) {
-        put_apacket(s->pkt_first);
-    }
     if (s->peer) {
-        s->peer->peer = 0;
+        s->peer->peer = nullptr;
         s->peer->close(s->peer);
-        s->peer = 0;
+        s->peer = nullptr;
     }
-    free(s);
+    delete s;
 }
 
 static asocket* create_smart_socket(void) {
     D("Creating smart socket");
-    asocket* s = reinterpret_cast<asocket*>(calloc(1, sizeof(asocket)));
-    if (s == NULL) fatal("cannot allocate socket");
+    asocket* s = new asocket();
     s->enqueue = smart_socket_enqueue;
     s->ready = smart_socket_ready;
-    s->shutdown = NULL;
+    s->shutdown = nullptr;
     s->close = smart_socket_close;
 
     D("SS(%d)", s->id);
diff --git a/adb/sysdeps.h b/adb/sysdeps.h
index 49c7847..f2911e0 100644
--- a/adb/sysdeps.h
+++ b/adb/sysdeps.h
@@ -31,6 +31,7 @@
 
 // Include this before open/close/unlink are defined as macros below.
 #include <android-base/errors.h>
+#include <android-base/macros.h>
 #include <android-base/unique_fd.h>
 #include <android-base/utf8.h>
 
@@ -38,34 +39,6 @@
 #include "sysdeps/network.h"
 #include "sysdeps/stat.h"
 
-/*
- * TEMP_FAILURE_RETRY is defined by some, but not all, versions of
- * <unistd.h>. (Alas, it is not as standard as we'd hoped!) So, if it's
- * not already defined, then define it here.
- */
-#ifndef TEMP_FAILURE_RETRY
-/* Used to retry syscalls that can return EINTR. */
-#define TEMP_FAILURE_RETRY(exp) ({         \
-    typeof (exp) _rc;                      \
-    do {                                   \
-        _rc = (exp);                       \
-    } while (_rc == -1 && errno == EINTR); \
-    _rc; })
-#endif
-
-// Some printf-like functions are implemented in terms of
-// android::base::StringAppendV, so they should use the same attribute for
-// compile-time format string checking. On Windows, if the mingw version of
-// vsnprintf is used in StringAppendV, use `gnu_printf' which allows z in %zd
-// and PRIu64 (and related) to be recognized by the compile-time checking.
-#define ADB_FORMAT_ARCHETYPE __printf__
-#ifdef __USE_MINGW_ANSI_STDIO
-#if __USE_MINGW_ANSI_STDIO
-#undef ADB_FORMAT_ARCHETYPE
-#define ADB_FORMAT_ARCHETYPE gnu_printf
-#endif
-#endif
-
 #ifdef _WIN32
 
 // Clang-only nullability specifiers
@@ -106,11 +79,6 @@
     return 0;
 }
 
-static __inline__  unsigned long adb_thread_id()
-{
-    return GetCurrentThreadId();
-}
-
 static __inline__ void  close_on_exec(int  fd)
 {
     /* nothing really */
@@ -125,14 +93,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)
@@ -231,14 +199,12 @@
 extern int adb_utime(const char *, struct utimbuf *);
 extern int adb_chmod(const char *, int);
 
-extern int adb_vfprintf(FILE *stream, const char *format, va_list ap)
-    __attribute__((__format__(ADB_FORMAT_ARCHETYPE, 2, 0)));
-extern int adb_vprintf(const char *format, va_list ap)
-    __attribute__((__format__(ADB_FORMAT_ARCHETYPE, 1, 0)));
-extern int adb_fprintf(FILE *stream, const char *format, ...)
-    __attribute__((__format__(ADB_FORMAT_ARCHETYPE, 2, 3)));
-extern int adb_printf(const char *format, ...)
-    __attribute__((__format__(ADB_FORMAT_ARCHETYPE, 1, 2)));
+extern int adb_vfprintf(FILE* stream, const char* format, va_list ap)
+        __attribute__((__format__(__printf__, 2, 0)));
+extern int adb_vprintf(const char* format, va_list ap) __attribute__((__format__(__printf__, 1, 0)));
+extern int adb_fprintf(FILE* stream, const char* format, ...)
+        __attribute__((__format__(__printf__, 2, 3)));
+extern int adb_printf(const char* format, ...) __attribute__((__format__(__printf__, 1, 2)));
 
 extern int adb_fputs(const char* buf, FILE* stream);
 extern int adb_fputc(int ch, FILE* stream);
@@ -348,7 +314,6 @@
 #else /* !_WIN32 a.k.a. Unix */
 
 #include <cutils/sockets.h>
-#include <cutils/threads.h>
 #include <fcntl.h>
 #include <poll.h>
 #include <signal.h>
@@ -433,14 +398,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
 
@@ -582,18 +543,12 @@
 #ifdef __APPLE__
     return pthread_setname_np(name.c_str());
 #else
-    const char *s = name.c_str();
-
-    // pthread_setname_np fails rather than truncating long strings.
-    const int max_task_comm_len = 16; // including the null terminator
-    if (name.length() > (max_task_comm_len - 1)) {
-        char buf[max_task_comm_len];
-        strncpy(buf, name.c_str(), sizeof(buf) - 1);
-        buf[sizeof(buf) - 1] = '\0';
-        s = buf;
-    }
-
-    return pthread_setname_np(pthread_self(), s) ;
+    // Both bionic and glibc's pthread_setname_np fails rather than truncating long strings.
+    // glibc doesn't have strlcpy, so we have to fake it.
+    char buf[16];  // MAX_TASK_COMM_LEN, but that's not exported by the kernel headers.
+    strncpy(buf, name.c_str(), sizeof(buf) - 1);
+    buf[sizeof(buf) - 1] = '\0';
+    return pthread_setname_np(pthread_self(), buf);
 #endif
 }
 
@@ -645,11 +600,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..4108aff
--- /dev/null
+++ b/adb/sysdeps/memory.h
@@ -0,0 +1,65 @@
+#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 internal {
+
+template <typename T>
+struct array_known_bounds;
+
+template <typename T>
+struct array_known_bounds<T[]> {
+    constexpr static bool value = false;
+};
+
+template <typename T, size_t N>
+struct array_known_bounds<T[N]> {
+    constexpr static bool value = true;
+};
+
+}  // namespace internal
+
+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)...));
+}
+
+template <typename T>
+typename std::enable_if<std::is_array<T>::value && !internal::array_known_bounds<T>::value,
+                        std::unique_ptr<T>>::type
+make_unique(std::size_t size) {
+    return std::unique_ptr<T>(new typename std::remove_extent<T>::type[size]());
+}
+
+template <typename T, typename... Args>
+typename std::enable_if<std::is_array<T>::value && internal::array_known_bounds<T>::value,
+                        std::unique_ptr<T>>::type
+make_unique(Args&&... args) = delete;
+
+}  // 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/uio.h b/adb/sysdeps/uio.h
new file mode 100644
index 0000000..d06ef89
--- /dev/null
+++ b/adb/sysdeps/uio.h
@@ -0,0 +1,39 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <sys/types.h>
+
+#if defined(_WIN32)
+
+// Layout of this struct must match struct WSABUF (verified via static assert in sysdeps_win32.cpp)
+struct adb_iovec {
+    size_t iov_len;
+    void* iov_base;
+};
+
+ssize_t adb_writev(int fd, const adb_iovec* iov, int iovcnt);
+
+#else
+
+#include <sys/uio.h>
+using adb_iovec = struct iovec;
+#define adb_writev writev
+
+#endif
+
+#pragma GCC poison writev
diff --git a/adb/sysdeps_test.cpp b/adb/sysdeps_test.cpp
index fd19882..79cebe6 100644
--- a/adb/sysdeps_test.cpp
+++ b/adb/sysdeps_test.cpp
@@ -121,10 +121,13 @@
     adb_pollfd pfd[3] = {};
     pfd[0].fd = fds[0];
     pfd[0].events = POLLRDNORM;
+    pfd[0].revents = ~0;
     pfd[1].fd = INT_MAX;
     pfd[1].events = POLLRDNORM;
+    pfd[1].revents = ~0;
     pfd[2].fd = fds[1];
     pfd[2].events = POLLWRNORM;
+    pfd[2].revents = ~0;
 
     ASSERT_TRUE(WriteFdExactly(fds[1], "foo", 4));
 
@@ -136,6 +139,17 @@
     EXPECT_EQ(POLLRDNORM, pfd[0].revents);
     EXPECT_EQ(POLLNVAL, pfd[1].revents);
     EXPECT_EQ(POLLWRNORM, pfd[2].revents);
+
+    // Make sure that we return immediately if an invalid FD is given.
+    pfd[0].fd = fds[0];
+    pfd[0].events = POLLRDNORM;
+    pfd[0].revents = ~0;
+    pfd[1].fd = INT_MAX;
+    pfd[1].events = POLLRDNORM;
+    pfd[1].revents = ~0;
+    EXPECT_EQ(2, adb_poll(pfd, 2, -1));
+    EXPECT_EQ(POLLRDNORM, pfd[0].revents);
+    EXPECT_EQ(POLLNVAL, pfd[1].revents);
 }
 
 TEST_F(sysdeps_poll, duplicate_fd) {
diff --git a/adb/sysdeps_win32.cpp b/adb/sysdeps_win32.cpp
index 5873b2b..c94d13f 100644
--- a/adb/sysdeps_win32.cpp
+++ b/adb/sysdeps_win32.cpp
@@ -36,6 +36,7 @@
 
 #include <android-base/errors.h>
 #include <android-base/logging.h>
+#include <android-base/macros.h>
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
 #include <android-base/utf8.h>
@@ -43,6 +44,8 @@
 #include "adb.h"
 #include "adb_utils.h"
 
+#include "sysdeps/uio.h"
+
 extern void fatal(const char *fmt, ...);
 
 /* forward declarations */
@@ -57,6 +60,7 @@
     int (*_fh_lseek)(FH, int, int);
     int (*_fh_read)(FH, void*, int);
     int (*_fh_write)(FH, const void*, int);
+    int (*_fh_writev)(FH, const adb_iovec*, int);
 } FHClassRec;
 
 static void _fh_file_init(FH);
@@ -64,6 +68,7 @@
 static int _fh_file_lseek(FH, int, int);
 static int _fh_file_read(FH, void*, int);
 static int _fh_file_write(FH, const void*, int);
+static int _fh_file_writev(FH, const adb_iovec*, int);
 
 static const FHClassRec _fh_file_class = {
     _fh_file_init,
@@ -71,6 +76,7 @@
     _fh_file_lseek,
     _fh_file_read,
     _fh_file_write,
+    _fh_file_writev,
 };
 
 static void _fh_socket_init(FH);
@@ -78,6 +84,7 @@
 static int _fh_socket_lseek(FH, int, int);
 static int _fh_socket_read(FH, void*, int);
 static int _fh_socket_write(FH, const void*, int);
+static int _fh_socket_writev(FH, const adb_iovec*, int);
 
 static const FHClassRec _fh_socket_class = {
     _fh_socket_init,
@@ -85,6 +92,7 @@
     _fh_socket_lseek,
     _fh_socket_read,
     _fh_socket_write,
+    _fh_socket_writev,
 };
 
 #define assert(cond)                                                                       \
@@ -150,7 +158,7 @@
         D( "_fh_from_int: invalid fd %d passed to %s", fd + WIN32_FH_BASE,
            func );
         errno = EBADF;
-        return NULL;
+        return nullptr;
     }
 
     f = &_win32_fhs[fd];
@@ -159,7 +167,7 @@
         D( "_fh_from_int: invalid fd %d passed to %s", fd + WIN32_FH_BASE,
            func );
         errno = EBADF;
-        return NULL;
+        return nullptr;
     }
 
     return f;
@@ -178,12 +186,12 @@
 static FH
 _fh_alloc( FHClass  clazz )
 {
-    FH   f = NULL;
+    FH   f = nullptr;
 
     std::lock_guard<std::mutex> lock(_win32_lock);
 
     for (int i = _win32_fh_next; i < WIN32_MAX_FHS; ++i) {
-        if (_win32_fhs[i].clazz == NULL) {
+        if (_win32_fhs[i].clazz == nullptr) {
             f = &_win32_fhs[i];
             _win32_fh_next = i + 1;
             f->clazz = clazz;
@@ -218,7 +226,7 @@
         f->name[0] = '\0';
         f->eof     = 0;
         f->used    = 0;
-        f->clazz   = NULL;
+        f->clazz   = nullptr;
     }
     return 0;
 }
@@ -248,57 +256,88 @@
 /**************************************************************************/
 /**************************************************************************/
 
-static void _fh_file_init( FH  f ) {
+static void _fh_file_init(FH f) {
     f->fh_handle = INVALID_HANDLE_VALUE;
 }
 
-static int _fh_file_close( FH  f ) {
-    CloseHandle( f->fh_handle );
+static int _fh_file_close(FH f) {
+    CloseHandle(f->fh_handle);
     f->fh_handle = INVALID_HANDLE_VALUE;
     return 0;
 }
 
-static int _fh_file_read( FH  f,  void*  buf, int   len ) {
-    DWORD  read_bytes;
+static int _fh_file_read(FH f, void* buf, int len) {
+    DWORD read_bytes;
 
-    if ( !ReadFile( f->fh_handle, buf, (DWORD)len, &read_bytes, NULL ) ) {
-        D( "adb_read: could not read %d bytes from %s", len, f->name );
+    if (!ReadFile(f->fh_handle, buf, (DWORD)len, &read_bytes, nullptr)) {
+        D("adb_read: could not read %d bytes from %s", len, f->name);
         errno = EIO;
         return -1;
     } else if (read_bytes < (DWORD)len) {
         f->eof = 1;
     }
-    return (int)read_bytes;
+    return read_bytes;
 }
 
-static int _fh_file_write( FH  f,  const void*  buf, int   len ) {
-    DWORD  wrote_bytes;
+static int _fh_file_write(FH f, const void* buf, int len) {
+    DWORD wrote_bytes;
 
-    if ( !WriteFile( f->fh_handle, buf, (DWORD)len, &wrote_bytes, NULL ) ) {
-        D( "adb_file_write: could not write %d bytes from %s", len, f->name );
+    if (!WriteFile(f->fh_handle, buf, (DWORD)len, &wrote_bytes, nullptr)) {
+        D("adb_file_write: could not write %d bytes from %s", len, f->name);
         errno = EIO;
         return -1;
     } else if (wrote_bytes < (DWORD)len) {
         f->eof = 1;
     }
-    return  (int)wrote_bytes;
+    return wrote_bytes;
 }
 
-static int _fh_file_lseek( FH  f, int  pos, int  origin ) {
-    DWORD  method;
-    DWORD  result;
+static int _fh_file_writev(FH f, const adb_iovec* iov, int iovcnt) {
+    if (iovcnt <= 0) {
+        errno = EINVAL;
+        return -1;
+    }
 
-    switch (origin)
-    {
-        case SEEK_SET:  method = FILE_BEGIN; break;
-        case SEEK_CUR:  method = FILE_CURRENT; break;
-        case SEEK_END:  method = FILE_END; break;
+    DWORD wrote_bytes = 0;
+
+    for (int i = 0; i < iovcnt; ++i) {
+        ssize_t rc = _fh_file_write(f, iov[i].iov_base, iov[i].iov_len);
+        if (rc == -1) {
+            return wrote_bytes > 0 ? wrote_bytes : -1;
+        } else if (rc == 0) {
+            return wrote_bytes;
+        }
+
+        wrote_bytes += rc;
+
+        if (static_cast<size_t>(rc) < iov[i].iov_len) {
+            return wrote_bytes;
+        }
+    }
+
+    return wrote_bytes;
+}
+
+static int _fh_file_lseek(FH f, int pos, int origin) {
+    DWORD method;
+    DWORD result;
+
+    switch (origin) {
+        case SEEK_SET:
+            method = FILE_BEGIN;
+            break;
+        case SEEK_CUR:
+            method = FILE_CURRENT;
+            break;
+        case SEEK_END:
+            method = FILE_END;
+            break;
         default:
             errno = EINVAL;
             return -1;
     }
 
-    result = SetFilePointer( f->fh_handle, pos, NULL, method );
+    result = SetFilePointer(f->fh_handle, pos, nullptr, method);
     if (result == INVALID_SET_FILE_POINTER) {
         errno = EIO;
         return -1;
@@ -308,7 +347,6 @@
     return (int)result;
 }
 
-
 /**************************************************************************/
 /**************************************************************************/
 /*****                                                                *****/
@@ -317,12 +355,11 @@
 /**************************************************************************/
 /**************************************************************************/
 
-int  adb_open(const char*  path, int  options)
-{
-    FH  f;
+int adb_open(const char* path, int options) {
+    FH f;
 
-    DWORD  desiredAccess       = 0;
-    DWORD  shareMode           = FILE_SHARE_READ | FILE_SHARE_WRITE;
+    DWORD desiredAccess = 0;
+    DWORD shareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
 
     switch (options) {
         case O_RDONLY:
@@ -340,8 +377,8 @@
             return -1;
     }
 
-    f = _fh_alloc( &_fh_file_class );
-    if ( !f ) {
+    f = _fh_alloc(&_fh_file_class);
+    if (!f) {
         return -1;
     }
 
@@ -349,21 +386,21 @@
     if (!android::base::UTF8ToWide(path, &path_wide)) {
         return -1;
     }
-    f->fh_handle = CreateFileW( path_wide.c_str(), desiredAccess, shareMode,
-                                NULL, OPEN_EXISTING, 0, NULL );
+    f->fh_handle =
+        CreateFileW(path_wide.c_str(), desiredAccess, shareMode, nullptr, OPEN_EXISTING, 0, nullptr);
 
-    if ( f->fh_handle == INVALID_HANDLE_VALUE ) {
+    if (f->fh_handle == INVALID_HANDLE_VALUE) {
         const DWORD err = GetLastError();
         _fh_close(f);
-        D( "adb_open: could not open '%s': ", path );
+        D("adb_open: could not open '%s': ", path);
         switch (err) {
             case ERROR_FILE_NOT_FOUND:
-                D( "file not found" );
+                D("file not found");
                 errno = ENOENT;
                 return -1;
 
             case ERROR_PATH_NOT_FOUND:
-                D( "path not found" );
+                D("path not found");
                 errno = ENOTDIR;
                 return -1;
 
@@ -374,18 +411,17 @@
         }
     }
 
-    snprintf( f->name, sizeof(f->name), "%d(%s)", _fh_to_int(f), path );
-    D( "adb_open: '%s' => fd %d", path, _fh_to_int(f) );
+    snprintf(f->name, sizeof(f->name), "%d(%s)", _fh_to_int(f), path);
+    D("adb_open: '%s' => fd %d", path, _fh_to_int(f));
     return _fh_to_int(f);
 }
 
 /* ignore mode on Win32 */
-int  adb_creat(const char*  path, int  mode)
-{
-    FH  f;
+int adb_creat(const char* path, int mode) {
+    FH f;
 
-    f = _fh_alloc( &_fh_file_class );
-    if ( !f ) {
+    f = _fh_alloc(&_fh_file_class);
+    if (!f) {
         return -1;
     }
 
@@ -393,23 +429,21 @@
     if (!android::base::UTF8ToWide(path, &path_wide)) {
         return -1;
     }
-    f->fh_handle = CreateFileW( path_wide.c_str(), GENERIC_WRITE,
-                                FILE_SHARE_READ | FILE_SHARE_WRITE,
-                                NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL,
-                                NULL );
+    f->fh_handle = CreateFileW(path_wide.c_str(), GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE,
+                               nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
 
-    if ( f->fh_handle == INVALID_HANDLE_VALUE ) {
+    if (f->fh_handle == INVALID_HANDLE_VALUE) {
         const DWORD err = GetLastError();
         _fh_close(f);
-        D( "adb_creat: could not open '%s': ", path );
+        D("adb_creat: could not open '%s': ", path);
         switch (err) {
             case ERROR_FILE_NOT_FOUND:
-                D( "file not found" );
+                D("file not found");
                 errno = ENOENT;
                 return -1;
 
             case ERROR_PATH_NOT_FOUND:
-                D( "path not found" );
+                D("path not found");
                 errno = ENOTDIR;
                 return -1;
 
@@ -419,57 +453,64 @@
                 return -1;
         }
     }
-    snprintf( f->name, sizeof(f->name), "%d(%s)", _fh_to_int(f), path );
-    D( "adb_creat: '%s' => fd %d", path, _fh_to_int(f) );
+    snprintf(f->name, sizeof(f->name), "%d(%s)", _fh_to_int(f), path);
+    D("adb_creat: '%s' => fd %d", path, _fh_to_int(f));
     return _fh_to_int(f);
 }
 
+int adb_read(int fd, void* buf, int len) {
+    FH f = _fh_from_int(fd, __func__);
 
-int  adb_read(int  fd, void* buf, int len)
-{
-    FH     f = _fh_from_int(fd, __func__);
-
-    if (f == NULL) {
+    if (f == nullptr) {
+        errno = EBADF;
         return -1;
     }
 
-    return f->clazz->_fh_read( f, buf, len );
+    return f->clazz->_fh_read(f, buf, len);
 }
 
+int adb_write(int fd, const void* buf, int len) {
+    FH f = _fh_from_int(fd, __func__);
 
-int  adb_write(int  fd, const void*  buf, int  len)
-{
-    FH     f = _fh_from_int(fd, __func__);
-
-    if (f == NULL) {
+    if (f == nullptr) {
+        errno = EBADF;
         return -1;
     }
 
     return f->clazz->_fh_write(f, buf, len);
 }
 
+ssize_t adb_writev(int fd, const adb_iovec* iov, int iovcnt) {
+    FH f = _fh_from_int(fd, __func__);
 
-int  adb_lseek(int  fd, int  pos, int  where)
-{
-    FH     f = _fh_from_int(fd, __func__);
+    if (f == nullptr) {
+        errno = EBADF;
+        return -1;
+    }
+
+    return f->clazz->_fh_writev(f, iov, iovcnt);
+}
+
+int adb_lseek(int fd, int pos, int where) {
+    FH f = _fh_from_int(fd, __func__);
 
     if (!f) {
+        errno = EBADF;
         return -1;
     }
 
     return f->clazz->_fh_lseek(f, pos, where);
 }
 
-
-int  adb_close(int  fd)
-{
-    FH   f = _fh_from_int(fd, __func__);
+int adb_close(int fd) {
+    FH f = _fh_from_int(fd, __func__);
 
     if (!f) {
+        errno = EBADF;
         return -1;
     }
 
-    D( "adb_close: %s", f->name);
+    D("adb_close: %s", f->name);
     _fh_close(f);
     return 0;
 }
@@ -529,6 +570,7 @@
     int skipped = 0;
     std::vector<WSAPOLLFD> sockets;
     std::vector<adb_pollfd*> original;
+
     for (size_t i = 0; i < nfds; ++i) {
         FH fh = _fh_from_int(fds[i].fd, __func__);
         if (!fh || !fh->used || fh->clazz != &_fh_socket_class) {
@@ -549,6 +591,11 @@
         return skipped;
     }
 
+    // If we have any invalid FDs in our FD set, make sure to return immediately.
+    if (skipped > 0) {
+        timeout = 0;
+    }
+
     int result = WSAPoll(sockets.data(), sockets.size(), timeout);
     if (result == SOCKET_ERROR) {
         _socket_set_errno(WSAGetLastError());
@@ -560,7 +607,7 @@
         original[i]->revents = sockets[i].revents;
     }
 
-    // WSAPoll appears to return the number of unique FDs with avaiable events, instead of how many
+    // WSAPoll appears to return the number of unique FDs with available events, instead of how many
     // of the pollfd elements have a non-zero revents field, which is what it and poll are specified
     // to do. Ignore its result and calculate the proper return value.
     result = 0;
@@ -576,7 +623,7 @@
     f->fh_socket = INVALID_SOCKET;
 }
 
-static int _fh_socket_close( FH  f ) {
+static int _fh_socket_close(FH f) {
     if (f->fh_socket != INVALID_SOCKET) {
         /* gently tell any peer that we're closing the socket */
         if (shutdown(f->fh_socket, SD_BOTH) == SOCKET_ERROR) {
@@ -597,13 +644,13 @@
     return 0;
 }
 
-static int _fh_socket_lseek( FH  f, int pos, int origin ) {
+static int _fh_socket_lseek(FH f, int pos, int origin) {
     errno = EPIPE;
     return -1;
 }
 
 static int _fh_socket_read(FH f, void* buf, int len) {
-    int  result = recv(f->fh_socket, reinterpret_cast<char*>(buf), len, 0);
+    int result = recv(f->fh_socket, reinterpret_cast<char*>(buf), len, 0);
     if (result == SOCKET_ERROR) {
         const DWORD err = WSAGetLastError();
         // WSAEWOULDBLOCK is normal with a non-blocking socket, so don't trace
@@ -615,11 +662,11 @@
         _socket_set_errno(err);
         result = -1;
     }
-    return  result;
+    return result;
 }
 
 static int _fh_socket_write(FH f, const void* buf, int len) {
-    int  result = send(f->fh_socket, reinterpret_cast<const char*>(buf), len, 0);
+    int result = send(f->fh_socket, reinterpret_cast<const char*>(buf), len, 0);
     if (result == SOCKET_ERROR) {
         const DWORD err = WSAGetLastError();
         // WSAEWOULDBLOCK is normal with a non-blocking socket, so don't trace
@@ -633,13 +680,44 @@
     } else {
         // According to https://code.google.com/p/chromium/issues/detail?id=27870
         // Winsock Layered Service Providers may cause this.
-        CHECK_LE(result, len) << "Tried to write " << len << " bytes to "
-                              << f->name << ", but " << result
-                              << " bytes reportedly written";
+        CHECK_LE(result, len) << "Tried to write " << len << " bytes to " << f->name << ", but "
+                              << result << " bytes reportedly written";
     }
     return result;
 }
 
+// Make sure that adb_iovec is compatible with WSABUF.
+static_assert(sizeof(adb_iovec) == sizeof(WSABUF), "");
+static_assert(SIZEOF_MEMBER(adb_iovec, iov_len) == SIZEOF_MEMBER(WSABUF, len), "");
+static_assert(offsetof(adb_iovec, iov_len) == offsetof(WSABUF, len), "");
+
+static_assert(SIZEOF_MEMBER(adb_iovec, iov_base) == SIZEOF_MEMBER(WSABUF, buf), "");
+static_assert(offsetof(adb_iovec, iov_base) == offsetof(WSABUF, buf), "");
+
+static int _fh_socket_writev(FH f, const adb_iovec* iov, int iovcnt) {
+    if (iovcnt <= 0) {
+        errno = EINVAL;
+        return -1;
+    }
+
+    WSABUF* wsabuf = reinterpret_cast<WSABUF*>(const_cast<adb_iovec*>(iov));
+    DWORD bytes_written = 0;
+    int result = WSASend(f->fh_socket, wsabuf, iovcnt, &bytes_written, 0, nullptr, nullptr);
+    if (result == SOCKET_ERROR) {
+        const DWORD err = WSAGetLastError();
+        // WSAEWOULDBLOCK is normal with a non-blocking socket, so don't trace
+        // that to reduce spam and confusion.
+        if (err != WSAEWOULDBLOCK) {
+            D("send fd %d failed: %s", _fh_to_int(f),
+              android::base::SystemErrorCodeToString(err).c_str());
+        }
+        _socket_set_errno(err);
+        result = -1;
+    }
+    CHECK_GE(static_cast<DWORD>(std::numeric_limits<int>::max()), bytes_written);
+    return static_cast<int>(bytes_written);
+}
+
 /**************************************************************************/
 /**************************************************************************/
 /*****                                                                *****/
@@ -648,23 +726,15 @@
 /**************************************************************************/
 /**************************************************************************/
 
-#include <winsock2.h>
-
-static int  _winsock_init;
-
-static void
-_init_winsock( void )
-{
-    // TODO: Multiple threads calling this may potentially cause multiple calls
-    // to WSAStartup() which offers no real benefit.
-    if (!_winsock_init) {
-        WSADATA  wsaData;
-        int      rc = WSAStartup( MAKEWORD(2,2), &wsaData);
+static int _init_winsock(void) {
+    static std::once_flag once;
+    std::call_once(once, []() {
+        WSADATA wsaData;
+        int rc = WSAStartup(MAKEWORD(2, 2), &wsaData);
         if (rc != 0) {
             fatal("adb: could not initialize Winsock: %s",
                   android::base::SystemErrorCodeToString(rc).c_str());
         }
-        _winsock_init = 1;
 
         // Note that we do not call atexit() to register WSACleanup to be called
         // at normal process termination because:
@@ -679,9 +749,12 @@
         //    setupapi.dll which tries to load wintrust.dll which tries to load
         //    crypt32.dll which calls atexit() which tries to acquire the C
         //    Runtime lock that the other thread holds.
-    }
+    });
+    return 0;
 }
 
+static int _winsock_init = _init_winsock();
+
 // Map a socket type to an explicit socket protocol instead of using the socket
 // protocol of 0. Explicit socket protocols are used by most apps and we should
 // do the same to reduce the chance of exercising uncommon code-paths that might
@@ -709,8 +782,6 @@
         return -1;
     }
 
-    if (!_winsock_init) _init_winsock();
-
     memset(&addr, 0, sizeof(addr));
     addr.sin_family = AF_INET;
     addr.sin_port = htons(port);
@@ -747,8 +818,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;
@@ -761,8 +830,6 @@
         return -1;
     }
 
-    if (!_winsock_init) _init_winsock();
-
     memset(&addr, 0, sizeof(addr));
     addr.sin_family = AF_INET;
     addr.sin_port = htons(port);
@@ -805,7 +872,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());
@@ -839,8 +906,6 @@
         return -1;
     }
 
-    if (!_winsock_init) _init_winsock();
-
     struct addrinfo hints;
     memset(&hints, 0, sizeof(hints));
     hints.ai_family = AF_UNSPEC;
@@ -908,52 +973,49 @@
     return fd;
 }
 
-int  adb_register_socket(SOCKET s) {
-    FH f = _fh_alloc( &_fh_socket_class );
+int adb_register_socket(SOCKET s) {
+    FH f = _fh_alloc(&_fh_socket_class);
     f->fh_socket = s;
     return _fh_to_int(f);
 }
 
 #undef accept
-int  adb_socket_accept(int  serverfd, struct sockaddr*  addr, socklen_t  *addrlen)
-{
-    FH   serverfh = _fh_from_int(serverfd, __func__);
+int adb_socket_accept(int serverfd, struct sockaddr* addr, socklen_t* addrlen) {
+    FH serverfh = _fh_from_int(serverfd, __func__);
 
-    if ( !serverfh || serverfh->clazz != &_fh_socket_class ) {
+    if (!serverfh || serverfh->clazz != &_fh_socket_class) {
         D("adb_socket_accept: invalid fd %d", serverfd);
         errno = EBADF;
         return -1;
     }
 
-    unique_fh fh(_fh_alloc( &_fh_socket_class ));
+    unique_fh fh(_fh_alloc(&_fh_socket_class));
     if (!fh) {
         PLOG(ERROR) << "adb_socket_accept: failed to allocate accepted socket "
                        "descriptor";
         return -1;
     }
 
-    fh->fh_socket = accept( serverfh->fh_socket, addr, addrlen );
+    fh->fh_socket = accept(serverfh->fh_socket, addr, addrlen);
     if (fh->fh_socket == INVALID_SOCKET) {
         const DWORD err = WSAGetLastError();
-        LOG(ERROR) << "adb_socket_accept: accept on fd " << serverfd <<
-                      " failed: " + android::base::SystemErrorCodeToString(err);
-        _socket_set_errno( err );
+        LOG(ERROR) << "adb_socket_accept: accept on fd " << serverfd
+                   << " failed: " + android::base::SystemErrorCodeToString(err);
+        _socket_set_errno(err);
         return -1;
     }
 
     const int fd = _fh_to_int(fh.get());
-    snprintf( fh->name, sizeof(fh->name), "%d(accept:%s)", fd, serverfh->name );
-    D( "adb_socket_accept on fd %d returns fd %d", serverfd, fd );
+    snprintf(fh->name, sizeof(fh->name), "%d(accept:%s)", fd, serverfh->name);
+    D("adb_socket_accept on fd %d returns fd %d", serverfd, fd);
     fh.release();
-    return  fd;
+    return fd;
 }
 
+int adb_setsockopt(int fd, int level, int optname, const void* optval, socklen_t optlen) {
+    FH fh = _fh_from_int(fd, __func__);
 
-int  adb_setsockopt( int  fd, int  level, int  optname, const void*  optval, socklen_t  optlen )
-{
-    FH   fh = _fh_from_int(fd, __func__);
-
-    if ( !fh || fh->clazz != &_fh_socket_class ) {
+    if (!fh || fh->clazz != &_fh_socket_class) {
         D("adb_setsockopt: invalid fd %d", fd);
         errno = EBADF;
         return -1;
@@ -963,13 +1025,13 @@
     // to set SOL_SOCKET, SO_SNDBUF/SO_RCVBUF, ignore it since the OS has
     // auto-tuning.
 
-    int result = setsockopt( fh->fh_socket, level, optname,
-                             reinterpret_cast<const char*>(optval), optlen );
-    if ( result == SOCKET_ERROR ) {
+    int result =
+        setsockopt(fh->fh_socket, level, optname, reinterpret_cast<const char*>(optval), optlen);
+    if (result == SOCKET_ERROR) {
         const DWORD err = WSAGetLastError();
-        D("adb_setsockopt: setsockopt on fd %d level %d optname %d failed: %s\n",
-          fd, level, optname, android::base::SystemErrorCodeToString(err).c_str());
-        _socket_set_errno( err );
+        D("adb_setsockopt: setsockopt on fd %d level %d optname %d failed: %s\n", fd, level,
+          optname, android::base::SystemErrorCodeToString(err).c_str());
+        _socket_set_errno(err);
         result = -1;
     }
     return result;
@@ -1013,9 +1075,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 +1084,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());
@@ -1639,7 +1700,7 @@
 
         // The following emulation code should write the output sequence to
         // either seqstr or to seqbuf and seqbuflen.
-        const char* seqstr = NULL;  // NULL terminated C-string
+        const char* seqstr = nullptr;  // NULL terminated C-string
         // Enough space for max sequence string below, plus modifiers and/or
         // escape prefix.
         char seqbuf[16];
@@ -1937,7 +1998,7 @@
         // * seqstr is set (and strlen can be used to determine the length).
         // * seqbuf and seqbuflen are set
         // Fallback to ch from Windows.
-        if (seqstr != NULL) {
+        if (seqstr != nullptr) {
             out = seqstr;
             outlen = strlen(seqstr);
         } else if (seqbuflen > 0) {
@@ -2007,9 +2068,9 @@
 }
 
 void stdin_raw_restore() {
-    if (_console_handle != NULL) {
+    if (_console_handle != nullptr) {
         const HANDLE in = _console_handle;
-        _console_handle = NULL;  // clear state
+        _console_handle = nullptr;  // clear state
 
         if (!SetConsoleMode(in, _old_console_mode)) {
             // This really should not fail.
@@ -2021,7 +2082,7 @@
 
 // Called by 'adb shell' and 'adb exec-in' (via unix_read()) to read from stdin.
 int unix_read_interruptible(int fd, void* buf, size_t len) {
-    if ((fd == STDIN_FILENO) && (_console_handle != NULL)) {
+    if ((fd == STDIN_FILENO) && (_console_handle != nullptr)) {
         // If it is a request to read from stdin, and stdin_raw_init() has been
         // called, and it successfully configured the console, then read from
         // the console using Win32 console APIs and partially emulate a unix
@@ -2381,7 +2442,7 @@
 
     // Write UTF-16 to the console.
     DWORD written = 0;
-    if (!WriteConsoleW(console, utf16.c_str(), utf16.length(), &written, NULL)) {
+    if (!WriteConsoleW(console, utf16.c_str(), utf16.length(), &written, nullptr)) {
         errno = EIO;
         return -1;
     }
@@ -2394,9 +2455,8 @@
 }
 
 // Function prototype because attributes cannot be placed on func definitions.
-static int _console_vfprintf(const HANDLE console, FILE* stream,
-                             const char *format, va_list ap)
-    __attribute__((__format__(ADB_FORMAT_ARCHETYPE, 3, 0)));
+static int _console_vfprintf(const HANDLE console, FILE* stream, const char* format, va_list ap)
+        __attribute__((__format__(__printf__, 3, 0)));
 
 // Internal function to format a UTF-8 string and write it to a Win32 console.
 // Returns -1 on error.
@@ -2426,7 +2486,7 @@
 
     // If there is an associated Win32 console, write to it specially,
     // otherwise defer to the regular C Runtime, passing it UTF-8.
-    if (console != NULL) {
+    if (console != nullptr) {
         return _console_vfprintf(console, stream, format, ap);
     } else {
         // If vfprintf is a macro, undefine it, so we can call the real
@@ -2517,7 +2577,7 @@
 
     // If there is an associated Win32 console, write to it specially,
     // otherwise defer to the regular C Runtime, passing it UTF-8.
-    if (console != NULL) {
+    if (console != nullptr) {
         return _console_fwrite(ptr, size, nmemb, stream, console);
     } else {
         // If fwrite is a macro, undefine it, so we can call the real
diff --git a/adb/test_adb.py b/adb/test_adb.py
index cb3e0d8..ddd3ff0 100644
--- a/adb/test_adb.py
+++ b/adb/test_adb.py
@@ -21,9 +21,11 @@
 """
 from __future__ import print_function
 
+import binascii
 import contextlib
 import os
 import random
+import select
 import socket
 import struct
 import subprocess
@@ -33,8 +35,117 @@
 import adb
 
 
-class NonApiTest(unittest.TestCase):
-    """Tests for ADB that aren't a part of the AndroidDevice API."""
+@contextlib.contextmanager
+def fake_adbd(protocol=socket.AF_INET, port=0):
+    """Creates a fake ADB daemon that just replies with a CNXN packet."""
+
+    serversock = socket.socket(protocol, socket.SOCK_STREAM)
+    serversock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+    if protocol == socket.AF_INET:
+        serversock.bind(('127.0.0.1', port))
+    else:
+        serversock.bind(('::1', port))
+    serversock.listen(1)
+
+    # A pipe that is used to signal the thread that it should terminate.
+    readpipe, writepipe = os.pipe()
+
+    def _adb_packet(command, arg0, arg1, data):
+        bin_command = struct.unpack('I', command)[0]
+        buf = struct.pack('IIIIII', bin_command, arg0, arg1, len(data), 0,
+                          bin_command ^ 0xffffffff)
+        buf += data
+        return buf
+
+    def _handle():
+        rlist = [readpipe, serversock]
+        cnxn_sent = {}
+        while True:
+            read_ready, _, _ = select.select(rlist, [], [])
+            for ready in read_ready:
+                if ready == readpipe:
+                    # Closure pipe
+                    os.close(ready)
+                    serversock.shutdown(socket.SHUT_RDWR)
+                    serversock.close()
+                    return
+                elif ready == serversock:
+                    # Server socket
+                    conn, _ = ready.accept()
+                    rlist.append(conn)
+                else:
+                    # Client socket
+                    data = ready.recv(1024)
+                    if not data or data.startswith('OPEN'):
+                        if ready in cnxn_sent:
+                            del cnxn_sent[ready]
+                        ready.shutdown(socket.SHUT_RDWR)
+                        ready.close()
+                        rlist.remove(ready)
+                        continue
+                    if ready in cnxn_sent:
+                        continue
+                    cnxn_sent[ready] = True
+                    ready.sendall(_adb_packet('CNXN', 0x01000001, 1024 * 1024,
+                                              'device::ro.product.name=fakeadb'))
+
+    port = serversock.getsockname()[1]
+    server_thread = threading.Thread(target=_handle)
+    server_thread.start()
+
+    try:
+        yield port
+    finally:
+        os.close(writepipe)
+        server_thread.join()
+
+
+@contextlib.contextmanager
+def adb_connect(unittest, serial):
+    """Context manager for an ADB connection.
+
+    This automatically disconnects when done with the connection.
+    """
+
+    output = subprocess.check_output(['adb', 'connect', serial])
+    unittest.assertEqual(output.strip(), 'connected to {}'.format(serial))
+
+    try:
+        yield
+    finally:
+        # Perform best-effort disconnection. Discard the output.
+        subprocess.Popen(['adb', 'disconnect', serial],
+                         stdout=subprocess.PIPE,
+                         stderr=subprocess.PIPE).communicate()
+
+
+@contextlib.contextmanager
+def adb_server():
+    """Context manager for an ADB server.
+
+    This creates an ADB server and returns the port it's listening on.
+    """
+
+    port = 5038
+    # Kill any existing server on this non-default port.
+    subprocess.check_output(['adb', '-P', str(port), 'kill-server'],
+                            stderr=subprocess.STDOUT)
+    read_pipe, write_pipe = os.pipe()
+    proc = subprocess.Popen(['adb', '-L', 'tcp:localhost:{}'.format(port),
+                             'fork-server', 'server',
+                             '--reply-fd', str(write_pipe)])
+    try:
+        os.close(write_pipe)
+        greeting = os.read(read_pipe, 1024)
+        assert greeting == 'OK\n', repr(greeting)
+        yield port
+    finally:
+        proc.terminate()
+        proc.wait()
+
+
+class CommandlineTest(unittest.TestCase):
+    """Tests for the ADB commandline."""
 
     def test_help(self):
         """Make sure we get _something_ out of help."""
@@ -56,28 +167,37 @@
                 revision_line, r'^Revision [0-9a-f]{12}-android$')
 
     def test_tcpip_error_messages(self):
-        p = subprocess.Popen(['adb', 'tcpip'], stdout=subprocess.PIPE,
-                             stderr=subprocess.STDOUT)
-        out, _ = p.communicate()
-        self.assertEqual(1, p.returncode)
-        self.assertIn('help message', out)
+        """Make sure 'adb tcpip' parsing is sane."""
+        proc = subprocess.Popen(['adb', 'tcpip'], stdout=subprocess.PIPE,
+                                stderr=subprocess.STDOUT)
+        out, _ = proc.communicate()
+        self.assertEqual(1, proc.returncode)
+        self.assertIn('requires an argument', out)
 
-        p = subprocess.Popen(['adb', 'tcpip', 'foo'], stdout=subprocess.PIPE,
-                             stderr=subprocess.STDOUT)
-        out, _ = p.communicate()
-        self.assertEqual(1, p.returncode)
-        self.assertIn('error', out)
+        proc = subprocess.Popen(['adb', 'tcpip', 'foo'], stdout=subprocess.PIPE,
+                                stderr=subprocess.STDOUT)
+        out, _ = proc.communicate()
+        self.assertEqual(1, proc.returncode)
+        self.assertIn('invalid port', out)
 
-    # Helper method that reads a pipe until it is closed, then sets the event.
-    def _read_pipe_and_set_event(self, pipe, event):
-        x = pipe.read()
+
+class ServerTest(unittest.TestCase):
+    """Tests for the ADB server."""
+
+    @staticmethod
+    def _read_pipe_and_set_event(pipe, event):
+        """Reads a pipe until it is closed, then sets the event."""
+        pipe.read()
         event.set()
 
-    # Test that launch_server() does not let the adb server inherit
-    # stdin/stdout/stderr handles which can cause callers of adb.exe to hang.
-    # This test also runs fine on unix even though the impetus is an issue
-    # unique to Windows.
     def test_handle_inheritance(self):
+        """Test that launch_server() does not inherit handles.
+
+        launch_server() should not let the adb server inherit
+        stdin/stdout/stderr handles, which can cause callers of adb.exe to hang.
+        This test also runs fine on unix even though the impetus is an issue
+        unique to Windows.
+        """
         # This test takes 5 seconds to run on Windows: if there is no adb server
         # running on the the port used below, adb kill-server tries to make a
         # TCP connection to a closed port and that takes 1 second on Windows;
@@ -99,29 +219,30 @@
 
         try:
             # Run the adb client and have it start the adb server.
-            p = subprocess.Popen(['adb', '-P', str(port), 'start-server'],
-                                 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
-                                 stderr=subprocess.PIPE)
+            proc = subprocess.Popen(['adb', '-P', str(port), 'start-server'],
+                                    stdin=subprocess.PIPE,
+                                    stdout=subprocess.PIPE,
+                                    stderr=subprocess.PIPE)
 
             # Start threads that set events when stdout/stderr are closed.
             stdout_event = threading.Event()
             stdout_thread = threading.Thread(
-                    target=self._read_pipe_and_set_event,
-                    args=(p.stdout, stdout_event))
+                target=ServerTest._read_pipe_and_set_event,
+                args=(proc.stdout, stdout_event))
             stdout_thread.daemon = True
             stdout_thread.start()
 
             stderr_event = threading.Event()
             stderr_thread = threading.Thread(
-                    target=self._read_pipe_and_set_event,
-                    args=(p.stderr, stderr_event))
+                target=ServerTest._read_pipe_and_set_event,
+                args=(proc.stderr, stderr_event))
             stderr_thread.daemon = True
             stderr_thread.start()
 
             # Wait for the adb client to finish. Once that has occurred, if
             # stdin/stderr/stdout are still open, it must be open in the adb
             # server.
-            p.wait()
+            proc.wait()
 
             # Try to write to stdin which we expect is closed. If it isn't
             # closed, we should get an IOError. If we don't get an IOError,
@@ -129,7 +250,7 @@
             # probably letting the adb server inherit stdin which would be
             # wrong.
             with self.assertRaises(IOError):
-                p.stdin.write('x')
+                proc.stdin.write('x')
 
             # Wait a few seconds for stdout/stderr to be closed (in the success
             # case, this won't wait at all). If there is a timeout, that means
@@ -143,8 +264,12 @@
             subprocess.check_output(['adb', '-P', str(port), 'kill-server'],
                                     stderr=subprocess.STDOUT)
 
-    # Use SO_LINGER to cause TCP RST segment to be sent on socket close.
+
+class EmulatorTest(unittest.TestCase):
+    """Tests for the emulator connection."""
+
     def _reset_socket_on_close(self, sock):
+        """Use SO_LINGER to cause TCP RST segment to be sent on socket close."""
         # The linger structure is two shorts on Windows, but two ints on Unix.
         linger_format = 'hh' if os.name == 'nt' else 'ii'
         l_onoff = 1
@@ -162,19 +287,18 @@
 
         Bug: https://code.google.com/p/android/issues/detail?id=21021
         """
-        port = 12345
-
         with contextlib.closing(
-                socket.socket(socket.AF_INET, socket.SOCK_STREAM)) as listener:
+            socket.socket(socket.AF_INET, socket.SOCK_STREAM)) as listener:
             # Use SO_REUSEADDR so subsequent runs of the test can grab the port
             # even if it is in TIME_WAIT.
             listener.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
-            listener.bind(('127.0.0.1', port))
+            listener.bind(('127.0.0.1', 0))
             listener.listen(4)
+            port = listener.getsockname()[1]
 
             # Now that listening has started, start adb emu kill, telling it to
             # connect to our mock emulator.
-            p = subprocess.Popen(
+            proc = subprocess.Popen(
                 ['adb', '-s', 'emulator-' + str(port), 'emu', 'kill'],
                 stderr=subprocess.STDOUT)
 
@@ -183,12 +307,16 @@
                 # If WSAECONNABORTED (10053) is raised by any socket calls,
                 # then adb probably isn't reading the data that we sent it.
                 conn.sendall('Android Console: type \'help\' for a list ' +
-                                'of commands\r\n')
+                             'of commands\r\n')
                 conn.sendall('OK\r\n')
 
-                with contextlib.closing(conn.makefile()) as f:
-                    self.assertEqual('kill\n', f.readline())
-                    self.assertEqual('quit\n', f.readline())
+                with contextlib.closing(conn.makefile()) as connf:
+                    line = connf.readline()
+                    if line.startswith('auth'):
+                        # Ignore the first auth line.
+                        line = connf.readline()
+                    self.assertEqual('kill\n', line)
+                    self.assertEqual('quit\n', connf.readline())
 
                 conn.sendall('OK: killing emulator, bye bye\r\n')
 
@@ -201,42 +329,117 @@
                 self._reset_socket_on_close(conn)
 
             # Wait for adb to finish, so we can check return code.
-            p.communicate()
+            proc.communicate()
 
             # If this fails, adb probably isn't ignoring WSAECONNRESET when
             # reading the response from the adb emu kill command (on Windows).
-            self.assertEqual(0, p.returncode)
+            self.assertEqual(0, proc.returncode)
+
+    def test_emulator_connect(self):
+        """Ensure that the emulator can connect.
+
+        Bug: http://b/78991667
+        """
+        with adb_server() as server_port:
+            with fake_adbd() as port:
+                serial = 'emulator-{}'.format(port - 1)
+                # Ensure that the emulator is not there.
+                try:
+                    subprocess.check_output(['adb', '-P', str(server_port),
+                                             '-s', serial, 'get-state'],
+                                            stderr=subprocess.STDOUT)
+                    self.fail('Device should not be available')
+                except subprocess.CalledProcessError as err:
+                    self.assertEqual(
+                        err.output.strip(),
+                        'error: device \'{}\' not found'.format(serial))
+
+                # Let the ADB server know that the emulator has started.
+                with contextlib.closing(
+                    socket.socket(socket.AF_INET, socket.SOCK_STREAM)) as sock:
+                    sock.connect(('localhost', server_port))
+                    command = 'host:emulator:{}'.format(port)
+                    sock.sendall('%04x%s' % (len(command), command))
+
+                # Ensure the emulator is there.
+                subprocess.check_call(['adb', '-P', str(server_port),
+                                       '-s', serial, 'wait-for-device'])
+                output = subprocess.check_output(['adb', '-P', str(server_port),
+                                                  '-s', serial, 'get-state'])
+                self.assertEqual(output.strip(), 'device')
+
+
+class ConnectionTest(unittest.TestCase):
+    """Tests for adb connect."""
 
     def test_connect_ipv4_ipv6(self):
         """Ensure that `adb connect localhost:1234` will try both IPv4 and IPv6.
 
         Bug: http://b/30313466
         """
-        ipv4 = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
-        ipv4.bind(('127.0.0.1', 0))
-        ipv4.listen(1)
+        for protocol in (socket.AF_INET, socket.AF_INET6):
+            try:
+                with fake_adbd(protocol=protocol) as port:
+                    serial = 'localhost:{}'.format(port)
+                    with adb_connect(self, serial):
+                        pass
+            except socket.error:
+                print("IPv6 not available, skipping")
+                continue
 
-        ipv6 = socket.socket(socket.AF_INET6, socket.SOCK_STREAM)
-        ipv6.bind(('::1', ipv4.getsockname()[1] + 1))
-        ipv6.listen(1)
+    def test_already_connected(self):
+        """Ensure that an already-connected device stays connected."""
 
-        for s in (ipv4, ipv6):
-            port = s.getsockname()[1]
-            output = subprocess.check_output(
-                ['adb', 'connect', 'localhost:{}'.format(port)])
+        with fake_adbd() as port:
+            serial = 'localhost:{}'.format(port)
+            with adb_connect(self, serial):
+                # b/31250450: this always returns 0 but probably shouldn't.
+                output = subprocess.check_output(['adb', 'connect', serial])
+                self.assertEqual(
+                    output.strip(), 'already connected to {}'.format(serial))
 
-            self.assertEqual(
-                output.strip(), 'connected to localhost:{}'.format(port))
-            s.close()
+    def test_reconnect(self):
+        """Ensure that a disconnected device reconnects."""
+
+        with fake_adbd() as port:
+            serial = 'localhost:{}'.format(port)
+            with adb_connect(self, serial):
+                output = subprocess.check_output(['adb', '-s', serial,
+                                                  'get-state'])
+                self.assertEqual(output.strip(), 'device')
+
+                # This will fail.
+                proc = subprocess.Popen(['adb', '-s', serial, 'shell', 'true'],
+                                        stdout=subprocess.PIPE,
+                                        stderr=subprocess.STDOUT)
+                output, _ = proc.communicate()
+                self.assertEqual(output.strip(), 'error: closed')
+
+                subprocess.check_call(['adb', '-s', serial, 'wait-for-device'])
+
+                output = subprocess.check_output(['adb', '-s', serial,
+                                                  'get-state'])
+                self.assertEqual(output.strip(), 'device')
+
+                # Once we explicitly kick a device, it won't attempt to
+                # reconnect.
+                output = subprocess.check_output(['adb', 'disconnect', serial])
+                self.assertEqual(
+                    output.strip(), 'disconnected {}'.format(serial))
+                try:
+                    subprocess.check_output(['adb', '-s', serial, 'get-state'],
+                                            stderr=subprocess.STDOUT)
+                    self.fail('Device should not be available')
+                except subprocess.CalledProcessError as err:
+                    self.assertEqual(
+                        err.output.strip(),
+                        'error: device \'{}\' not found'.format(serial))
 
 
 def main():
+    """Main entrypoint."""
     random.seed(0)
-    if len(adb.get_devices()) > 0:
-        suite = unittest.TestLoader().loadTestsFromName(__name__)
-        unittest.TextTestRunner(verbosity=3).run(suite)
-    else:
-        print('Test suite must be run with attached devices')
+    unittest.main(verbosity=3)
 
 
 if __name__ == '__main__':
diff --git a/adb/test_device.py b/adb/test_device.py
index 9e1a2ec..4abe7a7 100644
--- a/adb/test_device.py
+++ b/adb/test_device.py
@@ -31,14 +31,12 @@
 import subprocess
 import sys
 import tempfile
+import threading
 import time
 import unittest
 
-import mock
-
 import adb
 
-
 def requires_root(func):
     def wrapper(self, *args):
         if self.device.get_prop('ro.debuggable') != '1':
@@ -76,59 +74,6 @@
     return wrapper
 
 
-class GetDeviceTest(unittest.TestCase):
-    def setUp(self):
-        self.android_serial = os.getenv('ANDROID_SERIAL')
-        if 'ANDROID_SERIAL' in os.environ:
-            del os.environ['ANDROID_SERIAL']
-
-    def tearDown(self):
-        if self.android_serial is not None:
-            os.environ['ANDROID_SERIAL'] = self.android_serial
-        else:
-            if 'ANDROID_SERIAL' in os.environ:
-                del os.environ['ANDROID_SERIAL']
-
-    @mock.patch('adb.device.get_devices')
-    def test_explicit(self, mock_get_devices):
-        mock_get_devices.return_value = ['foo', 'bar']
-        device = adb.get_device('foo')
-        self.assertEqual(device.serial, 'foo')
-
-    @mock.patch('adb.device.get_devices')
-    def test_from_env(self, mock_get_devices):
-        mock_get_devices.return_value = ['foo', 'bar']
-        os.environ['ANDROID_SERIAL'] = 'foo'
-        device = adb.get_device()
-        self.assertEqual(device.serial, 'foo')
-
-    @mock.patch('adb.device.get_devices')
-    def test_arg_beats_env(self, mock_get_devices):
-        mock_get_devices.return_value = ['foo', 'bar']
-        os.environ['ANDROID_SERIAL'] = 'bar'
-        device = adb.get_device('foo')
-        self.assertEqual(device.serial, 'foo')
-
-    @mock.patch('adb.device.get_devices')
-    def test_no_such_device(self, mock_get_devices):
-        mock_get_devices.return_value = ['foo', 'bar']
-        self.assertRaises(adb.DeviceNotFoundError, adb.get_device, ['baz'])
-
-        os.environ['ANDROID_SERIAL'] = 'baz'
-        self.assertRaises(adb.DeviceNotFoundError, adb.get_device)
-
-    @mock.patch('adb.device.get_devices')
-    def test_unique_device(self, mock_get_devices):
-        mock_get_devices.return_value = ['foo']
-        device = adb.get_device()
-        self.assertEqual(device.serial, 'foo')
-
-    @mock.patch('adb.device.get_devices')
-    def test_no_unique_device(self, mock_get_devices):
-        mock_get_devices.return_value = ['foo', 'bar']
-        self.assertRaises(adb.NoUniqueDeviceError, adb.get_device)
-
-
 class DeviceTest(unittest.TestCase):
     def setUp(self):
         self.device = adb.get_device()
@@ -243,8 +188,6 @@
         finally:
             self.device.reverse_remove_all()
 
-    # Note: If you run this test when adb connect'd to a physical device over
-    # TCP, it will fail in adb reverse due to https://code.google.com/p/android/issues/detail?id=189821
     def test_forward_reverse_echo(self):
         """Send data through adb forward and read it back via adb reverse"""
         forward_port = 12345
@@ -549,6 +492,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):
@@ -784,7 +750,6 @@
             if host_dir is not None:
                 shutil.rmtree(host_dir)
 
-    @unittest.expectedFailure # b/25566053
     def test_push_empty(self):
         """Push a directory containing an empty directory to the device."""
         self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
@@ -900,6 +865,21 @@
             self.assertTrue('Permission denied' in output or
                             'Read-only file system' in output)
 
+    @requires_non_root
+    def test_push_directory_creation(self):
+        """Regression test for directory creation.
+
+        Bug: http://b/110953234
+        """
+        with tempfile.NamedTemporaryFile() as tmp_file:
+            tmp_file.write('\0' * 1024 * 1024)
+            tmp_file.flush()
+            remote_path = self.DEVICE_TEMP_DIR + '/test_push_directory_creation'
+            self.device.shell(['rm', '-rf', remote_path])
+
+            remote_path += '/filename'
+            self.device.push(local=tmp_file.name, remote=remote_path)
+
     def _test_pull(self, remote_file, checksum):
         tmp_write = tempfile.NamedTemporaryFile(mode='wb', delete=False)
         tmp_write.close()
@@ -1219,7 +1199,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)
@@ -1237,7 +1217,7 @@
                 return m.group(2)
         return None
 
-    def test_killed_when_pushing_a_large_file(self):
+    def disabled_test_killed_when_pushing_a_large_file(self):
         """
            While running adb push with a large file, kill adb server.
            Occasionally the device becomes offline. Because the device is still
@@ -1268,7 +1248,7 @@
         # 4. The device should be online
         self.assertEqual(self._get_device_state(serialno), 'device')
 
-    def test_killed_when_pulling_a_large_file(self):
+    def disabled_test_killed_when_pulling_a_large_file(self):
         """
            While running adb pull with a large file, kill adb server.
            Occasionally the device can't be connected. Because the device is trying to
@@ -1307,16 +1287,18 @@
         """
         # The values that trigger things are 507 (512 - 5 bytes from shell protocol) + 1024*n
         # Probe some surrounding values as well, for the hell of it.
-        for length in [506, 507, 508, 1018, 1019, 1020, 1530, 1531, 1532]:
-            cmd = ['dd', 'if=/dev/zero', 'bs={}'.format(length), 'count=1', '2>/dev/null;'
-                   'echo', 'foo']
-            rc, stdout, _ = self.device.shell_nocheck(cmd)
+        for base in [512] + range(1024, 1024 * 16, 1024):
+            for offset in [-6, -5, -4]:
+                length = base + offset
+                cmd = ['dd', 'if=/dev/zero', 'bs={}'.format(length), 'count=1', '2>/dev/null;'
+                       'echo', 'foo']
+                rc, stdout, _ = self.device.shell_nocheck(cmd)
 
-            self.assertEqual(0, rc)
+                self.assertEqual(0, rc)
 
-            # Output should be '\0' * length, followed by "foo\n"
-            self.assertEqual(length, len(stdout) - 4)
-            self.assertEqual(stdout, "\0" * length + "foo\n")
+                # Output should be '\0' * length, followed by "foo\n"
+                self.assertEqual(length, len(stdout) - 4)
+                self.assertEqual(stdout, "\0" * length + "foo\n")
 
 
 def main():
diff --git a/adb/transport.cpp b/adb/transport.cpp
index 2bbbefd..9222008 100644
--- a/adb/transport.cpp
+++ b/adb/transport.cpp
@@ -17,39 +17,50 @@
 #define TRACE_TAG TRANSPORT
 
 #include "sysdeps.h"
+#include "sysdeps/memory.h"
+
 #include "transport.h"
 
 #include <ctype.h>
 #include <errno.h>
+#include <inttypes.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
 
 #include <algorithm>
+#include <deque>
 #include <list>
 #include <mutex>
+#include <queue>
 #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"
+#include "sysdeps/chrono.h"
 
-static void transport_unref(atransport *t);
+static void register_transport(atransport* transport);
+static void remove_transport(atransport* transport);
+static void transport_unref(atransport* transport);
 
+// TODO: unordered_map<TransportId, atransport*>
 static auto& transport_list = *new std::list<atransport*>();
 static auto& pending_list = *new std::list<atransport*>();
 
-static std::mutex& transport_lock = *new std::mutex();
+static auto& transport_lock = *new std::recursive_mutex();
 
 const char* const kFeatureShell2 = "shell_v2";
 const char* const kFeatureCmd = "cmd";
@@ -57,6 +68,318 @@
 const char* const kFeatureLibusb = "libusb";
 const char* const kFeaturePushSync = "push_sync";
 
+namespace {
+
+// A class that helps the Clang Thread Safety Analysis deal with
+// std::unique_lock. Given that std::unique_lock is movable, and the analysis
+// can not currently perform alias analysis, it is not annotated. In order to
+// assert that the mutex is held, a ScopedAssumeLocked can be created just after
+// the std::unique_lock.
+class SCOPED_CAPABILITY ScopedAssumeLocked {
+  public:
+    ScopedAssumeLocked(std::mutex& mutex) ACQUIRE(mutex) {}
+    ~ScopedAssumeLocked() RELEASE() {}
+};
+
+#if ADB_HOST
+// Tracks and handles atransport*s that are attempting reconnection.
+class ReconnectHandler {
+  public:
+    ReconnectHandler() = default;
+    ~ReconnectHandler() = default;
+
+    // Starts the ReconnectHandler thread.
+    void Start();
+
+    // Requests the ReconnectHandler thread to stop.
+    void Stop();
+
+    // Adds the atransport* to the queue of reconnect attempts.
+    void TrackTransport(atransport* transport);
+
+  private:
+    // The main thread loop.
+    void Run();
+
+    // Tracks a reconnection attempt.
+    struct ReconnectAttempt {
+        atransport* transport;
+        std::chrono::steady_clock::time_point reconnect_time;
+        size_t attempts_left;
+
+        bool operator<(const ReconnectAttempt& rhs) const {
+            // std::priority_queue returns the largest element first, so we want attempts that have
+            // less time remaining (i.e. smaller time_points) to compare greater.
+            return reconnect_time > rhs.reconnect_time;
+        }
+    };
+
+    // Only retry for up to one minute.
+    static constexpr const std::chrono::seconds kDefaultTimeout = 10s;
+    static constexpr const size_t kMaxAttempts = 6;
+
+    // Protects all members.
+    std::mutex reconnect_mutex_;
+    bool running_ GUARDED_BY(reconnect_mutex_) = true;
+    std::thread handler_thread_;
+    std::condition_variable reconnect_cv_;
+    std::priority_queue<ReconnectAttempt> reconnect_queue_ GUARDED_BY(reconnect_mutex_);
+
+    DISALLOW_COPY_AND_ASSIGN(ReconnectHandler);
+};
+
+void ReconnectHandler::Start() {
+    check_main_thread();
+    handler_thread_ = std::thread(&ReconnectHandler::Run, this);
+}
+
+void ReconnectHandler::Stop() {
+    check_main_thread();
+    {
+        std::lock_guard<std::mutex> lock(reconnect_mutex_);
+        running_ = false;
+    }
+    reconnect_cv_.notify_one();
+    handler_thread_.join();
+
+    // Drain the queue to free all resources.
+    std::lock_guard<std::mutex> lock(reconnect_mutex_);
+    while (!reconnect_queue_.empty()) {
+        ReconnectAttempt attempt = reconnect_queue_.top();
+        reconnect_queue_.pop();
+        remove_transport(attempt.transport);
+    }
+}
+
+void ReconnectHandler::TrackTransport(atransport* transport) {
+    check_main_thread();
+    {
+        std::lock_guard<std::mutex> lock(reconnect_mutex_);
+        if (!running_) return;
+        // Arbitrary sleep to give adbd time to get ready, if we disconnected because it exited.
+        auto reconnect_time = std::chrono::steady_clock::now() + 250ms;
+        reconnect_queue_.emplace(
+                ReconnectAttempt{transport, reconnect_time, ReconnectHandler::kMaxAttempts});
+    }
+    reconnect_cv_.notify_one();
+}
+
+void ReconnectHandler::Run() {
+    while (true) {
+        ReconnectAttempt attempt;
+        {
+            std::unique_lock<std::mutex> lock(reconnect_mutex_);
+            ScopedAssumeLocked assume_lock(reconnect_mutex_);
+
+            if (!reconnect_queue_.empty()) {
+                // FIXME: libstdc++ (used on Windows) implements condition_variable with
+                //        system_clock as its clock, so we're probably hosed if the clock changes,
+                //        even if we use steady_clock throughout. This problem goes away once we
+                //        switch to libc++.
+                reconnect_cv_.wait_until(lock, reconnect_queue_.top().reconnect_time);
+            } else {
+                reconnect_cv_.wait(lock);
+            }
+
+            if (!running_) return;
+            if (reconnect_queue_.empty()) continue;
+
+            // Go back to sleep in case |reconnect_cv_| woke up spuriously and we still
+            // have more time to wait for the current attempt.
+            auto now = std::chrono::steady_clock::now();
+            if (reconnect_queue_.top().reconnect_time > now) {
+                continue;
+            }
+
+            attempt = reconnect_queue_.top();
+            reconnect_queue_.pop();
+            if (attempt.transport->kicked()) {
+                D("transport %s was kicked. giving up on it.", attempt.transport->serial.c_str());
+                remove_transport(attempt.transport);
+                continue;
+            }
+        }
+        D("attempting to reconnect %s", attempt.transport->serial.c_str());
+
+        if (!attempt.transport->Reconnect()) {
+            D("attempting to reconnect %s failed.", attempt.transport->serial.c_str());
+            if (attempt.attempts_left == 0) {
+                D("transport %s exceeded the number of retry attempts. giving up on it.",
+                  attempt.transport->serial.c_str());
+                remove_transport(attempt.transport);
+                continue;
+            }
+
+            std::lock_guard<std::mutex> lock(reconnect_mutex_);
+            reconnect_queue_.emplace(ReconnectAttempt{
+                    attempt.transport,
+                    std::chrono::steady_clock::now() + ReconnectHandler::kDefaultTimeout,
+                    attempt.attempts_left - 1});
+            continue;
+        }
+
+        D("reconnection to %s succeeded.", attempt.transport->serial.c_str());
+        register_transport(attempt.transport);
+    }
+}
+
+static auto& reconnect_handler = *new ReconnectHandler();
+
+#endif
+
+}  // namespace
+
+TransportId NextTransportId() {
+    static std::atomic<TransportId> next(1);
+    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() {
+    std::lock_guard<std::mutex> lock(mutex_);
+    if (started_) {
+        LOG(FATAL) << "BlockingConnectionAdapter(" << this->transport_name_
+                   << "): started multiple times";
+    }
+
+    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_);
+            ScopedAssumeLocked assume_locked(mutex_);
+            cv_.wait(lock, [this]() REQUIRES(mutex_) {
+                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"); });
+    });
+
+    started_ = true;
+}
+
+void BlockingConnectionAdapter::Stop() {
+    {
+        std::lock_guard<std::mutex> lock(mutex_);
+        if (!started_) {
+            LOG(INFO) << "BlockingConnectionAdapter(" << this->transport_name_ << "): not started";
+            return;
+        }
+
+        if (stopped_) {
+            LOG(INFO) << "BlockingConnectionAdapter(" << this->transport_name_
+                      << "): already stopped";
+            return;
+        }
+
+        stopped_ = true;
+    }
+
+    LOG(INFO) << "BlockingConnectionAdapter(" << this->transport_name_ << "): stopping";
+
+    this->underlying_->Close();
+    this->cv_.notify_one();
+
+    // Move the threads out into locals with the lock taken, and then unlock to let them exit.
+    std::thread read_thread;
+    std::thread write_thread;
+
+    {
+        std::lock_guard<std::mutex> lock(mutex_);
+        read_thread = std::move(read_thread_);
+        write_thread = std::move(write_thread_);
+    }
+
+    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::lock_guard<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 > MAX_PAYLOAD) {
+        D("remote local: read overflow (data length = %" PRIu32 ")", packet->msg.data_length);
+        return false;
+    }
+
+    packet->payload.resize(packet->msg.data_length);
+
+    if (!ReadFdExactly(fd_.get(), &packet->payload[0], packet->payload.size())) {
+        D("remote local: terminated (data)");
+        return false;
+    }
+
+    return true;
+}
+
+bool FdConnection::Write(apacket* packet) {
+    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;
+}
+
+void FdConnection::Close() {
+    adb_shutdown(fd_.get());
+    fd_.reset();
+}
+
 static std::string dump_packet(const char* name, const char* func, apacket* p) {
     unsigned command = p->msg.command;
     int len = p->msg.data_length;
@@ -89,218 +412,37 @@
 
     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;
-    p->msg.data_check = calculate_apacket_checksum(p);
+    // compute a checksum for connection/auth packets for compatibility reasons
+    if (t->get_protocol_version() >= A_VERSION_SKIP_CHECKSUM) {
+        p->msg.data_check = 0;
+    } else {
+        p->msg.data_check = calculate_apacket_checksum(p);
+    }
 
-    print_packet("send", p);
+    VLOG(TRANSPORT) << dump_packet(t->serial.c_str(), "to remote", p);
 
-    if (t == NULL) {
+    if (t == nullptr) {
         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.c_str());
+        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->read_from_remote(p, t) != 0) {
-                D("%s: remote read failed for transport", t->serial);
-                put_apacket(p);
-                break;
-            }
-#if ADB_HOST
-            if (p->msg.command == 0) {
-                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::mutex> lock(transport_lock);
+    std::lock_guard<std::recursive_mutex> lock(transport_lock);
     // As kick_transport() can be called from threads without guarantee that t is valid,
     // check if the transport is in transport_list first.
+    //
+    // TODO(jmgao): WTF? Is this actually true?
     if (std::find(transport_list.begin(), transport_list.end(), t) != transport_list.end()) {
         t->Kick();
     }
@@ -308,7 +450,7 @@
 
 static int transport_registration_send = -1;
 static int transport_registration_recv = -1;
-static fdevent transport_registration_fde;
+static fdevent* transport_registration_fde;
 
 #if ADB_HOST
 
@@ -319,8 +461,9 @@
  */
 struct device_tracker {
     asocket socket;
-    int update_needed;
-    device_tracker* next;
+    bool update_needed = false;
+    bool long_output = false;
+    device_tracker* next = nullptr;
 };
 
 /* linked list of all device trackers */
@@ -330,7 +473,7 @@
     device_tracker** pnode = &device_tracker_list;
     device_tracker* node = *pnode;
 
-    std::lock_guard<std::mutex> lock(transport_lock);
+    std::lock_guard<std::recursive_mutex> lock(transport_lock);
     while (node) {
         if (node == tracker) {
             *pnode = node->next;
@@ -347,28 +490,29 @@
 
     D("device tracker %p removed", tracker);
     if (peer) {
-        peer->peer = NULL;
+        peer->peer = nullptr;
         peer->close(peer);
     }
     device_tracker_remove(tracker);
-    free(tracker);
+    delete tracker;
 }
 
-static int device_tracker_enqueue(asocket* socket, apacket* p) {
+static int device_tracker_enqueue(asocket* socket, apacket::payload_type) {
     /* you can't read from a device tracker, close immediately */
-    put_apacket(p);
     device_tracker_close(socket);
     return -1;
 }
 
 static int device_tracker_send(device_tracker* tracker, const std::string& string) {
-    apacket* p = get_apacket();
     asocket* peer = tracker->socket.peer;
 
-    snprintf(reinterpret_cast<char*>(p->data), 5, "%04x", static_cast<int>(string.size()));
-    memcpy(&p->data[4], string.data(), string.size());
-    p->len = 4 + string.size();
-    return peer->enqueue(peer, p);
+    apacket::payload_type data;
+    data.resize(4 + string.size());
+    char buf[5];
+    snprintf(buf, sizeof(buf), "%04x", static_cast<int>(string.size()));
+    memcpy(&data[0], buf, 4);
+    memcpy(&data[4], string.data(), string.size());
+    return peer->enqueue(peer, std::move(data));
 }
 
 static void device_tracker_ready(asocket* socket) {
@@ -376,16 +520,16 @@
 
     // We want to send the device list when the tracker connects
     // for the first time, even if no update occurred.
-    if (tracker->update_needed > 0) {
-        tracker->update_needed = 0;
+    if (tracker->update_needed) {
+        tracker->update_needed = false;
 
-        std::string transports = list_transports(false);
+        std::string transports = list_transports(tracker->long_output);
         device_tracker_send(tracker, transports);
     }
 }
 
-asocket* create_device_tracker(void) {
-    device_tracker* tracker = reinterpret_cast<device_tracker*>(calloc(1, sizeof(*tracker)));
+asocket* create_device_tracker(bool long_output) {
+    device_tracker* tracker = new device_tracker();
     if (tracker == nullptr) fatal("cannot allocate device tracker");
 
     D("device tracker %p created", tracker);
@@ -393,7 +537,8 @@
     tracker->socket.enqueue = device_tracker_enqueue;
     tracker->socket.ready = device_tracker_ready;
     tracker->socket.close = device_tracker_close;
-    tracker->update_needed = 1;
+    tracker->update_needed = true;
+    tracker->long_output = long_output;
 
     tracker->next = device_tracker_list;
     device_tracker_list = tracker;
@@ -403,7 +548,7 @@
 
 // Check if all of the USB transports are connected.
 bool iterate_transports(std::function<bool(const atransport*)> fn) {
-    std::lock_guard<std::mutex> lock(transport_lock);
+    std::lock_guard<std::recursive_mutex> lock(transport_lock);
     for (const auto& t : transport_list) {
         if (!fn(t)) {
             return false;
@@ -482,9 +627,8 @@
     return 0;
 }
 
-static void transport_registration_func(int _fd, unsigned ev, void* data) {
+static void transport_registration_func(int _fd, unsigned ev, void*) {
     tmsg m;
-    int s[2];
     atransport* t;
 
     if (!(ev & FDE_READ)) {
@@ -498,25 +642,13 @@
     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.c_str());
 
         {
-            std::lock_guard<std::mutex> lock(transport_lock);
+            std::lock_guard<std::recursive_mutex> lock(transport_lock);
             transport_list.remove(t);
         }
 
-        if (t->product) free(t->product);
-        if (t->serial) free(t->serial);
-        if (t->model) free(t->model);
-        if (t->device) free(t->device);
-        if (t->devpath) free(t->devpath);
-
         delete t;
 
         update_transports();
@@ -525,35 +657,55 @@
 
     /* don't create transport threads for inaccessible devices */
     if (t->GetConnectionState() != kCsNoPerm) {
-        /* initial references are the two threads */
-        t->ref_count = 2;
+        // The connection gets a reference to the atransport. It will release it
+        // upon a read/write error.
+        t->ref_count++;
+        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.c_str());
+                return false;
+            }
 
-        if (adb_socketpair(s)) {
-            fatal_errno("cannot open transport socketpair");
-        }
+            VLOG(TRANSPORT) << dump_packet(t->serial.c_str(), "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.c_str(), 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
     }
 
     {
-        std::lock_guard<std::mutex> lock(transport_lock);
-        pending_list.remove(t);
-        transport_list.push_front(t);
+        std::lock_guard<std::recursive_mutex> lock(transport_lock);
+        auto it = std::find(pending_list.begin(), pending_list.end(), t);
+        if (it != pending_list.end()) {
+            pending_list.remove(t);
+            transport_list.push_front(t);
+        }
     }
 
     update_transports();
 }
 
+#if ADB_HOST
+void init_reconnect_handler(void) {
+    reconnect_handler.Start();
+}
+#endif
+
 void init_transport_registration(void) {
     int s[2];
 
@@ -565,15 +717,17 @@
     transport_registration_send = s[0];
     transport_registration_recv = s[1];
 
-    fdevent_install(&transport_registration_fde, transport_registration_recv,
-                    transport_registration_func, 0);
-
-    fdevent_set(&transport_registration_fde, FDE_READ);
+    transport_registration_fde =
+        fdevent_create(transport_registration_recv, transport_registration_func, nullptr);
+    fdevent_set(transport_registration_fde, FDE_READ);
 }
 
 void kick_all_transports() {
+#if ADB_HOST
+    reconnect_handler.Stop();
+#endif
     // To avoid only writing part of a packet to a transport after exit, kick all transports.
-    std::lock_guard<std::mutex> lock(transport_lock);
+    std::lock_guard<std::recursive_mutex> lock(transport_lock);
     for (auto t : transport_list) {
         t->Kick();
     }
@@ -584,7 +738,7 @@
     tmsg m;
     m.transport = transport;
     m.action = 1;
-    D("transport: %s registered", transport->serial);
+    D("transport: %s registered", transport->serial.c_str());
     if (transport_write_action(transport_registration_send, &m)) {
         fatal_errno("cannot write transport registration socket\n");
     }
@@ -594,55 +748,71 @@
     tmsg m;
     m.transport = transport;
     m.action = 0;
-    D("transport: %s removed", transport->serial);
+    D("transport: %s removed", transport->serial.c_str());
     if (transport_write_action(transport_registration_send, &m)) {
         fatal_errno("cannot write transport registration socket\n");
     }
 }
 
 static void transport_unref(atransport* t) {
+    check_main_thread();
     CHECK(t != nullptr);
 
-    size_t old_refcount = t->ref_count--;
-    CHECK_GT(old_refcount, 0u);
-
-    if (old_refcount == 1u) {
-        D("transport: %s unref (kicking and closing)", t->serial);
-        t->close(t);
+    std::lock_guard<std::recursive_mutex> lock(transport_lock);
+    CHECK_GT(t->ref_count, 0u);
+    t->ref_count--;
+    if (t->ref_count == 0) {
+        t->connection()->Stop();
+#if ADB_HOST
+        if (t->IsTcpDevice() && !t->kicked()) {
+            D("transport: %s unref (attempting reconnection)", t->serial.c_str());
+            reconnect_handler.TrackTransport(t);
+        } else {
+            D("transport: %s unref (kicking and closing)", t->serial.c_str());
+            remove_transport(t);
+        }
+#else
+        D("transport: %s unref (kicking and closing)", t->serial.c_str());
         remove_transport(t);
+#endif
+
     } else {
-        D("transport: %s unref (count=%zu)", t->serial, old_refcount - 1);
+        D("transport: %s unref (count=%zu)", t->serial.c_str(), t->ref_count);
     }
 }
 
-static int qual_match(const char* to_test, const char* prefix, const char* qual,
+static int qual_match(const std::string& to_test, const char* prefix, const std::string& qual,
                       bool sanitize_qual) {
-    if (!to_test || !*to_test) /* Return true if both the qual and to_test are null strings. */
-        return !qual || !*qual;
+    if (to_test.empty()) /* Return true if both the qual and to_test are empty strings. */
+        return qual.empty();
 
-    if (!qual) return 0;
+    if (qual.empty()) return 0;
 
+    const char* ptr = to_test.c_str();
     if (prefix) {
         while (*prefix) {
-            if (*prefix++ != *to_test++) return 0;
+            if (*prefix++ != *ptr++) return 0;
         }
     }
 
-    while (*qual) {
-        char ch = *qual++;
+    for (char ch : qual) {
         if (sanitize_qual && !isalnum(ch)) ch = '_';
-        if (ch != *to_test++) return 0;
+        if (ch != *ptr++) return 0;
     }
 
-    /* Everything matched so far.  Return true if *to_test is a NUL. */
-    return !*to_test;
+    /* Everything matched so far.  Return true if *ptr is a NUL. */
+    return !*ptr;
 }
 
-atransport* acquire_one_transport(TransportType type, const char* serial, bool* is_ambiguous,
-                                  std::string* error_out, bool accept_any_state) {
+atransport* acquire_one_transport(TransportType type, const char* serial, TransportId transport_id,
+                                  bool* is_ambiguous, std::string* error_out,
+                                  bool accept_any_state) {
     atransport* result = nullptr;
 
-    if (serial) {
+    if (transport_id != 0) {
+        *error_out =
+            android::base::StringPrintf("no device with transport id '%" PRIu64 "'", transport_id);
+    } else if (serial) {
         *error_out = android::base::StringPrintf("device '%s' not found", serial);
     } else if (type == kTransportLocal) {
         *error_out = "no emulators found";
@@ -652,17 +822,19 @@
         *error_out = "no devices found";
     }
 
-    std::unique_lock<std::mutex> lock(transport_lock);
+    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;
         }
 
-        // Check for matching serial number.
-        if (serial) {
+        if (transport_id) {
+            if (t->id == transport_id) {
+                result = t;
+                break;
+            }
+        } else if (serial) {
             if (t->MatchesTarget(serial)) {
                 if (result) {
                     *error_out = "more than one device";
@@ -702,22 +874,41 @@
     }
     lock.unlock();
 
-    // Don't return unauthorized devices; the caller can't do anything with them.
-    if (result && result->GetConnectionState() == kCsUnauthorized && !accept_any_state) {
-        *error_out = "device unauthorized.\n";
-        char* ADB_VENDOR_KEYS = getenv("ADB_VENDOR_KEYS");
-        *error_out += "This adb server's $ADB_VENDOR_KEYS is ";
-        *error_out += ADB_VENDOR_KEYS ? ADB_VENDOR_KEYS : "not set";
-        *error_out += "\n";
-        *error_out += "Try 'adb kill-server' if that seems wrong.\n";
-        *error_out += "Otherwise check for a confirmation dialog on your device.";
-        result = nullptr;
-    }
+    if (result && !accept_any_state) {
+        // The caller requires an active transport.
+        // Make sure that we're actually connected.
+        ConnectionState state = result->GetConnectionState();
+        switch (state) {
+            case kCsConnecting:
+                *error_out = "device still connecting";
+                result = nullptr;
+                break;
 
-    // Don't return offline devices; the caller can't do anything with them.
-    if (result && result->GetConnectionState() == kCsOffline && !accept_any_state) {
-        *error_out = "device offline";
-        result = nullptr;
+            case kCsAuthorizing:
+                *error_out = "device still authorizing";
+                result = nullptr;
+                break;
+
+            case kCsUnauthorized: {
+                *error_out = "device unauthorized.\n";
+                char* ADB_VENDOR_KEYS = getenv("ADB_VENDOR_KEYS");
+                *error_out += "This adb server's $ADB_VENDOR_KEYS is ";
+                *error_out += ADB_VENDOR_KEYS ? ADB_VENDOR_KEYS : "not set";
+                *error_out += "\n";
+                *error_out += "Try 'adb kill-server' if that seems wrong.\n";
+                *error_out += "Otherwise check for a confirmation dialog on your device.";
+                result = nullptr;
+                break;
+            }
+
+            case kCsOffline:
+                *error_out = "device offline";
+                result = nullptr;
+                break;
+
+            default:
+                break;
+        }
     }
 
     if (result) {
@@ -727,23 +918,38 @@
     return result;
 }
 
+bool ConnectionWaitable::WaitForConnection(std::chrono::milliseconds timeout) {
+    std::unique_lock<std::mutex> lock(mutex_);
+    ScopedAssumeLocked assume_locked(mutex_);
+    return cv_.wait_for(lock, timeout, [&]() REQUIRES(mutex_) {
+        return connection_established_ready_;
+    }) && connection_established_;
+}
+
+void ConnectionWaitable::SetConnectionEstablished(bool success) {
+    {
+        std::lock_guard<std::mutex> lock(mutex_);
+        if (connection_established_ready_) return;
+        connection_established_ready_ = true;
+        connection_established_ = success;
+        D("connection established with %d", success);
+    }
+    cv_.notify_one();
+}
+
+atransport::~atransport() {
+    // If the connection callback had not been run before, run it now.
+    SetConnectionEstablished(false);
+}
+
 int atransport::Write(apacket* p) {
-#if ADB_HOST
-    std::lock_guard<std::mutex> lock(write_msg_lock_);
-#endif
-    return write_func_(p, this);
+    return this->connection()->Write(std::unique_ptr<apacket>(p)) ? 0 : -1;
 }
 
 void atransport::Kick() {
-    if (!kicked_) {
-        kicked_ = true;
-        CHECK(kick_func_ != nullptr);
-#if ADB_HOST
-        // On host, adb server should avoid writing part of a packet, so don't
-        // kick a transport whiling writing a packet.
-        std::lock_guard<std::mutex> lock(write_msg_lock_);
-#endif
-        kick_func_(this);
+    if (!kicked_.exchange(true)) {
+        D("kicking transport %p %s", this, this->serial.c_str());
+        this->connection()->Stop();
     }
 }
 
@@ -756,7 +962,12 @@
     connection_state_ = state;
 }
 
-const std::string atransport::connection_state_name() const {
+void atransport::SetConnection(std::unique_ptr<Connection> connection) {
+    std::lock_guard<std::mutex> lock(mutex_);
+    connection_ = std::shared_ptr<Connection>(std::move(connection));
+}
+
+std::string atransport::connection_state_name() const {
     ConnectionState state = GetConnectionState();
     switch (state) {
         case kCsOffline:
@@ -775,6 +986,10 @@
             return "sideload";
         case kCsUnauthorized:
             return "unauthorized";
+        case kCsAuthorizing:
+            return "authorizing";
+        case kCsConnecting:
+            return "connecting";
         default:
             return "unknown";
     }
@@ -852,7 +1067,7 @@
 }
 
 bool atransport::MatchesTarget(const std::string& target) const {
-    if (serial) {
+    if (!serial.empty()) {
         if (target == serial) {
             return true;
         } else if (type == kTransportLocal) {
@@ -881,31 +1096,43 @@
         }
     }
 
-    return (devpath && target == devpath) ||
-           qual_match(target.c_str(), "product:", product, false) ||
-           qual_match(target.c_str(), "model:", model, true) ||
-           qual_match(target.c_str(), "device:", device, false);
+    return (target == devpath) || qual_match(target, "product:", product, false) ||
+           qual_match(target, "model:", model, true) ||
+           qual_match(target, "device:", device, false);
+}
+
+void atransport::SetConnectionEstablished(bool success) {
+    connection_waitable_->SetConnectionEstablished(success);
+}
+
+bool atransport::Reconnect() {
+    return reconnect_(this);
 }
 
 #if ADB_HOST
 
-static void append_transport_info(std::string* result, const char* key, const char* value,
-                                  bool sanitize) {
-    if (value == nullptr || *value == '\0') {
+// We use newline as our delimiter, make sure to never output it.
+static std::string sanitize(std::string str, bool alphanumeric) {
+    auto pred = alphanumeric ? [](const char c) { return !isalnum(c); }
+                             : [](const char c) { return c == '\n'; };
+    std::replace_if(str.begin(), str.end(), pred, '_');
+    return str;
+}
+
+static void append_transport_info(std::string* result, const char* key, const std::string& value,
+                                  bool alphanumeric) {
+    if (value.empty()) {
         return;
     }
 
     *result += ' ';
     *result += key;
-
-    for (const char* p = value; *p; ++p) {
-        result->push_back((!sanitize || isalnum(*p)) ? *p : '_');
-    }
+    *result += sanitize(value, alphanumeric);
 }
 
 static void append_transport(const atransport* t, std::string* result, bool long_listing) {
-    const char* serial = t->serial;
-    if (!serial || !serial[0]) {
+    std::string serial = t->serial;
+    if (serial.empty()) {
         serial = "(no serial number)";
     }
 
@@ -914,28 +1141,42 @@
         *result += '\t';
         *result += t->connection_state_name();
     } else {
-        android::base::StringAppendF(result, "%-22s %s", serial, t->connection_state_name().c_str());
+        android::base::StringAppendF(result, "%-22s %s", serial.c_str(),
+                                     t->connection_state_name().c_str());
 
         append_transport_info(result, "", t->devpath, false);
         append_transport_info(result, "product:", t->product, false);
         append_transport_info(result, "model:", t->model, true);
         append_transport_info(result, "device:", t->device, false);
+
+        // Put id at the end, so that anyone parsing the output here can always find it by scanning
+        // backwards from newlines, even with hypothetical devices named 'transport_id:1'.
+        *result += " transport_id:";
+        *result += std::to_string(t->id);
     }
     *result += '\n';
 }
 
 std::string list_transports(bool long_listing) {
-    std::string result;
+    std::lock_guard<std::recursive_mutex> lock(transport_lock);
 
-    std::lock_guard<std::mutex> lock(transport_lock);
-    for (const auto& t : transport_list) {
+    auto sorted_transport_list = transport_list;
+    sorted_transport_list.sort([](atransport*& x, atransport*& y) {
+        if (x->type != y->type) {
+            return x->type < y->type;
+        }
+        return x->serial < y->serial;
+    });
+
+    std::string result;
+    for (const auto& t : sorted_transport_list) {
         append_transport(t, &result, long_listing);
     }
     return result;
 }
 
 void close_usb_devices(std::function<bool(const atransport*)> predicate) {
-    std::lock_guard<std::mutex> lock(transport_lock);
+    std::lock_guard<std::recursive_mutex> lock(transport_lock);
     for (auto& t : transport_list) {
         if (predicate(t)) {
             t->Kick();
@@ -949,8 +1190,9 @@
 }
 #endif  // ADB_HOST
 
-int register_socket_transport(int s, const char* serial, int port, int local) {
-    atransport* t = new atransport();
+int register_socket_transport(unique_fd s, const char* serial, int port, int local,
+                              atransport::ReconnectCallback reconnect) {
+    atransport* t = new atransport(std::move(reconnect), kCsOffline);
 
     if (!serial) {
         char buf[32];
@@ -958,47 +1200,54 @@
         serial = buf;
     }
 
-    D("transport: %s init'ing for socket %d, on port %d", serial, s, port);
-    if (init_socket_transport(t, s, port, local) < 0) {
+    D("transport: %s init'ing for socket %d, on port %d", serial, s.get(), port);
+    if (init_socket_transport(t, std::move(s), port, local) < 0) {
         delete t;
         return -1;
     }
 
-    std::unique_lock<std::mutex> lock(transport_lock);
+    std::unique_lock<std::recursive_mutex> lock(transport_lock);
     for (const auto& transport : pending_list) {
-        if (transport->serial && strcmp(serial, transport->serial) == 0) {
+        if (strcmp(serial, transport->serial.c_str()) == 0) {
             VLOG(TRANSPORT) << "socket transport " << transport->serial
                             << " is already in pending_list and fails to register";
             delete t;
-            return -1;
+            return -EALREADY;
         }
     }
 
     for (const auto& transport : transport_list) {
-        if (transport->serial && strcmp(serial, transport->serial) == 0) {
+        if (strcmp(serial, transport->serial.c_str()) == 0) {
             VLOG(TRANSPORT) << "socket transport " << transport->serial
                             << " is already in transport_list and fails to register";
             delete t;
-            return -1;
+            return -EALREADY;
         }
     }
 
     pending_list.push_front(t);
-    t->serial = strdup(serial);
+    t->serial = serial;
 
     lock.unlock();
 
+    auto waitable = t->connection_waitable();
     register_transport(t);
-    return 0;
+
+    if (local == 1) {
+        // Do not wait for emulator transports.
+        return 0;
+    }
+
+    return waitable->WaitForConnection(std::chrono::seconds(10)) ? 0 : -1;
 }
 
 #if ADB_HOST
 atransport* find_transport(const char* serial) {
     atransport* result = nullptr;
 
-    std::lock_guard<std::mutex> lock(transport_lock);
+    std::lock_guard<std::recursive_mutex> lock(transport_lock);
     for (auto& t : transport_list) {
-        if (t->serial && strcmp(serial, t->serial) == 0) {
+        if (strcmp(serial, t->serial.c_str()) == 0) {
             result = t;
             break;
         }
@@ -1008,7 +1257,7 @@
 }
 
 void kick_all_tcp_devices() {
-    std::lock_guard<std::mutex> lock(transport_lock);
+    std::lock_guard<std::recursive_mutex> lock(transport_lock);
     for (auto& t : transport_list) {
         if (t->IsTcpDevice()) {
             // Kicking breaks the read_transport thread of this transport out of any read, then
@@ -1024,20 +1273,20 @@
 
 void register_usb_transport(usb_handle* usb, const char* serial, const char* devpath,
                             unsigned writeable) {
-    atransport* t = new atransport((writeable ? kCsOffline : kCsNoPerm));
+    atransport* t = new atransport(writeable ? kCsOffline : kCsNoPerm);
 
     D("transport: %p init'ing for usb_handle %p (sn='%s')", t, usb, serial ? serial : "");
     init_usb_transport(t, usb);
     if (serial) {
-        t->serial = strdup(serial);
+        t->serial = serial;
     }
 
     if (devpath) {
-        t->devpath = strdup(devpath);
+        t->devpath = devpath;
     }
 
     {
-        std::lock_guard<std::mutex> lock(transport_lock);
+        std::lock_guard<std::recursive_mutex> lock(transport_lock);
         pending_list.push_front(t);
     }
 
@@ -1046,9 +1295,14 @@
 
 // This should only be used for transports with connection_state == kCsNoPerm.
 void unregister_usb_transport(usb_handle* usb) {
-    std::lock_guard<std::mutex> lock(transport_lock);
-    transport_list.remove_if(
-        [usb](atransport* t) { return t->usb == usb && t->GetConnectionState() == kCsNoPerm; });
+    std::lock_guard<std::recursive_mutex> lock(transport_lock);
+    transport_list.remove_if([usb](atransport* t) {
+        auto connection = t->connection();
+        if (auto usb_connection = dynamic_cast<UsbConnection*>(connection.get())) {
+            return usb_connection->handle_ == usb && t->GetConnectionState() == kCsNoPerm;
+        }
+        return false;
+    });
 }
 
 bool check_header(apacket* p, atransport* t) {
@@ -1067,10 +1321,6 @@
     return true;
 }
 
-bool check_data(apacket* p) {
-    return calculate_apacket_checksum(p) == p->msg.data_check;
-}
-
 #if ADB_HOST
 std::shared_ptr<RSA> atransport::NextKey() {
     if (keys_.empty()) keys_ = adb_auth_get_private_keys();
@@ -1079,11 +1329,4 @@
     keys_.pop_front();
     return result;
 }
-bool atransport::SetSendConnectOnError() {
-    if (has_send_connect_on_error_) {
-        return false;
-    }
-    has_send_connect_on_error_ = true;
-    return true;
-}
 #endif
diff --git a/adb/transport.h b/adb/transport.h
index 4a89ed9..1844ae8 100644
--- a/adb/transport.h
+++ b/adb/transport.h
@@ -20,18 +20,24 @@
 #include <sys/types.h>
 
 #include <atomic>
+#include <chrono>
+#include <condition_variable>
 #include <deque>
 #include <functional>
 #include <list>
 #include <memory>
 #include <mutex>
 #include <string>
+#include <thread>
 #include <unordered_set>
 
-#include "adb.h"
-
+#include <android-base/macros.h>
+#include <android-base/thread_annotations.h>
 #include <openssl/rsa.h>
 
+#include "adb.h"
+#include "adb_unique_fd.h"
+
 typedef std::unordered_set<std::string> FeatureSet;
 
 const FeatureSet& supported_features();
@@ -54,82 +60,201 @@
 // The server supports `push --sync`.
 extern const char* const kFeaturePushSync;
 
+TransportId NextTransportId();
+
+// Abstraction for a non-blocking packet transport.
+struct Connection {
+    Connection() = default;
+    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_;
+
+    static std::unique_ptr<Connection> FromFd(unique_fd fd);
+};
+
+// 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;
+    virtual bool Write(apacket* packet) = 0;
+
+    // Terminate a connection.
+    // This method must be thread-safe, and must cause concurrent Reads/Writes to terminate.
+    // Formerly known as 'Kick' in atransport.
+    virtual void Close() = 0;
+};
+
+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 started_ GUARDED_BY(mutex_) = false;
+    bool stopped_ GUARDED_BY(mutex_) = false;
+
+    std::unique_ptr<BlockingConnection> underlying_;
+    std::thread read_thread_ GUARDED_BY(mutex_);
+    std::thread write_thread_ GUARDED_BY(mutex_);
+
+    std::deque<std::unique_ptr<apacket>> write_queue_ GUARDED_BY(mutex_);
+    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;
+    bool Write(apacket* packet) override final;
+
+    void Close() override;
+
+  private:
+    unique_fd fd_;
+};
+
+struct UsbConnection : public BlockingConnection {
+    explicit UsbConnection(usb_handle* handle) : handle_(handle) {}
+    ~UsbConnection();
+
+    bool Read(apacket* packet) override final;
+    bool Write(apacket* packet) override final;
+
+    void Close() override final;
+
+    usb_handle* handle_;
+};
+
+// Waits for a transport's connection to be not pending. This is a separate
+// object so that the transport can be destroyed and another thread can be
+// notified of it in a race-free way.
+class ConnectionWaitable {
+  public:
+    ConnectionWaitable() = default;
+    ~ConnectionWaitable() = default;
+
+    // Waits until the first CNXN packet has been received by the owning
+    // atransport, or the specified timeout has elapsed. Can be called from any
+    // thread.
+    //
+    // Returns true if the CNXN packet was received in a timely fashion, false
+    // otherwise.
+    bool WaitForConnection(std::chrono::milliseconds timeout);
+
+    // Can be called from any thread when the connection stops being pending.
+    // Only the first invocation will be acknowledged, the rest will be no-ops.
+    void SetConnectionEstablished(bool success);
+
+  private:
+    bool connection_established_ GUARDED_BY(mutex_) = false;
+    bool connection_established_ready_ GUARDED_BY(mutex_) = false;
+    std::mutex mutex_;
+    std::condition_variable cv_;
+
+    DISALLOW_COPY_AND_ASSIGN(ConnectionWaitable);
+};
+
 class atransport {
-public:
+  public:
     // TODO(danalbert): We expose waaaaaaay too much stuff because this was
     // historically just a struct, but making the whole thing a more idiomatic
     // class in one go is a very large change. Given how bad our testing is,
     // it's better to do this piece by piece.
 
-    atransport(ConnectionState state = kCsOffline) : ref_count(0), connection_state_(state) {
-        transport_fde = {};
-        protocol_version = A_VERSION;
+    using ReconnectCallback = std::function<bool(atransport*)>;
+
+    atransport(ReconnectCallback reconnect, ConnectionState state)
+        : id(NextTransportId()),
+          kicked_(false),
+          connection_state_(state),
+          connection_waitable_(std::make_shared<ConnectionWaitable>()),
+          connection_(nullptr),
+          reconnect_(std::move(reconnect)) {
+        // Initialize protocol to min version for compatibility with older versions.
+        // Version will be updated post-connect.
+        protocol_version = A_VERSION_MIN;
         max_payload = MAX_PAYLOAD;
     }
-    virtual ~atransport() {}
+    atransport(ConnectionState state = kCsOffline)
+        : atransport([](atransport*) { return false; }, state) {}
+    virtual ~atransport();
 
-    int (*read_from_remote)(apacket* p, atransport* t) = nullptr;
-    void (*close)(atransport* t) = nullptr;
-
-    void SetWriteFunction(int (*write_func)(apacket*, atransport*)) { write_func_ = write_func; }
-    void SetKickFunction(void (*kick_func)(atransport*)) {
-        kick_func_ = kick_func;
-    }
-    bool IsKicked() {
-        return kicked_;
-    }
     int Write(apacket* p);
     void Kick();
+    bool kicked() const { return kicked_; }
 
     // ConnectionState can be read by all threads, but can only be written in the main thread.
     ConnectionState GetConnectionState() const;
     void SetConnectionState(ConnectionState state);
 
-    int fd = -1;
-    int transport_socket = -1;
-    fdevent transport_fde;
-    std::atomic<size_t> ref_count;
-    uint32_t sync_token = 0;
+    void SetConnection(std::unique_ptr<Connection> connection);
+    std::shared_ptr<Connection> connection() {
+        std::lock_guard<std::mutex> lock(mutex_);
+        return connection_;
+    }
+
+    const TransportId id;
+    size_t ref_count = 0;
     bool online = false;
     TransportType type = kTransportAny;
 
-    // USB handle or socket fd as needed.
-    usb_handle* usb = nullptr;
-    int sfd = -1;
-
     // Used to identify transports for clients.
-    char* serial = nullptr;
-    char* product = nullptr;
-    char* model = nullptr;
-    char* device = nullptr;
-    char* devpath = nullptr;
-    void SetLocalPortForEmulator(int port) {
-        CHECK_EQ(local_port_for_emulator_, -1);
-        local_port_for_emulator_ = port;
-    }
+    std::string serial;
+    std::string product;
+    std::string model;
+    std::string device;
+    std::string devpath;
 
-    bool GetLocalPortForEmulator(int* port) const {
-        if (type == kTransportLocal && local_port_for_emulator_ != -1) {
-            *port = local_port_for_emulator_;
-            return true;
-        }
-        return false;
-    }
-
-    bool IsTcpDevice() const {
-        return type == kTransportLocal && local_port_for_emulator_ == -1;
-    }
+    bool IsTcpDevice() const { return type == kTransportLocal; }
 
 #if ADB_HOST
     std::shared_ptr<RSA> NextKey();
-    bool SetSendConnectOnError();
 #endif
 
     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.empty() ? serial : "<unknown>"; }
+    std::string connection_state_name() const;
 
     void update_version(int version, size_t payload);
     int get_protocol_version() const;
@@ -163,11 +288,19 @@
     // This is to make it easier to use the same network target for both fastboot and adb.
     bool MatchesTarget(const std::string& target) const;
 
-private:
-    int local_port_for_emulator_ = -1;
-    bool kicked_ = false;
-    void (*kick_func_)(atransport*) = nullptr;
-    int (*write_func_)(apacket*, atransport*) = nullptr;
+    // Notifies that the atransport is no longer waiting for the connection
+    // being established.
+    void SetConnectionEstablished(bool success);
+
+    // Gets a shared reference to the ConnectionWaitable.
+    std::shared_ptr<ConnectionWaitable> connection_waitable() { return connection_waitable_; }
+
+    // Attempts to reconnect with the underlying Connection. Returns true if the
+    // reconnection attempt succeeded.
+    bool Reconnect();
+
+  private:
+    std::atomic<bool> kicked_;
 
     // A set of features transmitted in the banner with the initial connection.
     // This is stored in the banner as 'features=feature0,feature1,etc'.
@@ -181,22 +314,34 @@
     std::atomic<ConnectionState> connection_state_;
 #if ADB_HOST
     std::deque<std::shared_ptr<RSA>> keys_;
-    std::mutex write_msg_lock_;
-    bool has_send_connect_on_error_ = false;
 #endif
 
+    // A sharable object that can be used to wait for the atransport's
+    // connection to be established.
+    std::shared_ptr<ConnectionWaitable> connection_waitable_;
+
+    // The underlying connection object.
+    std::shared_ptr<Connection> connection_ GUARDED_BY(mutex_);
+
+    // A callback that will be invoked when the atransport needs to reconnect.
+    ReconnectCallback reconnect_;
+
+    std::mutex mutex_;
+
     DISALLOW_COPY_AND_ASSIGN(atransport);
 };
 
 /*
  * Obtain a transport from the available transports.
  * If serial is non-null then only the device with that serial will be chosen.
+ * If transport_id is non-zero then only the device with that transport ID will be chosen.
  * If multiple devices/emulators would match, *is_ambiguous (if non-null)
  * is set to true and nullptr returned.
  * If no suitable transport is found, error is set and nullptr returned.
  */
-atransport* acquire_one_transport(TransportType type, const char* serial, bool* is_ambiguous,
-                                  std::string* error_out, bool accept_any_state = false);
+atransport* acquire_one_transport(TransportType type, const char* serial, TransportId transport_id,
+                                  bool* is_ambiguous, std::string* error_out,
+                                  bool accept_any_state = false);
 void kick_transport(atransport* t);
 void update_transports(void);
 
@@ -204,6 +349,7 @@
 // Stops iteration and returns false if fn returns false, otherwise returns true.
 bool iterate_transports(std::function<bool(const atransport*)> fn);
 
+void init_reconnect_handler(void);
 void init_transport_registration(void);
 void init_mdns_transport_discovery(void);
 std::string list_transports(bool long_listing);
@@ -218,19 +364,19 @@
 void connect_device(const std::string& address, std::string* response);
 
 /* cause new transports to be init'd and added to the list */
-int register_socket_transport(int s, const char* serial, int port, int local);
+int register_socket_transport(unique_fd s, const char* serial, int port, int local,
+                              atransport::ReconnectCallback reconnect);
 
 // This should only be used for transports with connection_state == kCsNoPerm.
 void unregister_usb_transport(usb_handle* usb);
 
 bool check_header(apacket* p, atransport* t);
-bool check_data(apacket* p);
 
 void close_usb_devices();
 void close_usb_devices(std::function<bool(const atransport*)> predicate);
 
 void send_packet(apacket* p, atransport* t);
 
-asocket* create_device_tracker(void);
+asocket* create_device_tracker(bool long_output);
 
 #endif   /* __TRANSPORT_H */
diff --git a/adb/transport_benchmark.cpp b/adb/transport_benchmark.cpp
new file mode 100644
index 0000000..022808f
--- /dev/null
+++ b/adb/transport_benchmark.cpp
@@ -0,0 +1,194 @@
+/*
+ * 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 <malloc.h>
+#include <stdio.h>
+
+#include <android-base/logging.h>
+#include <benchmark/benchmark.h>
+
+#include "adb_trace.h"
+#include "sysdeps.h"
+#include "transport.h"
+
+#define ADB_CONNECTION_BENCHMARK(benchmark_name, ...)                          \
+    BENCHMARK_TEMPLATE(benchmark_name, FdConnection, ##__VA_ARGS__)            \
+        ->Arg(1)                                                               \
+        ->Arg(16384)                                                           \
+        ->Arg(MAX_PAYLOAD)                                                     \
+        ->UseRealTime();                                                       \
+    BENCHMARK_TEMPLATE(benchmark_name, NonblockingFdConnection, ##__VA_ARGS__) \
+        ->Arg(1)                                                               \
+        ->Arg(16384)                                                           \
+        ->Arg(MAX_PAYLOAD)                                                     \
+        ->UseRealTime()
+
+struct NonblockingFdConnection;
+template <typename ConnectionType>
+std::unique_ptr<Connection> MakeConnection(unique_fd fd);
+
+template <>
+std::unique_ptr<Connection> MakeConnection<FdConnection>(unique_fd fd) {
+    auto fd_connection = std::make_unique<FdConnection>(std::move(fd));
+    return std::make_unique<BlockingConnectionAdapter>(std::move(fd_connection));
+}
+
+template <>
+std::unique_ptr<Connection> MakeConnection<NonblockingFdConnection>(unique_fd fd) {
+    return Connection::FromFd(std::move(fd));
+}
+
+template <typename ConnectionType>
+void BM_Connection_Unidirectional(benchmark::State& state) {
+    int fds[2];
+    if (adb_socketpair(fds) != 0) {
+        LOG(FATAL) << "failed to create socketpair";
+    }
+
+    auto client = MakeConnection<ConnectionType>(unique_fd(fds[0]));
+    auto server = MakeConnection<ConnectionType>(unique_fd(fds[1]));
+
+    std::atomic<size_t> received_bytes;
+
+    client->SetReadCallback([](Connection*, std::unique_ptr<apacket>) -> bool { return true; });
+    server->SetReadCallback([&received_bytes](Connection*, std::unique_ptr<apacket> packet) -> bool {
+        received_bytes += packet->payload.size();
+        return true;
+    });
+
+    client->SetErrorCallback(
+        [](Connection*, const std::string& error) { LOG(INFO) << "client closed: " << error; });
+    server->SetErrorCallback(
+        [](Connection*, const std::string& error) { LOG(INFO) << "server closed: " << error; });
+
+    client->Start();
+    server->Start();
+
+    for (auto _ : state) {
+        size_t data_size = state.range(0);
+        std::unique_ptr<apacket> packet = std::make_unique<apacket>();
+        memset(&packet->msg, 0, sizeof(packet->msg));
+        packet->msg.command = A_WRTE;
+        packet->msg.data_length = data_size;
+        packet->payload.resize(data_size);
+
+        memset(&packet->payload[0], 0xff, data_size);
+
+        received_bytes = 0;
+        client->Write(std::move(packet));
+        while (received_bytes < data_size) {
+            continue;
+        }
+    }
+    state.SetBytesProcessed(static_cast<int64_t>(state.iterations()) * state.range(0));
+
+    client->Stop();
+    server->Stop();
+}
+
+ADB_CONNECTION_BENCHMARK(BM_Connection_Unidirectional);
+
+enum class ThreadPolicy {
+    MainThread,
+    SameThread,
+};
+
+template <typename ConnectionType, enum ThreadPolicy Policy>
+void BM_Connection_Echo(benchmark::State& state) {
+    int fds[2];
+    if (adb_socketpair(fds) != 0) {
+        LOG(FATAL) << "failed to create socketpair";
+    }
+
+    auto client = MakeConnection<ConnectionType>(unique_fd(fds[0]));
+    auto server = MakeConnection<ConnectionType>(unique_fd(fds[1]));
+
+    std::atomic<size_t> received_bytes;
+
+    fdevent_reset();
+    std::thread fdevent_thread([]() { fdevent_loop(); });
+
+    client->SetReadCallback([&received_bytes](Connection*, std::unique_ptr<apacket> packet) -> bool {
+        received_bytes += packet->payload.size();
+        return true;
+    });
+
+    static const auto handle_packet = [](Connection* connection, std::unique_ptr<apacket> packet) {
+        connection->Write(std::move(packet));
+    };
+
+    server->SetReadCallback([](Connection* connection, std::unique_ptr<apacket> packet) -> bool {
+        if (Policy == ThreadPolicy::MainThread) {
+            auto raw_packet = packet.release();
+            fdevent_run_on_main_thread([connection, raw_packet]() {
+                std::unique_ptr<apacket> packet(raw_packet);
+                handle_packet(connection, std::move(packet));
+            });
+        } else {
+            handle_packet(connection, std::move(packet));
+        }
+        return true;
+    });
+
+    client->SetErrorCallback(
+        [](Connection*, const std::string& error) { LOG(INFO) << "client closed: " << error; });
+    server->SetErrorCallback(
+        [](Connection*, const std::string& error) { LOG(INFO) << "server closed: " << error; });
+
+    client->Start();
+    server->Start();
+
+    for (auto _ : state) {
+        size_t data_size = state.range(0);
+        std::unique_ptr<apacket> packet = std::make_unique<apacket>();
+        memset(&packet->msg, 0, sizeof(packet->msg));
+        packet->msg.command = A_WRTE;
+        packet->msg.data_length = data_size;
+        packet->payload.resize(data_size);
+
+        memset(&packet->payload[0], 0xff, data_size);
+
+        received_bytes = 0;
+        client->Write(std::move(packet));
+        while (received_bytes < data_size) {
+            continue;
+        }
+    }
+    state.SetBytesProcessed(static_cast<int64_t>(state.iterations()) * state.range(0));
+
+    client->Stop();
+    server->Stop();
+
+    // TODO: Make it so that you don't need to poke the fdevent loop to make it terminate?
+    fdevent_terminate_loop();
+    fdevent_run_on_main_thread([]() {});
+
+    fdevent_thread.join();
+}
+
+ADB_CONNECTION_BENCHMARK(BM_Connection_Echo, ThreadPolicy::SameThread);
+ADB_CONNECTION_BENCHMARK(BM_Connection_Echo, ThreadPolicy::MainThread);
+
+int main(int argc, char** argv) {
+    // Set M_DECAY_TIME so that our allocations aren't immediately purged on free.
+    mallopt(M_DECAY_TIME, 1);
+
+    android::base::SetMinimumLogSeverity(android::base::WARNING);
+    adb_trace_init(argv);
+    ::benchmark::Initialize(&argc, argv);
+    if (::benchmark::ReportUnrecognizedArguments(argc, argv)) return 1;
+    ::benchmark::RunSpecifiedBenchmarks();
+}
diff --git a/adb/transport_fd.cpp b/adb/transport_fd.cpp
new file mode 100644
index 0000000..85f3c52
--- /dev/null
+++ b/adb/transport_fd.cpp
@@ -0,0 +1,239 @@
+/*
+ * 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 <stdint.h>
+
+#include <deque>
+#include <mutex>
+#include <string>
+#include <thread>
+
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <android-base/thread_annotations.h>
+
+#include "adb_unique_fd.h"
+#include "adb_utils.h"
+#include "sysdeps.h"
+#include "sysdeps/memory.h"
+#include "transport.h"
+#include "types.h"
+
+static void CreateWakeFds(unique_fd* read, unique_fd* write) {
+    // TODO: eventfd on linux?
+    int wake_fds[2];
+    int rc = adb_socketpair(wake_fds);
+    set_file_block_mode(wake_fds[0], false);
+    set_file_block_mode(wake_fds[1], false);
+    CHECK_EQ(0, rc);
+    *read = unique_fd(wake_fds[0]);
+    *write = unique_fd(wake_fds[1]);
+}
+
+struct NonblockingFdConnection : public Connection {
+    NonblockingFdConnection(unique_fd fd) : started_(false), fd_(std::move(fd)) {
+        set_file_block_mode(fd_.get(), false);
+        CreateWakeFds(&wake_fd_read_, &wake_fd_write_);
+    }
+
+    void SetRunning(bool value) {
+        std::lock_guard<std::mutex> lock(run_mutex_);
+        running_ = value;
+    }
+
+    bool IsRunning() {
+        std::lock_guard<std::mutex> lock(run_mutex_);
+        return running_;
+    }
+
+    void Run(std::string* error) {
+        SetRunning(true);
+        while (IsRunning()) {
+            adb_pollfd pfds[2] = {
+                {.fd = fd_.get(), .events = POLLIN},
+                {.fd = wake_fd_read_.get(), .events = POLLIN},
+            };
+
+            {
+                std::lock_guard<std::mutex> lock(this->write_mutex_);
+                if (!writable_) {
+                    pfds[0].events |= POLLOUT;
+                }
+            }
+
+            int rc = adb_poll(pfds, 2, -1);
+            if (rc == -1) {
+                *error = android::base::StringPrintf("poll failed: %s", strerror(errno));
+                return;
+            } else if (rc == 0) {
+                LOG(FATAL) << "poll timed out with an infinite timeout?";
+            }
+
+            if (pfds[0].revents) {
+                if ((pfds[0].revents & POLLOUT)) {
+                    std::lock_guard<std::mutex> lock(this->write_mutex_);
+                    WriteResult result = DispatchWrites();
+                    switch (result) {
+                        case WriteResult::Error:
+                            *error = "write failed";
+                            return;
+
+                        case WriteResult::Completed:
+                            writable_ = true;
+                            break;
+
+                        case WriteResult::TryAgain:
+                            break;
+                    }
+                }
+
+                if (pfds[0].revents & POLLIN) {
+                    // TODO: Should we be getting blocks from a free list?
+                    auto block = std::make_unique<IOVector::block_type>(MAX_PAYLOAD);
+                    rc = adb_read(fd_.get(), &(*block)[0], block->size());
+                    if (rc == -1) {
+                        *error = std::string("read failed: ") + strerror(errno);
+                        return;
+                    } else if (rc == 0) {
+                        *error = "read failed: EOF";
+                        return;
+                    }
+                    block->resize(rc);
+                    read_buffer_.append(std::move(block));
+
+                    if (!read_header_ && read_buffer_.size() >= sizeof(amessage)) {
+                        auto header_buf = read_buffer_.take_front(sizeof(amessage)).coalesce();
+                        CHECK_EQ(sizeof(amessage), header_buf.size());
+                        read_header_ = std::make_unique<amessage>();
+                        memcpy(read_header_.get(), header_buf.data(), sizeof(amessage));
+                    }
+
+                    if (read_header_ && read_buffer_.size() >= read_header_->data_length) {
+                        auto data_chain = read_buffer_.take_front(read_header_->data_length);
+
+                        // TODO: Make apacket carry around a IOVector instead of coalescing.
+                        auto payload = data_chain.coalesce<apacket::payload_type>();
+                        auto packet = std::make_unique<apacket>();
+                        packet->msg = *read_header_;
+                        packet->payload = std::move(payload);
+                        read_header_ = nullptr;
+                        read_callback_(this, std::move(packet));
+                    }
+                }
+            }
+
+            if (pfds[1].revents) {
+                uint64_t buf;
+                rc = adb_read(wake_fd_read_.get(), &buf, sizeof(buf));
+                CHECK_EQ(static_cast<int>(sizeof(buf)), rc);
+
+                // We were woken up either to add POLLOUT to our events, or to exit.
+                // Do nothing.
+            }
+        }
+    }
+
+    void Start() override final {
+        if (started_.exchange(true)) {
+            LOG(FATAL) << "Connection started multiple times?";
+        }
+
+        thread_ = std::thread([this]() {
+            std::string error = "connection closed";
+            Run(&error);
+            this->error_callback_(this, error);
+        });
+    }
+
+    void Stop() override final {
+        SetRunning(false);
+        WakeThread();
+        thread_.join();
+    }
+
+    void WakeThread() {
+        uint64_t buf = 0;
+        if (TEMP_FAILURE_RETRY(adb_write(wake_fd_write_.get(), &buf, sizeof(buf))) != sizeof(buf)) {
+            LOG(FATAL) << "failed to wake up thread";
+        }
+    }
+
+    enum class WriteResult {
+        Error,
+        Completed,
+        TryAgain,
+    };
+
+    WriteResult DispatchWrites() REQUIRES(write_mutex_) {
+        CHECK(!write_buffer_.empty());
+        if (!writable_) {
+            return WriteResult::TryAgain;
+        }
+
+        auto iovs = write_buffer_.iovecs();
+        ssize_t rc = adb_writev(fd_.get(), iovs.data(), iovs.size());
+        if (rc == -1) {
+            return WriteResult::Error;
+        } else if (rc == 0) {
+            errno = 0;
+            return WriteResult::Error;
+        }
+
+        // TODO: Implement a more efficient drop_front?
+        write_buffer_.take_front(rc);
+        if (write_buffer_.empty()) {
+            return WriteResult::Completed;
+        }
+
+        // There's data left in the range, which means our write returned early.
+        return WriteResult::TryAgain;
+    }
+
+    bool Write(std::unique_ptr<apacket> packet) final {
+        std::lock_guard<std::mutex> lock(write_mutex_);
+        const char* header_begin = reinterpret_cast<const char*>(&packet->msg);
+        const char* header_end = header_begin + sizeof(packet->msg);
+        auto header_block = std::make_unique<IOVector::block_type>(header_begin, header_end);
+        write_buffer_.append(std::move(header_block));
+        if (!packet->payload.empty()) {
+            write_buffer_.append(std::make_unique<IOVector::block_type>(std::move(packet->payload)));
+        }
+        return DispatchWrites() != WriteResult::Error;
+    }
+
+    std::thread thread_;
+
+    std::atomic<bool> started_;
+    std::mutex run_mutex_;
+    bool running_ GUARDED_BY(run_mutex_);
+
+    std::unique_ptr<amessage> read_header_;
+    IOVector read_buffer_;
+
+    unique_fd fd_;
+    unique_fd wake_fd_read_;
+    unique_fd wake_fd_write_;
+
+    std::mutex write_mutex_;
+    bool writable_ GUARDED_BY(write_mutex_) = true;
+    IOVector write_buffer_ GUARDED_BY(write_mutex_);
+
+    IOVector incoming_queue_;
+};
+
+std::unique_ptr<Connection> Connection::FromFd(unique_fd fd) {
+    return std::make_unique<NonblockingFdConnection>(std::move(fd));
+}
diff --git a/adb/transport_local.cpp b/adb/transport_local.cpp
index 6cedd92..9398ceb 100644
--- a/adb/transport_local.cpp
+++ b/adb/transport_local.cpp
@@ -28,10 +28,12 @@
 #include <condition_variable>
 #include <mutex>
 #include <thread>
+#include <unordered_map>
 #include <vector>
 
 #include <android-base/parsenetaddress.h>
 #include <android-base/stringprintf.h>
+#include <android-base/thread_annotations.h>
 #include <cutils/sockets.h>
 
 #if !ADB_HOST
@@ -40,8 +42,10 @@
 
 #include "adb.h"
 #include "adb_io.h"
+#include "adb_unique_fd.h"
 #include "adb_utils.h"
 #include "sysdeps/chrono.h"
+#include "sysdeps/memory.h"
 
 #if ADB_HOST
 
@@ -53,77 +57,35 @@
 
 static std::mutex& local_transports_lock = *new std::mutex();
 
-/* we keep a list of opened transports. The atransport struct knows to which
- * local transport it is connected. The list is used to detect when we're
- * trying to connect twice to a given local transport.
- */
-static atransport*  local_transports[ ADB_LOCAL_TRANSPORT_MAX ];
+// We keep a map from emulator port to transport.
+// TODO: weak_ptr?
+static auto& local_transports GUARDED_BY(local_transports_lock) =
+    *new std::unordered_map<int, atransport*>();
 #endif /* ADB_HOST */
 
-static int remote_read(apacket *p, atransport *t)
-{
-    if (!ReadFdExactly(t->sfd, &p->msg, sizeof(amessage))) {
-        D("remote local: read terminated (message)");
-        return -1;
-    }
-
-    if (!check_header(p, t)) {
-        D("bad header: terminated (data)");
-        return -1;
-    }
-
-    if (!ReadFdExactly(t->sfd, p->data, p->msg.data_length)) {
-        D("remote local: terminated (data)");
-        return -1;
-    }
-
-    if (!check_data(p)) {
-        D("bad data: terminated (data)");
-        return -1;
-    }
-
-    return 0;
-}
-
-static int remote_write(apacket *p, atransport *t)
-{
-    int   length = p->msg.data_length;
-
-    if(!WriteFdExactly(t->sfd, &p->msg, sizeof(amessage) + length)) {
-        D("remote local: write terminated");
-        return -1;
-    }
-
-    return 0;
-}
-
 bool local_connect(int port) {
     std::string dummy;
-    return local_connect_arbitrary_ports(port-1, port, &dummy) == 0;
+    return local_connect_arbitrary_ports(port - 1, port, &dummy) == 0;
 }
 
-void connect_device(const std::string& address, std::string* response) {
-    if (address.empty()) {
-        *response = "empty address";
-        return;
-    }
-
+std::tuple<unique_fd, int, std::string> tcp_connect(const std::string& address,
+                                                    std::string* response) {
     std::string serial;
     std::string host;
     int port = DEFAULT_ADB_LOCAL_TRANSPORT_PORT;
     if (!android::base::ParseNetAddress(address, &host, &port, &serial, response)) {
-        return;
+        return std::make_tuple(unique_fd(), port, serial);
     }
 
     std::string error;
-    int fd = network_connect(host.c_str(), port, SOCK_STREAM, 10, &error);
+    unique_fd fd(network_connect(host.c_str(), port, SOCK_STREAM, 10, &error));
     if (fd == -1) {
         *response = android::base::StringPrintf("unable to connect to %s: %s",
                                                 serial.c_str(), error.c_str());
-        return;
+        return std::make_tuple(std::move(fd), port, serial);
     }
 
-    D("client: connected %s remote on fd %d", serial.c_str(), fd);
+    D("client: connected %s remote on fd %d", serial.c_str(), fd.get());
     close_on_exec(fd);
     disable_tcp_nagle(fd);
 
@@ -132,10 +94,45 @@
         D("warning: failed to configure TCP keepalives (%s)", strerror(errno));
     }
 
-    int ret = register_socket_transport(fd, serial.c_str(), port, 0);
+    return std::make_tuple(std::move(fd), port, serial);
+}
+
+void connect_device(const std::string& address, std::string* response) {
+    if (address.empty()) {
+        *response = "empty address";
+        return;
+    }
+
+    unique_fd fd;
+    int port;
+    std::string serial;
+    std::tie(fd, port, serial) = tcp_connect(address, response);
+    auto reconnect = [address](atransport* t) {
+        std::string response;
+        unique_fd fd;
+        int port;
+        std::string serial;
+        std::tie(fd, port, serial) = tcp_connect(address, &response);
+        if (fd == -1) {
+            D("reconnect failed: %s", response.c_str());
+            return false;
+        }
+
+        // This invokes the part of register_socket_transport() that needs to be
+        // invoked if the atransport* has already been setup. This eventually
+        // calls atransport->SetConnection() with a newly created Connection*
+        // that will in turn send the CNXN packet.
+        return init_socket_transport(t, std::move(fd), port, 0) >= 0;
+    };
+
+    int ret =
+            register_socket_transport(std::move(fd), serial.c_str(), port, 0, std::move(reconnect));
     if (ret < 0) {
-        adb_close(fd);
-        *response = android::base::StringPrintf("already connected to %s", serial.c_str());
+        if (ret == -EALREADY) {
+            *response = android::base::StringPrintf("already connected to %s", serial.c_str());
+        } else {
+            *response = android::base::StringPrintf("failed to connect to %s", serial.c_str());
+        }
     } else {
         *response = android::base::StringPrintf("connected to %s", serial.c_str());
     }
@@ -143,7 +140,7 @@
 
 
 int local_connect_arbitrary_ports(int console_port, int adb_port, std::string* error) {
-    int fd = -1;
+    unique_fd fd;
 
 #if ADB_HOST
     if (find_emulator_transport_by_adb_port(adb_port) != nullptr ||
@@ -153,22 +150,22 @@
 
     const char *host = getenv("ADBHOST");
     if (host) {
-        fd = network_connect(host, adb_port, SOCK_STREAM, 0, error);
+        fd.reset(network_connect(host, adb_port, SOCK_STREAM, 0, error));
     }
 #endif
     if (fd < 0) {
-        fd = network_loopback_client(adb_port, SOCK_STREAM, error);
+        fd.reset(network_loopback_client(adb_port, SOCK_STREAM, error));
     }
 
     if (fd >= 0) {
-        D("client: connected on remote on fd %d", fd);
-        close_on_exec(fd);
-        disable_tcp_nagle(fd);
+        D("client: connected on remote on fd %d", fd.get());
+        close_on_exec(fd.get());
+        disable_tcp_nagle(fd.get());
         std::string serial = getEmulatorSerialString(console_port);
-        if (register_socket_transport(fd, serial.c_str(), adb_port, 1) == 0) {
+        if (register_socket_transport(std::move(fd), serial.c_str(), adb_port, 1,
+                                      [](atransport*) { return false; }) == 0) {
             return 0;
         }
-        adb_close(fd);
     }
     return -1;
 }
@@ -245,33 +242,31 @@
 #else // ADB_HOST
 
 static void server_socket_thread(int port) {
-    int serverfd, fd;
+    unique_fd serverfd;
 
     adb_thread_setname("server socket");
     D("transport: server_socket_thread() starting");
-    serverfd = -1;
-    for(;;) {
-        if(serverfd == -1) {
-            std::string error;
-            serverfd = network_inaddr_any_server(port, SOCK_STREAM, &error);
-            if(serverfd < 0) {
-                D("server: cannot bind socket yet: %s", error.c_str());
-                std::this_thread::sleep_for(1s);
-                continue;
-            }
-            close_on_exec(serverfd);
+    while (serverfd == -1) {
+        std::string error;
+        serverfd.reset(network_inaddr_any_server(port, SOCK_STREAM, &error));
+        if (serverfd < 0) {
+            D("server: cannot bind socket yet: %s", error.c_str());
+            std::this_thread::sleep_for(1s);
+            continue;
         }
+        close_on_exec(serverfd.get());
+    }
 
+    while (true) {
         D("server: trying to get new connection from %d", port);
-        fd = adb_socket_accept(serverfd, nullptr, nullptr);
-        if(fd >= 0) {
-            D("server: new connection on fd %d", fd);
-            close_on_exec(fd);
-            disable_tcp_nagle(fd);
-            std::string serial = android::base::StringPrintf("host-%d", fd);
-            if (register_socket_transport(fd, serial.c_str(), port, 1) != 0) {
-                adb_close(fd);
-            }
+        unique_fd fd(adb_socket_accept(serverfd, nullptr, nullptr));
+        if (fd >= 0) {
+            D("server: new connection on fd %d", fd.get());
+            close_on_exec(fd.get());
+            disable_tcp_nagle(fd.get());
+            std::string serial = android::base::StringPrintf("host-%d", fd.get());
+            register_socket_transport(std::move(fd), serial.c_str(), port, 1,
+                                      [](atransport*) { return false; });
         }
     }
     D("transport: server_socket_thread() exiting");
@@ -288,7 +283,7 @@
 #define open    adb_open
 #define read    adb_read
 #define write   adb_write
-#include <system/qemu_pipe.h>
+#include <qemu_pipe.h>
 #undef open
 #undef read
 #undef write
@@ -332,7 +327,6 @@
     /* 'ok' reply from the adb QEMUD service. */
     static const char _ok_resp[] = "ok";
 
-    int fd;
     char tmp[256];
     char con_name[32];
 
@@ -343,7 +337,7 @@
     snprintf(con_name, sizeof(con_name), "pipe:qemud:adb:%d", port);
 
     /* Connect to the adb QEMUD service. */
-    fd = qemu_pipe_open(con_name);
+    unique_fd fd(qemu_pipe_open(con_name));
     if (fd < 0) {
         /* This could be an older version of the emulator, that doesn't
          * implement adb QEMUD service. Fall back to the old TCP way. */
@@ -352,30 +346,28 @@
         return;
     }
 
-    for(;;) {
+    while (true) {
         /*
          * Wait till the host creates a new connection.
          */
 
         /* Send the 'accept' request. */
-        if (WriteFdExactly(fd, _accept_req, strlen(_accept_req))) {
+        if (WriteFdExactly(fd.get(), _accept_req, strlen(_accept_req))) {
             /* Wait for the response. In the response we expect 'ok' on success,
              * or 'ko' on failure. */
-            if (!ReadFdExactly(fd, tmp, 2) || memcmp(tmp, _ok_resp, 2)) {
+            if (!ReadFdExactly(fd.get(), tmp, 2) || memcmp(tmp, _ok_resp, 2)) {
                 D("Accepting ADB host connection has failed.");
-                adb_close(fd);
             } else {
                 /* Host is connected. Register the transport, and start the
                  * exchange. */
-                std::string serial = android::base::StringPrintf("host-%d", fd);
-                if (register_socket_transport(fd, serial.c_str(), port, 1) != 0 ||
-                    !WriteFdExactly(fd, _start_req, strlen(_start_req))) {
-                    adb_close(fd);
-                }
+                std::string serial = android::base::StringPrintf("host-%d", fd.get());
+                WriteFdExactly(fd.get(), _start_req, strlen(_start_req));
+                register_socket_transport(std::move(fd), serial.c_str(), port, 1,
+                                          [](atransport*) { return false; });
             }
 
             /* Prepare for accepting of the next ADB host connection. */
-            fd = qemu_pipe_open(con_name);
+            fd.reset(qemu_pipe_open(con_name));
             if (fd < 0) {
                 D("adb service become unavailable.");
                 return;
@@ -428,130 +420,84 @@
     std::thread(func, port).detach();
 }
 
-static void remote_kick(atransport *t)
-{
-    int fd = t->sfd;
-    t->sfd = -1;
-    adb_shutdown(fd);
-    adb_close(fd);
-
 #if ADB_HOST
-    int  nn;
-    std::lock_guard<std::mutex> lock(local_transports_lock);
-    for (nn = 0; nn < ADB_LOCAL_TRANSPORT_MAX; nn++) {
-        if (local_transports[nn] == t) {
-            local_transports[nn] = NULL;
-            break;
-        }
-    }
-#endif
-}
+struct EmulatorConnection : public FdConnection {
+    EmulatorConnection(unique_fd fd, int local_port)
+        : FdConnection(std::move(fd)), local_port_(local_port) {}
 
-static void remote_close(atransport *t)
-{
-    int fd = t->sfd;
-    if (fd != -1) {
-        t->sfd = -1;
-        adb_close(fd);
-    }
-#if ADB_HOST
-    int local_port;
-    if (t->GetLocalPortForEmulator(&local_port)) {
-        VLOG(TRANSPORT) << "remote_close, local_port = " << local_port;
+    ~EmulatorConnection() {
+        VLOG(TRANSPORT) << "remote_close, local_port = " << local_port_;
         std::unique_lock<std::mutex> lock(retry_ports_lock);
         RetryPort port;
-        port.port = local_port;
+        port.port = local_port_;
         port.retry_count = LOCAL_PORT_RETRY_COUNT;
         retry_ports.push_back(port);
         retry_ports_cond.notify_one();
     }
-#endif
-}
 
+    void Close() override {
+        std::lock_guard<std::mutex> lock(local_transports_lock);
+        local_transports.erase(local_port_);
+        FdConnection::Close();
+    }
 
-#if ADB_HOST
+    int local_port_;
+};
+
 /* Only call this function if you already hold local_transports_lock. */
 static atransport* find_emulator_transport_by_adb_port_locked(int adb_port)
-{
-    int i;
-    for (i = 0; i < ADB_LOCAL_TRANSPORT_MAX; i++) {
-        int local_port;
-        if (local_transports[i] && local_transports[i]->GetLocalPortForEmulator(&local_port)) {
-            if (local_port == adb_port) {
-                return local_transports[i];
-            }
-        }
+    REQUIRES(local_transports_lock) {
+    auto it = local_transports.find(adb_port);
+    if (it == local_transports.end()) {
+        return nullptr;
     }
-    return NULL;
+    return it->second;
 }
 
-std::string getEmulatorSerialString(int console_port)
-{
+std::string getEmulatorSerialString(int console_port) {
     return android::base::StringPrintf("emulator-%d", console_port);
 }
 
-atransport* find_emulator_transport_by_adb_port(int adb_port)
-{
+atransport* find_emulator_transport_by_adb_port(int adb_port) {
     std::lock_guard<std::mutex> lock(local_transports_lock);
-    atransport* result = find_emulator_transport_by_adb_port_locked(adb_port);
-    return result;
+    return find_emulator_transport_by_adb_port_locked(adb_port);
 }
 
-atransport* find_emulator_transport_by_console_port(int console_port)
-{
+atransport* find_emulator_transport_by_console_port(int console_port) {
     return find_transport(getEmulatorSerialString(console_port).c_str());
 }
-
-
-/* Only call this function if you already hold local_transports_lock. */
-int get_available_local_transport_index_locked()
-{
-    int i;
-    for (i = 0; i < ADB_LOCAL_TRANSPORT_MAX; i++) {
-        if (local_transports[i] == NULL) {
-            return i;
-        }
-    }
-    return -1;
-}
-
-int get_available_local_transport_index()
-{
-    std::lock_guard<std::mutex> lock(local_transports_lock);
-    int result = get_available_local_transport_index_locked();
-    return result;
-}
 #endif
 
-int init_socket_transport(atransport *t, int s, int adb_port, int local)
-{
-    int  fail = 0;
+int init_socket_transport(atransport* t, unique_fd fd, int adb_port, int local) {
+    int fail = 0;
 
-    t->SetKickFunction(remote_kick);
-    t->SetWriteFunction(remote_write);
-    t->close = remote_close;
-    t->read_from_remote = remote_read;
-    t->sfd = s;
-    t->sync_token = 1;
     t->type = kTransportLocal;
 
 #if ADB_HOST
+    // Emulator connection.
     if (local) {
+        auto emulator_connection = std::make_unique<EmulatorConnection>(std::move(fd), adb_port);
+        t->SetConnection(
+            std::make_unique<BlockingConnectionAdapter>(std::move(emulator_connection)));
         std::lock_guard<std::mutex> lock(local_transports_lock);
-        t->SetLocalPortForEmulator(adb_port);
         atransport* existing_transport = find_emulator_transport_by_adb_port_locked(adb_port);
-        int index = get_available_local_transport_index_locked();
-        if (existing_transport != NULL) {
+        if (existing_transport != nullptr) {
             D("local transport for port %d already registered (%p)?", adb_port, existing_transport);
             fail = -1;
-        } else if (index < 0) {
+        } else if (local_transports.size() >= ADB_LOCAL_TRANSPORT_MAX) {
             // Too many emulators.
             D("cannot register more emulators. Maximum is %d", ADB_LOCAL_TRANSPORT_MAX);
             fail = -1;
         } else {
-            local_transports[index] = t;
+            local_transports[adb_port] = t;
         }
+
+        return fail;
     }
 #endif
+
+    // Regular tcp connection.
+    auto fd_connection = std::make_unique<FdConnection>(std::move(fd));
+    t->SetConnection(std::make_unique<BlockingConnectionAdapter>(std::move(fd_connection)));
     return fail;
 }
diff --git a/adb/transport_mdns.cpp b/adb/transport_mdns.cpp
deleted file mode 100644
index 3603f09..0000000
--- a/adb/transport_mdns.cpp
+++ /dev/null
@@ -1,285 +0,0 @@
-/*
- * Copyright (C) 2016 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.
- */
-
-#define TRACE_TAG TRANSPORT
-
-#include "transport.h"
-
-#ifdef _WIN32
-#include <winsock2.h>
-#else
-#include <arpa/inet.h>
-#endif
-
-#include <thread>
-
-#include <android-base/stringprintf.h>
-#include <dns_sd.h>
-
-#include "adb_mdns.h"
-#include "adb_trace.h"
-#include "fdevent.h"
-#include "sysdeps.h"
-
-static DNSServiceRef service_ref;
-static fdevent service_ref_fde;
-
-// Use adb_DNSServiceRefSockFD() instead of calling DNSServiceRefSockFD()
-// directly so that the socket is put through the appropriate compatibility
-// layers to work with the rest of ADB's internal APIs.
-static inline int adb_DNSServiceRefSockFD(DNSServiceRef ref) {
-    return adb_register_socket(DNSServiceRefSockFD(ref));
-}
-#define DNSServiceRefSockFD ___xxx_DNSServiceRefSockFD
-
-static void DNSSD_API register_service_ip(DNSServiceRef sdRef,
-                                          DNSServiceFlags flags,
-                                          uint32_t interfaceIndex,
-                                          DNSServiceErrorType errorCode,
-                                          const char* hostname,
-                                          const sockaddr* address,
-                                          uint32_t ttl,
-                                          void* context);
-
-static void pump_service_ref(int /*fd*/, unsigned ev, void* data) {
-    DNSServiceRef* ref = reinterpret_cast<DNSServiceRef*>(data);
-
-    if (ev & FDE_READ)
-        DNSServiceProcessResult(*ref);
-}
-
-class AsyncServiceRef {
-  public:
-    bool Initialized() {
-        return initialized_;
-    }
-
-    virtual ~AsyncServiceRef() {
-        if (! initialized_) {
-            return;
-        }
-
-        DNSServiceRefDeallocate(sdRef_);
-        fdevent_remove(&fde_);
-    }
-
-  protected:
-    DNSServiceRef sdRef_;
-
-    void Initialize() {
-        fdevent_install(&fde_, adb_DNSServiceRefSockFD(sdRef_),
-                        pump_service_ref, &sdRef_);
-        fdevent_set(&fde_, FDE_READ);
-        initialized_ = true;
-    }
-
-  private:
-    bool initialized_;
-    fdevent fde_;
-};
-
-class ResolvedService : public AsyncServiceRef {
-  public:
-    virtual ~ResolvedService() = default;
-
-    ResolvedService(std::string name, uint32_t interfaceIndex,
-                    const char* hosttarget, uint16_t port) :
-            name_(name),
-            port_(port) {
-
-        /* TODO: We should be able to get IPv6 support by adding
-         * kDNSServiceProtocol_IPv6 to the flags below. However, when we do
-         * this, we get served link-local addresses that are usually useless to
-         * connect to. What's more, we seem to /only/ get those and nothing else.
-         * If we want IPv6 in the future we'll have to figure out why.
-         */
-        DNSServiceErrorType ret =
-            DNSServiceGetAddrInfo(
-                &sdRef_, 0, interfaceIndex,
-                kDNSServiceProtocol_IPv4, hosttarget,
-                register_service_ip, reinterpret_cast<void*>(this));
-
-        if (ret != kDNSServiceErr_NoError) {
-            D("Got %d from DNSServiceGetAddrInfo.", ret);
-        } else {
-            Initialize();
-        }
-    }
-
-    void Connect(const sockaddr* address) {
-        char ip_addr[INET6_ADDRSTRLEN];
-        const void* ip_addr_data;
-        const char* addr_format;
-
-        if (address->sa_family == AF_INET) {
-            ip_addr_data =
-                &reinterpret_cast<const sockaddr_in*>(address)->sin_addr;
-            addr_format = "%s:%hu";
-        } else if (address->sa_family == AF_INET6) {
-            ip_addr_data =
-                &reinterpret_cast<const sockaddr_in6*>(address)->sin6_addr;
-            addr_format = "[%s]:%hu";
-        } else { // Should be impossible
-            D("mDNS resolved non-IP address.");
-            return;
-        }
-
-        // Winsock version requires the const cast Because Microsoft.
-        if (!inet_ntop(address->sa_family, const_cast<void*>(ip_addr_data),
-                       ip_addr, INET6_ADDRSTRLEN)) {
-            D("Could not convert IP address to string.");
-            return;
-        }
-
-        std::string response;
-        connect_device(android::base::StringPrintf(addr_format, ip_addr, port_),
-                       &response);
-        D("Connect to %s (%s:%hu) : %s", name_.c_str(), ip_addr, port_,
-          response.c_str());
-    }
-
-  private:
-    std::string name_;
-    const uint16_t port_;
-};
-
-static void DNSSD_API register_service_ip(DNSServiceRef /*sdRef*/,
-                                          DNSServiceFlags /*flags*/,
-                                          uint32_t /*interfaceIndex*/,
-                                          DNSServiceErrorType /*errorCode*/,
-                                          const char* /*hostname*/,
-                                          const sockaddr* address,
-                                          uint32_t /*ttl*/,
-                                          void* context) {
-    D("Got IP for service.");
-    std::unique_ptr<ResolvedService> data(
-        reinterpret_cast<ResolvedService*>(context));
-    data->Connect(address);
-}
-
-static void DNSSD_API register_resolved_mdns_service(DNSServiceRef sdRef,
-                                                     DNSServiceFlags flags,
-                                                     uint32_t interfaceIndex,
-                                                     DNSServiceErrorType errorCode,
-                                                     const char* fullname,
-                                                     const char* hosttarget,
-                                                     uint16_t port,
-                                                     uint16_t txtLen,
-                                                     const unsigned char* txtRecord,
-                                                     void* context);
-
-class DiscoveredService : public AsyncServiceRef {
-  public:
-    DiscoveredService(uint32_t interfaceIndex, const char* serviceName,
-                      const char* regtype, const char* domain)
-        : serviceName_(serviceName) {
-
-        DNSServiceErrorType ret =
-            DNSServiceResolve(&sdRef_, 0, interfaceIndex, serviceName, regtype,
-                              domain, register_resolved_mdns_service,
-                              reinterpret_cast<void*>(this));
-
-        if (ret != kDNSServiceErr_NoError) {
-            D("Got %d from DNSServiceResolve.", ret);
-        } else {
-            Initialize();
-        }
-    }
-
-    const char* ServiceName() {
-        return serviceName_.c_str();
-    }
-
-  private:
-    std::string serviceName_;
-};
-
-static void DNSSD_API register_resolved_mdns_service(DNSServiceRef sdRef,
-                                                     DNSServiceFlags flags,
-                                                     uint32_t interfaceIndex,
-                                                     DNSServiceErrorType errorCode,
-                                                     const char* fullname,
-                                                     const char* hosttarget,
-                                                     uint16_t port,
-                                                     uint16_t /*txtLen*/,
-                                                     const unsigned char* /*txtRecord*/,
-                                                     void* context) {
-    D("Resolved a service.");
-    std::unique_ptr<DiscoveredService> discovered(
-        reinterpret_cast<DiscoveredService*>(context));
-
-    if (errorCode != kDNSServiceErr_NoError) {
-        D("Got error %d resolving service.", errorCode);
-        return;
-    }
-
-
-    auto resolved =
-        new ResolvedService(discovered->ServiceName(),
-                            interfaceIndex, hosttarget, ntohs(port));
-
-    if (! resolved->Initialized()) {
-        delete resolved;
-    }
-
-    if (flags) { /* Only ever equals MoreComing or 0 */
-        discovered.release();
-    }
-}
-
-static void DNSSD_API register_mdns_transport(DNSServiceRef sdRef,
-                                              DNSServiceFlags flags,
-                                              uint32_t interfaceIndex,
-                                              DNSServiceErrorType errorCode,
-                                              const char* serviceName,
-                                              const char* regtype,
-                                              const char* domain,
-                                              void*  /*context*/) {
-    D("Registering a transport.");
-    if (errorCode != kDNSServiceErr_NoError) {
-        D("Got error %d during mDNS browse.", errorCode);
-        DNSServiceRefDeallocate(sdRef);
-        fdevent_remove(&service_ref_fde);
-        return;
-    }
-
-    auto discovered = new DiscoveredService(interfaceIndex, serviceName,
-                                            regtype, domain);
-
-    if (! discovered->Initialized()) {
-        delete discovered;
-    }
-}
-
-void init_mdns_transport_discovery_thread(void) {
-    DNSServiceErrorType errorCode = DNSServiceBrowse(&service_ref, 0, 0, kADBServiceType, nullptr,
-                                                     register_mdns_transport, nullptr);
-
-    if (errorCode != kDNSServiceErr_NoError) {
-        D("Got %d initiating mDNS browse.", errorCode);
-        return;
-    }
-
-    fdevent_run_on_main_thread([]() {
-        fdevent_install(&service_ref_fde, adb_DNSServiceRefSockFD(service_ref), pump_service_ref,
-                        &service_ref);
-        fdevent_set(&service_ref_fde, FDE_READ);
-    });
-}
-
-void init_mdns_transport_discovery(void) {
-    std::thread(init_mdns_transport_discovery_thread).detach();
-}
diff --git a/adb/transport_mdns_unsupported.cpp b/adb/transport_mdns_unsupported.cpp
deleted file mode 100644
index 387d341..0000000
--- a/adb/transport_mdns_unsupported.cpp
+++ /dev/null
@@ -1,18 +0,0 @@
-/*
- * Copyright (C) 2016 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.
- */
-
-/* For when mDNS discovery is unsupported */
-void init_mdns_transport_discovery(void) {}
diff --git a/adb/transport_test.cpp b/adb/transport_test.cpp
index 68689d4..8c628d8 100644
--- a/adb/transport_test.cpp
+++ b/adb/transport_test.cpp
@@ -20,22 +20,6 @@
 
 #include "adb.h"
 
-TEST(transport, kick_transport) {
-  atransport t;
-  static size_t kick_count;
-  kick_count = 0;
-  // Mutate some member so we can test that the function is run.
-  t.SetKickFunction([](atransport* trans) { kick_count++; });
-  ASSERT_FALSE(t.IsKicked());
-  t.Kick();
-  ASSERT_TRUE(t.IsKicked());
-  ASSERT_EQ(1u, kick_count);
-  // A transport can only be kicked once.
-  t.Kick();
-  ASSERT_TRUE(t.IsKicked());
-  ASSERT_EQ(1u, kick_count);
-}
-
 static void DisconnectFunc(void* arg, atransport*) {
     int* count = reinterpret_cast<int*>(arg);
     ++*count;
@@ -102,9 +86,9 @@
     ASSERT_EQ(0U, t.features().size());
     ASSERT_EQ(kCsHost, t.GetConnectionState());
 
-    ASSERT_EQ(nullptr, t.product);
-    ASSERT_EQ(nullptr, t.model);
-    ASSERT_EQ(nullptr, t.device);
+    ASSERT_EQ(std::string(), t.product);
+    ASSERT_EQ(std::string(), t.model);
+    ASSERT_EQ(std::string(), t.device);
 }
 
 TEST(transport, parse_banner_product_features) {
diff --git a/adb/transport_usb.cpp b/adb/transport_usb.cpp
index 2f46920..602970c 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>
@@ -27,14 +28,21 @@
 
 #if ADB_HOST
 
+#if defined(__APPLE__)
+#define CHECK_PACKET_OVERFLOW 0
+#else
+#define CHECK_PACKET_OVERFLOW 1
+#endif
+
 // Call usb_read using a buffer having a multiple of usb_get_max_packet_size() bytes
 // to avoid overflow. See http://libusb.sourceforge.net/api-1.0/packetoverflow.html.
 static int UsbReadMessage(usb_handle* h, amessage* msg) {
     D("UsbReadMessage");
 
+#if CHECK_PACKET_OVERFLOW
     size_t usb_packet_size = usb_get_max_packet_size(h);
-    CHECK(usb_packet_size >= sizeof(*msg));
-    CHECK(usb_packet_size < 4096);
+    CHECK_GE(usb_packet_size, sizeof(*msg));
+    CHECK_LT(usb_packet_size, 4096ULL);
 
     char buffer[4096];
     int n = usb_read(h, buffer, usb_packet_size);
@@ -44,6 +52,9 @@
     }
     memcpy(msg, buffer, sizeof(*msg));
     return n;
+#else
+    return usb_read(h, msg, sizeof(*msg));
+#endif
 }
 
 // Call usb_read using a buffer having a multiple of usb_get_max_packet_size() bytes
@@ -51,8 +62,12 @@
 static int UsbReadPayload(usb_handle* h, apacket* p) {
     D("UsbReadPayload(%d)", p->msg.data_length);
 
+    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(sizeof(p->data) % usb_packet_size == 0);
 
     // Round the data length up to the nearest packet size boundary.
     // The device won't send a zero packet for packet size aligned payloads,
@@ -62,29 +77,33 @@
     if (rem_size) {
         len += usb_packet_size - rem_size;
     }
-    CHECK(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
+    p->payload.resize(p->msg.data_length);
+    return usb_read(h, &p->payload[0], p->payload.size());
+#endif
 }
 
-static int remote_read(apacket* p, atransport* t) {
-    int n = UsbReadMessage(t->usb, &p->msg);
+static int remote_read(apacket* p, usb_handle* usb) {
+    int n = UsbReadMessage(usb, &p->msg);
     if (n < 0) {
         D("remote usb: read terminated (message)");
         return -1;
     }
-    if (static_cast<size_t>(n) != sizeof(p->msg) || !check_header(p, t)) {
-        D("remote usb: check_header failed, skip it");
-        goto err_msg;
-    }
-    if (t->GetConnectionState() == kCsOffline) {
-        // If we read a wrong msg header declaring a large message payload, don't read its payload.
-        // Otherwise we may miss true messages from the device.
-        if (p->msg.command != A_CNXN && p->msg.command != A_AUTH) {
-            goto err_msg;
-        }
+    if (static_cast<size_t>(n) != sizeof(p->msg)) {
+        D("remote usb: read received unexpected header length %d", n);
+        return -1;
     }
     if (p->msg.data_length) {
-        n = UsbReadPayload(t->usb, p);
+        n = UsbReadPayload(usb, p);
         if (n < 0) {
             D("remote usb: terminated (data)");
             return -1;
@@ -92,22 +111,7 @@
         if (static_cast<uint32_t>(n) != p->msg.data_length) {
             D("remote usb: read payload failed (need %u bytes, give %d bytes), skip it",
               p->msg.data_length, n);
-            goto err_msg;
-        }
-    }
-    if (!check_data(p)) {
-        D("remote usb: check_data failed, skip it");
-        goto err_msg;
-    }
-    return 0;
-
-err_msg:
-    p->msg.command = 0;
-    if (t->GetConnectionState() == kCsOffline) {
-        // If the data toggle of ep_out on device and ep_in on host are not the same, we may receive
-        // an error message. In this case, resend one A_CNXN message to connect the device.
-        if (t->SetSendConnectOnError()) {
-            SendConnectOnHost(t);
+            return -1;
         }
     }
     return 0;
@@ -117,79 +121,72 @@
 
 // On Android devices, we rely on the kernel to provide buffered read.
 // So we can recover automatically from EOVERFLOW.
-static int remote_read(apacket *p, atransport *t)
-{
-    if (usb_read(t->usb, &p->msg, sizeof(amessage))) {
-        D("remote usb: read terminated (message)");
-        return -1;
-    }
-
-    if (!check_header(p, t)) {
-        D("remote usb: check_header failed");
+static int remote_read(apacket* p, usb_handle* usb) {
+    if (usb_read(usb, &p->msg, sizeof(amessage)) != sizeof(amessage)) {
+        PLOG(ERROR) << "remote usb: read terminated (message)";
         return -1;
     }
 
     if (p->msg.data_length) {
-        if (usb_read(t->usb, p->data, p->msg.data_length)) {
-            D("remote usb: terminated (data)");
+        if (p->msg.data_length > MAX_PAYLOAD) {
+            PLOG(ERROR) << "remote usb: read overflow (data length = " << p->msg.data_length << ")";
             return -1;
         }
-    }
 
-    if (!check_data(p)) {
-        D("remote usb: check_data failed");
-        return -1;
+        p->payload.resize(p->msg.data_length);
+        if (usb_read(usb, &p->payload[0], p->payload.size())
+                != static_cast<int>(p->payload.size())) {
+            PLOG(ERROR) << "remote usb: terminated (data)";
+            return -1;
+        }
     }
 
     return 0;
 }
 #endif
 
-static int remote_write(apacket *p, atransport *t)
-{
-    unsigned size = p->msg.data_length;
-
-    if (usb_write(t->usb, &p->msg, sizeof(amessage))) {
-        D("remote usb: 1 - write terminated");
-        return -1;
-    }
-    if(p->msg.data_length == 0) return 0;
-    if (usb_write(t->usb, &p->data, size)) {
-        D("remote usb: 2 - write terminated");
-        return -1;
-    }
-
-    return 0;
+UsbConnection::~UsbConnection() {
+    usb_close(handle_);
 }
 
-static void remote_close(atransport *t)
-{
-    usb_close(t->usb);
-    t->usb = 0;
+bool UsbConnection::Read(apacket* packet) {
+    int rc = remote_read(packet, handle_);
+    return rc == 0;
 }
 
-static void remote_kick(atransport* t) {
-    usb_kick(t->usb);
+bool UsbConnection::Write(apacket* packet) {
+    int size = packet->msg.data_length;
+
+    if (usb_write(handle_, &packet->msg, sizeof(packet->msg)) != sizeof(packet->msg)) {
+        PLOG(ERROR) << "remote usb: 1 - write terminated";
+        return false;
+    }
+
+    if (packet->msg.data_length != 0 && usb_write(handle_, packet->payload.data(), size) != size) {
+        PLOG(ERROR) << "remote usb: 2 - write terminated";
+        return false;
+    }
+
+    return true;
+}
+
+void UsbConnection::Close() {
+    usb_kick(handle_);
 }
 
 void init_usb_transport(atransport* t, usb_handle* h) {
     D("transport: usb");
-    t->close = remote_close;
-    t->SetKickFunction(remote_kick);
-    t->SetWriteFunction(remote_write);
-    t->read_from_remote = remote_read;
-    t->sync_token = 1;
+    auto connection = std::make_unique<UsbConnection>(h);
+    t->SetConnection(std::make_unique<BlockingConnectionAdapter>(std::move(connection)));
     t->type = kTransportUsb;
-    t->usb = h;
 }
 
-int is_adb_interface(int usb_class, int usb_subclass, int usb_protocol)
-{
+int is_adb_interface(int usb_class, int usb_subclass, int usb_protocol) {
     return (usb_class == ADB_CLASS && usb_subclass == ADB_SUBCLASS && usb_protocol == ADB_PROTOCOL);
 }
 
 bool should_use_libusb() {
-#if defined(_WIN32) || !ADB_HOST
+#if !ADB_HOST
     return false;
 #else
     static bool enable = getenv("ADB_LIBUSB") && strcmp(getenv("ADB_LIBUSB"), "1") == 0;
diff --git a/adb/types.h b/adb/types.h
new file mode 100644
index 0000000..a3e5d48
--- /dev/null
+++ b/adb/types.h
@@ -0,0 +1,339 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <algorithm>
+#include <deque>
+#include <memory>
+#include <type_traits>
+#include <utility>
+#include <vector>
+
+#include <android-base/logging.h>
+
+#include "sysdeps/memory.h"
+#include "sysdeps/uio.h"
+
+// Essentially std::vector<char>, except without zero initialization or reallocation.
+struct Block {
+    using iterator = char*;
+
+    Block() {}
+
+    explicit Block(size_t size) { allocate(size); }
+
+    template <typename Iterator>
+    Block(Iterator begin, Iterator end) : Block(end - begin) {
+        std::copy(begin, end, data_.get());
+    }
+
+    Block(const Block& copy) = delete;
+    Block(Block&& move) {
+        std::swap(data_, move.data_);
+        std::swap(capacity_, move.capacity_);
+        std::swap(size_, move.size_);
+    }
+
+    Block& operator=(const Block& copy) = delete;
+    Block& operator=(Block&& move) {
+        clear();
+
+        std::swap(data_, move.data_);
+        std::swap(capacity_, move.capacity_);
+        std::swap(size_, move.size_);
+
+        return *this;
+    }
+
+    ~Block() { clear(); }
+
+    void resize(size_t new_size) {
+        if (!data_) {
+            allocate(new_size);
+        } else {
+            CHECK_GE(capacity_, new_size);
+            size_ = new_size;
+        }
+    }
+
+    template <typename InputIt>
+    void assign(InputIt begin, InputIt end) {
+        clear();
+        allocate(end - begin);
+        std::copy(begin, end, data_.get());
+    }
+
+    void clear() {
+        data_.reset();
+        capacity_ = 0;
+        size_ = 0;
+    }
+
+    size_t capacity() const { return capacity_; }
+    size_t size() const { return size_; }
+    bool empty() const { return size() == 0; }
+
+    char* data() { return data_.get(); }
+    const char* data() const { return data_.get(); }
+
+    char* begin() { return data_.get(); }
+    const char* begin() const { return data_.get(); }
+
+    char* end() { return data() + size_; }
+    const char* end() const { return data() + size_; }
+
+    char& operator[](size_t idx) { return data()[idx]; }
+    const char& operator[](size_t idx) const { return data()[idx]; }
+
+    bool operator==(const Block& rhs) const {
+        return size() == rhs.size() && memcmp(data(), rhs.data(), size()) == 0;
+    }
+
+  private:
+    void allocate(size_t size) {
+        CHECK(data_ == nullptr);
+        CHECK_EQ(0ULL, capacity_);
+        CHECK_EQ(0ULL, size_);
+        if (size != 0) {
+            data_ = std::make_unique<char[]>(size);
+            capacity_ = size;
+            size_ = size;
+        }
+    }
+
+    std::unique_ptr<char[]> data_;
+    size_t capacity_ = 0;
+    size_t size_ = 0;
+};
+
+struct amessage {
+    uint32_t command;     /* command identifier constant      */
+    uint32_t arg0;        /* first argument                   */
+    uint32_t arg1;        /* second argument                  */
+    uint32_t data_length; /* length of payload (0 is allowed) */
+    uint32_t data_check;  /* checksum of data payload         */
+    uint32_t magic;       /* command ^ 0xffffffff             */
+};
+
+struct apacket {
+    using payload_type = Block;
+    amessage msg;
+    payload_type payload;
+};
+
+struct IOVector {
+    using value_type = char;
+    using block_type = Block;
+    using size_type = size_t;
+
+    IOVector() {}
+
+    explicit IOVector(std::unique_ptr<block_type> block) {
+        append(std::move(block));
+    }
+
+    IOVector(const IOVector& copy) = delete;
+    IOVector(IOVector&& move) : IOVector() {
+        *this = std::move(move);
+    }
+
+    IOVector& operator=(const IOVector& copy) = delete;
+    IOVector& operator=(IOVector&& move) {
+        chain_ = std::move(move.chain_);
+        chain_length_ = move.chain_length_;
+        begin_offset_ = move.begin_offset_;
+        end_offset_ = move.end_offset_;
+
+        move.chain_.clear();
+        move.chain_length_ = 0;
+        move.begin_offset_ = 0;
+        move.end_offset_ = 0;
+
+        return *this;
+    }
+
+    size_type size() const { return chain_length_ - begin_offset_ - end_offset_; }
+    bool empty() const { return size() == 0; }
+
+    void clear() {
+        chain_length_ = 0;
+        begin_offset_ = 0;
+        end_offset_ = 0;
+        chain_.clear();
+    }
+
+    // Split the first |len| bytes out of this chain into its own.
+    IOVector take_front(size_type len) {
+        IOVector head;
+
+        if (len == 0) {
+            return head;
+        }
+        CHECK_GE(size(), len);
+
+        std::shared_ptr<const block_type> first_block = chain_.front();
+        CHECK_GE(first_block->size(), begin_offset_);
+        head.append_shared(std::move(first_block));
+        head.begin_offset_ = begin_offset_;
+
+        while (head.size() < len) {
+            pop_front_block();
+            CHECK(!chain_.empty());
+
+            head.append_shared(chain_.front());
+        }
+
+        if (head.size() == len) {
+            // Head takes full ownership of the last block it took.
+            head.end_offset_ = 0;
+            begin_offset_ = 0;
+            pop_front_block();
+        } else {
+            // Head takes partial ownership of the last block it took.
+            size_t bytes_taken = head.size() - len;
+            head.end_offset_ = bytes_taken;
+            CHECK_GE(chain_.front()->size(), bytes_taken);
+            begin_offset_ = chain_.front()->size() - bytes_taken;
+        }
+
+        return head;
+    }
+
+    // Add a nonempty block to the chain.
+    // The end of the chain must be a complete block (i.e. end_offset_ == 0).
+    void append(std::unique_ptr<const block_type> block) {
+        CHECK_NE(0ULL, block->size());
+        CHECK_EQ(0ULL, end_offset_);
+        chain_length_ += block->size();
+        chain_.emplace_back(std::move(block));
+    }
+
+    void append(block_type&& block) { append(std::make_unique<block_type>(std::move(block))); }
+
+    void trim_front() {
+        if (begin_offset_ == 0) {
+            return;
+        }
+
+        const block_type* first_block = chain_.front().get();
+        auto copy = std::make_unique<block_type>(first_block->size() - begin_offset_);
+        memcpy(copy->data(), first_block->data() + begin_offset_, copy->size());
+        chain_.front() = std::move(copy);
+
+        chain_length_ -= begin_offset_;
+        begin_offset_ = 0;
+    }
+
+  private:
+    // append, except takes a shared_ptr.
+    // Private to prevent exterior mutation of blocks.
+    void append_shared(std::shared_ptr<const block_type> block) {
+        CHECK_NE(0ULL, block->size());
+        CHECK_EQ(0ULL, end_offset_);
+        chain_length_ += block->size();
+        chain_.emplace_back(std::move(block));
+    }
+
+    // Drop the front block from the chain, and update chain_length_ appropriately.
+    void pop_front_block() {
+        chain_length_ -= chain_.front()->size();
+        begin_offset_ = 0;
+        chain_.pop_front();
+    }
+
+    // Iterate over the blocks with a callback with an operator()(const char*, size_t).
+    template <typename Fn>
+    void iterate_blocks(Fn&& callback) const {
+        if (chain_.size() == 0) {
+            return;
+        }
+
+        for (size_t i = 0; i < chain_.size(); ++i) {
+            const std::shared_ptr<const block_type>& block = chain_.at(i);
+            const char* begin = block->data();
+            size_t length = block->size();
+
+            // Note that both of these conditions can be true if there's only one block.
+            if (i == 0) {
+                CHECK_GE(block->size(), begin_offset_);
+                begin += begin_offset_;
+                length -= begin_offset_;
+            }
+
+            if (i == chain_.size() - 1) {
+                CHECK_GE(length, end_offset_);
+                length -= end_offset_;
+            }
+
+            callback(begin, length);
+        }
+    }
+
+  public:
+    // Copy all of the blocks into a single block.
+    template <typename CollectionType = block_type>
+    CollectionType coalesce() const {
+        CollectionType result;
+        if (size() == 0) {
+            return result;
+        }
+
+        result.resize(size());
+
+        size_t offset = 0;
+        iterate_blocks([&offset, &result](const char* data, size_t len) {
+            memcpy(&result[offset], data, len);
+            offset += len;
+        });
+
+        return result;
+    }
+
+    template <typename FunctionType>
+    auto coalesced(FunctionType&& f) const ->
+        typename std::result_of<FunctionType(const char*, size_t)>::type {
+        if (chain_.size() == 1) {
+            // If we only have one block, we can use it directly.
+            return f(chain_.front()->data() + begin_offset_, size());
+        } else {
+            // Otherwise, copy to a single block.
+            auto data = coalesce();
+            return f(data.data(), data.size());
+        }
+    }
+
+    // Get a list of iovecs that can be used to write out all of the blocks.
+    std::vector<adb_iovec> iovecs() const {
+        std::vector<adb_iovec> result;
+        iterate_blocks([&result](const char* data, size_t len) {
+            adb_iovec iov;
+            iov.iov_base = const_cast<char*>(data);
+            iov.iov_len = len;
+            result.emplace_back(iov);
+        });
+
+        return result;
+    }
+
+  private:
+    // Total length of all of the blocks in the chain.
+    size_t chain_length_ = 0;
+
+    size_t begin_offset_ = 0;
+    size_t end_offset_ = 0;
+    std::deque<std::shared_ptr<const block_type>> chain_;
+};
diff --git a/adb/types_test.cpp b/adb/types_test.cpp
new file mode 100644
index 0000000..31ab90a
--- /dev/null
+++ b/adb/types_test.cpp
@@ -0,0 +1,119 @@
+/*
+ * 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 <gtest/gtest.h>
+
+#include "sysdeps/memory.h"
+#include "types.h"
+
+static std::unique_ptr<IOVector::block_type> create_block(const std::string& string) {
+    return std::make_unique<IOVector::block_type>(string.begin(), string.end());
+}
+
+static std::unique_ptr<IOVector::block_type> create_block(char value, size_t len) {
+    auto block = std::make_unique<IOVector::block_type>();
+    block->resize(len);
+    memset(&(*block)[0], value, len);
+    return block;
+}
+
+template <typename T>
+static std::unique_ptr<IOVector::block_type> copy_block(T&& block) {
+    auto copy = std::make_unique<IOVector::block_type>();
+    copy->assign(block->begin(), block->end());
+    return copy;
+}
+
+TEST(IOVector, empty) {
+    // Empty IOVector.
+    IOVector bc;
+    CHECK_EQ(0ULL, bc.coalesce().size());
+}
+
+TEST(IOVector, single_block) {
+    // A single block.
+    auto block = create_block('x', 100);
+    IOVector bc;
+    bc.append(copy_block(block));
+    ASSERT_EQ(100ULL, bc.size());
+    auto coalesced = bc.coalesce();
+    ASSERT_EQ(*block, coalesced);
+}
+
+TEST(IOVector, single_block_split) {
+    // One block split.
+    IOVector bc;
+    bc.append(create_block("foobar"));
+    IOVector foo = bc.take_front(3);
+    ASSERT_EQ(3ULL, foo.size());
+    ASSERT_EQ(3ULL, bc.size());
+    ASSERT_EQ(*create_block("foo"), foo.coalesce());
+    ASSERT_EQ(*create_block("bar"), bc.coalesce());
+}
+
+TEST(IOVector, aligned_split) {
+    IOVector bc;
+    bc.append(create_block("foo"));
+    bc.append(create_block("bar"));
+    bc.append(create_block("baz"));
+    ASSERT_EQ(9ULL, bc.size());
+
+    IOVector foo = bc.take_front(3);
+    ASSERT_EQ(3ULL, foo.size());
+    ASSERT_EQ(*create_block("foo"), foo.coalesce());
+
+    IOVector bar = bc.take_front(3);
+    ASSERT_EQ(3ULL, bar.size());
+    ASSERT_EQ(*create_block("bar"), bar.coalesce());
+
+    IOVector baz = bc.take_front(3);
+    ASSERT_EQ(3ULL, baz.size());
+    ASSERT_EQ(*create_block("baz"), baz.coalesce());
+
+    ASSERT_EQ(0ULL, bc.size());
+}
+
+TEST(IOVector, misaligned_split) {
+    IOVector bc;
+    bc.append(create_block("foo"));
+    bc.append(create_block("bar"));
+    bc.append(create_block("baz"));
+    bc.append(create_block("qux"));
+    bc.append(create_block("quux"));
+
+    // Aligned left, misaligned right, across multiple blocks.
+    IOVector foob = bc.take_front(4);
+    ASSERT_EQ(4ULL, foob.size());
+    ASSERT_EQ(*create_block("foob"), foob.coalesce());
+
+    // Misaligned left, misaligned right, in one block.
+    IOVector a = bc.take_front(1);
+    ASSERT_EQ(1ULL, a.size());
+    ASSERT_EQ(*create_block("a"), a.coalesce());
+
+    // Misaligned left, misaligned right, across two blocks.
+    IOVector rba = bc.take_front(3);
+    ASSERT_EQ(3ULL, rba.size());
+    ASSERT_EQ(*create_block("rba"), rba.coalesce());
+
+    // Misaligned left, misaligned right, across three blocks.
+    IOVector zquxquu = bc.take_front(7);
+    ASSERT_EQ(7ULL, zquxquu.size());
+    ASSERT_EQ(*create_block("zquxquu"), zquxquu.coalesce());
+
+    ASSERT_EQ(1ULL, bc.size());
+    ASSERT_EQ(*create_block("x"), bc.coalesce());
+}
diff --git a/adb/usb.h b/adb/usb.h
index f428ede..cd83c42 100644
--- a/adb/usb.h
+++ b/adb/usb.h
@@ -29,8 +29,8 @@
     void usb_kick(handle_ref_type h);                            \
     size_t usb_get_max_packet_size(handle_ref_type)
 
-#if defined(_WIN32) || !ADB_HOST
-// Windows and the daemon have a single implementation.
+#if !ADB_HOST
+// The daemon has a single implementation.
 
 struct usb_handle;
 ADB_USB_INTERFACE(usb_handle*);
diff --git a/adf/OWNERS b/adf/OWNERS
new file mode 100644
index 0000000..72b8b5a
--- /dev/null
+++ b/adf/OWNERS
@@ -0,0 +1,2 @@
+ghackmann@google.com
+marissaw@google.com
diff --git a/adf/libadf/Android.bp b/adf/libadf/Android.bp
index c276c53..8eef2ea 100644
--- a/adf/libadf/Android.bp
+++ b/adf/libadf/Android.bp
@@ -12,8 +12,12 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-cc_library_static {
+cc_library {
     name: "libadf",
+    vendor_available: true,
+    vndk: {
+        enabled: true,
+    },
     srcs: ["adf.cpp"],
     cflags: ["-Werror"],
     local_include_dirs: ["include"],
diff --git a/base/Android.bp b/base/Android.bp
index b636dc3..46a0233 100644
--- a/base/Android.bp
+++ b/base/Android.bp
@@ -14,15 +14,19 @@
 // limitations under the License.
 //
 
-libbase_cppflags = [
-    "-Wall",
-    "-Wextra",
-    "-Werror",
-]
+cc_defaults {
+    name: "libbase_cflags_defaults",
+    cflags: [
+        "-Wall",
+        "-Werror",
+        "-Wextra",
+    ],
+}
 
 cc_library_headers {
     name: "libbase_headers",
     vendor_available: true,
+    recovery_available: true,
     host_supported: true,
     export_include_dirs: ["include"],
 
@@ -36,64 +40,45 @@
     },
 }
 
-cc_library {
-    name: "libbase",
-    vendor_available: true,
-    clang: true,
-    host_supported: true,
+cc_defaults {
+    name: "libbase_defaults",
+    defaults: ["libbase_cflags_defaults"],
     srcs: [
+        "chrono_utils.cpp",
         "file.cpp",
         "logging.cpp",
         "parsenetaddress.cpp",
+        "properties.cpp",
         "quick_exit.cpp",
         "stringprintf.cpp",
         "strings.cpp",
+        "threads.cpp",
         "test_utils.cpp",
     ],
 
-    header_libs: [
-        "libbase_headers",
-    ],
-    export_header_lib_headers: ["libbase_headers"],
-
-    cppflags: libbase_cppflags,
     shared_libs: ["liblog"],
     target: {
         android: {
-            srcs: [
-                "errors_unix.cpp",
-                "properties.cpp",
-                "chrono_utils.cpp",
-            ],
-            cppflags: ["-Wexit-time-destructors"],
             sanitize: {
                 misc_undefined: ["integer"],
             },
 
         },
+        linux: {
+            srcs: [
+                "errors_unix.cpp",
+            ],
+            cppflags: ["-Wexit-time-destructors"],
+        },
         darwin: {
             srcs: [
-                "chrono_utils.cpp",
                 "errors_unix.cpp",
             ],
             cppflags: ["-Wexit-time-destructors"],
         },
         linux_bionic: {
-            srcs: [
-                "chrono_utils.cpp",
-                "errors_unix.cpp",
-            ],
-            cppflags: ["-Wexit-time-destructors"],
             enabled: true,
         },
-        linux: {
-            srcs: [
-                "chrono_utils.cpp",
-                "errors_unix.cpp",
-            ],
-            cppflags: ["-Wexit-time-destructors"],
-            host_ldlibs: ["-lrt"],
-        },
         windows: {
             srcs: [
                 "errors_windows.cpp",
@@ -104,47 +89,70 @@
     },
 }
 
+cc_library {
+    name: "libbase",
+    defaults: ["libbase_defaults"],
+    vendor_available: true,
+    recovery_available: true,
+    host_supported: true,
+    vndk: {
+        enabled: true,
+        support_system_process: true,
+    },
+    header_libs: [
+        "libbase_headers",
+    ],
+    export_header_lib_headers: ["libbase_headers"],
+}
+
+cc_library_static {
+    name: "libbase_ndk",
+    defaults: ["libbase_defaults"],
+    sdk_version: "current",
+    stl: "c++_static",
+    export_include_dirs: ["include"],
+}
+
 // Tests
 // ------------------------------------------------------------------------------
 cc_test {
     name: "libbase_test",
+    defaults: ["libbase_cflags_defaults"],
     host_supported: true,
-    clang: true,
     srcs: [
         "endian_test.cpp",
         "errors_test.cpp",
         "file_test.cpp",
         "logging_test.cpp",
+        "macros_test.cpp",
         "parsedouble_test.cpp",
         "parseint_test.cpp",
         "parsenetaddress_test.cpp",
+        "properties_test.cpp",
         "quick_exit_test.cpp",
         "scopeguard_test.cpp",
         "stringprintf_test.cpp",
         "strings_test.cpp",
         "test_main.cpp",
+        "test_utils_test.cpp",
+        "unique_fd_test.cpp",
     ],
     target: {
         android: {
-            srcs: [
-                "chrono_utils_test.cpp",
-                "properties_test.cpp"
-            ],
             sanitize: {
                 misc_undefined: ["integer"],
             },
         },
         linux: {
             srcs: ["chrono_utils_test.cpp"],
-            host_ldlibs: ["-lrt"],
         },
         windows: {
             srcs: ["utf8_test.cpp"],
+            cflags: ["-Wno-unused-parameter"],
             enabled: true,
         },
     },
     local_include_dirs: ["."],
-    cppflags: libbase_cppflags,
     shared_libs: ["libbase"],
     compile_multilib: "both",
     multilib: {
@@ -155,4 +163,5 @@
             suffix: "64",
         },
     },
+    test_suites: ["device-tests"],
 }
diff --git a/base/OWNERS b/base/OWNERS
new file mode 100644
index 0000000..97777f7
--- /dev/null
+++ b/base/OWNERS
@@ -0,0 +1,3 @@
+enh@google.com
+jmgao@google.com
+tomcherry@google.com
diff --git a/base/chrono_utils.cpp b/base/chrono_utils.cpp
index b6bf701..19080a5 100644
--- a/base/chrono_utils.cpp
+++ b/base/chrono_utils.cpp
@@ -22,15 +22,15 @@
 namespace base {
 
 boot_clock::time_point boot_clock::now() {
-#ifdef __ANDROID__
+#ifdef __linux__
   timespec ts;
   clock_gettime(CLOCK_BOOTTIME, &ts);
   return boot_clock::time_point(std::chrono::seconds(ts.tv_sec) +
                                 std::chrono::nanoseconds(ts.tv_nsec));
 #else
-  // Darwin does not support clock_gettime.
+  // Darwin and Windows do not support clock_gettime.
   return boot_clock::time_point();
-#endif  // __ANDROID__
+#endif  // __linux__
 }
 
 std::ostream& operator<<(std::ostream& os, const Timer& t) {
diff --git a/base/file.cpp b/base/file.cpp
index a2f2887..d6fe753 100644
--- a/base/file.cpp
+++ b/base/file.cpp
@@ -153,6 +153,37 @@
   return true;
 }
 
+#if defined(_WIN32)
+// Windows implementation of pread. Note that this DOES move the file descriptors read position,
+// but it does so atomically.
+static ssize_t pread(int fd, void* data, size_t byte_count, off64_t offset) {
+  DWORD bytes_read;
+  OVERLAPPED overlapped;
+  memset(&overlapped, 0, sizeof(OVERLAPPED));
+  overlapped.Offset = static_cast<DWORD>(offset);
+  overlapped.OffsetHigh = static_cast<DWORD>(offset >> 32);
+  if (!ReadFile(reinterpret_cast<HANDLE>(_get_osfhandle(fd)), data, static_cast<DWORD>(byte_count),
+                &bytes_read, &overlapped)) {
+    // In case someone tries to read errno (since this is masquerading as a POSIX call)
+    errno = EIO;
+    return -1;
+  }
+  return static_cast<ssize_t>(bytes_read);
+}
+#endif
+
+bool ReadFullyAtOffset(int fd, void* data, size_t byte_count, off64_t offset) {
+  uint8_t* p = reinterpret_cast<uint8_t*>(data);
+  while (byte_count > 0) {
+    ssize_t n = TEMP_FAILURE_RETRY(pread(fd, p, byte_count, offset));
+    if (n <= 0) return false;
+    p += n;
+    byte_count -= n;
+    offset += n;
+  }
+  return true;
+}
+
 bool WriteFully(int fd, const void* data, size_t byte_count) {
   const uint8_t* p = reinterpret_cast<const uint8_t*>(data);
   size_t remaining = byte_count;
@@ -168,17 +199,23 @@
 bool RemoveFileIfExists(const std::string& path, std::string* err) {
   struct stat st;
 #if defined(_WIN32)
-  //TODO: Windows version can't handle symbol link correctly.
+  // TODO: Windows version can't handle symbolic links correctly.
   int result = stat(path.c_str(), &st);
   bool file_type_removable = (result == 0 && S_ISREG(st.st_mode));
 #else
   int result = lstat(path.c_str(), &st);
   bool file_type_removable = (result == 0 && (S_ISREG(st.st_mode) || S_ISLNK(st.st_mode)));
 #endif
+  if (result == -1) {
+    if (errno == ENOENT || errno == ENOTDIR) return true;
+    if (err != nullptr) *err = strerror(errno);
+    return false;
+  }
+
   if (result == 0) {
     if (!file_type_removable) {
       if (err != nullptr) {
-        *err = "is not a regular or symbol link file";
+        *err = "is not a regular file or symbolic link";
       }
       return false;
     }
diff --git a/base/file_test.cpp b/base/file_test.cpp
index 02b431d..6794652 100644
--- a/base/file_test.cpp
+++ b/base/file_test.cpp
@@ -26,6 +26,10 @@
 
 #include "android-base/test_utils.h"
 
+#if !defined(_WIN32)
+#include <pwd.h>
+#endif
+
 TEST(file, ReadFileToString_ENOENT) {
   std::string s("hello");
   errno = 0;
@@ -115,7 +119,7 @@
   ASSERT_FALSE(android::base::ReadFully(tf.fd, &s[0], s.size()));
 }
 
-TEST(file, RemoveFileIfExist) {
+TEST(file, RemoveFileIfExists) {
   TemporaryFile tf;
   ASSERT_TRUE(tf.fd != -1);
   close(tf.fd);
@@ -126,9 +130,43 @@
   TemporaryDir td;
   ASSERT_FALSE(android::base::RemoveFileIfExists(td.path));
   ASSERT_FALSE(android::base::RemoveFileIfExists(td.path, &err));
-  ASSERT_EQ("is not a regular or symbol link file", err);
+  ASSERT_EQ("is not a regular file or symbolic link", err);
 }
 
+TEST(file, RemoveFileIfExists_ENOTDIR) {
+  TemporaryFile tf;
+  close(tf.fd);
+  tf.fd = -1;
+  std::string err{"xxx"};
+  ASSERT_TRUE(android::base::RemoveFileIfExists(std::string{tf.path} + "/abc", &err));
+  ASSERT_EQ("xxx", err);
+}
+
+#if !defined(_WIN32)
+TEST(file, RemoveFileIfExists_EACCES) {
+  // EACCES -- one of the directories in the path has no search permission
+  // root can bypass permission restrictions, so drop root.
+  if (getuid() == 0) {
+    passwd* shell = getpwnam("shell");
+    setgid(shell->pw_gid);
+    setuid(shell->pw_uid);
+  }
+
+  TemporaryDir td;
+  TemporaryFile tf(td.path);
+  close(tf.fd);
+  tf.fd = -1;
+  std::string err{"xxx"};
+  // Remove dir's search permission.
+  ASSERT_TRUE(chmod(td.path, S_IRUSR | S_IWUSR) == 0);
+  ASSERT_FALSE(android::base::RemoveFileIfExists(tf.path, &err));
+  ASSERT_EQ("Permission denied", err);
+  // Set dir's search permission again.
+  ASSERT_TRUE(chmod(td.path, S_IRWXU) == 0);
+  ASSERT_TRUE(android::base::RemoveFileIfExists(tf.path, &err));
+}
+#endif
+
 TEST(file, Readlink) {
 #if !defined(_WIN32)
   // Linux doesn't allow empty symbolic links.
diff --git a/base/include/android-base/chrono_utils.h b/base/include/android-base/chrono_utils.h
index 7679d4c..11fcf71 100644
--- a/base/include/android-base/chrono_utils.h
+++ b/base/include/android-base/chrono_utils.h
@@ -14,13 +14,14 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_BASE_CHRONO_UTILS_H
-#define ANDROID_BASE_CHRONO_UTILS_H
+#pragma once
 
 #include <chrono>
 #include <sstream>
 
+#if __cplusplus > 201103L && !defined(__WIN32)  // C++14
 using namespace std::chrono_literals;
+#endif
 
 namespace android {
 namespace base {
@@ -50,5 +51,3 @@
 
 }  // namespace base
 }  // namespace android
-
-#endif  // ANDROID_BASE_CHRONO_UTILS_H
diff --git a/base/include/android-base/endian.h b/base/include/android-base/endian.h
index 6eb677c..cbbd8c9 100644
--- a/base/include/android-base/endian.h
+++ b/base/include/android-base/endian.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_BASE_ENDIAN_H
-#define ANDROID_BASE_ENDIAN_H
+#pragma once
 
 /* A cross-platform equivalent of bionic's <sys/endian.h>. */
 
@@ -86,5 +85,3 @@
 #define le64toh(x) (x)
 
 #endif
-
-#endif  // ANDROID_BASE_ENDIAN_H
diff --git a/base/include/android-base/errors.h b/base/include/android-base/errors.h
index 04c299c..06f29fc 100644
--- a/base/include/android-base/errors.h
+++ b/base/include/android-base/errors.h
@@ -27,8 +27,7 @@
 // special handling to get the error string. Refer to Microsoft documentation
 // to determine which error code to check for each function.
 
-#ifndef ANDROID_BASE_ERRORS_H
-#define ANDROID_BASE_ERRORS_H
+#pragma once
 
 #include <string>
 
@@ -42,5 +41,3 @@
 
 }  // namespace base
 }  // namespace android
-
-#endif  // ANDROID_BASE_ERRORS_H
diff --git a/base/include/android-base/file.h b/base/include/android-base/file.h
index 651f529..908690b 100644
--- a/base/include/android-base/file.h
+++ b/base/include/android-base/file.h
@@ -14,16 +14,21 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_BASE_FILE_H
-#define ANDROID_BASE_FILE_H
+#pragma once
 
 #include <sys/stat.h>
+#include <sys/types.h>
 #include <string>
 
 #if !defined(_WIN32) && !defined(O_BINARY)
 #define O_BINARY 0
 #endif
 
+#if defined(__APPLE__)
+/* Mac OS has always had a 64-bit off_t, so it doesn't have off64_t. */
+typedef off_t off64_t;
+#endif
+
 namespace android {
 namespace base {
 
@@ -42,6 +47,17 @@
 #endif
 
 bool ReadFully(int fd, void* data, size_t byte_count);
+
+// Reads `byte_count` bytes from the file descriptor at the specified offset.
+// Returns false if there was an IO error or EOF was reached before reading `byte_count` bytes.
+//
+// NOTE: On Linux/Mac, this function wraps pread, which provides atomic read support without
+// modifying the read pointer of the file descriptor. On Windows, however, the read pointer does
+// get modified. This means that ReadFullyAtOffset can be used concurrently with other calls to the
+// same function, but concurrently seeking or reading incrementally can lead to unexpected
+// behavior.
+bool ReadFullyAtOffset(int fd, void* data, size_t byte_count, off64_t offset);
+
 bool WriteFully(int fd, const void* data, size_t byte_count);
 
 bool RemoveFileIfExists(const std::string& path, std::string* err = nullptr);
@@ -61,5 +77,3 @@
 
 }  // namespace base
 }  // namespace android
-
-#endif // ANDROID_BASE_FILE_H
diff --git a/base/include/android-base/logging.h b/base/include/android-base/logging.h
index 548b286..f94cc25 100644
--- a/base/include/android-base/logging.h
+++ b/base/include/android-base/logging.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_BASE_LOGGING_H
-#define ANDROID_BASE_LOGGING_H
+#pragma once
 
 //
 // Google-style C++ logging.
@@ -42,6 +41,10 @@
 // By default, output goes to logcat on Android and stderr on the host.
 // A process can use `SetLogger` to decide where all logging goes.
 // Implementations are provided for logcat, stderr, and dmesg.
+//
+// By default, the process' name is used as the log tag.
+// Code can choose a specific log tag by defining LOG_TAG
+// before including this header.
 
 // This header also provides assertions:
 //
@@ -63,6 +66,16 @@
 
 #include "android-base/macros.h"
 
+// Note: DO NOT USE DIRECTLY. Use LOG_TAG instead.
+#ifdef _LOG_TAG_INTERNAL
+#error "_LOG_TAG_INTERNAL must not be defined"
+#endif
+#ifdef LOG_TAG
+#define _LOG_TAG_INTERNAL LOG_TAG
+#else
+#define _LOG_TAG_INTERNAL nullptr
+#endif
+
 namespace android {
 namespace base {
 
@@ -86,11 +99,23 @@
                                        unsigned int, const char*)>;
 using AbortFunction = std::function<void(const char*)>;
 
+// Loggers for use with InitLogging/SetLogger.
+
+// Log to the kernel log (dmesg).
 void KernelLogger(LogId, LogSeverity, const char*, const char*, unsigned int, const char*);
+// Log to stderr in the full logcat format (with pid/tid/time/tag details).
 void StderrLogger(LogId, LogSeverity, const char*, const char*, unsigned int, const char*);
+// Log just the message to stdout/stderr (without pid/tid/time/tag details).
+// The choice of stdout versus stderr is based on the severity.
+// Errors are also prefixed by the program name (as with err(3)/error(3)).
+// Useful for replacing printf(3)/perror(3)/err(3)/error(3) in command-line tools.
+void StdioLogger(LogId, LogSeverity, const char*, const char*, unsigned int, const char*);
 
 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.
@@ -201,10 +226,10 @@
 
 // Get an ostream that can be used for logging at the given severity and to the
 // given destination. The same notes as for LOG_STREAM apply.
-#define LOG_STREAM_TO(dest, severity)                                   \
-  ::android::base::LogMessage(__FILE__, __LINE__,                       \
-                              ::android::base::dest,                    \
-                              SEVERITY_LAMBDA(severity), -1).stream()
+#define LOG_STREAM_TO(dest, severity)                                           \
+  ::android::base::LogMessage(__FILE__, __LINE__, ::android::base::dest,        \
+                              SEVERITY_LAMBDA(severity), _LOG_TAG_INTERNAL, -1) \
+      .stream()
 
 // Logs a message to logcat on Android otherwise to stderr. If the severity is
 // FATAL it also causes an abort. For example:
@@ -231,10 +256,10 @@
 #define PLOG(severity) PLOG_TO(DEFAULT, severity)
 
 // Behaves like PLOG, but logs to the specified log ID.
-#define PLOG_TO(dest, severity)                                              \
-  LOGGING_PREAMBLE(severity) &&                                              \
-      ::android::base::LogMessage(__FILE__, __LINE__, ::android::base::dest, \
-                                  SEVERITY_LAMBDA(severity), errno)          \
+#define PLOG_TO(dest, severity)                                                        \
+  LOGGING_PREAMBLE(severity) &&                                                        \
+      ::android::base::LogMessage(__FILE__, __LINE__, ::android::base::dest,           \
+                                  SEVERITY_LAMBDA(severity), _LOG_TAG_INTERNAL, errno) \
           .stream()
 
 // Marker that code is yet to be implemented.
@@ -247,23 +272,26 @@
 //
 //     CHECK(false == true) results in a log message of
 //       "Check failed: false == true".
-#define CHECK(x)                                                                \
-  LIKELY((x)) || ABORT_AFTER_LOG_FATAL_EXPR(false) ||                           \
-      ::android::base::LogMessage(                                              \
-          __FILE__, __LINE__, ::android::base::DEFAULT, ::android::base::FATAL, \
-          -1).stream()                                                          \
+#define CHECK(x)                                                                 \
+  LIKELY((x)) || ABORT_AFTER_LOG_FATAL_EXPR(false) ||                            \
+      ::android::base::LogMessage(__FILE__, __LINE__, ::android::base::DEFAULT,  \
+                                  ::android::base::FATAL, _LOG_TAG_INTERNAL, -1) \
+              .stream()                                                          \
           << "Check failed: " #x << " "
 
+// clang-format off
 // Helper for CHECK_xx(x,y) macros.
-#define CHECK_OP(LHS, RHS, OP)                                              \
-  for (auto _values = ::android::base::MakeEagerEvaluator(LHS, RHS);        \
-       UNLIKELY(!(_values.lhs OP _values.rhs));                             \
-       /* empty */)                                                         \
-  ABORT_AFTER_LOG_FATAL                                                     \
-  ::android::base::LogMessage(__FILE__, __LINE__, ::android::base::DEFAULT, \
-                              ::android::base::FATAL, -1).stream()          \
-      << "Check failed: " << #LHS << " " << #OP << " " << #RHS              \
-      << " (" #LHS "=" << _values.lhs << ", " #RHS "=" << _values.rhs << ") "
+#define CHECK_OP(LHS, RHS, OP)                                                                 \
+  for (auto _values = ::android::base::MakeEagerEvaluator(LHS, RHS);                           \
+       UNLIKELY(!(_values.lhs OP _values.rhs));                                                \
+       /* empty */)                                                                            \
+  ABORT_AFTER_LOG_FATAL                                                                        \
+  ::android::base::LogMessage(__FILE__, __LINE__, ::android::base::DEFAULT,                    \
+                              ::android::base::FATAL, _LOG_TAG_INTERNAL, -1)                   \
+          .stream()                                                                            \
+      << "Check failed: " << #LHS << " " << #OP << " " << #RHS << " (" #LHS "=" << _values.lhs \
+      << ", " #RHS "=" << _values.rhs << ") "
+// clang-format on
 
 // Check whether a condition holds between x and y, LOG(FATAL) if not. The value
 // of the expressions x and y is evaluated once. Extra logging can be appended
@@ -278,14 +306,17 @@
 #define CHECK_GE(x, y) CHECK_OP(x, y, >= )
 #define CHECK_GT(x, y) CHECK_OP(x, y, > )
 
+// clang-format off
 // Helper for CHECK_STRxx(s1,s2) macros.
 #define CHECK_STROP(s1, s2, sense)                                             \
   while (UNLIKELY((strcmp(s1, s2) == 0) != (sense)))                           \
     ABORT_AFTER_LOG_FATAL                                                      \
     ::android::base::LogMessage(__FILE__, __LINE__, ::android::base::DEFAULT,  \
-                                ::android::base::FATAL, -1).stream()           \
+                                ::android::base::FATAL, _LOG_TAG_INTERNAL, -1) \
+        .stream()                                                              \
         << "Check failed: " << "\"" << (s1) << "\""                            \
         << ((sense) ? " == " : " != ") << "\"" << (s2) << "\""
+// clang-format on
 
 // Check for string (const char*) equality between s1 and s2, LOG(FATAL) if not.
 #define CHECK_STREQ(s1, s2) CHECK_STROP(s1, s2, true)
@@ -400,8 +431,8 @@
 // of a CHECK. The destructor will abort if the severity is FATAL.
 class LogMessage {
  public:
-  LogMessage(const char* file, unsigned int line, LogId id,
-             LogSeverity severity, int error);
+  LogMessage(const char* file, unsigned int line, LogId id, LogSeverity severity, const char* tag,
+             int error);
 
   ~LogMessage();
 
@@ -410,8 +441,8 @@
   std::ostream& stream();
 
   // The routine that performs the actual logging.
-  static void LogLine(const char* file, unsigned int line, LogId id,
-                      LogSeverity severity, const char* msg);
+  static void LogLine(const char* file, unsigned int line, LogId id, LogSeverity severity,
+                      const char* tag, const char* msg);
 
  private:
   const std::unique_ptr<LogMessageData> data_;
@@ -438,4 +469,27 @@
 }  // namespace base
 }  // namespace android
 
-#endif  // ANDROID_BASE_LOGGING_H
+namespace std {
+
+// Emit a warning of ostream<< with std::string*. The intention was most likely to print *string.
+//
+// Note: for this to work, we need to have this in a namespace.
+// Note: lots of ifdef magic to make this work with Clang (platform) vs GCC (windows tools)
+// Note: using diagnose_if(true) under Clang and nothing under GCC/mingw as there is no common
+//       attribute support.
+// Note: using a pragma because "-Wgcc-compat" (included in "-Weverything") complains about
+//       diagnose_if.
+// Note: to print the pointer, use "<< static_cast<const void*>(string_pointer)" instead.
+// Note: a not-recommended alternative is to let Clang ignore the warning by adding
+//       -Wno-user-defined-warnings to CPPFLAGS.
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wgcc-compat"
+#define OSTREAM_STRING_POINTER_USAGE_WARNING \
+    __attribute__((diagnose_if(true, "Unexpected logging of string pointer", "warning")))
+inline std::ostream& operator<<(std::ostream& stream, const std::string* string_pointer)
+    OSTREAM_STRING_POINTER_USAGE_WARNING {
+  return stream << static_cast<const void*>(string_pointer);
+}
+#pragma clang diagnostic pop
+
+}  // namespace std
diff --git a/base/include/android-base/macros.h b/base/include/android-base/macros.h
index 88bbe8a..49cc0c9 100644
--- a/base/include/android-base/macros.h
+++ b/base/include/android-base/macros.h
@@ -14,12 +14,13 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_BASE_MACROS_H
-#define ANDROID_BASE_MACROS_H
+#pragma once
 
 #include <stddef.h>  // for size_t
 #include <unistd.h>  // for TEMP_FAILURE_RETRY
 
+#include <utility>
+
 // bionic and glibc both have TEMP_FAILURE_RETRY, but eg Mac OS' libc doesn't.
 #ifndef TEMP_FAILURE_RETRY
 #define TEMP_FAILURE_RETRY(exp)            \
@@ -114,6 +115,8 @@
   ((sizeof(a) / sizeof(*(a))) / \
     static_cast<size_t>(!(sizeof(a) % sizeof(*(a)))))
 
+#define SIZEOF_MEMBER(t, f) sizeof(std::declval<t>().f)
+
 // Changing this definition will cause you a lot of pain.  A majority of
 // vendor code defines LIKELY and UNLIKELY this way, and includes
 // this header through an indirect path.
@@ -167,16 +170,19 @@
 //
 //  In either case this macro has no effect on runtime behavior and performance
 //  of code.
-#if defined(__clang__) && defined(__has_warning)
-#if __has_feature(cxx_attributes) && __has_warning("-Wimplicit-fallthrough")
 #define FALLTHROUGH_INTENDED [[clang::fallthrough]]  // NOLINT
-#endif
-#endif
 
-#ifndef FALLTHROUGH_INTENDED
-#define FALLTHROUGH_INTENDED \
-  do {                       \
-  } while (0)
+// Current ABI string
+#if defined(__arm__)
+#define ABI_STRING "arm"
+#elif defined(__aarch64__)
+#define ABI_STRING "arm64"
+#elif defined(__i386__)
+#define ABI_STRING "x86"
+#elif defined(__x86_64__)
+#define ABI_STRING "x86_64"
+#elif defined(__mips__) && !defined(__LP64__)
+#define ABI_STRING "mips"
+#elif defined(__mips__) && defined(__LP64__)
+#define ABI_STRING "mips64"
 #endif
-
-#endif  // ANDROID_BASE_MACROS_H
diff --git a/base/include/android-base/memory.h b/base/include/android-base/memory.h
index 9971226..0277a03 100644
--- a/base/include/android-base/memory.h
+++ b/base/include/android-base/memory.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_BASE_MEMORY_H
-#define ANDROID_BASE_MEMORY_H
+#pragma once
 
 namespace android {
 namespace base {
@@ -37,5 +36,3 @@
 
 } // namespace base
 } // namespace android
-
-#endif  // ANDROID_BASE_MEMORY_H
diff --git a/base/include/android-base/parsedouble.h b/base/include/android-base/parsedouble.h
index daa6902..c273c61 100644
--- a/base/include/android-base/parsedouble.h
+++ b/base/include/android-base/parsedouble.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_BASE_PARSEDOUBLE_H
-#define ANDROID_BASE_PARSEDOUBLE_H
+#pragma once
 
 #include <errno.h>
 #include <stdlib.h>
@@ -46,5 +45,3 @@
 
 }  // namespace base
 }  // namespace android
-
-#endif  // ANDROID_BASE_PARSEDOUBLE_H
diff --git a/base/include/android-base/parseint.h b/base/include/android-base/parseint.h
index 2c8570e..bb54c99 100644
--- a/base/include/android-base/parseint.h
+++ b/base/include/android-base/parseint.h
@@ -14,11 +14,11 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_BASE_PARSEINT_H
-#define ANDROID_BASE_PARSEINT_H
+#pragma once
 
 #include <errno.h>
 #include <stdlib.h>
+#include <string.h>
 
 #include <limits>
 #include <string>
@@ -26,36 +26,53 @@
 namespace android {
 namespace base {
 
-// Parses the unsigned decimal integer in the string 's' and sets 'out' to
-// that value. Optionally allows the caller to define a 'max' beyond which
-// otherwise valid values will be rejected. Returns boolean success; 'out'
+// Parses the unsigned decimal or hexadecimal integer in the string 's' and sets
+// 'out' to that value. Optionally allows the caller to define a 'max' beyond
+// which otherwise valid values will be rejected. Returns boolean success; 'out'
 // is untouched if parsing fails.
 template <typename T>
-bool ParseUint(const char* s, T* out,
-               T max = std::numeric_limits<T>::max()) {
+bool ParseUint(const char* s, T* out, T max = std::numeric_limits<T>::max(),
+               bool allow_suffixes = false) {
   int base = (s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) ? 16 : 10;
   errno = 0;
   char* end;
   unsigned long long int result = strtoull(s, &end, base);
-  if (errno != 0 || s == end || *end != '\0') {
-    return false;
+  if (errno != 0 || end == s) return false;
+  if (*end != '\0') {
+    const char* suffixes = "bkmgtpe";
+    const char* suffix;
+    if (!allow_suffixes || (suffix = strchr(suffixes, tolower(*end))) == nullptr) return false;
+    if (__builtin_mul_overflow(result, 1ULL << (10 * (suffix - suffixes)), &result)) return false;
   }
   if (max < result) {
     return false;
   }
-  *out = static_cast<T>(result);
+  if (out != nullptr) {
+    *out = static_cast<T>(result);
+  }
   return true;
 }
 
 // TODO: string_view
 template <typename T>
-bool ParseUint(const std::string& s, T* out,
-               T max = std::numeric_limits<T>::max()) {
-  return ParseUint(s.c_str(), out, max);
+bool ParseUint(const std::string& s, T* out, T max = std::numeric_limits<T>::max(),
+               bool allow_suffixes = false) {
+  return ParseUint(s.c_str(), out, max, allow_suffixes);
 }
 
-// Parses the signed decimal integer in the string 's' and sets 'out' to
-// that value. Optionally allows the caller to define a 'min' and 'max
+template <typename T>
+bool ParseByteCount(const char* s, T* out, T max = std::numeric_limits<T>::max()) {
+  return ParseUint(s, out, max, true);
+}
+
+// TODO: string_view
+template <typename T>
+bool ParseByteCount(const std::string& s, T* out, T max = std::numeric_limits<T>::max()) {
+  return ParseByteCount(s.c_str(), out, max);
+}
+
+// Parses the signed decimal or hexadecimal integer in the string 's' and sets
+// 'out' to that value. Optionally allows the caller to define a 'min' and 'max'
 // beyond which otherwise valid values will be rejected. Returns boolean
 // success; 'out' is untouched if parsing fails.
 template <typename T>
@@ -72,7 +89,9 @@
   if (result < min || max < result) {
     return false;
   }
-  *out = static_cast<T>(result);
+  if (out != nullptr) {
+    *out = static_cast<T>(result);
+  }
   return true;
 }
 
@@ -86,5 +105,3 @@
 
 }  // namespace base
 }  // namespace android
-
-#endif  // ANDROID_BASE_PARSEINT_H
diff --git a/base/include/android-base/parsenetaddress.h b/base/include/android-base/parsenetaddress.h
index b4ac025..47f8b5f 100644
--- a/base/include/android-base/parsenetaddress.h
+++ b/base/include/android-base/parsenetaddress.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_BASE_PARSENETADDRESS_H
-#define ANDROID_BASE_PARSENETADDRESS_H
+#pragma once
 
 #include <string>
 
@@ -34,5 +33,3 @@
 
 }  // namespace base
 }  // namespace android
-
-#endif  // ANDROID_BASE_PARSENETADDRESS_H
diff --git a/base/include/android-base/properties.h b/base/include/android-base/properties.h
index 041586c..31e5273 100644
--- a/base/include/android-base/properties.h
+++ b/base/include/android-base/properties.h
@@ -14,15 +14,10 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_BASE_PROPERTIES_H
-#define ANDROID_BASE_PROPERTIES_H
+#pragma once
 
 #include <sys/cdefs.h>
 
-#if !defined(__BIONIC__)
-#error Only bionic supports system properties.
-#endif
-
 #include <chrono>
 #include <limits>
 #include <string>
@@ -62,16 +57,18 @@
 // Waits for the system property `key` to have the value `expected_value`.
 // Times out after `relative_timeout`.
 // Returns true on success, false on timeout.
+#if defined(__BIONIC__)
 bool WaitForProperty(const std::string& key, const std::string& expected_value,
                      std::chrono::milliseconds relative_timeout = std::chrono::milliseconds::max());
+#endif
 
 // Waits for the system property `key` to be created.
 // Times out after `relative_timeout`.
 // Returns true on success, false on timeout.
+#if defined(__BIONIC__)
 bool WaitForPropertyCreation(const std::string& key, std::chrono::milliseconds relative_timeout =
                                                          std::chrono::milliseconds::max());
+#endif
 
 } // namespace base
 } // namespace android
-
-#endif  // ANDROID_BASE_PROPERTIES_H
diff --git a/base/include/android-base/scopeguard.h b/base/include/android-base/scopeguard.h
index abcf4bc..e6a9d10 100644
--- a/base/include/android-base/scopeguard.h
+++ b/base/include/android-base/scopeguard.h
@@ -14,23 +14,29 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_BASE_SCOPEGUARD_H
-#define ANDROID_BASE_SCOPEGUARD_H
+#pragma once
 
-#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,16 +51,17 @@
   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
 }  // namespace android
-
-#endif  // ANDROID_BASE_SCOPEGUARD_H
diff --git a/base/include/android-base/stringprintf.h b/base/include/android-base/stringprintf.h
index cf666ab..93c56af 100644
--- a/base/include/android-base/stringprintf.h
+++ b/base/include/android-base/stringprintf.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_BASE_STRINGPRINTF_H
-#define ANDROID_BASE_STRINGPRINTF_H
+#pragma once
 
 #include <stdarg.h>
 #include <string>
@@ -24,33 +23,18 @@
 namespace base {
 
 // These printf-like functions are implemented in terms of vsnprintf, so they
-// use the same attribute for compile-time format string checking. On Windows,
-// if the mingw version of vsnprintf is used, use `gnu_printf' which allows z
-// in %zd and PRIu64 (and related) to be recognized by the compile-time
-// checking.
-#define FORMAT_ARCHETYPE __printf__
-#ifdef __USE_MINGW_ANSI_STDIO
-#if __USE_MINGW_ANSI_STDIO
-#undef FORMAT_ARCHETYPE
-#define FORMAT_ARCHETYPE gnu_printf
-#endif
-#endif
+// use the same attribute for compile-time format string checking.
 
 // Returns a string corresponding to printf-like formatting of the arguments.
-std::string StringPrintf(const char* fmt, ...)
-    __attribute__((__format__(FORMAT_ARCHETYPE, 1, 2)));
+std::string StringPrintf(const char* fmt, ...) __attribute__((__format__(__printf__, 1, 2)));
 
 // Appends a printf-like formatting of the arguments to 'dst'.
 void StringAppendF(std::string* dst, const char* fmt, ...)
-    __attribute__((__format__(FORMAT_ARCHETYPE, 2, 3)));
+    __attribute__((__format__(__printf__, 2, 3)));
 
 // Appends a printf-like formatting of the arguments to 'dst'.
 void StringAppendV(std::string* dst, const char* format, va_list ap)
-    __attribute__((__format__(FORMAT_ARCHETYPE, 2, 0)));
-
-#undef FORMAT_ARCHETYPE
+    __attribute__((__format__(__printf__, 2, 0)));
 
 }  // namespace base
 }  // namespace android
-
-#endif  // ANDROID_BASE_STRINGPRINTF_H
diff --git a/base/include/android-base/strings.h b/base/include/android-base/strings.h
index f5f5c11..9c35560 100644
--- a/base/include/android-base/strings.h
+++ b/base/include/android-base/strings.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_BASE_STRINGS_H
-#define ANDROID_BASE_STRINGS_H
+#pragma once
 
 #include <sstream>
 #include <string>
@@ -57,17 +56,21 @@
 extern template std::string Join(const std::vector<const char*>&, const std::string&);
 
 // Tests whether 's' starts with 'prefix'.
+// TODO: string_view
 bool StartsWith(const std::string& s, const char* prefix);
 bool StartsWithIgnoreCase(const std::string& s, const char* prefix);
+bool StartsWith(const std::string& s, const std::string& prefix);
+bool StartsWithIgnoreCase(const std::string& s, const std::string& prefix);
 
 // Tests whether 's' ends with 'suffix'.
+// 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& 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);
 
 }  // namespace base
 }  // namespace android
-
-#endif  // ANDROID_BASE_STRINGS_H
diff --git a/base/include/android-base/test_utils.h b/base/include/android-base/test_utils.h
index c0bf0c1..9e2ea97 100644
--- a/base/include/android-base/test_utils.h
+++ b/base/include/android-base/test_utils.h
@@ -14,9 +14,9 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_BASE_TEST_UTILS_H
-#define ANDROID_BASE_TEST_UTILS_H
+#pragma once
 
+#include <regex>
 #include <string>
 
 #include <android-base/macros.h>
@@ -24,14 +24,23 @@
 class TemporaryFile {
  public:
   TemporaryFile();
+  explicit TemporaryFile(const std::string& tmp_dir);
   ~TemporaryFile();
 
+  // 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];
 
  private:
   void init(const std::string& tmp_dir);
 
+  bool remove_file_ = true;
+
   DISALLOW_COPY_AND_ASSIGN(TemporaryFile);
 };
 
@@ -48,21 +57,59 @@
   DISALLOW_COPY_AND_ASSIGN(TemporaryDir);
 };
 
-class CapturedStderr {
+class CapturedStdFd {
  public:
-  CapturedStderr();
-  ~CapturedStderr();
+  CapturedStdFd(int std_fd);
+  ~CapturedStdFd();
 
   int fd() const;
+  std::string str();
 
  private:
-  void init();
-  void reset();
+  void Init();
+  void Reset();
 
   TemporaryFile temp_file_;
-  int old_stderr_;
+  int std_fd_;
+  int old_fd_;
 
-  DISALLOW_COPY_AND_ASSIGN(CapturedStderr);
+  DISALLOW_COPY_AND_ASSIGN(CapturedStdFd);
 };
 
-#endif  // ANDROID_BASE_TEST_UTILS_H
+class CapturedStderr : public CapturedStdFd {
+ public:
+  CapturedStderr() : CapturedStdFd(STDERR_FILENO) {}
+};
+
+class CapturedStdout : public CapturedStdFd {
+ public:
+  CapturedStdout() : CapturedStdFd(STDOUT_FILENO) {}
+};
+
+#define ASSERT_MATCH(str, pattern)                                             \
+  do {                                                                         \
+    if (!std::regex_search((str), std::regex((pattern)))) {                    \
+      FAIL() << "regex mismatch: expected " << (pattern) << " in:\n" << (str); \
+    }                                                                          \
+  } while (0)
+
+#define ASSERT_NOT_MATCH(str, pattern)                                                     \
+  do {                                                                                     \
+    if (std::regex_search((str), std::regex((pattern)))) {                                 \
+      FAIL() << "regex mismatch: expected to not find " << (pattern) << " in:\n" << (str); \
+    }                                                                                      \
+  } while (0)
+
+#define EXPECT_MATCH(str, pattern)                                                    \
+  do {                                                                                \
+    if (!std::regex_search((str), std::regex((pattern)))) {                           \
+      ADD_FAILURE() << "regex mismatch: expected " << (pattern) << " in:\n" << (str); \
+    }                                                                                 \
+  } while (0)
+
+#define EXPECT_NOT_MATCH(str, pattern)                                                            \
+  do {                                                                                            \
+    if (std::regex_search((str), std::regex((pattern)))) {                                        \
+      ADD_FAILURE() << "regex mismatch: expected to not find " << (pattern) << " in:\n" << (str); \
+    }                                                                                             \
+  } while (0)
diff --git a/base/include/android-base/thread_annotations.h b/base/include/android-base/thread_annotations.h
index fbb5923..5c55e63 100644
--- a/base/include/android-base/thread_annotations.h
+++ b/base/include/android-base/thread_annotations.h
@@ -14,14 +14,9 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_BASE_THREAD_ANNOTATIONS_H
-#define ANDROID_BASE_THREAD_ANNOTATIONS_H
+#pragma once
 
-#if defined(__SUPPORT_TS_ANNOTATION__) || defined(__clang__)
-#define THREAD_ANNOTATION_ATTRIBUTE__(x)   __attribute__((x))
-#else
-#define THREAD_ANNOTATION_ATTRIBUTE__(x)   // no-op
-#endif
+#define THREAD_ANNOTATION_ATTRIBUTE__(x) __attribute__((x))
 
 #define CAPABILITY(x) \
       THREAD_ANNOTATION_ATTRIBUTE__(capability(x))
@@ -38,6 +33,12 @@
 #define PT_GUARDED_BY(x) \
       THREAD_ANNOTATION_ATTRIBUTE__(pt_guarded_by(x))
 
+#define EXCLUSIVE_LOCKS_REQUIRED(...) \
+      THREAD_ANNOTATION_ATTRIBUTE__(exclusive_locks_required(__VA_ARGS__))
+
+#define SHARED_LOCKS_REQUIRED(...) \
+      THREAD_ANNOTATION_ATTRIBUTE__(shared_locks_required(__VA_ARGS__))
+
 #define ACQUIRED_BEFORE(...) \
       THREAD_ANNOTATION_ATTRIBUTE__(acquired_before(__VA_ARGS__))
 
@@ -103,5 +104,3 @@
 
 #define NO_THREAD_SAFETY_ANALYSIS \
       THREAD_ANNOTATION_ATTRIBUTE__(no_thread_safety_analysis)
-
-#endif  // ANDROID_BASE_THREAD_ANNOTATIONS_H
diff --git a/base/include/android-base/threads.h b/base/include/android-base/threads.h
new file mode 100644
index 0000000..dba1fc6
--- /dev/null
+++ b/base/include/android-base/threads.h
@@ -0,0 +1,30 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <stdint.h>
+
+namespace android {
+namespace base {
+uint64_t GetThreadId();
+}
+}  // namespace android
+
+#if defined(__GLIBC__)
+// bionic has this Linux-specifix call, but glibc doesn't.
+extern "C" int tgkill(int tgid, int tid, int sig);
+#endif
diff --git a/base/include/android-base/unique_fd.h b/base/include/android-base/unique_fd.h
index 6cfcfcd..c6936f1 100644
--- a/base/include/android-base/unique_fd.h
+++ b/base/include/android-base/unique_fd.h
@@ -14,9 +14,15 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_BASE_UNIQUE_FD_H
-#define ANDROID_BASE_UNIQUE_FD_H
+#pragma once
 
+#include <fcntl.h>
+
+#if !defined(_WIN32)
+#include <sys/socket.h>
+#endif
+
+#include <sys/types.h>
 #include <unistd.h>
 
 // DO NOT INCLUDE OTHER LIBBASE HEADERS!
@@ -36,10 +42,35 @@
 //
 // unique_fd is also known as ScopedFd/ScopedFD/scoped_fd; mentioned here to help
 // you find this class if you're searching for one of those names.
+
+#if defined(__BIONIC__)
+#include <android/fdsan.h>
+#endif
+
 namespace android {
 namespace base {
 
 struct DefaultCloser {
+#if defined(__BIONIC__)
+  static void Tag(int fd, void* old_addr, void* new_addr) {
+    if (android_fdsan_exchange_owner_tag) {
+      uint64_t old_tag = android_fdsan_create_owner_tag(ANDROID_FDSAN_OWNER_TYPE_UNIQUE_FD,
+                                                        reinterpret_cast<uint64_t>(old_addr));
+      uint64_t new_tag = android_fdsan_create_owner_tag(ANDROID_FDSAN_OWNER_TYPE_UNIQUE_FD,
+                                                        reinterpret_cast<uint64_t>(new_addr));
+      android_fdsan_exchange_owner_tag(fd, old_tag, new_tag);
+    }
+  }
+  static void Close(int fd, void* addr) {
+    if (android_fdsan_close_with_tag) {
+      uint64_t tag = android_fdsan_create_owner_tag(ANDROID_FDSAN_OWNER_TYPE_UNIQUE_FD,
+                                                    reinterpret_cast<uint64_t>(addr));
+      android_fdsan_close_with_tag(fd, tag);
+    } else {
+      close(fd);
+    }
+  }
+#else
   static void Close(int fd) {
     // Even if close(2) fails with EINTR, the fd will have been closed.
     // Using TEMP_FAILURE_RETRY will either lead to EBADF or closing someone
@@ -47,40 +78,75 @@
     // http://lkml.indiana.edu/hypermail/linux/kernel/0509.1/0877.html
     ::close(fd);
   }
+#endif
 };
 
 template <typename Closer>
 class unique_fd_impl final {
  public:
-  unique_fd_impl() : value_(-1) {}
+  unique_fd_impl() {}
 
-  explicit unique_fd_impl(int value) : value_(value) {}
+  explicit unique_fd_impl(int fd) { reset(fd); }
   ~unique_fd_impl() { reset(); }
 
-  unique_fd_impl(unique_fd_impl&& other) : value_(other.release()) {}
+  unique_fd_impl(unique_fd_impl&& other) { reset(other.release()); }
   unique_fd_impl& operator=(unique_fd_impl&& s) {
-    reset(s.release());
+    int fd = s.fd_;
+    s.fd_ = -1;
+    reset(fd, &s);
     return *this;
   }
 
-  void reset(int new_value = -1) {
-    if (value_ != -1) {
-      Closer::Close(value_);
-    }
-    value_ = new_value;
-  }
+  void reset(int new_value = -1) { reset(new_value, nullptr); }
 
-  int get() const { return value_; }
+  int get() const { return fd_; }
   operator int() const { return get(); }
 
   int release() __attribute__((warn_unused_result)) {
-    int ret = value_;
-    value_ = -1;
+    tag(fd_, this, nullptr);
+    int ret = fd_;
+    fd_ = -1;
     return ret;
   }
 
  private:
-  int value_;
+  void reset(int new_value, void* previous_tag) {
+    if (fd_ != -1) {
+      close(fd_, this);
+    }
+
+    fd_ = new_value;
+    if (new_value != -1) {
+      tag(new_value, previous_tag, this);
+    }
+  }
+
+  int fd_ = -1;
+
+  // Template magic to use Closer::Tag if available, and do nothing if not.
+  // If Closer::Tag exists, this implementation is preferred, because int is a better match.
+  // If not, this implementation is SFINAEd away, and the no-op below is the only one that exists.
+  template <typename T = Closer>
+  static auto tag(int fd, void* old_tag, void* new_tag)
+      -> decltype(T::Tag(fd, old_tag, new_tag), void()) {
+    T::Tag(fd, old_tag, new_tag);
+  }
+
+  template <typename T = Closer>
+  static void tag(long, void*, void*) {
+    // No-op.
+  }
+
+  // Same as above, to select between Closer::Close(int) and Closer::Close(int, void*).
+  template <typename T = Closer>
+  static auto close(int fd, void* tag_value) -> decltype(T::Close(fd, tag_value), void()) {
+    T::Close(fd, tag_value);
+  }
+
+  template <typename T = Closer>
+  static auto close(int fd, void*) -> decltype(T::Close(fd), void()) {
+    T::Close(fd);
+  }
 
   unique_fd_impl(const unique_fd_impl&);
   void operator=(const unique_fd_impl&);
@@ -88,17 +154,56 @@
 
 using unique_fd = unique_fd_impl<DefaultCloser>;
 
+#if !defined(_WIN32)
+
+// Inline functions, so that they can be used header-only.
+template <typename Closer>
+inline bool Pipe(unique_fd_impl<Closer>* read, unique_fd_impl<Closer>* write) {
+  int pipefd[2];
+
+#if defined(__linux__)
+  if (pipe2(pipefd, O_CLOEXEC) != 0) {
+    return false;
+  }
+#else  // defined(__APPLE__)
+  if (pipe(pipefd) != 0) {
+    return false;
+  }
+
+  if (fcntl(pipefd[0], F_SETFD, FD_CLOEXEC) != 0 || fcntl(pipefd[1], F_SETFD, FD_CLOEXEC) != 0) {
+    close(pipefd[0]);
+    close(pipefd[1]);
+    return false;
+  }
+#endif
+
+  read->reset(pipefd[0]);
+  write->reset(pipefd[1]);
+  return true;
+}
+
+template <typename Closer>
+inline bool Socketpair(int domain, int type, int protocol, unique_fd_impl<Closer>* left,
+                       unique_fd_impl<Closer>* right) {
+  int sockfd[2];
+  if (socketpair(domain, type, protocol, sockfd) != 0) {
+    return false;
+  }
+  left->reset(sockfd[0]);
+  right->reset(sockfd[1]);
+  return true;
+}
+
+template <typename Closer>
+inline bool Socketpair(int type, unique_fd_impl<Closer>* left, unique_fd_impl<Closer>* right) {
+  return Socketpair(AF_UNIX, type, 0, left, right);
+}
+
+#endif  // !defined(_WIN32)
+
 }  // namespace base
 }  // namespace android
 
 template <typename T>
 int close(const android::base::unique_fd_impl<T>&)
-#if defined(__clang__)
-  __attribute__((__unavailable__(
-#else
-  __attribute__((__error__(
-#endif
-    "close called on unique_fd"
-  )));
-
-#endif  // ANDROID_BASE_UNIQUE_FD_H
+    __attribute__((__unavailable__("close called on unique_fd")));
diff --git a/base/include/android-base/utf8.h b/base/include/android-base/utf8.h
index c9cc1ab..4b91623 100755
--- a/base/include/android-base/utf8.h
+++ b/base/include/android-base/utf8.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_BASE_UTF8_H
-#define ANDROID_BASE_UTF8_H
+#pragma once
 
 #ifdef _WIN32
 #include <string>
@@ -102,5 +101,3 @@
 }  // namespace utf8
 }  // namespace base
 }  // namespace android
-
-#endif  // ANDROID_BASE_UTF8_H
diff --git a/base/logging.cpp b/base/logging.cpp
index 6357b4b..d60d91d 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>
 
@@ -45,7 +46,7 @@
 
 // Headers for LogMessage::LogLine.
 #ifdef __ANDROID__
-#include <log/log.h>
+#include <android/log.h>
 #include <android/set_abort_message.h>
 #else
 #include <sys/types.h>
@@ -53,48 +54,19 @@
 #endif
 
 #include <android-base/macros.h>
+#include <android-base/parseint.h>
 #include <android-base/strings.h>
+#include <android-base/threads.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
+namespace android {
+namespace base {
 
-#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__)
-  return syscall(SYS_thread_selfid);
-#elif defined(__linux__)
-  return syscall(__NR_gettid);
-#elif defined(_WIN32)
-  return GetCurrentThreadId();
-#endif
-}
-
-namespace {
+// BSD-based systems like Android/macOS have getprogname(). Others need us to provide one.
+#if defined(__GLIBC__) || defined(_WIN32)
+static const char* getprogname() {
 #if defined(__GLIBC__)
-const char* getprogname() {
   return program_invocation_short_name;
-}
 #elif defined(_WIN32)
-const char* getprogname() {
   static bool first = true;
   static char progname[MAX_PATH] = {};
 
@@ -111,12 +83,42 @@
   }
 
   return progname;
+#endif
 }
 #endif
-} // namespace
 
-namespace android {
-namespace base {
+static const char* GetFileBasename(const char* file) {
+  // We can't use basename(3) even on Unix because the Mac doesn't
+  // have a non-modifying basename.
+  const char* last_slash = strrchr(file, '/');
+  if (last_slash != nullptr) {
+    return last_slash + 1;
+  }
+#if defined(_WIN32)
+  const char* last_backslash = strrchr(file, '\\');
+  if (last_backslash != nullptr) {
+    return last_backslash + 1;
+  }
+#endif
+  return file;
+}
+
+#if defined(__linux__)
+static int OpenKmsg() {
+#if defined(__ANDROID__)
+  // pick up 'file w /dev/kmsg' environment from daemon's init rc file
+  const auto val = getenv("ANDROID_FILE__dev_kmsg");
+  if (val != nullptr) {
+    int fd;
+    if (android::base::ParseInt(val, &fd, 0)) {
+      auto flags = fcntl(fd, F_GETFL);
+      if ((flags != -1) && ((flags & O_ACCMODE) == O_WRONLY)) return fd;
+    }
+  }
+#endif
+  return TEMP_FAILURE_RETRY(open("/dev/kmsg", O_WRONLY | O_CLOEXEC));
+}
+#endif
 
 static std::mutex& LoggingLock() {
   static auto& logging_lock = *new std::mutex();
@@ -137,9 +139,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;
@@ -163,7 +183,7 @@
   static_assert(arraysize(kLogSeverityToKernelLogLevel) == android::base::FATAL + 1,
                 "Mismatch in size of kLogSeverityToKernelLogLevel and values in LogSeverity");
 
-  static int klog_fd = TEMP_FAILURE_RETRY(open("/dev/kmsg", O_WRONLY | O_CLOEXEC));
+  static int klog_fd = OpenKmsg();
   if (klog_fd == -1) return;
 
   int level = kLogSeverityToKernelLogLevel[severity];
@@ -185,8 +205,8 @@
 }
 #endif
 
-void StderrLogger(LogId, LogSeverity severity, const char*, const char* file,
-                  unsigned int line, const char* message) {
+void StderrLogger(LogId, LogSeverity severity, const char* tag, const char* file, unsigned int line,
+                  const char* message) {
   struct tm now;
   time_t t = time(nullptr);
 
@@ -203,8 +223,18 @@
   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", ProgramInvocationName().c_str(),
-          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 StdioLogger(LogId, LogSeverity severity, const char* /*tag*/, const char* /*file*/,
+                 unsigned int /*line*/, const char* message) {
+  if (severity >= WARNING) {
+    fflush(stdout);
+    fprintf(stderr, "%s: %s\n", GetFileBasename(getprogname()), message);
+  } else {
+    fprintf(stdout, "%s\n", message);
+  }
 }
 
 void DefaultAborter(const char* abort_message) {
@@ -267,8 +297,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");
@@ -322,34 +351,18 @@
   Aborter() = std::move(aborter);
 }
 
-static const char* GetFileBasename(const char* file) {
-  // We can't use basename(3) even on Unix because the Mac doesn't
-  // have a non-modifying basename.
-  const char* last_slash = strrchr(file, '/');
-  if (last_slash != nullptr) {
-    return last_slash + 1;
-  }
-#if defined(_WIN32)
-  const char* last_backslash = strrchr(file, '\\');
-  if (last_backslash != nullptr) {
-    return last_backslash + 1;
-  }
-#endif
-  return file;
-}
-
 // This indirection greatly reduces the stack impact of having lots of
 // checks/logging in a function.
 class LogMessageData {
  public:
-  LogMessageData(const char* file, unsigned int line, LogId id,
-                 LogSeverity severity, int error)
+  LogMessageData(const char* file, unsigned int line, LogId id, LogSeverity severity,
+                 const char* tag, int error)
       : file_(GetFileBasename(file)),
         line_number_(line),
         id_(id),
         severity_(severity),
-        error_(error) {
-  }
+        tag_(tag),
+        error_(error) {}
 
   const char* GetFile() const {
     return file_;
@@ -363,6 +376,8 @@
     return severity_;
   }
 
+  const char* GetTag() const { return tag_; }
+
   LogId GetId() const {
     return id_;
   }
@@ -385,15 +400,15 @@
   const unsigned int line_number_;
   const LogId id_;
   const LogSeverity severity_;
+  const char* const tag_;
   const int error_;
 
   DISALLOW_COPY_AND_ASSIGN(LogMessageData);
 };
 
-LogMessage::LogMessage(const char* file, unsigned int line, LogId id,
-                       LogSeverity severity, int error)
-    : data_(new LogMessageData(file, line, id, severity, error)) {
-}
+LogMessage::LogMessage(const char* file, unsigned int line, LogId id, LogSeverity severity,
+                       const char* tag, int error)
+    : data_(new LogMessageData(file, line, id, severity, tag, error)) {}
 
 LogMessage::~LogMessage() {
   // Check severity again. This is duplicate work wrt/ LOG macros, but not LOG_STREAM.
@@ -411,16 +426,16 @@
     // Do the actual logging with the lock held.
     std::lock_guard<std::mutex> lock(LoggingLock());
     if (msg.find('\n') == std::string::npos) {
-      LogLine(data_->GetFile(), data_->GetLineNumber(), data_->GetId(),
-              data_->GetSeverity(), msg.c_str());
+      LogLine(data_->GetFile(), data_->GetLineNumber(), data_->GetId(), data_->GetSeverity(),
+              data_->GetTag(), msg.c_str());
     } else {
       msg += '\n';
       size_t i = 0;
       while (i < msg.size()) {
         size_t nl = msg.find('\n', i);
         msg[nl] = '\0';
-        LogLine(data_->GetFile(), data_->GetLineNumber(), data_->GetId(),
-                data_->GetSeverity(), &msg[i]);
+        LogLine(data_->GetFile(), data_->GetLineNumber(), data_->GetId(), data_->GetSeverity(),
+                data_->GetTag(), &msg[i]);
         // Undo the zero-termination so we can give the complete message to the aborter.
         msg[nl] = '\n';
         i = nl + 1;
@@ -438,10 +453,17 @@
   return data_->GetBuffer();
 }
 
-void LogMessage::LogLine(const char* file, unsigned int line, LogId id,
-                         LogSeverity severity, const char* message) {
-  const char* tag = ProgramInvocationName().c_str();
-  Logger()(id, severity, tag, file, line, message);
+void LogMessage::LogLine(const char* file, unsigned int line, LogId id, LogSeverity severity,
+                         const char* tag, const char* 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);
+  }
 }
 
 LogSeverity GetMinimumLogSeverity() {
diff --git a/base/logging_test.cpp b/base/logging_test.cpp
index adb041b..75b4ea0 100644
--- a/base/logging_test.cpp
+++ b/base/logging_test.cpp
@@ -192,6 +192,7 @@
 #undef CHECK_WOULD_LOG_ENABLED
 
 
+#if !defined(_WIN32)
 static std::string make_log_pattern(android::base::LogSeverity severity,
                                     const char* message) {
   static const char log_characters[] = "VDIWEFF";
@@ -203,21 +204,29 @@
       "%c \\d+-\\d+ \\d+:\\d+:\\d+ \\s*\\d+ \\s*\\d+ %s:\\d+] %s",
       log_char, basename(&holder[0]), message);
 }
+#endif
 
-static void CheckMessage(const CapturedStderr& cap,
-                         android::base::LogSeverity severity, const char* expected) {
-  std::string output;
-  ASSERT_EQ(0, lseek(cap.fd(), 0, SEEK_SET));
-  android::base::ReadFdToString(cap.fd(), &output);
+static void CheckMessage(CapturedStderr& cap, android::base::LogSeverity severity,
+                         const char* expected, const char* expected_tag = nullptr) {
+  std::string output = cap.str();
 
   // We can't usefully check the output of any of these on Windows because we
   // don't have std::regex, but we can at least make sure we printed at least as
   // 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
 }
@@ -598,3 +607,37 @@
 __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);
+}
+
+TEST(logging, StdioLogger) {
+  std::string err_str;
+  std::string out_str;
+  {
+    CapturedStderr cap_err;
+    CapturedStdout cap_out;
+    android::base::SetLogger(android::base::StdioLogger);
+    LOG(INFO) << "out";
+    LOG(ERROR) << "err";
+    err_str = cap_err.str();
+    out_str = cap_out.str();
+  }
+
+  // For INFO we expect just the literal "out\n".
+  ASSERT_EQ("out\n", out_str) << out_str;
+  // Whereas ERROR logging includes the program name.
+  ASSERT_EQ(android::base::Basename(android::base::GetExecutablePath()) + ": err\n", err_str)
+      << err_str;
+}
diff --git a/base/macros_test.cpp b/base/macros_test.cpp
new file mode 100644
index 0000000..2b522db
--- /dev/null
+++ b/base/macros_test.cpp
@@ -0,0 +1,30 @@
+/*
+ * 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/macros.h"
+
+#include <stdint.h>
+
+#include <gtest/gtest.h>
+
+TEST(macros, SIZEOF_MEMBER_macro) {
+  struct S {
+    int32_t i32;
+    double d;
+  };
+  ASSERT_EQ(4U, SIZEOF_MEMBER(S, i32));
+  ASSERT_EQ(8U, SIZEOF_MEMBER(S, d));
+}
diff --git a/base/parseint_test.cpp b/base/parseint_test.cpp
index 483b1d3..8f9ed77 100644
--- a/base/parseint_test.cpp
+++ b/base/parseint_test.cpp
@@ -36,6 +36,10 @@
   ASSERT_EQ(12, i);
   ASSERT_FALSE(android::base::ParseInt("-12", &i, 0, 15));
   ASSERT_FALSE(android::base::ParseInt("16", &i, 0, 15));
+
+  ASSERT_FALSE(android::base::ParseInt<int>("x", nullptr));
+  ASSERT_FALSE(android::base::ParseInt<int>("123x", nullptr));
+  ASSERT_TRUE(android::base::ParseInt<int>("1234", nullptr));
 }
 
 TEST(parseint, unsigned_smoke) {
@@ -55,6 +59,10 @@
   ASSERT_EQ(12u, i);
   ASSERT_FALSE(android::base::ParseUint("-12", &i, 15u));
   ASSERT_FALSE(android::base::ParseUint("16", &i, 15u));
+
+  ASSERT_FALSE(android::base::ParseUint<unsigned short>("x", nullptr));
+  ASSERT_FALSE(android::base::ParseUint<unsigned short>("123x", nullptr));
+  ASSERT_TRUE(android::base::ParseUint<unsigned short>("1234", nullptr));
 }
 
 TEST(parseint, no_implicit_octal) {
@@ -96,3 +104,44 @@
   ASSERT_FALSE(android::base::ParseInt("456x", &u));
   ASSERT_EQ(123u, u);
 }
+
+TEST(parseint, ParseByteCount) {
+  uint64_t i = 0;
+  ASSERT_TRUE(android::base::ParseByteCount("123b", &i));
+  ASSERT_EQ(123ULL, i);
+
+  ASSERT_TRUE(android::base::ParseByteCount("8k", &i));
+  ASSERT_EQ(8ULL * 1024, i);
+
+  ASSERT_TRUE(android::base::ParseByteCount("8M", &i));
+  ASSERT_EQ(8ULL * 1024 * 1024, i);
+
+  ASSERT_TRUE(android::base::ParseByteCount("6g", &i));
+  ASSERT_EQ(6ULL * 1024 * 1024 * 1024, i);
+
+  ASSERT_TRUE(android::base::ParseByteCount("1T", &i));
+  ASSERT_EQ(1ULL * 1024 * 1024 * 1024 * 1024, i);
+
+  ASSERT_TRUE(android::base::ParseByteCount("2p", &i));
+  ASSERT_EQ(2ULL * 1024 * 1024 * 1024 * 1024 * 1024, i);
+
+  ASSERT_TRUE(android::base::ParseByteCount("4e", &i));
+  ASSERT_EQ(4ULL * 1024 * 1024 * 1024 * 1024 * 1024 * 1024, i);
+}
+
+TEST(parseint, ParseByteCount_invalid_suffix) {
+  unsigned u;
+  ASSERT_FALSE(android::base::ParseByteCount("1x", &u));
+}
+
+TEST(parseint, ParseByteCount_overflow) {
+  uint64_t u64;
+  ASSERT_FALSE(android::base::ParseByteCount("4294967295E", &u64));
+
+  uint16_t u16;
+  ASSERT_TRUE(android::base::ParseByteCount("63k", &u16));
+  ASSERT_EQ(63U * 1024, u16);
+  ASSERT_TRUE(android::base::ParseByteCount("65535b", &u16));
+  ASSERT_EQ(65535U, u16);
+  ASSERT_FALSE(android::base::ParseByteCount("65k", &u16));
+}
diff --git a/base/properties.cpp b/base/properties.cpp
index 816bca0..d5a5918 100644
--- a/base/properties.cpp
+++ b/base/properties.cpp
@@ -14,37 +14,25 @@
  * limitations under the License.
  */
 
-#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
-
 #include "android-base/properties.h"
 
+#if defined(__BIONIC__)
+#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
 #include <sys/system_properties.h>
 #include <sys/_system_properties.h>
+#endif
 
 #include <algorithm>
 #include <chrono>
+#include <limits>
+#include <map>
 #include <string>
 
 #include <android-base/parseint.h>
 
-using namespace std::chrono_literals;
-
 namespace android {
 namespace base {
 
-std::string GetProperty(const std::string& key, const std::string& default_value) {
-  const prop_info* pi = __system_property_find(key.c_str());
-  if (pi == nullptr) return default_value;
-
-  char buf[PROP_VALUE_MAX];
-  if (__system_property_read(pi, nullptr, buf) > 0) return buf;
-
-  // If the property exists but is empty, also return the default value.
-  // Since we can't remove system properties, "empty" is traditionally
-  // the same as "missing" (this was true for cutils' property_get).
-  return default_value;
-}
-
 bool GetBoolProperty(const std::string& key, bool default_value) {
   std::string value = GetProperty(key, "");
   if (value == "1" || value == "y" || value == "yes" || value == "on" || value == "true") {
@@ -81,10 +69,43 @@
 template uint32_t GetUintProperty(const std::string&, uint32_t, uint32_t);
 template uint64_t GetUintProperty(const std::string&, uint64_t, uint64_t);
 
+#if !defined(__BIONIC__)
+static std::map<std::string, std::string>& g_properties = *new std::map<std::string, std::string>;
+static int __system_property_set(const char* key, const char* value) {
+  g_properties[key] = value;
+  return 0;
+}
+#endif
+
+std::string GetProperty(const std::string& key, const std::string& default_value) {
+  std::string property_value;
+#if defined(__BIONIC__)
+  const prop_info* pi = __system_property_find(key.c_str());
+  if (pi == nullptr) return default_value;
+
+  __system_property_read_callback(pi,
+                                  [](void* cookie, const char*, const char* value, unsigned) {
+                                    auto property_value = reinterpret_cast<std::string*>(cookie);
+                                    *property_value = value;
+                                  },
+                                  &property_value);
+#else
+  auto it = g_properties.find(key);
+  if (it == g_properties.end()) return default_value;
+  property_value = it->second;
+#endif
+  // If the property exists but is empty, also return the default value.
+  // Since we can't remove system properties, "empty" is traditionally
+  // the same as "missing" (this was true for cutils' property_get).
+  return property_value.empty() ? default_value : property_value;
+}
+
 bool SetProperty(const std::string& key, const std::string& value) {
   return (__system_property_set(key.c_str(), value.c_str()) == 0);
 }
 
+#if defined(__BIONIC__)
+
 struct WaitForPropertyData {
   bool done;
   const std::string* expected_value;
@@ -104,7 +125,7 @@
 static void DurationToTimeSpec(timespec& ts, const std::chrono::milliseconds d) {
   auto s = std::chrono::duration_cast<std::chrono::seconds>(d);
   auto ns = std::chrono::duration_cast<std::chrono::nanoseconds>(d - s);
-  ts.tv_sec = s.count();
+  ts.tv_sec = std::min<std::chrono::seconds::rep>(s.count(), std::numeric_limits<time_t>::max());
   ts.tv_nsec = ns.count();
 }
 
@@ -171,5 +192,7 @@
   return (WaitForPropertyCreation(key, relative_timeout, start_time) != nullptr);
 }
 
+#endif
+
 }  // namespace base
 }  // namespace android
diff --git a/base/properties_test.cpp b/base/properties_test.cpp
index de5f3dc..e7d4880 100644
--- a/base/properties_test.cpp
+++ b/base/properties_test.cpp
@@ -23,7 +23,9 @@
 #include <string>
 #include <thread>
 
-using namespace std::chrono_literals;
+#if !defined(_WIN32)
+using namespace std::literals;
+#endif
 
 TEST(properties, smoke) {
   android::base::SetProperty("debug.libbase.property_test", "hello");
@@ -126,6 +128,7 @@
 TEST(properties, GetUintProperty_uint64_t) { CheckGetUintProperty<uint64_t>(); }
 
 TEST(properties, WaitForProperty) {
+#if defined(__BIONIC__)
   std::atomic<bool> flag{false};
   std::thread thread([&]() {
     std::this_thread::sleep_for(100ms);
@@ -138,9 +141,13 @@
   flag = true;
   ASSERT_TRUE(android::base::WaitForProperty("debug.libbase.WaitForProperty_test", "b", 1s));
   thread.join();
+#else
+  GTEST_LOG_(INFO) << "This test does nothing on the host.\n";
+#endif
 }
 
 TEST(properties, WaitForProperty_timeout) {
+#if defined(__BIONIC__)
   auto t0 = std::chrono::steady_clock::now();
   ASSERT_FALSE(android::base::WaitForProperty("debug.libbase.WaitForProperty_timeout_test", "a",
                                               200ms));
@@ -149,9 +156,13 @@
   ASSERT_GE(std::chrono::duration_cast<std::chrono::milliseconds>(t1 - t0), 200ms);
   // Upper bounds on timing are inherently flaky, but let's try...
   ASSERT_LT(std::chrono::duration_cast<std::chrono::milliseconds>(t1 - t0), 600ms);
+#else
+  GTEST_LOG_(INFO) << "This test does nothing on the host.\n";
+#endif
 }
 
 TEST(properties, WaitForProperty_MaxTimeout) {
+#if defined(__BIONIC__)
   std::atomic<bool> flag{false};
   std::thread thread([&]() {
     android::base::SetProperty("debug.libbase.WaitForProperty_test", "a");
@@ -165,9 +176,13 @@
   // Test that this does not immediately return false due to overflow issues with the timeout.
   ASSERT_TRUE(android::base::WaitForProperty("debug.libbase.WaitForProperty_test", "b"));
   thread.join();
+#else
+  GTEST_LOG_(INFO) << "This test does nothing on the host.\n";
+#endif
 }
 
 TEST(properties, WaitForProperty_NegativeTimeout) {
+#if defined(__BIONIC__)
   std::atomic<bool> flag{false};
   std::thread thread([&]() {
     android::base::SetProperty("debug.libbase.WaitForProperty_test", "a");
@@ -181,9 +196,13 @@
   // Assert that this immediately returns with a negative timeout
   ASSERT_FALSE(android::base::WaitForProperty("debug.libbase.WaitForProperty_test", "b", -100ms));
   thread.join();
+#else
+  GTEST_LOG_(INFO) << "This test does nothing on the host.\n";
+#endif
 }
 
 TEST(properties, WaitForPropertyCreation) {
+#if defined(__BIONIC__)
   std::thread thread([&]() {
     std::this_thread::sleep_for(100ms);
     android::base::SetProperty("debug.libbase.WaitForPropertyCreation_test", "a");
@@ -192,9 +211,13 @@
   ASSERT_TRUE(android::base::WaitForPropertyCreation(
           "debug.libbase.WaitForPropertyCreation_test", 1s));
   thread.join();
+#else
+  GTEST_LOG_(INFO) << "This test does nothing on the host.\n";
+#endif
 }
 
 TEST(properties, WaitForPropertyCreation_timeout) {
+#if defined(__BIONIC__)
   auto t0 = std::chrono::steady_clock::now();
   ASSERT_FALSE(android::base::WaitForPropertyCreation(
           "debug.libbase.WaitForPropertyCreation_timeout_test", 200ms));
@@ -203,4 +226,7 @@
   ASSERT_GE(std::chrono::duration_cast<std::chrono::milliseconds>(t1 - t0), 200ms);
   // Upper bounds on timing are inherently flaky, but let's try...
   ASSERT_LT(std::chrono::duration_cast<std::chrono::milliseconds>(t1 - t0), 600ms);
+#else
+  GTEST_LOG_(INFO) << "This test does nothing on the host.\n";
+#endif
 }
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/strings.cpp b/base/strings.cpp
index bfdaf12..a8bb2a9 100644
--- a/base/strings.cpp
+++ b/base/strings.cpp
@@ -91,12 +91,20 @@
   return strncmp(s.c_str(), prefix, strlen(prefix)) == 0;
 }
 
+bool StartsWith(const std::string& s, const std::string& prefix) {
+  return strncmp(s.c_str(), prefix.c_str(), prefix.size()) == 0;
+}
+
 bool StartsWithIgnoreCase(const std::string& s, const char* prefix) {
   return strncasecmp(s.c_str(), prefix, strlen(prefix)) == 0;
 }
 
-static bool EndsWith(const std::string& s, const char* suffix, bool case_sensitive) {
-  size_t suffix_length = strlen(suffix);
+bool StartsWithIgnoreCase(const std::string& s, const std::string& prefix) {
+  return strncasecmp(s.c_str(), prefix.c_str(), prefix.size()) == 0;
+}
+
+static bool EndsWith(const std::string& s, const char* suffix, size_t suffix_length,
+                     bool case_sensitive) {
   size_t string_length = s.size();
   if (suffix_length > string_length) {
     return false;
@@ -106,11 +114,19 @@
 }
 
 bool EndsWith(const std::string& s, const char* suffix) {
-  return EndsWith(s, suffix, true);
+  return EndsWith(s, suffix, strlen(suffix), true);
+}
+
+bool EndsWith(const std::string& s, const std::string& suffix) {
+  return EndsWith(s, suffix.c_str(), suffix.size(), true);
 }
 
 bool EndsWithIgnoreCase(const std::string& s, const char* suffix) {
-  return EndsWith(s, suffix, false);
+  return EndsWith(s, suffix, strlen(suffix), false);
+}
+
+bool EndsWithIgnoreCase(const std::string& s, const std::string& suffix) {
+  return EndsWith(s, suffix.c_str(), suffix.size(), false);
 }
 
 bool EqualsIgnoreCase(const std::string& lhs, const std::string& rhs) {
diff --git a/base/strings_test.cpp b/base/strings_test.cpp
index 7ed5b2b..b8639ea 100644
--- a/base/strings_test.cpp
+++ b/base/strings_test.cpp
@@ -51,6 +51,14 @@
   ASSERT_EQ("bar", parts[2]);
 }
 
+TEST(strings, split_with_trailing_empty_part) {
+  std::vector<std::string> parts = android::base::Split("foo,bar,", ",");
+  ASSERT_EQ(3U, parts.size());
+  ASSERT_EQ("foo", parts[0]);
+  ASSERT_EQ("bar", parts[1]);
+  ASSERT_EQ("", parts[2]);
+}
+
 TEST(strings, split_null_char) {
   std::vector<std::string> parts =
       android::base::Split(std::string("foo\0bar", 7), std::string("\0", 1));
@@ -245,6 +253,26 @@
   ASSERT_FALSE(android::base::EndsWithIgnoreCase("foobar", "FOO"));
 }
 
+TEST(strings, StartsWith_std_string) {
+  ASSERT_TRUE(android::base::StartsWith("hello", std::string{"hell"}));
+  ASSERT_FALSE(android::base::StartsWith("goodbye", std::string{"hell"}));
+}
+
+TEST(strings, StartsWithIgnoreCase_std_string) {
+  ASSERT_TRUE(android::base::StartsWithIgnoreCase("HeLlO", std::string{"hell"}));
+  ASSERT_FALSE(android::base::StartsWithIgnoreCase("GoOdByE", std::string{"hell"}));
+}
+
+TEST(strings, EndsWith_std_string) {
+  ASSERT_TRUE(android::base::EndsWith("hello", std::string{"lo"}));
+  ASSERT_FALSE(android::base::EndsWith("goodbye", std::string{"lo"}));
+}
+
+TEST(strings, EndsWithIgnoreCase_std_string) {
+  ASSERT_TRUE(android::base::EndsWithIgnoreCase("HeLlO", std::string{"lo"}));
+  ASSERT_FALSE(android::base::EndsWithIgnoreCase("GoOdByE", std::string{"lo"}));
+}
+
 TEST(strings, EqualsIgnoreCase) {
   ASSERT_TRUE(android::base::EqualsIgnoreCase("foo", "FOO"));
   ASSERT_TRUE(android::base::EqualsIgnoreCase("FOO", "foo"));
diff --git a/base/test_utils.cpp b/base/test_utils.cpp
index 636477d..5096369 100644
--- a/base/test_utils.cpp
+++ b/base/test_utils.cpp
@@ -14,7 +14,6 @@
  * limitations under the License.
  */
 
-#include "android-base/logging.h"
 #include "android-base/test_utils.h"
 
 #include <fcntl.h>
@@ -33,6 +32,9 @@
 
 #include <string>
 
+#include <android-base/file.h>
+#include <android-base/logging.h>
+
 #ifdef _WIN32
 int mkstemp(char* template_name) {
   if (_mktemp(template_name) == nullptr) {
@@ -84,9 +86,23 @@
   init(GetSystemTempDir());
 }
 
+TemporaryFile::TemporaryFile(const std::string& tmp_dir) {
+  init(tmp_dir);
+}
+
 TemporaryFile::~TemporaryFile() {
-  close(fd);
-  unlink(path);
+  if (fd != -1) {
+    close(fd);
+  }
+  if (remove_file_) {
+    unlink(path);
+  }
+}
+
+int TemporaryFile::release() {
+  int result = fd;
+  fd = -1;
+  return result;
 }
 
 void TemporaryFile::init(const std::string& tmp_dir) {
@@ -109,31 +125,38 @@
   return (mkdtemp(path) != nullptr);
 }
 
-CapturedStderr::CapturedStderr() : old_stderr_(-1) {
-  init();
+CapturedStdFd::CapturedStdFd(int std_fd) : std_fd_(std_fd), old_fd_(-1) {
+  Init();
 }
 
-CapturedStderr::~CapturedStderr() {
-  reset();
+CapturedStdFd::~CapturedStdFd() {
+  Reset();
 }
 
-int CapturedStderr::fd() const {
+int CapturedStdFd::fd() const {
   return temp_file_.fd;
 }
 
-void CapturedStderr::init() {
+std::string CapturedStdFd::str() {
+  std::string result;
+  CHECK_EQ(0, TEMP_FAILURE_RETRY(lseek(fd(), 0, SEEK_SET)));
+  android::base::ReadFdToString(fd(), &result);
+  return result;
+}
+
+void CapturedStdFd::Init() {
 #if defined(_WIN32)
   // On Windows, stderr is often buffered, so make sure it is unbuffered so
   // that we can immediately read back what was written to stderr.
-  CHECK_EQ(0, setvbuf(stderr, NULL, _IONBF, 0));
+  if (std_fd_ == STDERR_FILENO) CHECK_EQ(0, setvbuf(stderr, NULL, _IONBF, 0));
 #endif
-  old_stderr_ = dup(STDERR_FILENO);
-  CHECK_NE(-1, old_stderr_);
-  CHECK_NE(-1, dup2(fd(), STDERR_FILENO));
+  old_fd_ = dup(std_fd_);
+  CHECK_NE(-1, old_fd_);
+  CHECK_NE(-1, dup2(fd(), std_fd_));
 }
 
-void CapturedStderr::reset() {
-  CHECK_NE(-1, dup2(old_stderr_, STDERR_FILENO));
-  CHECK_EQ(0, close(old_stderr_));
+void CapturedStdFd::Reset() {
+  CHECK_NE(-1, dup2(old_fd_, std_fd_));
+  CHECK_EQ(0, close(old_fd_));
   // Note: cannot restore prior setvbuf() setting.
 }
diff --git a/base/test_utils_test.cpp b/base/test_utils_test.cpp
new file mode 100644
index 0000000..597271a
--- /dev/null
+++ b/base/test_utils_test.cpp
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2017 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/test_utils.h"
+
+#include <gtest/gtest-spi.h>
+#include <gtest/gtest.h>
+
+namespace android {
+namespace base {
+
+TEST(TestUtilsTest, AssertMatch) {
+  ASSERT_MATCH("foobar", R"(fo+baz?r)");
+  EXPECT_FATAL_FAILURE(ASSERT_MATCH("foobar", R"(foobaz)"), "regex mismatch");
+}
+
+TEST(TestUtilsTest, AssertNotMatch) {
+  ASSERT_NOT_MATCH("foobar", R"(foobaz)");
+  EXPECT_FATAL_FAILURE(ASSERT_NOT_MATCH("foobar", R"(foobar)"), "regex mismatch");
+}
+
+TEST(TestUtilsTest, ExpectMatch) {
+  EXPECT_MATCH("foobar", R"(fo+baz?r)");
+  EXPECT_NONFATAL_FAILURE(EXPECT_MATCH("foobar", R"(foobaz)"), "regex mismatch");
+}
+
+TEST(TestUtilsTest, ExpectNotMatch) {
+  EXPECT_NOT_MATCH("foobar", R"(foobaz)");
+  EXPECT_NONFATAL_FAILURE(EXPECT_NOT_MATCH("foobar", R"(foobar)"), "regex mismatch");
+}
+
+}  // namespace base
+}  // namespace android
diff --git a/base/threads.cpp b/base/threads.cpp
new file mode 100644
index 0000000..48f6197
--- /dev/null
+++ b/base/threads.cpp
@@ -0,0 +1,54 @@
+/*
+ * 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
+
+#if defined(__GLIBC__)
+int tgkill(int tgid, int tid, int sig) {
+  return syscall(__NR_tgkill, tgid, tid, sig);
+}
+#endif
diff --git a/base/unique_fd_test.cpp b/base/unique_fd_test.cpp
new file mode 100644
index 0000000..3fdf12a
--- /dev/null
+++ b/base/unique_fd_test.cpp
@@ -0,0 +1,54 @@
+/*
+ * 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/unique_fd.h"
+
+#include <gtest/gtest.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+using android::base::unique_fd;
+
+TEST(unique_fd, unowned_close) {
+#if defined(__BIONIC__)
+  unique_fd fd(open("/dev/null", O_RDONLY));
+  EXPECT_DEATH(close(fd.get()), "incorrect tag");
+#endif
+}
+
+TEST(unique_fd, untag_on_release) {
+  unique_fd fd(open("/dev/null", O_RDONLY));
+  close(fd.release());
+}
+
+TEST(unique_fd, move) {
+  unique_fd fd(open("/dev/null", O_RDONLY));
+  unique_fd fd_moved = std::move(fd);
+  ASSERT_EQ(-1, fd.get());
+  ASSERT_GT(fd_moved.get(), -1);
+}
+
+TEST(unique_fd, unowned_close_after_move) {
+#if defined(__BIONIC__)
+  unique_fd fd(open("/dev/null", O_RDONLY));
+  unique_fd fd_moved = std::move(fd);
+  ASSERT_EQ(-1, fd.get());
+  ASSERT_GT(fd_moved.get(), -1);
+  EXPECT_DEATH(close(fd_moved.get()), "incorrect tag");
+#endif
+}
diff --git a/base/utf8.cpp b/base/utf8.cpp
index 5984fb0..adb46d0 100644
--- a/base/utf8.cpp
+++ b/base/utf8.cpp
@@ -195,7 +195,7 @@
   return _wfopen(name_utf16.c_str(), mode_utf16.c_str());
 }
 
-int mkdir(const char* name, mode_t mode) {
+int mkdir(const char* name, mode_t) {
   std::wstring name_utf16;
   if (!UTF8PathToWindowsLongPath(name, &name_utf16)) {
     return -1;
diff --git a/bootstat/Android.bp b/bootstat/Android.bp
index bc90a6e..8fc2171 100644
--- a/bootstat/Android.bp
+++ b/bootstat/Android.bp
@@ -32,9 +32,6 @@
         "liblog",
         "libmetricslogger",
     ],
-    whole_static_libs: ["libgtest_prod"],
-    // Clang is required because of C++14
-    clang: true,
 }
 
 // bootstat static library
@@ -67,6 +64,14 @@
     defaults: ["bootstat_defaults"],
     static_libs: ["libbootstat"],
     init_rc: ["bootstat.rc"],
+    product_variables: {
+        pdk: {
+            enabled: false,
+        },
+        debuggable: {
+            init_rc: ["bootstat-debug.rc"],
+        },
+    },
     srcs: ["bootstat.cpp"],
 }
 
diff --git a/bootstat/OWNERS b/bootstat/OWNERS
new file mode 100644
index 0000000..7fe0443
--- /dev/null
+++ b/bootstat/OWNERS
@@ -0,0 +1 @@
+jhawkins@google.com
diff --git a/bootstat/boot_event_record_store.cpp b/bootstat/boot_event_record_store.cpp
index 99d9405..e2a4b04 100644
--- a/bootstat/boot_event_record_store.cpp
+++ b/bootstat/boot_event_record_store.cpp
@@ -58,16 +58,15 @@
 }
 
 void BootEventRecordStore::AddBootEvent(const std::string& event) {
-    auto uptime = std::chrono::duration_cast<std::chrono::seconds>(
-        android::base::boot_clock::now().time_since_epoch());
-    AddBootEventWithValue(event, uptime.count());
+  auto uptime = std::chrono::duration_cast<std::chrono::seconds>(
+      android::base::boot_clock::now().time_since_epoch());
+  AddBootEventWithValue(event, uptime.count());
 }
 
 // The implementation of AddBootEventValue makes use of the mtime file
 // attribute to store the value associated with a boot event in order to
 // optimize on-disk size requirements and small-file thrashing.
-void BootEventRecordStore::AddBootEventWithValue(
-    const std::string& event, int32_t value) {
+void BootEventRecordStore::AddBootEventWithValue(const std::string& event, int32_t value) {
   std::string record_path = GetBootEventPath(event);
   int record_fd = creat(record_path.c_str(), S_IRUSR | S_IWUSR);
   if (record_fd == -1) {
@@ -96,8 +95,7 @@
   close(record_fd);
 }
 
-bool BootEventRecordStore::GetBootEvent(
-    const std::string& event, BootEventRecord* record) const {
+bool BootEventRecordStore::GetBootEvent(const std::string& event, BootEventRecord* record) const {
   CHECK_NE(static_cast<BootEventRecord*>(nullptr), record);
   CHECK(!event.empty());
 
@@ -112,8 +110,7 @@
   return true;
 }
 
-std::vector<BootEventRecordStore::BootEventRecord> BootEventRecordStore::
-    GetAllBootEvents() const {
+std::vector<BootEventRecordStore::BootEventRecord> BootEventRecordStore::GetAllBootEvents() const {
   std::vector<BootEventRecord> events;
 
   std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(store_path_.c_str()), closedir);
@@ -147,8 +144,7 @@
   store_path_ = path;
 }
 
-std::string BootEventRecordStore::GetBootEventPath(
-    const std::string& event) const {
+std::string BootEventRecordStore::GetBootEventPath(const std::string& event) const {
   DCHECK_EQ('/', store_path_.back());
   return store_path_ + event;
 }
diff --git a/bootstat/boot_event_record_store.h b/bootstat/boot_event_record_store.h
index a2b8318..f872c85 100644
--- a/bootstat/boot_event_record_store.h
+++ b/bootstat/boot_event_record_store.h
@@ -17,12 +17,12 @@
 #ifndef BOOT_EVENT_RECORD_STORE_H_
 #define BOOT_EVENT_RECORD_STORE_H_
 
+#include <android-base/macros.h>
+#include <gtest/gtest_prod.h>
 #include <cstdint>
 #include <string>
 #include <utility>
 #include <vector>
-#include <android-base/macros.h>
-#include <gtest/gtest_prod.h>
 
 // BootEventRecordStore manages the persistence of boot events to the record
 // store and the retrieval of all boot event records from the store.
diff --git a/bootstat/boot_event_record_store_test.cpp b/bootstat/boot_event_record_store_test.cpp
index d98169b..4b7ab36 100644
--- a/bootstat/boot_event_record_store_test.cpp
+++ b/bootstat/boot_event_record_store_test.cpp
@@ -94,20 +94,16 @@
 
 // Returns the time in seconds since boot.
 time_t GetUptimeSeconds() {
-    return std::chrono::duration_cast<std::chrono::seconds>(
-               android::base::boot_clock::now().time_since_epoch())
-        .count();
+  return std::chrono::duration_cast<std::chrono::seconds>(
+             android::base::boot_clock::now().time_since_epoch())
+      .count();
 }
 
 class BootEventRecordStoreTest : public ::testing::Test {
  public:
-  BootEventRecordStoreTest() {
-    store_path_ = std::string(store_dir_.path) + "/";
-  }
+  BootEventRecordStoreTest() { store_path_ = std::string(store_dir_.path) + "/"; }
 
-  const std::string& GetStorePathForTesting() const {
-    return store_path_;
-  }
+  const std::string& GetStorePathForTesting() const { return store_path_; }
 
  private:
   void TearDown() {
@@ -159,9 +155,7 @@
   store.AddBootEvent("triassic");
 
   const std::string EXPECTED_NAMES[] = {
-    "cretaceous",
-    "jurassic",
-    "triassic",
+      "cretaceous", "jurassic", "triassic",
   };
 
   auto events = store.GetAllBootEvents();
diff --git a/bootstat/boot_reason_test.sh b/bootstat/boot_reason_test.sh
new file mode 100755
index 0000000..71d3ecb
--- /dev/null
+++ b/bootstat/boot_reason_test.sh
@@ -0,0 +1,1254 @@
+#! /bin/bash
+#
+# Bootstat boot reason tests
+#
+# throughout testing:
+# - manual tests can only run on eng/userdebug builds
+# - watch adb logcat -b all -d -s bootstat
+# - watch adb logcat -b all -d | audit2allow
+# - wait until screen is up, boot has completed, can mean wait for
+#   sys.boot_completed=1 and sys.logbootcomplete=1 to be true
+#
+# All test frames, and nothing else, must be function names prefixed and
+# specifiged with the pattern 'test_<test>() {' as this is also how the
+# script discovers the full list of tests by inspecting its own code.
+#
+
+# Helper variables
+
+SPACE=" "
+ESCAPE=""
+TAB="	"
+GREEN="${ESCAPE}[38;5;40m"
+RED="${ESCAPE}[38;5;196m"
+NORMAL="${ESCAPE}[0m"
+# Best guess to an average device's reboot time, refined as tests return
+DURATION_DEFAULT=45
+STOP_ON_FAILURE=false
+
+# Helper functions
+
+[ "USAGE: inFastboot
+
+Returns: true if device is in fastboot mode" ]
+inFastboot() {
+  fastboot devices | grep "^${ANDROID_SERIAL}[${SPACE}${TAB}]" > /dev/null
+}
+
+[ "USAGE: inAdb
+
+Returns: true if device is in adb mode" ]
+inAdb() {
+  adb devices | grep -v 'List of devices attached' | grep "^${ANDROID_SERIAL}[${SPACE}${TAB}]" > /dev/null
+}
+
+[ "USAGE: hasPstore
+
+Returns: true if device (likely) has pstore data" ]
+hasPstore() {
+  if inAdb && [ 0 -eq `adb shell su root ls /sys/fs/pstore | wc -l` ]; then
+    false
+  fi
+}
+
+[ "USAGE: get_property <prop>
+
+Returns the property value" ]
+get_property() {
+  adb shell getprop ${1} 2>&1 </dev/null
+}
+
+[ "USAGE: isDebuggable
+
+Returns: true if device is (likely) a debug build" ]
+isDebuggable() {
+  if inAdb && [ 1 -ne `get_property ro.debuggable` ]; then
+    false
+  fi
+}
+
+[ "USAGE: checkDebugBuild [--noerror]
+
+Returns: true if device is a userdebug or eng release" ]
+checkDebugBuild() {
+  if isDebuggable; then
+    echo "INFO: '${TEST}' test requires userdebug build"
+  elif [ -n "${1}" ]; then
+    echo "WARNING: '${TEST}' test requires userdebug build"
+    false
+  else
+    echo "ERROR: '${TEST}' test requires userdebug build, skipping FAILURE"
+    duration_prefix="~"
+    duration_estimate=1
+    false
+  fi >&2
+}
+
+[ "USAGE: setBootloaderBootReason [value]
+
+Returns: true if device supports and set boot reason injection" ]
+setBootloaderBootReason() {
+  inAdb || ( echo "ERROR: device not in adb mode." >&2 ; false ) || return 1
+  if [ -z "`adb shell ls /etc/init/bootstat-debug.rc 2>/dev/null`" ]; then
+    echo "ERROR: '${TEST}' test requires /etc/init/bootstat-debug.rc" >&2
+    return 1
+  fi
+  checkDebugBuild || return 1
+  if adb shell su root "cat /proc/cmdline | tr '\\0 ' '\\n\\n'" |
+     grep '^androidboot[.]bootreason=[^ ]' >/dev/null; then
+    echo "ERROR: '${TEST}' test requires a device with a bootloader that" >&2
+    echo "       does not set androidboot.bootreason kernel parameter." >&2
+    return 1
+  fi
+  adb shell su root setprop persist.test.boot.reason "'${1}'" 2>/dev/null
+  test_reason="`get_property persist.test.boot.reason`"
+  if [ X"${test_reason}" != X"${1}" ]; then
+    echo "ERROR: can not set persist.test.boot.reason to '${1}'." >&2
+    return 1
+  fi
+}
+
+[ "USAGE: enterPstore
+
+Prints a warning string requiring functional pstore
+
+Returns: pstore_ok variable set to true or false" ]
+enterPstore() {
+  if hasPstore; then
+    echo "INFO: '${TEST}' test requires functional and reliable pstore"
+    pstore_ok=true
+  else
+    echo "WARNING: '${TEST}' test requires functional pstore"
+    pstore_ok=false
+  fi >&2
+  ${pstore_ok}
+}
+
+[ "USAGE: exitPstore
+
+Prints an error string requiring functional pstore
+
+Returns: clears error if pstore dysfunctional" ]
+exitPstore() {
+  save_ret=${?}
+  if [ ${save_ret} != 0 ]; then
+    if hasPstore; then
+      return ${save_ret}
+    fi
+    if [ true = ${pstore_ok} ]; then
+      echo "WARNING: '${TEST}' test requires functional pstore"
+      return ${save_ret}
+    fi
+    echo "ERROR: '${TEST}' test requires functional pstore, skipping FAILURE"
+    duration_prefix="~"
+    duration_estimate=1
+  fi >&2
+}
+
+[ "USAGE: format_duration <seconds>
+
+human readable output whole seconds, whole minutes or mm:ss" ]
+format_duration() {
+  if [ -z "${1}" ]; then
+    echo unknown
+    return
+  fi
+  seconds=`expr ${1} % 60`
+  minutes=`expr ${1} / 60`
+  if [ 0 -eq ${minutes} ]; then
+    if [ 1 -eq ${1} ]; then
+      echo 1 second
+      return
+    fi
+    echo ${1} seconds
+    return
+  elif [ 60 -eq ${1} ]; then
+    echo 1 minute
+    return
+  elif [ 0 -eq ${seconds} ]; then
+    echo ${minutes} minutes
+    return
+  fi
+  echo ${minutes}:`expr ${seconds} / 10``expr ${seconds} % 10`
+}
+
+wait_for_screen_timeout=900
+[ "USAGE: wait_for_screen [-n] [TIMEOUT]
+
+-n - echo newline at exit
+TIMEOUT - default `format_duration ${wait_for_screen_timeout}`" ]
+wait_for_screen() {
+  exit_function=true
+  if [ X"-n" = X"${1}" ]; then
+    exit_function=echo
+    shift
+  fi
+  timeout=${wait_for_screen_timeout}
+  if [ ${#} -gt 0 ]; then
+    timeout=${1}
+    shift
+  fi
+  counter=0
+  while true; do
+    if inFastboot; then
+      fastboot reboot
+    elif inAdb; then
+      if [ 0 != ${counter} ]; then
+        adb wait-for-device </dev/null >/dev/null 2>/dev/null
+      fi
+      if [ -n "`get_property sys.boot.reason`" ]
+      then
+        vals=`get_property |
+              sed -n 's/[[]sys[.]\(boot_completed\|logbootcomplete\)[]]: [[]\([01]\)[]]$/\1=\2/p'`
+        if [ "${vals}" = "`echo boot_completed=1 ; echo logbootcomplete=1`" ]
+        then
+          sleep 1
+          break
+        fi
+        if [ "${vals}" = "`echo logbootcomplete=1 ; echo boot_completed=1`" ]
+        then
+          sleep 1
+          break
+        fi
+      fi
+    fi
+    counter=`expr ${counter} + 1`
+    if [ ${counter} -gt ${timeout} ]; then
+      ${exit_function}
+      echo "ERROR: wait_for_screen() timed out (`format_duration ${timeout}`)" >&2
+      return 1
+    fi
+    sleep 1
+  done
+  ${exit_function}
+}
+
+[ "USAGE: EXPECT_EQ <lval> <rval> [message]
+
+Returns true if (regex) lval matches rval" ]
+EXPECT_EQ() {
+  lval="${1}"
+  rval="${2}"
+  shift 2
+  if ! ( echo X"${rval}" | grep '^X'"${lval}"'$' >/dev/null 2>/dev/null ); then
+    if [ `echo ${lval}${rval}${*} | wc -c` -gt 50 -o "${rval}" != "${rval%
+*}" ]; then
+      echo "ERROR: expected \"${lval}\"" >&2
+      echo "       got \"${rval}\"" |
+        sed ': again
+             N
+             s/\(\n\)\([^ ]\)/\1             \2/
+             t again' >&2
+      if [ -n "${*}" ] ; then
+        echo "       ${*}" >&2
+      fi
+    else
+      echo "ERROR: expected \"${lval}\" got \"${rval}\" ${*}" >&2
+    fi
+    return 1
+  fi
+  if [ -n "${*}" ] ; then
+    if [ X"${lval}" != X"${rval}" ]; then
+      if [ `echo ${lval}${rval}${*} | wc -c` -gt 60 -o "${rval}" != "${rval%
+*}" ]; then
+        echo "INFO: ok \"${lval}\"" >&2
+        echo "       = \"${rval}\"" |
+          sed ': again
+               N
+               s/\(\n\)\([^ ]\)/\1          \2/
+               t again' >&2
+        if [ -n "${*}" ] ; then
+          echo "      ${*}" >&2
+        fi
+      else
+        echo "INFO: ok \"${lval}\" = \"${rval}\" ${*}" >&2
+      fi
+    else
+      echo "INFO: ok \"${lval}\" ${*}" >&2
+    fi
+  fi
+  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
+and the incoming return value is true as well (wired-or)" ]
+EXPECT_PROPERTY() {
+  save_ret=${?}
+  property="${1}"
+  value="${2}"
+  shift 2
+  val=`get_property ${property}`
+  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}
+}
+
+[ "USAGE: report_bootstat_logs <expected> ...
+
+if not prefixed with a minus (-), <expected> will become a series of expected
+matches:
+
+    bootstat: Canonical boot reason: <expected_property_value>
+
+If prefixed with a minus, <expected> will look for an exact match after
+removing the minux prefix.  All expected content is _dropped_ from the output
+and in essence forms a known blacklist, unexpected content will show.
+
+Report any logs, minus a known blacklist, preserve the current exit status" ]
+report_bootstat_logs() {
+  save_ret=${?}
+  match=
+  for i in "${@}"; do
+    if [ X"${i}" != X"${i#-}" ] ; then
+      match="${match}
+${i#-}"
+    else
+      match="${match}
+bootstat: Canonical boot reason: ${i}"
+    fi
+  done
+  adb logcat -b all -d |
+  grep bootstat[^e] |
+  grep -v -F "bootstat: Service started: /system/bin/bootstat --record_boot_complete${match}
+bootstat: Failed to read /data/misc/bootstat/post_decrypt_time_elapsed: No such file or directory
+bootstat: Failed to parse boot time record: /data/misc/bootstat/post_decrypt_time_elapsed
+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...
+init    : Parsing file /system/etc/init/bootstat-debug.rc...
+init    : processing action (persist.test.boot.reason=*) from (/system/etc/init/bootstat-debug.rc:
+init    : Command 'setprop ro.boot.bootreason \${persist.test.boot.reason}' action=persist.test.boot.reason=* (/system/etc/init/bootstat-debug.rc:
+init    : processing action (post-fs-data) from (/system/etc/init/bootstat.rc
+init    : processing action (boot) from (/system/etc/init/bootstat.rc
+init    : processing action (ro.boot.bootreason=*) from (/system/etc/init/bootstat.rc
+init    : processing action (sys.boot_completed=1 && sys.logbootcomplete=1) from (/system/etc/init/bootstat.rc
+ (/system/bin/bootstat --record_boot_complete --record_boot_reason --record_time_since_factory_reset -l)'
+ (/system/bin/bootstat --set_system_boot_reason --record_boot_complete --record_boot_reason --record_time_since_factory_reset -l)'
+ (/system/bin/bootstat -r post_decrypt_time_elapsed)'
+init    : Command 'exec - system log -- /system/bin/bootstat --record_boot_complete' action=sys.boot_completed=1 && sys.logbootcomplete=1 (/system/etc/init/bootstat.rc:
+init    : Command 'exec - system log -- /system/bin/bootstat --record_boot_reason' action=sys.boot_completed=1 && sys.logbootcomplete=1 (/system/etc/init/bootstat.rc:
+init    : Command 'exec - system log -- /system/bin/bootstat --record_time_since_factory_reset' action=sys.boot_completed=1 && sys.logbootcomplete=1 (/system/etc/init/bootstat.rc:
+init    : Command 'exec_background - system log -- /system/bin/bootstat --set_system_boot_reason --record_boot_complete --record_boot_reason --record_time_since_factory_reset -l' action=sys.boot_completed=1 && sys.logbootcomplete=1 (/system/etc/init/bootstat.rc
+ (/system/bin/bootstat --record_boot_complete)'...
+ (/system/bin/bootstat --record_boot_complete)' (pid${SPACE}
+ (/system/bin/bootstat --record_boot_reason)'...
+ (/system/bin/bootstat --record_boot_reason)' (pid${SPACE}
+ (/system/bin/bootstat --record_time_since_factory_reset)'...
+ (/system/bin/bootstat --record_time_since_factory_reset)' (pid${SPACE}
+ (/system/bin/bootstat -l)'...
+ (/system/bin/bootstat -l)' (pid " |
+  grep -v 'bootstat: Unknown boot reason: $' # Hikey Special
+  return ${save_ret}
+}
+
+[ "USAGE: start_test [message]
+
+Record start of test, preserve exit status" ]
+start_test() {
+  save_ret=${?}
+  duration_prefix="~"
+  duration_estimate=1
+  START=`date +%s`
+  echo "${GREEN}[ RUN      ]${NORMAL} ${TEST} ${*}"
+  return ${save_ret}
+}
+
+duration_sum_diff=0
+duration_num=0
+[ "USAGE: duration_test [[prefix]seconds]
+
+Report the adjusted and expected test duration" ]
+duration_test() {
+  duration_prefix=${1%%[0123456789]*}
+  if [ -z "${duration_prefix}" ]; then
+    duration_prefix="~"
+  fi
+  duration_estimate="${1#${duration_prefix}}"
+  if [ -z "${duration_estimate}" ]; then
+    duration_estimate="${DURATION_DEFAULT}"
+  fi
+  duration_new_estimate="${duration_estimate}"
+  if [ 0 -ne ${duration_num} ]; then
+    duration_new_estimate=`expr ${duration_new_estimate} + \
+      \( ${duration_num} / 2 + ${duration_sum_diff} \) / ${duration_num}`
+    # guard against catastrophe
+    if [ -z "${duration_new_estimate}" ]; then
+      duration_new_estimate=${duration_estimate}
+    fi
+  fi
+  # negative values are so undignified
+  if [ 0 -ge ${duration_new_estimate} ]; then
+    duration_new_estimate=1
+  fi
+  echo "INFO: expected duration of '${TEST}' test" \
+       "${duration_prefix}`format_duration ${duration_new_estimate}`" >&2
+}
+
+[ "USAGE: end_test [message]
+
+Document duration and success of test, preserve exit status" ]
+end_test() {
+  save_ret=${?}
+  END=`date +%s`
+  duration=`expr ${END} - ${START} 2>/dev/null`
+  [ 0 -ge ${duration} ] ||
+    echo "INFO: '${TEST}' test duration `format_duration ${duration}`" >&2
+  if [ ${save_ret} = 0 ]; then
+    if [ 0 -lt ${duration} -a 0 -lt ${duration_estimate} -a \( \
+           X"~" = X"${duration_prefix}" -o \
+           ${duration_estimate} -gt ${duration} \) ]; then
+      duration_sum_diff=`expr ${duration_sum_diff} + \
+                              ${duration} - ${duration_estimate}`
+      duration_num=`expr ${duration_num} + 1`
+    fi
+    echo "${GREEN}[       OK ]${NORMAL} ${TEST} ${*}"
+  else
+    echo "${RED}[  FAILED  ]${NORMAL} ${TEST} ${*}"
+    if ${STOP_ON_FAILURE}; then
+      exit ${save_ret}
+    fi
+  fi
+  return ${save_ret}
+}
+
+[ "USAGE: wrap_test <test> [message]
+
+All tests below are wrapped with this helper" ]
+wrap_test() {
+  if [ -z "${1}" -o X"nothing" = X"${1}" ]; then
+    return
+  fi
+  TEST=${1}
+  shift
+  start_test ${1}
+  eval test_${TEST}
+  end_test ${2}
+}
+
+[ "USAGE: validate_reason <value>
+
+Check property for CTS compliance with our expectations. Return a cleansed
+string representing what is acceptable.
+
+NB: must also roughly match heuristics in system/core/bootstat/bootstat.cpp" ]
+validate_reason() {
+  var=`echo -n ${*} |
+       tr '[A-Z]' '[a-z]' |
+       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,?* ) ;;
+    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,?* ) ;;
+    # Aliases and Heuristics
+    *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" ;;
+    *2sec_reboot* )                         var="cold,rtc,2sec" ;;
+    *wdt_by_pass_pwk* )                     var="warm" ;;
+    wdt )                                   var="reboot" ;;
+    *tool_by_pass_pwk* )                    var="reboot,tool" ;;
+    *bootloader* )                          var="bootloader" ;;
+    * )                                     var="reboot" ;;
+  esac
+  echo ${var}
+}
+
+[ "USAGE: validate_property <property>
+
+Check property for CTS compliance with our expectations. Return a cleansed
+string representing what is acceptable.
+
+NB: must also roughly match heuristics in system/core/bootstat/bootstat.cpp" ]
+validate_property() {
+  val=`get_property ${1}`
+  ret=`validate_reason "${val}"`
+  if [ "reboot" = "${ret}" ]; then
+    ret=`validate_reason "reboot,${val}"`
+  fi
+  echo ${ret}
+}
+
+[ "USAGE: check_boilerblate_properties
+
+Check for common property values" ]
+check_boilerplate_properties() {
+  EXPECT_PROPERTY persist.sys.boot.reason ""
+  save_ret=${?}
+  reason=`validate_property sys.boot.reason`
+  ( exit ${save_ret} )  # because one can not just do ?=${save_ret}
+  EXPECT_PROPERTY persist.sys.boot.reason.history "${reason},[1-9][0-9]*\(\|[^0-9].*\)"
+}
+
+#
+# Actual test frames
+#
+
+[ "USAGE: test_properties
+
+properties test
+- (wait until screen is up, boot has completed)
+- adb shell getprop ro.boot.bootreason (bootloader reason)
+- adb shell getprop persist.sys.boot.reason (last reason)
+- adb shell getprop sys.boot.reason.last (last last reason)
+- adb shell getprop sys.boot.reason (system reason)
+- NB: all should have a value that is compliant with our known set." ]
+test_properties() {
+  duration_test 1
+  wait_for_screen
+  retval=0
+  # sys.boot.reason is last for a reason
+  check_set="ro.boot.bootreason sys.boot.reason.last sys.boot.reason"
+  bootloader=""
+  # NB: this test could fail if performed _after_ optional_factory_reset test
+  # and will report
+  #  ERROR: expected "reboot" got ""
+  #        for Android property sys.boot.reason.last
+  # following is mitigation for the persist.sys.boot.reason, skip it
+  if [ "reboot,factory_reset" = "`validate_property ro.boot_bootreason`" ]; then
+    check_set="ro.boot.bootreason sys.boot.reason"
+    bootloader="bootloader"
+  fi
+  for prop in ${check_set}; do
+    reason=`validate_property ${prop}`
+    EXPECT_PROPERTY ${prop} ${reason} || retval=${?}
+  done
+  check_boilerplate_properties || retval=${?}
+  report_bootstat_logs ${reason} ${bootloader}
+  return ${retval}
+}
+
+[ "USAGE: test_ota
+
+ota test
+- rm out/.kati_stamp-* out/build_date.txt out/build_number.txt
+- rm out/target/product/*/*/*.prop
+- rm -r out/target/product/*/obj/ETC/system_build_prop_intermediates
+- m
+- NB: ro.build.date.utc should update
+- fastboot flashall
+- (wait until screen is up, boot has completed)
+- adb shell getprop sys.boot.reason
+- NB: should report ota
+
+Decision to change the build itself rather than trick bootstat by
+rummaging through its data files was made." ]
+test_ota() {
+  duration_test ">300"
+  echo "      extended by build and flashing times" >&2
+  if [ -z "${TARGET_PRODUCT}" -o \
+       -z "${ANDROID_PRODUCT_OUT}" -o \
+       -z "${ANDROID_BUILD_TOP}" -o \
+       -z "${TARGET_BUILD_VARIANT}" ]; then
+    echo "ERROR: Missing envsetup.sh and lunch" >&2
+    return 1
+  fi
+  rm ${ANDROID_PRODUCT_OUT%/out/*}/out/.kati_stamp-* ||
+    true
+  rm ${ANDROID_PRODUCT_OUT%/out/*}/out/build_date.txt ||
+    true
+  rm ${ANDROID_PRODUCT_OUT%/out/*}/out/build_number.txt ||
+    true
+  rm ${ANDROID_PRODUCT_OUT}/*/*.prop ||
+    true
+  rm -r ${ANDROID_PRODUCT_OUT}/obj/ETC/system_build_prop_intermediates ||
+    true
+  pushd ${ANDROID_BUILD_TOP} >&2
+  make -j50 >&2
+  if [ ${?} != 0 ]; then
+    popd >&2
+    return 1
+  fi
+  if ! inFastboot; then
+    adb reboot-bootloader >&2
+  fi
+  fastboot flashall >&2
+  popd >&2
+  wait_for_screen
+  EXPECT_PROPERTY sys.boot.reason "\(reboot,ota\|bootloader\)"
+  EXPECT_PROPERTY sys.boot.reason.last bootloader
+  check_boilerplate_properties
+  report_bootstat_logs reboot,ota bootloader
+}
+
+[ "USAGE: test_optional_ota
+
+fast and fake (touch build_date on device to make it different)" ]
+test_optional_ota() {
+  checkDebugBuild || return
+  duration_test
+  adb shell su root touch /data/misc/bootstat/build_date >&2
+  adb reboot ota
+  wait_for_screen
+  EXPECT_PROPERTY sys.boot.reason reboot,ota
+  EXPECT_PROPERTY sys.boot.reason.last reboot,ota
+  check_boilerplate_properties
+  report_bootstat_logs reboot,ota
+}
+
+[ "USAGE: [TEST=<test>] blind_reboot_test
+
+Simple tests helper
+- adb reboot <test>
+- (wait until screen is up, boot has completed)
+- adb shell getprop sys.boot.reason
+- NB: should report <test>, or reboot,<test> depending on canonical rules
+
+We interleave the simple reboot tests between the hard/complex ones
+as a means of checking sanity and any persistent side effect of the
+other tests." ]
+blind_reboot_test() {
+  duration_test
+  case ${TEST} in
+    bootloader | recovery | cold | hard | warm ) reason=${TEST} ;;
+    *)                                           reason=reboot,${TEST#optional_} ;;
+  esac
+  adb reboot ${TEST#optional_}
+  wait_for_screen
+  bootloader_reason=`validate_property ro.boot.bootreason`
+  EXPECT_PROPERTY ro.boot.bootreason ${bootloader_reason}
+  # to make sys.boot.reason report user friendly
+  reasons=${reason}
+  if [ "${bootloader_reason}" != "${reason}" -a -n "${bootloader_reason}" ]; then
+    reasons="\(${reason}\|${bootloader_reason}\)"
+  fi
+  EXPECT_PROPERTY sys.boot.reason ${reasons}
+  EXPECT_PROPERTY sys.boot.reason.last ${reason}
+  check_boilerplate_properties
+  report_bootstat_logs ${reason} ${bootloader_reason}
+}
+
+[ "USAGE: test_cold
+
+cold test
+- adb reboot cold
+- (wait until screen is up, boot has completed)
+- adb shell getprop sys.boot.reason
+- NB: should report cold" ]
+test_cold() {
+  blind_reboot_test
+}
+
+[ "USAGE: test_factory_reset
+
+factory_reset test
+- adb shell su root rm /data/misc/bootstat/build_date
+- adb reboot
+- (wait until screen is up, boot has completed)
+- adb shell getprop sys.boot.reason
+- NB: should report factory_reset
+
+Decision to rummage through bootstat data files was made as
+a _real_ factory_reset is too destructive to the device." ]
+test_factory_reset() {
+  checkDebugBuild || return
+  duration_test
+  adb shell su root rm /data/misc/bootstat/build_date >&2
+  adb reboot >&2
+  wait_for_screen
+  EXPECT_PROPERTY sys.boot.reason reboot,factory_reset
+  EXPECT_PROPERTY sys.boot.reason.last "reboot,.*"
+  check_boilerplate_properties
+  report_bootstat_logs reboot,factory_reset reboot, reboot,adb \
+    "-bootstat: Failed to read /data/misc/bootstat/build_date: No such file or directory" \
+    "-bootstat: Failed to parse boot time record: /data/misc/bootstat/build_date"
+}
+
+[ "USAGE: test_optional_factory_reset
+
+factory_reset test
+- adb reboot-bootloader
+- fastboot format userdata
+- fastboot reboot
+- (wait until screen is up, boot has completed)
+- adb shell getprop sys.boot.reason
+- NB: should report factory_reset
+
+For realz, and disruptive" ]
+test_optional_factory_reset() {
+  duration_test 60
+  if ! inFastboot; then
+    adb reboot-bootloader
+  fi
+  fastboot format userdata >&2
+  save_ret=${?}
+  if [ 0 != ${save_ret} ]; then
+    echo "ERROR: fastboot can not format userdata" >&2
+  fi
+  fastboot reboot >&2
+  wait_for_screen
+  ( exit ${save_ret} )  # because one can not just do ?=${save_ret}
+  EXPECT_PROPERTY sys.boot.reason reboot,factory_reset
+  EXPECT_PROPERTY sys.boot.reason.last ""
+  check_boilerplate_properties
+  report_bootstat_logs reboot,factory_reset bootloader \
+    "-bootstat: Failed to read /data/misc/bootstat/last_boot_time_utc: No such file or directory" \
+    "-bootstat: Failed to parse boot time record: /data/misc/bootstat/last_boot_time_utc" \
+    "-bootstat: Failed to read /data/misc/bootstat/build_date: No such file or directory" \
+    "-bootstat: Failed to parse boot time record: /data/misc/bootstat/build_date" \
+    "-bootstat: Failed to read /data/misc/bootstat/factory_reset: No such file or directory" \
+    "-bootstat: Failed to parse boot time record: /data/misc/bootstat/factory_reset"
+}
+
+[ "USAGE: test_hard
+
+hard test:
+- adb reboot hard
+- (wait until screen is up, boot has completed)
+- adb shell getprop sys.boot.reason
+- NB: should report hard" ]
+test_hard() {
+  blind_reboot_test
+}
+
+[ "USAGE: test_battery
+
+battery test (trick):
+- echo healthd: battery l=2<space> | adb shell su root tee /dev/kmsg
+- adb reboot cold
+- (wait until screen is up, boot has completed)
+- adb shell getprop sys.boot.reason
+- NB: should report reboot,battery, unless healthd managed to log
+  before reboot in above trick.
+
+- Bonus points (manual extras)
+- Make sure the following is added to the /init.rc file in post-fs
+  section before logd is started:
+    +    setprop logd.kernel false
+    +    rm /sys/fs/pstore/console-ramoops
+    +    rm /sys/fs/pstore/console-ramoops-0
+    +    write /dev/kmsg \"healthd: battery l=2${SPACE}
+    +\"
+- adb reboot fs
+- (wait until screen is up, boot has completed)
+- adb shell getprop sys.boot.reason
+- NB: should report reboot,battery
+- (replace set logd.kernel true to the above, and retry test)" ]
+test_battery() {
+  checkDebugBuild || return
+  duration_test 120
+  enterPstore
+  # Send it _many_ times to combat devices with flakey pstore
+  for i in a b c d e f g h i j k l m n o p q r s t u v w x y z; do
+    echo 'healthd: battery l=2 ' | adb shell su root tee /dev/kmsg >/dev/null
+  done
+  adb reboot cold >&2
+  adb wait-for-device
+  wait_for_screen
+  adb shell su root \
+    cat /proc/fs/pstore/console-ramoops \
+        /proc/fs/pstore/console-ramoops-0 2>/dev/null |
+    grep 'healthd: battery l=' |
+    tail -1 |
+    grep 'healthd: battery l=2 ' >/dev/null || (
+      if ! EXPECT_PROPERTY sys.boot.reason reboot,battery >/dev/null 2>/dev/null; then
+        # retry
+        for i in a b c d e f g h i j k l m n o p q r s t u v w x y z; do
+          echo 'healthd: battery l=2 ' | adb shell su root tee /dev/kmsg >/dev/null
+        done
+        adb reboot cold >&2
+        adb wait-for-device
+        wait_for_screen
+      fi
+    )
+
+  EXPECT_PROPERTY sys.boot.reason shutdown,battery
+  EXPECT_PROPERTY sys.boot.reason.last cold
+  check_boilerplate_properties
+  report_bootstat_logs shutdown,battery "-bootstat: Battery level at shutdown 2%"
+  exitPstore
+}
+
+[ "USAGE: test_optional_battery
+
+battery shutdown test:
+- adb shell setprop sys.powerctl shutdown,battery
+- (power up the device)
+- (wait until screen is up, boot has completed)
+- adb shell getprop sys.boot.reason
+- NB: should report shutdown,battery" ]
+test_optional_battery() {
+  duration_test ">60"
+  echo "      power on request" >&2
+  adb shell setprop sys.powerctl shutdown,battery
+  sleep 5
+  echo -n "WARNING: Please power device back up, waiting ... " >&2
+  wait_for_screen -n >&2
+  EXPECT_PROPERTY sys.boot.reason shutdown,battery
+  EXPECT_PROPERTY sys.boot.reason.last shutdown,battery
+  check_boilerplate_properties
+  report_bootstat_logs shutdown,battery
+}
+
+[ "USAGE: test_optional_battery_thermal
+
+battery thermal shutdown test:
+- adb shell setprop sys.powerctl shutdown,thermal,battery
+- (power up the device)
+- (wait until screen is up, boot has completed)
+- adb shell getprop sys.boot.reason
+- NB: should report shutdown,thermal,battery" ]
+test_optional_battery_thermal() {
+  duration_test ">60"
+  echo "      power on request" >&2
+  adb shell setprop sys.powerctl shutdown,thermal,battery
+  sleep 5
+  echo -n "WARNING: Please power device back up, waiting ... " >&2
+  wait_for_screen -n >&2
+  EXPECT_PROPERTY sys.boot.reason shutdown,thermal,battery
+  EXPECT_PROPERTY sys.boot.reason.last shutdown,thermal,battery
+  check_boilerplate_properties
+  report_bootstat_logs shutdown,thermal,battery
+}
+
+[ "USAGE: test_unknown
+
+unknown test
+- adb reboot unknown
+- (wait until screen is up, boot has completed)
+- adb shell getprop sys.boot.reason
+- NB: should report reboot,unknown
+- NB: expect log \"... I bootstat: Unknown boot reason: reboot,unknown\"" ]
+test_unknown() {
+  blind_reboot_test
+}
+
+[ "USAGE: test_kernel_panic
+
+kernel_panic test:
+- 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_kernel_panic() {
+  checkDebugBuild || return
+  duration_test ">90"
+  panic_msg="kernel_panic,sysrq"
+  enterPstore
+  if [ ${?} != 0 ]; then
+    echo "         or functional bootloader" >&2
+    panic_msg="\(kernel_panic,sysrq\|kernel_panic\)"
+    pstore_ok=true
+  fi
+  echo c | adb shell su root tee /proc/sysrq-trigger >/dev/null
+  wait_for_screen
+  EXPECT_PROPERTY sys.boot.reason ${panic_msg}
+  EXPECT_PROPERTY sys.boot.reason.last ${panic_msg}
+  check_boilerplate_properties
+  report_bootstat_logs kernel_panic,sysrq
+  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 sys.boot.reason.last ${panic_msg}
+  check_boilerplate_properties
+  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 sys.boot.reason.last ${panic_msg}
+  check_boilerplate_properties
+  report_bootstat_logs kernel_panic,hung
+  exitPstore
+}
+
+[ "USAGE: test_warm
+
+warm test
+- adb reboot warm
+- (wait until screen is up, boot has completed)
+- adb shell getprop sys.boot.reason
+- NB: should report warm" ]
+test_warm() {
+  blind_reboot_test
+}
+
+[ "USAGE: test_thermal_shutdown
+
+thermal shutdown test:
+- adb shell setprop sys.powerctl shutdown,thermal
+- (power up the device)
+- (wait until screen is up, boot has completed)
+- adb shell getprop sys.boot.reason
+- NB: should report shutdown,thermal" ]
+test_thermal_shutdown() {
+  duration_test ">60"
+  echo "      power on request" >&2
+  adb shell setprop sys.powerctl shutdown,thermal
+  sleep 5
+  echo -n "WARNING: Please power device back up, waiting ... " >&2
+  wait_for_screen -n >&2
+  EXPECT_PROPERTY sys.boot.reason shutdown,thermal
+  EXPECT_PROPERTY sys.boot.reason.last shutdown,thermal
+  check_boilerplate_properties
+  report_bootstat_logs shutdown,thermal
+}
+
+[ "USAGE: test_userrequested_shutdown
+
+userrequested shutdown test:
+- adb shell setprop sys.powerctl shutdown,userrequested
+- (power up the device)
+- (wait until screen is up, boot has completed)
+- adb shell getprop sys.boot.reason
+- NB: should report shutdown,userrequested" ]
+test_userrequested_shutdown() {
+  duration_test ">60"
+  echo "      power on request" >&2
+  adb shell setprop sys.powerctl shutdown,userrequested
+  sleep 5
+  echo -n "WARNING: Please power device back up, waiting ... " >&2
+  wait_for_screen -n >&2
+  EXPECT_PROPERTY sys.boot.reason shutdown,userrequested
+  EXPECT_PROPERTY sys.boot.reason.last shutdown,userrequested
+  check_boilerplate_properties
+  report_bootstat_logs shutdown,userrequested
+}
+
+[ "USAGE: test_shell_reboot
+
+shell reboot test:
+- adb shell reboot
+- (wait until screen is up, boot has completed)
+- adb shell getprop sys.boot.reason
+- NB: should report reboot,shell" ]
+test_shell_reboot() {
+  duration_test
+  adb shell reboot
+  wait_for_screen
+  EXPECT_PROPERTY sys.boot.reason reboot,shell
+  EXPECT_PROPERTY sys.boot.reason.last reboot,shell
+  check_boilerplate_properties
+  report_bootstat_logs reboot,shell
+}
+
+[ "USAGE: test_adb_reboot
+
+adb reboot test:
+- adb reboot
+- (wait until screen is up, boot has completed)
+- adb shell getprop sys.boot.reason
+- NB: should report reboot,adb" ]
+test_adb_reboot() {
+  duration_test
+  adb reboot
+  wait_for_screen
+  EXPECT_PROPERTY sys.boot.reason reboot,adb
+  EXPECT_PROPERTY sys.boot.reason.last reboot,adb
+  check_boilerplate_properties
+  report_bootstat_logs reboot,adb
+}
+
+[ "USAGE: test_rescueparty
+
+rescueparty test
+- adb reboot rescueparty
+- (wait until screen is up, boot has completed)
+- adb shell getprop sys.boot.reason
+- adb shell getprop ro.boot.bootreason
+- NB: should report reboot,rescueparty" ]
+test_optional_rescueparty() {
+  blind_reboot_test
+  echo "WARNING: legacy devices are allowed to fail following ro.boot.bootreason result" >&2
+  EXPECT_PROPERTY ro.boot.bootreason reboot,rescueparty
+}
+
+[ "USAGE: test_Its_Just_So_Hard_reboot
+
+Its Just So Hard reboot test:
+- adb shell reboot 'Its Just So Hard'
+- (wait until screen is up, boot has completed)
+- adb shell getprop sys.boot.reason
+- NB: should report reboot,its_just_so_hard
+- NB: expect log \"... I bootstat: Unknown boot reason: reboot,its_just_so_hard\"" ]
+test_Its_Just_So_Hard_reboot() {
+  if isDebuggable; then       # see below
+    duration_test
+  else
+    duration_test `expr ${DURATION_DEFAULT} + ${DURATION_DEFAULT}`
+  fi
+  adb shell 'reboot "Its Just So Hard"'
+  wait_for_screen
+  EXPECT_PROPERTY sys.boot.reason reboot,its_just_so_hard
+  EXPECT_PROPERTY sys.boot.reason.last reboot,its_just_so_hard
+  check_boilerplate_properties
+  report_bootstat_logs reboot,its_just_so_hard
+}
+
+[ "USAGE: run_bootloader [value [expected]]
+
+bootloader boot reason injection tests:
+- setBootloaderBootReason value
+- adb shell reboot
+- (wait until screen is up, boot has completed)
+- adb shell getprop sys.boot.reason
+- NB: should report reboot,value" ]
+run_bootloader() {
+  bootloader_expected="${1}"
+  if [ -z "${bootloader_expected}" ]; then
+    bootloader_expected="${TEST#bootloader_}"
+  fi
+  if ! setBootloaderBootReason ${bootloader_expected}; then
+    echo "       Skipping FAILURE." 2>&1
+    return
+  fi
+  duration_test
+  if [ X"warm" = X"${bootloader_expected}" ]; then
+    last_expected=cold
+  else
+    last_expected=warm
+  fi
+  adb reboot ${last_expected}
+  wait_for_screen
+  # Reset so that other tests do not get unexpected injection
+  setBootloaderBootReason
+  # Determine the expected values
+  sys_expected="${2}"
+  if [ -z "${sys_expected}" ]; then
+    sys_expected="`validate_reason ${bootloader_expected}`"
+    if [ "reboot" = "${sys_expected}" ]; then
+      sys_expected="${last_expected}"
+    fi
+  else
+    sys_expected=`validate_reason ${sys_expected}`
+  fi
+  case ${sys_expected} in
+    kernel_panic | kernel_panic,* | watchdog | watchdog,* )
+      last_expected=${sys_expected}
+      ;;
+  esac
+  # Check values
+  EXPECT_PROPERTY ro.boot.bootreason "${bootloader_expected}"
+  EXPECT_PROPERTY sys.boot.reason "${sys_expected}"
+  EXPECT_PROPERTY sys.boot.reason.last "${last_expected}"
+  check_boilerplate_properties
+  report_bootstat_logs "${sys_expected}"
+}
+
+[ "USAGE: test_bootloader_<type>
+
+bootloader boot reasons test injection" ]
+test_bootloader_normal() {
+  run_bootloader
+}
+
+test_bootloader_watchdog() {
+  run_bootloader
+}
+
+test_bootloader_kernel_panic() {
+  run_bootloader
+}
+
+test_bootloader_oem_powerkey() {
+  run_bootloader
+}
+
+test_bootloader_wdog_reset() {
+  run_bootloader
+}
+
+test_bootloader_cold() {
+  run_bootloader
+}
+
+test_bootloader_warm() {
+  run_bootloader
+}
+
+test_bootloader_hard() {
+  run_bootloader
+}
+
+test_bootloader_recovery() {
+  run_bootloader
+}
+
+[ "USAGE: ${0##*/} [-s SERIAL] [tests]
+
+Mainline executive to run the above tests" ]
+
+# Rudimentary argument parsing
+
+if [ ${#} -ge 2 -a X"-s" = X"${1}" ]; then
+  export ANDROID_SERIAL="${2}"
+  shift 2
+fi
+
+# Helpful for debugging, allows us to import the functions.
+if [ X"--macros" != X"${1}" ]; then
+
+  if [ X"--help" = X"${1}" -o X"-h" = X"${1}" -o X"-?" = X"${1}" ]; then
+    echo "USAGE: ${0##*/} [-s SERIAL] [tests]"
+    echo tests - `sed -n 's/^test_\([^ ()]*\)() {/\1/p' $0 </dev/null`
+    exit 0
+  fi
+
+  if [ X"--stop" = X"${1}" ]; then
+    STOP_ON_FAILURE=true
+    shift
+  fi
+
+  # Check if all conditions for the script are sane
+
+  if [ -z "${ANDROID_SERIAL}" ]; then
+    ndev=`(
+        adb devices | grep -v 'List of devices attached'
+        fastboot devices
+      ) |
+      grep -v "^[${SPACE}${TAB}]*\$" |
+      wc -l`
+    if [ ${ndev} -gt 1 ]; then
+      echo "ERROR: no target device specified, ${ndev} connected" >&2
+      echo "${RED}[  FAILED  ]${NORMAL}"
+      exit 1
+    fi
+    echo "WARNING: no target device specified" >&2
+  fi
+
+  ret=0
+
+  # Test Series
+  if [ X"all" = X"${*}" ]; then
+    # automagically pick up all test_<function>s.
+    eval set nothing `sed -n 's/^test_\([^ ()]*\)() {/\1/p' $0 </dev/null`
+    if [ X"nothing" = X"${1}" ]; then
+      shift 1
+    fi
+  fi
+  if [ -z "$*" ]; then
+    # automagically pick up all test_<function>, except test_optional_<function>.
+    eval set nothing `sed -n 's/^test_\([^ ()]*\)() {/\1/p' $0 </dev/null |
+                              grep -v '^optional_'`
+    if [ -z "${2}" ]; then
+      # Hard coded should shell fail to find them (search/permission issues)
+      eval set properties ota cold factory_reset hard battery unknown \
+               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_cold bootloader_warm \
+               bootloader_hard bootloader_recovery
+    fi
+    if [ X"nothing" = X"${1}" ]; then
+      shift 1
+    fi
+  fi
+  echo "INFO: selected test(s): ${@}" >&2
+  echo
+  # Prepare device
+  setBootloaderBootReason 2>/dev/null
+  # Start pouring through the tests.
+  failures=
+  successes=
+  for t in "${@}"; do
+    wrap_test ${t}
+    retval=${?}
+    if [ 0 = ${retval} ]; then
+      if [ -z "${successes}" ]; then
+        successes=${t}
+      else
+        successes="${successes} ${t}"
+      fi
+    else
+      ret=${retval}
+      if [ -z "${failures}" ]; then
+        failures=${t}
+      else
+        failures="${failures} ${t}"
+      fi
+    fi
+    echo
+  done
+
+  if [ -n "${successes}" ]; then
+    echo "${GREEN}[  PASSED  ]${NORMAL} ${successes}"
+  fi
+  if [ -n "${failures}" ]; then
+    echo "${RED}[  FAILED  ]${NORMAL} ${failures}"
+  fi
+  exit ${ret}
+
+fi
diff --git a/bootstat/bootstat-debug.rc b/bootstat/bootstat-debug.rc
new file mode 100644
index 0000000..6a00440
--- /dev/null
+++ b/bootstat/bootstat-debug.rc
@@ -0,0 +1,7 @@
+# This file is the userdebug LOCAL_INIT_RC file for the bootstat command.
+
+# FOR TESTING
+# For devices w/o bootloader boot reason reported, mirror test boot reason
+# to bootloader boot reason to allow test to inject reasons
+on property:persist.test.boot.reason=*
+    setprop ro.boot.bootreason ${persist.test.boot.reason}
diff --git a/bootstat/bootstat.cpp b/bootstat/bootstat.cpp
index 344fa9a..4d9f1ac 100644
--- a/bootstat/bootstat.cpp
+++ b/bootstat/bootstat.cpp
@@ -19,6 +19,7 @@
 // uploaded to Android log storage via Tron.
 
 #include <getopt.h>
+#include <sys/klog.h>
 #include <unistd.h>
 
 #include <chrono>
@@ -26,16 +27,22 @@
 #include <cstddef>
 #include <cstdio>
 #include <ctime>
+#include <iterator>
 #include <map>
 #include <memory>
+#include <regex>
 #include <string>
+#include <utility>
 #include <vector>
 
 #include <android-base/chrono_utils.h>
+#include <android-base/file.h>
 #include <android-base/logging.h>
 #include <android-base/parseint.h>
+#include <android-base/properties.h>
 #include <android-base/strings.h>
 #include <android/log.h>
+#include <cutils/android_reboot.h>
 #include <cutils/properties.h>
 #include <metricslogger/metrics_logger.h>
 
@@ -57,8 +64,7 @@
 // Records the named boot |event| to the record store. If |value| is non-empty
 // and is a proper string representation of an integer value, the converted
 // integer value is associated with the boot event.
-void RecordBootEventFromCommandLine(
-    const std::string& event, const std::string& value_str) {
+void RecordBootEventFromCommandLine(const std::string& event, const std::string& value_str) {
   BootEventRecordStore boot_event_store;
   if (!value_str.empty()) {
     int32_t value = 0;
@@ -81,7 +87,7 @@
   }
 }
 
-void ShowHelp(const char *cmd) {
+void ShowHelp(const char* cmd) {
   fprintf(stderr, "Usage: %s [options]\n", cmd);
   fprintf(stderr,
           "options include:\n"
@@ -97,7 +103,7 @@
 
 // Constructs a readable, printable string from the givencommand line
 // arguments.
-std::string GetCommandLine(int argc, char **argv) {
+std::string GetCommandLine(int argc, char** argv) {
   std::string cmd;
   for (int i = 0; i < argc; ++i) {
     cmd += argv[i];
@@ -118,6 +124,15 @@
   return std::string(&temp[0], len);
 }
 
+bool SetProperty(const char* key, const std::string& val) {
+  return property_set(key, val.c_str()) == 0;
+}
+
+bool SetProperty(const char* key, const char* val) {
+  return property_set(key, val) == 0;
+}
+
+constexpr int32_t kEmptyBootReason = 0;
 constexpr int32_t kUnknownBootReason = 1;
 
 // A mapping from boot reason string, as read from the ro.boot.bootreason
@@ -125,50 +140,177 @@
 // the boot_reason metric may refer to this mapping to discern the histogram
 // values.
 const std::map<std::string, int32_t> kBootReasonMap = {
-  {"unknown", kUnknownBootReason},
-  {"normal", 2},
-  {"recovery", 3},
-  {"reboot", 4},
-  {"PowerKey", 5},
-  {"hard_reset", 6},
-  {"kernel_panic", 7},
-  {"rpm_err", 8},
-  {"hw_reset", 9},
-  {"tz_err", 10},
-  {"adsp_err", 11},
-  {"modem_err", 12},
-  {"mba_err", 13},
-  {"Watchdog", 14},
-  {"Panic", 15},
-  {"power_key", 16},
-  {"power_on", 17},
-  {"Reboot", 18},
-  {"rtc", 19},
-  {"edl", 20},
-  {"oem_pon1", 21},
-  {"oem_powerkey", 22},
-  {"oem_unknown_reset", 23},
-  {"srto: HWWDT reset SC", 24},
-  {"srto: HWWDT reset platform", 25},
-  {"srto: bootloader", 26},
-  {"srto: kernel panic", 27},
-  {"srto: kernel watchdog reset", 28},
-  {"srto: normal", 29},
-  {"srto: reboot", 30},
-  {"srto: reboot-bootloader", 31},
-  {"srto: security watchdog reset", 32},
-  {"srto: wakesrc", 33},
-  {"srto: watchdog", 34},
-  {"srto:1-1", 35},
-  {"srto:omap_hsmm", 36},
-  {"srto:phy0", 37},
-  {"srto:rtc0", 38},
-  {"srto:touchpad", 39},
-  {"watchdog", 40},
-  {"watchdogr", 41},
-  {"wdog_bark", 42},
-  {"wdog_bite", 43},
-  {"wdog_reset", 44},
+    {"empty", kEmptyBootReason},
+    {"__BOOTSTAT_UNKNOWN__", kUnknownBootReason},
+    {"normal", 2},
+    {"recovery", 3},
+    {"reboot", 4},
+    {"PowerKey", 5},
+    {"hard_reset", 6},
+    {"kernel_panic", 7},
+    {"rpm_err", 8},
+    {"hw_reset", 9},
+    {"tz_err", 10},
+    {"adsp_err", 11},
+    {"modem_err", 12},
+    {"mba_err", 13},
+    {"Watchdog", 14},
+    {"Panic", 15},
+    {"power_key", 16},
+    {"power_on", 17},
+    {"Reboot", 18},
+    {"rtc", 19},
+    {"edl", 20},
+    {"oem_pon1", 21},
+    {"oem_powerkey", 22},
+    {"oem_unknown_reset", 23},
+    {"srto: HWWDT reset SC", 24},
+    {"srto: HWWDT reset platform", 25},
+    {"srto: bootloader", 26},
+    {"srto: kernel panic", 27},
+    {"srto: kernel watchdog reset", 28},
+    {"srto: normal", 29},
+    {"srto: reboot", 30},
+    {"srto: reboot-bootloader", 31},
+    {"srto: security watchdog reset", 32},
+    {"srto: wakesrc", 33},
+    {"srto: watchdog", 34},
+    {"srto:1-1", 35},
+    {"srto:omap_hsmm", 36},
+    {"srto:phy0", 37},
+    {"srto:rtc0", 38},
+    {"srto:touchpad", 39},
+    {"watchdog", 40},
+    {"watchdogr", 41},
+    {"wdog_bark", 42},
+    {"wdog_bite", 43},
+    {"wdog_reset", 44},
+    {"shutdown,", 45},  // Trailing comma is intentional.
+    {"shutdown,userrequested", 46},
+    {"reboot,bootloader", 47},
+    {"reboot,cold", 48},
+    {"reboot,recovery", 49},
+    {"thermal_shutdown", 50},
+    {"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},
+    {"reboot,kernel_power_off_charging__reboot_system", 59},  // Can not happen
+    {"thermal-shutdown", 60},
+    {"shutdown,thermal", 61},
+    {"shutdown,battery", 62},
+    {"reboot,ota", 63},
+    {"reboot,factory_reset", 64},
+    {"reboot,", 65},
+    {"reboot,shell", 66},
+    {"reboot,adb", 67},
+    {"reboot,userrequested", 68},
+    {"shutdown,container", 69},  // Host OS asking Android Container to shutdown
+    {"cold,powerkey", 70},
+    {"warm,s3_wakeup", 71},
+    {"hard,hw_reset", 72},
+    {"shutdown,suspend", 73},    // Suspend to RAM
+    {"shutdown,hibernate", 74},  // Suspend to DISK
+    {"power_on_key", 75},
+    {"reboot_by_key", 76},
+    {"wdt_by_pass_pwk", 77},
+    {"reboot_longkey", 78},
+    {"powerkey", 79},
+    {"usb", 80},
+    {"wdt", 81},
+    {"tool_by_pass_pwk", 82},
+    {"2sec_reboot", 83},
+    {"reboot,by_key", 84},
+    {"reboot,longkey", 85},
+    {"reboot,2sec", 86},  // Deprecate in two years, replaced with cold,rtc,2sec
+    {"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
+    {"reboot,rescueparty", 90},
+    {"charge", 91},
+    {"oem_tz_crash", 92},
+    {"uvlo", 93},
+    {"oem_ps_hold", 94},
+    {"abnormal_reset", 95},
+    {"oemerr_unknown", 96},
+    {"reboot_fastboot_mode", 97},
+    {"watchdog_apps_bite", 98},
+    {"xpu_err", 99},
+    {"power_on_usb", 100},
+    {"watchdog_rpm", 101},
+    {"watchdog_nonsec", 102},
+    {"watchdog_apps_bark", 103},
+    {"reboot_dmverity_corrupted", 104},
+    {"reboot_smpl", 105},
+    {"watchdog_sdi_apps_reset", 106},
+    {"smpl", 107},
+    {"oem_modem_failed_to_powerup", 108},
+    {"reboot_normal", 109},
+    {"oem_lpass_cfg", 110},
+    {"oem_xpu_ns_error", 111},
+    {"power_key_press", 112},
+    {"hardware_reset", 113},
+    {"reboot_by_powerkey", 114},
+    {"reboot_verity", 115},
+    {"oem_rpm_undef_error", 116},
+    {"oem_crash_on_the_lk", 117},
+    {"oem_rpm_reset", 118},
+    {"oem_lpass_cfg", 119},
+    {"oem_xpu_ns_error", 120},
+    {"factory_cable", 121},
+    {"oem_ar6320_failed_to_powerup", 122},
+    {"watchdog_rpm_bite", 123},
+    {"power_on_cable", 124},
+    {"reboot_unknown", 125},
+    {"wireless_charger", 126},
+    {"0x776655ff", 127},
+    {"oem_thermal_bite_reset", 128},
+    {"charger", 129},
+    {"pon1", 130},
+    {"unknown", 131},
+    {"reboot_rtc", 132},
+    {"cold_boot", 133},
+    {"hard_rst", 134},
+    {"power-on", 135},
+    {"oem_adsp_resetting_the_soc", 136},
+    {"kpdpwr", 137},
+    {"oem_modem_timeout_waiting", 138},
+    {"usb_chg", 139},
+    {"warm_reset_0x02", 140},
+    {"warm_reset_0x80", 141},
+    {"pon_reason_0xb0", 142},
+    {"reboot_download", 143},
+    {"reboot_recovery_mode", 144},
+    {"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},
+    {"kernel_panic,sysrq,livelock,alarm", 161},   // llkd
+    {"kernel_panic,sysrq,livelock,driver", 162},  // llkd
+    {"kernel_panic,sysrq,livelock,zombie", 163},  // llkd
+    {"kernel_panic,modem", 164},
+    {"kernel_panic,adsp", 165},
+    {"kernel_panic,dsps", 166},
+    {"kernel_panic,wcnss", 167},
+    {"kernel_panic,_sde_encoder_phys_cmd_handle_ppdone_timeout", 168},
 };
 
 // Converts a string value representing the reason the system booted to an
@@ -180,10 +322,637 @@
     return mapping->second;
   }
 
+  if (boot_reason.empty()) {
+    return kEmptyBootReason;
+  }
+
   LOG(INFO) << "Unknown boot reason: " << boot_reason;
   return kUnknownBootReason;
 }
 
+// Canonical list of supported primary reboot reasons.
+const std::vector<const std::string> knownReasons = {
+    // clang-format off
+    // kernel
+    "watchdog",
+    "kernel_panic",
+    // strong
+    "recovery",    // Should not happen from ro.boot.bootreason
+    "bootloader",  // Should not happen from ro.boot.bootreason
+    // blunt
+    "cold",
+    "hard",
+    "warm",
+    // super blunt
+    "shutdown",    // Can not happen from ro.boot.bootreason
+    "reboot",      // Default catch-all for anything unknown
+    // clang-format on
+};
+
+// Returns true if the supplied reason prefix is considered detailed enough.
+bool isStrongRebootReason(const std::string& r) {
+  for (auto& s : knownReasons) {
+    if (s == "cold") break;
+    // Prefix defined as terminated by a nul or comma (,).
+    if (android::base::StartsWith(r, s) && ((r.length() == s.length()) || (r[s.length()] == ','))) {
+      return true;
+    }
+  }
+  return false;
+}
+
+// Returns true if the supplied reason prefix is associated with the kernel.
+bool isKernelRebootReason(const std::string& r) {
+  for (auto& s : knownReasons) {
+    if (s == "recovery") break;
+    // Prefix defined as terminated by a nul or comma (,).
+    if (android::base::StartsWith(r, s) && ((r.length() == s.length()) || (r[s.length()] == ','))) {
+      return true;
+    }
+  }
+  return false;
+}
+
+// Returns true if the supplied reason prefix is considered known.
+bool isKnownRebootReason(const std::string& r) {
+  for (auto& s : knownReasons) {
+    // Prefix defined as terminated by a nul or comma (,).
+    if (android::base::StartsWith(r, s) && ((r.length() == s.length()) || (r[s.length()] == ','))) {
+      return true;
+    }
+  }
+  return false;
+}
+
+// If the reboot reason should be improved, report true if is too blunt.
+bool isBluntRebootReason(const std::string& r) {
+  if (isStrongRebootReason(r)) return false;
+
+  if (!isKnownRebootReason(r)) return true;  // Can not support unknown as detail
+
+  size_t pos = 0;
+  while ((pos = r.find(',', pos)) != std::string::npos) {
+    ++pos;
+    std::string next(r.substr(pos));
+    if (next.length() == 0) break;
+    if (next[0] == ',') continue;
+    if (!isKnownRebootReason(next)) return false;  // Unknown subreason is good.
+    if (isStrongRebootReason(next)) return false;  // eg: reboot,reboot
+  }
+  return true;
+}
+
+bool readPstoreConsole(std::string& console) {
+  if (android::base::ReadFileToString("/sys/fs/pstore/console-ramoops-0", &console)) {
+    return true;
+  }
+  return android::base::ReadFileToString("/sys/fs/pstore/console-ramoops", &console);
+}
+
+// Implement a variant of std::string::rfind that is resilient to errors in
+// the data stream being inspected.
+class pstoreConsole {
+ private:
+  const size_t kBitErrorRate = 8;  // number of bits per error
+  const std::string& console;
+
+  // Number of bits that differ between the two arguments l and r.
+  // Returns zero if the values for l and r are identical.
+  size_t numError(uint8_t l, uint8_t r) const { return std::bitset<8>(l ^ r).count(); }
+
+  // A string comparison function, reports the number of errors discovered
+  // in the match to a maximum of the bitLength / kBitErrorRate, at that
+  // point returning npos to indicate match is too poor.
+  //
+  // Since called in rfind which works backwards, expect cache locality will
+  // help if we check in reverse here as well for performance.
+  //
+  // Assumption: l (from console.c_str() + pos) is long enough to house
+  //             _r.length(), checked in rfind caller below.
+  //
+  size_t numError(size_t pos, const std::string& _r) const {
+    const char* l = console.c_str() + pos;
+    const char* r = _r.c_str();
+    size_t n = _r.length();
+    const uint8_t* le = reinterpret_cast<const uint8_t*>(l) + n;
+    const uint8_t* re = reinterpret_cast<const uint8_t*>(r) + n;
+    size_t count = 0;
+    n = 0;
+    do {
+      // individual character bit error rate > threshold + slop
+      size_t num = numError(*--le, *--re);
+      if (num > ((8 + kBitErrorRate) / kBitErrorRate)) return std::string::npos;
+      // total bit error rate > threshold + slop
+      count += num;
+      ++n;
+      if (count > ((n * 8 + kBitErrorRate - (n > 2)) / kBitErrorRate)) {
+        return std::string::npos;
+      }
+    } while (le != reinterpret_cast<const uint8_t*>(l));
+    return count;
+  }
+
+ public:
+  explicit pstoreConsole(const std::string& console) : console(console) {}
+  // scope of argument must be equal to or greater than scope of pstoreConsole
+  explicit pstoreConsole(const std::string&& console) = delete;
+  explicit pstoreConsole(std::string&& console) = delete;
+
+  // Our implementation of rfind, use exact match first, then resort to fuzzy.
+  size_t rfind(const std::string& needle) const {
+    size_t pos = console.rfind(needle);  // exact match?
+    if (pos != std::string::npos) return pos;
+
+    // Check to make sure needle fits in console string.
+    pos = console.length();
+    if (needle.length() > pos) return std::string::npos;
+    pos -= needle.length();
+    // fuzzy match to maximum kBitErrorRate
+    for (;;) {
+      if (numError(pos, needle) != std::string::npos) return pos;
+      if (pos == 0) break;
+      --pos;
+    }
+    return std::string::npos;
+  }
+
+  // Our implementation of find, use only fuzzy match.
+  size_t find(const std::string& needle, size_t start = 0) const {
+    // Check to make sure needle fits in console string.
+    if (needle.length() > console.length()) return std::string::npos;
+    const size_t last_pos = console.length() - needle.length();
+    // fuzzy match to maximum kBitErrorRate
+    for (size_t pos = start; pos <= last_pos; ++pos) {
+      if (numError(pos, needle) != std::string::npos) return pos;
+    }
+    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 correctForBitError(std::string& reason, const std::string& needle) {
+  bool corrected = false;
+  if (reason.length() < needle.length()) return corrected;
+  const pstoreConsole console(reason);
+  const size_t last_pos = reason.length() - needle.length();
+  for (size_t pos = 0; pos <= last_pos; pos += needle.length()) {
+    pos = console.find(needle, pos);
+    if (pos == std::string::npos) break;
+
+    // exact match has no malice
+    if (needle == reason.substr(pos, needle.length())) continue;
+
+    corrected = true;
+    reason = reason.substr(0, pos) + needle + reason.substr(pos + needle.length());
+  }
+  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) ||
+      (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";
+    return true;
+  }
+  if (console.rfind("Kernel BUG at ") != std::string::npos) {
+    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"},
+        {"subsys-restart: Resetting the SoC - modem crashed.", "modem"},
+        {"subsys-restart: Resetting the SoC - adsp crashed.", "adsp"},
+        {"subsys-restart: Resetting the SoC - dsps crashed.", "dsps"},
+        {"subsys-restart: Resetting the SoC - wcnss crashed.", "wcnss"},
+    };
+
+    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;
+}
+
+bool addKernelPanicSubReason(const std::string& content, std::string& ret) {
+  return addKernelPanicSubReason(pstoreConsole(content), ret);
+}
+
+const char system_reboot_reason_property[] = "sys.boot.reason";
+const char last_reboot_reason_property[] = LAST_REBOOT_REASON_PROPERTY;
+const char last_last_reboot_reason_property[] = "sys.boot.reason.last";
+constexpr size_t history_reboot_reason_size = 4;
+const char history_reboot_reason_property[] = LAST_REBOOT_REASON_PROPERTY ".history";
+const char bootloader_reboot_reason_property[] = "ro.boot.bootreason";
+
+// Land system_boot_reason into system_reboot_reason_property.
+// Shift system_boot_reason into history_reboot_reason_property.
+void BootReasonAddToHistory(const std::string& system_boot_reason) {
+  if (system_boot_reason.empty()) return;
+  LOG(INFO) << "Canonical boot reason: " << system_boot_reason;
+  auto old_system_boot_reason = GetProperty(system_reboot_reason_property);
+  if (!SetProperty(system_reboot_reason_property, system_boot_reason)) {
+    SetProperty(system_reboot_reason_property, system_boot_reason.substr(0, PROPERTY_VALUE_MAX - 1));
+  }
+  auto reason_history = android::base::Split(GetProperty(history_reboot_reason_property), "\n");
+  static auto mark = time(nullptr);
+  auto mark_str = std::string(",") + std::to_string(mark);
+  auto marked_system_boot_reason = system_boot_reason + mark_str;
+  if (!reason_history.empty()) {
+    // delete any entries that we just wrote in a previous
+    // call and leveraging duplicate line handling
+    auto last = old_system_boot_reason + mark_str;
+    // trim the list to (history_reboot_reason_size - 1)
+    ssize_t max = history_reboot_reason_size;
+    for (auto it = reason_history.begin(); it != reason_history.end();) {
+      if (it->empty() || (last == *it) || (marked_system_boot_reason == *it) || (--max <= 0)) {
+        it = reason_history.erase(it);
+      } else {
+        last = *it;
+        ++it;
+      }
+    }
+  }
+  // insert at the front, concatenating mark (<epoch time>) detail to the value.
+  reason_history.insert(reason_history.begin(), marked_system_boot_reason);
+  // If the property string is too long ( > PROPERTY_VALUE_MAX)
+  // we get an error, so trim out last entry and try again.
+  while (!(SetProperty(history_reboot_reason_property, android::base::Join(reason_history, '\n')))) {
+    auto it = std::prev(reason_history.end());
+    if (it == reason_history.end()) break;
+    reason_history.erase(it);
+  }
+}
+
+// Scrub, Sanitize, Standardize and Enhance the boot reason string supplied.
+std::string BootReasonStrToReason(const std::string& boot_reason) {
+  std::string ret(GetProperty(system_reboot_reason_property));
+  std::string reason(boot_reason);
+  // If sys.boot.reason == ro.boot.bootreason, let's re-evaluate
+  if (reason == ret) ret = "";
+
+  transformReason(reason);
+
+  // Is the current system boot reason sys.boot.reason valid?
+  if (!isKnownRebootReason(ret)) ret = "";
+
+  if (ret == "") {
+    // Is the bootloader boot reason ro.boot.bootreason known?
+    std::vector<std::string> words(android::base::Split(reason, ",_-"));
+    for (auto& s : knownReasons) {
+      std::string blunt;
+      for (auto& r : words) {
+        if (r == s) {
+          if (isBluntRebootReason(s)) {
+            blunt = s;
+          } else {
+            ret = s;
+            break;
+          }
+        }
+      }
+      if (ret == "") ret = blunt;
+      if (ret != "") break;
+    }
+  }
+
+  if (ret == "") {
+    // 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 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|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"},
+        {"cold,rtc,2sec", "2sec_reboot"},
+        {"!warm", "wdt_by_pass_pwk"},  // change flavour of blunt
+        {"!reboot", "^wdt$"},          // change flavour of blunt
+        {"reboot,tool", "tool_by_pass_pwk"},
+        {"!reboot,longkey", "reboot_longkey"},
+        {"!reboot,longkey", "kpdpwr"},
+        {"bootloader", ""},
+    };
+
+    for (auto& s : aliasReasons) {
+      size_t firstHasNot = s.first[0] == '!';
+      if (!firstHasNot && (reason.find(s.first) != std::string::npos)) {
+        ret = s.first;
+        break;
+      }
+      if (s.second.size() && std::regex_search(reason, std::regex(s.second))) {
+        ret = s.first.substr(firstHasNot);
+        break;
+      }
+    }
+  }
+
+  // If watchdog is the reason, see if there is a security angle?
+  if (ret == "watchdog") {
+    if (reason.find("sec") != std::string::npos) {
+      ret += ",security";
+    }
+  }
+
+  if (ret == "kernel_panic") {
+    // Check to see if last klog has some refinement hints.
+    std::string content;
+    if (readPstoreConsole(content)) {
+      addKernelPanicSubReason(content, ret);
+    }
+  } else if (isBluntRebootReason(ret)) {
+    // Check the other available reason resources if the reason is still blunt.
+
+    // Check to see if last klog has some refinement hints.
+    std::string content;
+    if (readPstoreConsole(content)) {
+      const pstoreConsole console(content);
+      // The toybox reboot command used directly (unlikely)? But also
+      // catches init's response to Android's more controlled reboot command.
+      if (console.rfind("reboot: Power down") != std::string::npos) {
+        ret = "shutdown";  // Still too blunt, but more accurate.
+        // ToDo: init should record the shutdown reason to kernel messages ala:
+        //           init: shutdown system with command 'last_reboot_reason'
+        //       so that if pstore has persistence we can get some details
+        //       that could be missing in last_reboot_reason_property.
+      }
+
+      static const char cmd[] = "reboot: Restarting system with command '";
+      size_t pos = console.rfind(cmd);
+      if (pos != std::string::npos) {
+        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.
+          } else if (isKnownRebootReason(subReason)) {
+            ret = subReason;
+          } else {
+            ret = "reboot," + subReason;  // legitimize unknown reasons
+          }
+        }
+        // Some bootloaders shutdown results record in last kernel message.
+        if (!strcmp(ret.c_str(), "reboot,kernel_power_off_charging__reboot_system")) {
+          ret = "shutdown";
+        }
+      }
+
+      // Check for kernel panics, allowed to override reboot command.
+      if (!addKernelPanicSubReason(console, ret) &&
+          // check for long-press power down
+          ((console.rfind("Power held for ") != std::string::npos) ||
+           (console.rfind("charger: [") != std::string::npos))) {
+        ret = "cold";
+      }
+    }
+
+    // TODO: use the HAL to get battery level (http://b/77725702).
+
+    // Is there a controlled shutdown hint in last_reboot_reason_property?
+    if (isBluntRebootReason(ret)) {
+      // Content buffer no longer will have console data. Beware if more
+      // checks added below, that depend on parsing console content.
+      content = GetProperty(last_reboot_reason_property);
+      transformReason(content);
+
+      // Anything in last is better than 'super-blunt' reboot or shutdown.
+      if ((ret == "") || (ret == "reboot") || (ret == "shutdown") || !isBluntRebootReason(content)) {
+        ret = content;
+      }
+    }
+
+    // Other System Health HAL reasons?
+
+    // ToDo: /proc/sys/kernel/boot_reason needs a HAL interface to
+    //       possibly offer hardware-specific clues from the PMIC.
+  }
+
+  // If unknown left over from above, make it "reboot,<boot_reason>"
+  if (ret == "") {
+    ret = "reboot";
+    if (android::base::StartsWith(reason, "reboot")) {
+      reason = reason.substr(strlen("reboot"));
+      while ((reason[0] == ',') || (reason[0] == '_')) {
+        reason = reason.substr(1);
+      }
+    }
+    if (reason != "") {
+      ret += ",";
+      ret += reason;
+    }
+  }
+
+  LOG(INFO) << "Canonical boot reason: " << ret;
+  return ret;
+}
+
 // Returns the appropriate metric key prefix for the boot_complete metric such
 // that boot metrics after a system update are labeled as ota_boot_complete;
 // otherwise, they are labeled as boot_complete.  This method encapsulates the
@@ -205,17 +974,18 @@
   if (!boot_event_store.GetBootEvent(kBuildDateKey, &record)) {
     boot_complete_prefix = "factory_reset_" + boot_complete_prefix;
     boot_event_store.AddBootEventWithValue(kBuildDateKey, build_date);
+    BootReasonAddToHistory("reboot,factory_reset");
   } else if (build_date != record.second) {
     boot_complete_prefix = "ota_" + boot_complete_prefix;
     boot_event_store.AddBootEventWithValue(kBuildDateKey, build_date);
+    BootReasonAddToHistory("reboot,ota");
   }
 
   return boot_complete_prefix;
 }
 
 // Records the value of a given ro.boottime.init property in milliseconds.
-void RecordInitBootTimeProp(
-    BootEventRecordStore* boot_event_store, const char* property) {
+void RecordInitBootTimeProp(BootEventRecordStore* boot_event_store, const char* property) {
   std::string value = GetProperty(property);
 
   int32_t time_in_ms;
@@ -288,22 +1058,50 @@
   boot_event_store->AddBootEventWithValue("absolute_boot_time", absolute_total.count());
 }
 
+// Gets the boot time offset. This is useful when Android is running in a
+// container, because the boot_clock is not reset when Android reboots.
+std::chrono::nanoseconds GetBootTimeOffset() {
+  static const int64_t boottime_offset =
+      android::base::GetIntProperty<int64_t>("ro.boot.boottime_offset", 0);
+  return std::chrono::nanoseconds(boottime_offset);
+}
+
+// Returns the current uptime, accounting for any offset in the CLOCK_BOOTTIME
+// clock.
+android::base::boot_clock::duration GetUptime() {
+  return android::base::boot_clock::now().time_since_epoch() - GetBootTimeOffset();
+}
+
+void SetSystemBootReason() {
+  const std::string bootloader_boot_reason(GetProperty(bootloader_reboot_reason_property));
+  const std::string system_boot_reason(BootReasonStrToReason(bootloader_boot_reason));
+  // Record the scrubbed system_boot_reason to the property
+  BootReasonAddToHistory(system_boot_reason);
+  // Shift last_reboot_reason_property to last_last_reboot_reason_property
+  std::string last_boot_reason(GetProperty(last_reboot_reason_property));
+  if (last_boot_reason.empty() || isKernelRebootReason(system_boot_reason)) {
+    last_boot_reason = system_boot_reason;
+  } else {
+    transformReason(last_boot_reason);
+  }
+  SetProperty(last_last_reboot_reason_property, last_boot_reason);
+  SetProperty(last_reboot_reason_property, "");
+}
+
 // Records several metrics related to the time it takes to boot the device,
 // including disambiguating boot time on encrypted or non-encrypted devices.
 void RecordBootComplete() {
   BootEventRecordStore boot_event_store;
   BootEventRecordStore::BootEventRecord record;
 
-  auto time_since_epoch = android::base::boot_clock::now().time_since_epoch();
-  auto uptime = std::chrono::duration_cast<std::chrono::seconds>(time_since_epoch);
+  auto uptime_ns = GetUptime();
+  auto uptime_s = std::chrono::duration_cast<std::chrono::seconds>(uptime_ns);
   time_t current_time_utc = time(nullptr);
 
   if (boot_event_store.GetBootEvent("last_boot_time_utc", &record)) {
     time_t last_boot_time_utc = record.second;
-    time_t time_since_last_boot = difftime(current_time_utc,
-                                           last_boot_time_utc);
-    boot_event_store.AddBootEventWithValue("time_since_last_boot",
-                                           time_since_last_boot);
+    time_t time_since_last_boot = difftime(current_time_utc, last_boot_time_utc);
+    boot_event_store.AddBootEventWithValue("time_since_last_boot", time_since_last_boot);
   }
 
   boot_event_store.AddBootEventWithValue("last_boot_time_utc", current_time_utc);
@@ -322,20 +1120,20 @@
     // Log the amount of time elapsed until the device is decrypted, which
     // includes the variable amount of time the user takes to enter the
     // decryption password.
-    boot_event_store.AddBootEventWithValue("boot_decryption_complete", uptime.count());
+    boot_event_store.AddBootEventWithValue("boot_decryption_complete", uptime_s.count());
 
     // Subtract the decryption time to normalize the boot cycle timing.
-    std::chrono::seconds boot_complete = std::chrono::seconds(uptime.count() - record.second);
+    std::chrono::seconds boot_complete = std::chrono::seconds(uptime_s.count() - record.second);
     boot_event_store.AddBootEventWithValue(boot_complete_prefix + "_post_decrypt",
                                            boot_complete.count());
   } else {
-      boot_event_store.AddBootEventWithValue(boot_complete_prefix + "_no_encryption",
-                                             uptime.count());
+    boot_event_store.AddBootEventWithValue(boot_complete_prefix + "_no_encryption",
+                                           uptime_s.count());
   }
 
   // Record the total time from device startup to boot complete, regardless of
   // encryption state.
-  boot_event_store.AddBootEventWithValue(boot_complete_prefix, uptime.count());
+  boot_event_store.AddBootEventWithValue(boot_complete_prefix, uptime_s.count());
 
   RecordInitBootTimeProp(&boot_event_store, "ro.boottime.init");
   RecordInitBootTimeProp(&boot_event_store, "ro.boottime.init.selinux");
@@ -344,16 +1142,38 @@
   const BootloaderTimingMap bootloader_timings = GetBootLoaderTimings();
   RecordBootloaderTimings(&boot_event_store, bootloader_timings);
 
-  auto uptime_ms = std::chrono::duration_cast<std::chrono::milliseconds>(time_since_epoch);
+  auto uptime_ms = std::chrono::duration_cast<std::chrono::milliseconds>(uptime_ns);
   RecordAbsoluteBootTime(&boot_event_store, bootloader_timings, uptime_ms);
 }
 
 // Records the boot_reason metric by querying the ro.boot.bootreason system
 // property.
 void RecordBootReason() {
-  int32_t boot_reason = BootReasonStrToEnum(GetProperty("ro.boot.bootreason"));
+  const std::string reason(GetProperty(bootloader_reboot_reason_property));
+
+  if (reason.empty()) {
+    // Log an empty boot reason value as '<EMPTY>' to ensure the value is intentional
+    // (and not corruption anywhere else in the reporting pipeline).
+    android::metricslogger::LogMultiAction(android::metricslogger::ACTION_BOOT,
+                                           android::metricslogger::FIELD_PLATFORM_REASON, "<EMPTY>");
+  } else {
+    android::metricslogger::LogMultiAction(android::metricslogger::ACTION_BOOT,
+                                           android::metricslogger::FIELD_PLATFORM_REASON, reason);
+  }
+
+  // Log the raw bootloader_boot_reason property value.
+  int32_t boot_reason = BootReasonStrToEnum(reason);
   BootEventRecordStore boot_event_store;
   boot_event_store.AddBootEventWithValue("boot_reason", boot_reason);
+
+  // Log the scrubbed system_boot_reason.
+  const std::string system_reason(GetProperty(system_reboot_reason_property));
+  int32_t system_boot_reason = BootReasonStrToEnum(system_reason);
+  boot_event_store.AddBootEventWithValue("system_boot_reason", system_boot_reason);
+
+  if (reason == "") {
+    SetProperty(bootloader_reboot_reason_property, system_reason);
+  }
 }
 
 // Records two metrics related to the user resetting a device: the time at
@@ -367,21 +1187,20 @@
 
   if (current_time_utc < 0) {
     // UMA does not display negative values in buckets, so convert to positive.
-    android::metricslogger::LogHistogram(
-        "factory_reset_current_time_failure", std::abs(current_time_utc));
+    android::metricslogger::LogHistogram("factory_reset_current_time_failure",
+                                         std::abs(current_time_utc));
 
     // Logging via BootEventRecordStore to see if using android::metricslogger::LogHistogram
     // is losing records somehow.
-    boot_event_store.AddBootEventWithValue(
-        "factory_reset_current_time_failure", std::abs(current_time_utc));
+    boot_event_store.AddBootEventWithValue("factory_reset_current_time_failure",
+                                           std::abs(current_time_utc));
     return;
   } else {
     android::metricslogger::LogHistogram("factory_reset_current_time", current_time_utc);
 
     // Logging via BootEventRecordStore to see if using android::metricslogger::LogHistogram
     // is losing records somehow.
-    boot_event_store.AddBootEventWithValue(
-        "factory_reset_current_time", current_time_utc);
+    boot_event_store.AddBootEventWithValue("factory_reset_current_time", current_time_utc);
   }
 
   // The factory_reset boot event does not exist after the device is reset, so
@@ -401,18 +1220,15 @@
 
   // Logging via BootEventRecordStore to see if using android::metricslogger::LogHistogram
   // is losing records somehow.
-  boot_event_store.AddBootEventWithValue(
-      "factory_reset_record_value", factory_reset_utc);
+  boot_event_store.AddBootEventWithValue("factory_reset_record_value", factory_reset_utc);
 
-  time_t time_since_factory_reset = difftime(current_time_utc,
-                                             factory_reset_utc);
-  boot_event_store.AddBootEventWithValue("time_since_factory_reset",
-                                         time_since_factory_reset);
+  time_t time_since_factory_reset = difftime(current_time_utc, factory_reset_utc);
+  boot_event_store.AddBootEventWithValue("time_since_factory_reset", time_since_factory_reset);
 }
 
 }  // namespace
 
-int main(int argc, char **argv) {
+int main(int argc, char** argv) {
   android::base::InitLogging(argv);
 
   const std::string cmd_line = GetCommandLine(argc, argv);
@@ -420,19 +1236,23 @@
 
   int option_index = 0;
   static const char value_str[] = "value";
+  static const char system_boot_reason_str[] = "set_system_boot_reason";
   static const char boot_complete_str[] = "record_boot_complete";
   static const char boot_reason_str[] = "record_boot_reason";
   static const char factory_reset_str[] = "record_time_since_factory_reset";
   static const struct option long_options[] = {
-    { "help",            no_argument,       NULL,   'h' },
-    { "log",             no_argument,       NULL,   'l' },
-    { "print",           no_argument,       NULL,   'p' },
-    { "record",          required_argument, NULL,   'r' },
-    { value_str,         required_argument, NULL,   0 },
-    { boot_complete_str, no_argument,       NULL,   0 },
-    { boot_reason_str,   no_argument,       NULL,   0 },
-    { factory_reset_str, no_argument,       NULL,   0 },
-    { NULL,              0,                 NULL,   0 }
+      // clang-format off
+      { "help",                 no_argument,       NULL,   'h' },
+      { "log",                  no_argument,       NULL,   'l' },
+      { "print",                no_argument,       NULL,   'p' },
+      { "record",               required_argument, NULL,   'r' },
+      { value_str,              required_argument, NULL,   0 },
+      { system_boot_reason_str, no_argument,       NULL,   0 },
+      { boot_complete_str,      no_argument,       NULL,   0 },
+      { boot_reason_str,        no_argument,       NULL,   0 },
+      { factory_reset_str,      no_argument,       NULL,   0 },
+      { NULL,                   0,                 NULL,   0 }
+      // clang-format on
   };
 
   std::string boot_event;
@@ -447,6 +1267,8 @@
           // |optarg| is an external variable set by getopt representing
           // the option argument.
           value = optarg;
+        } else if (option_name == system_boot_reason_str) {
+          SetSystemBootReason();
         } else if (option_name == boot_complete_str) {
           RecordBootComplete();
         } else if (option_name == boot_reason_str) {
diff --git a/bootstat/bootstat.rc b/bootstat/bootstat.rc
index f4756d5..1300a27 100644
--- a/bootstat/bootstat.rc
+++ b/bootstat/bootstat.rc
@@ -1,7 +1,43 @@
 # This file is the LOCAL_INIT_RC file for the bootstat command.
 
+# mirror bootloader boot reason to system boot reason
+on property:ro.boot.bootreason=*
+    setprop sys.boot.reason ${ro.boot.bootreason}
+
 on post-fs-data
-    mkdir /data/misc/bootstat 0700 root root
+    mkdir /data/misc/bootstat 0700 system log
+    # To deal with ota transition resulting from a change in DAC from
+    # root.root to system.log, may be deleted after ota has settled.
+    chown system log /data/misc/bootstat/absolute_boot_time
+    chown system log /data/misc/bootstat/boot_complete
+    chown system log /data/misc/bootstat/boot_complete_no_encryption
+    chown system log /data/misc/bootstat/boot_reason
+    chown system log /data/misc/bootstat/boottime.bootloader.1BLE
+    chown system log /data/misc/bootstat/boottime.bootloader.1BLL
+    chown system log /data/misc/bootstat/boottime.bootloader.2BLE
+    chown system log /data/misc/bootstat/boottime.bootloader.2BLL
+    chown system log /data/misc/bootstat/boottime.bootloader.AVB
+    chown system log /data/misc/bootstat/boottime.bootloader.KD
+    chown system log /data/misc/bootstat/boottime.bootloader.KL
+    chown system log /data/misc/bootstat/boottime.bootloader.ODT
+    chown system log /data/misc/bootstat/boottime.bootloader.SW
+    chown system log /data/misc/bootstat/boottime.bootloader.total
+    chown system log /data/misc/bootstat/build_date
+    chown system log /data/misc/bootstat/factory_reset
+    chown system log /data/misc/bootstat/factory_reset_boot_complete
+    chown system log /data/misc/bootstat/factory_reset_boot_complete_no_encryption
+    chown system log /data/misc/bootstat/factory_reset_current_time
+    chown system log /data/misc/bootstat/factory_reset_record_value
+    chown system log /data/misc/bootstat/last_boot_time_utc
+    chown system log /data/misc/bootstat/ota_boot_complete
+    chown system log /data/misc/bootstat/ota_boot_complete_no_encryption
+    chown system log /data/misc/bootstat/post_decrypt_time_elapsed
+    chown system log /data/misc/bootstat/ro.boottime.init
+    chown system log /data/misc/bootstat/ro.boottime.init.cold_boot_wait
+    chown system log /data/misc/bootstat/ro.boottime.init.selinux
+    chown system log /data/misc/bootstat/time_since_factory_reset
+    chown system log /data/misc/bootstat/time_since_last_boot
+    # end ota transitional support
 
 # Record the time at which the user has successfully entered the pin to decrypt
 # the device, /data is decrypted, and the system is entering the main boot phase.
@@ -10,7 +46,7 @@
 # property:init.svc.bootanim=running: The boot animation is running
 # property:ro.crypto.type=block: FDE device
 on post-fs-data && property:init.svc.bootanim=running && property:ro.crypto.type=block
-    exec - root root -- /system/bin/bootstat -r post_decrypt_time_elapsed
+    exec_background - system log -- /system/bin/bootstat -r post_decrypt_time_elapsed
 
 # sys.logbootcomplete is a signal to enable the bootstat logging mechanism.
 # This signaling is necessary to prevent logging boot metrics after a runtime
@@ -32,14 +68,9 @@
 
 # Record boot complete metrics.
 on property:sys.boot_completed=1 && property:sys.logbootcomplete=1
+    # Converts bootloader boot reason to system boot reason
     # Record boot_complete and related stats (decryption, etc).
-    exec - root root -- /system/bin/bootstat --record_boot_complete
-
     # Record the boot reason.
-    exec - root root -- /system/bin/bootstat --record_boot_reason
-
     # Record time since factory reset.
-    exec - root root -- /system/bin/bootstat --record_time_since_factory_reset
-
     # Log all boot events.
-    exec - root root -- /system/bin/bootstat -l
+    exec_background - system log -- /system/bin/bootstat --set_system_boot_reason --record_boot_complete --record_boot_reason --record_time_since_factory_reset -l
diff --git a/debuggerd/Android.bp b/debuggerd/Android.bp
index f86aaa0..1b20157 100644
--- a/debuggerd/Android.bp
+++ b/debuggerd/Android.bp
@@ -4,16 +4,20 @@
         "-Wall",
         "-Wextra",
         "-Werror",
+        "-Wno-unused-argument",
+        "-Wno-unused-function",
         "-Wno-nullability-completeness",
         "-Os",
     ],
+    cpp_std: "gnu++17",
 
     local_include_dirs: ["include"],
 }
 
 cc_library_headers {
     name: "libdebuggerd_common_headers",
-    export_include_dirs: ["common/include"]
+    export_include_dirs: ["common/include"],
+    recovery_available: true,
 }
 
 cc_library_shared {
@@ -36,13 +40,14 @@
     ],
 
     export_header_lib_headers: ["libdebuggerd_common_headers"],
-    export_include_dirs: ["tombstoned/include"]
+    export_include_dirs: ["tombstoned/include"],
 }
 
 // Utility library to tombstoned and get an output fd.
 cc_library_static {
     name: "libtombstoned_client_static",
     defaults: ["debuggerd_defaults"],
+    recovery_available: true,
     srcs: [
         "tombstoned/tombstoned_client.cpp",
         "util.cpp",
@@ -57,19 +62,24 @@
     ],
 
     export_header_lib_headers: ["libdebuggerd_common_headers"],
-    export_include_dirs: ["tombstoned/include"]
+    export_include_dirs: ["tombstoned/include"],
 }
 
 // Core implementation, linked into libdebuggerd_handler and the dynamic linker.
 cc_library_static {
     name: "libdebuggerd_handler_core",
     defaults: ["debuggerd_defaults"],
+    recovery_available: true,
     srcs: ["handler/debuggerd_handler.cpp"],
 
-    header_libs: ["libdebuggerd_common_headers"],
+    header_libs: [
+        "libbase_headers",
+        "libdebuggerd_common_headers",
+    ],
 
     whole_static_libs: [
         "libasync_safe",
+        "libcutils",
         "libdebuggerd",
     ],
 
@@ -94,6 +104,7 @@
 cc_library_static {
     name: "libdebuggerd_handler_fallback",
     defaults: ["debuggerd_defaults"],
+    recovery_available: true,
     srcs: [
         "handler/debuggerd_fallback.cpp",
     ],
@@ -105,10 +116,17 @@
         "libbase",
         "libdebuggerd",
         "libbacktrace",
-        "libunwind",
+        "libunwindstack",
+        "libdexfile",
         "liblzma",
         "libcutils",
     ],
+    target: {
+        recovery: {
+            cflags: ["-DNO_LIBDEXFILE_SUPPORT"],
+            exclude_static_libs: ["libdexfile"],
+        },
+    },
 
     export_include_dirs: ["include"],
 }
@@ -135,6 +153,7 @@
 cc_library_static {
     name: "libdebuggerd",
     defaults: ["debuggerd_defaults"],
+    recovery_available: true,
 
     srcs: [
         "libdebuggerd/backtrace.cpp",
@@ -144,33 +163,12 @@
         "libdebuggerd/utility.cpp",
     ],
 
-    target: {
-        android_arm: {
-            srcs: ["libdebuggerd/arm/machine.cpp"],
-        },
-        android_arm64: {
-            srcs: ["libdebuggerd/arm64/machine.cpp"],
-        },
-        android_mips: {
-            srcs: ["libdebuggerd/mips/machine.cpp"],
-        },
-        android_mips64: {
-            srcs: ["libdebuggerd/mips64/machine.cpp"],
-        },
-        android_x86: {
-            srcs: ["libdebuggerd/x86/machine.cpp"],
-        },
-        android_x86_64: {
-            srcs: ["libdebuggerd/x86_64/machine.cpp"],
-        },
-    },
-
     local_include_dirs: ["libdebuggerd/include"],
     export_include_dirs: ["libdebuggerd/include"],
 
     static_libs: [
         "libbacktrace",
-        "libunwind",
+        "libunwindstack",
         "liblzma",
         "libbase",
         "libcutils",
@@ -188,8 +186,6 @@
         "libdebuggerd/test/elf_fake.cpp",
         "libdebuggerd/test/log_fake.cpp",
         "libdebuggerd/test/open_files_list_test.cpp",
-        "libdebuggerd/test/property_fake.cpp",
-        "libdebuggerd/test/ptrace_fake.cpp",
         "libdebuggerd/test/tombstone_test.cpp",
     ],
 
@@ -199,7 +195,10 @@
                 "client/debuggerd_client_test.cpp",
                 "debuggerd_test.cpp",
             ],
-            static_libs: ["libasync_safe", "libtombstoned_client_static"],
+            static_libs: [
+                "libasync_safe",
+                "libtombstoned_client_static",
+            ],
         },
     },
 
@@ -209,11 +208,13 @@
         "libcutils",
         "libdebuggerd_client",
         "liblog",
-        "libnativehelper"
+        "libminijail",
+        "libnativehelper",
     ],
 
     static_libs: [
         "libdebuggerd",
+        "libunwindstack",
     ],
 
     local_include_dirs: [
@@ -229,6 +230,18 @@
             stem: "debuggerd_test64",
         },
     },
+
+    test_suites: ["device-tests"],
+}
+
+cc_benchmark {
+    name: "debuggerd_benchmark",
+    defaults: ["debuggerd_defaults"],
+    srcs: ["debuggerd_benchmark.cpp"],
+    shared_libs: [
+        "libbase",
+        "libdebuggerd_client",
+    ],
 }
 
 cc_binary {
@@ -260,6 +273,7 @@
         "libbase",
         "liblog",
         "libprocinfo",
+        "libunwindstack",
     ],
 }
 
@@ -298,7 +312,7 @@
         "liblog",
     ],
 
-    init_rc: ["tombstoned/tombstoned.rc"]
+    init_rc: ["tombstoned/tombstoned.rc"],
 }
 
 subdirs = [
diff --git a/debuggerd/Android.mk b/debuggerd/Android.mk
new file mode 100644
index 0000000..c03b41d
--- /dev/null
+++ b/debuggerd/Android.mk
@@ -0,0 +1,24 @@
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := crash_dump.policy
+LOCAL_MODULE_CLASS := ETC
+LOCAL_MULTILIB := both
+
+ifeq ($(TARGET_ARCH), $(filter $(TARGET_ARCH), arm arm64))
+LOCAL_MODULE_STEM_32 := crash_dump.arm.policy
+LOCAL_MODULE_STEM_64 := crash_dump.arm64.policy
+endif
+
+ifeq ($(TARGET_ARCH), $(filter $(TARGET_ARCH), x86 x86_64))
+LOCAL_MODULE_STEM_32 := crash_dump.x86.policy
+LOCAL_MODULE_STEM_64 := crash_dump.x86_64.policy
+endif
+
+LOCAL_MODULE_PATH := $(TARGET_OUT)/etc/seccomp_policy
+LOCAL_SRC_FILES_arm := seccomp_policy/crash_dump.arm.policy
+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/OWNERS b/debuggerd/OWNERS
new file mode 100644
index 0000000..bfeedca
--- /dev/null
+++ b/debuggerd/OWNERS
@@ -0,0 +1,2 @@
+cferris@google.com
+jmgao@google.com
diff --git a/debuggerd/crash_dump.cpp b/debuggerd/crash_dump.cpp
index 4b1e51d..40cf6c3 100644
--- a/debuggerd/crash_dump.cpp
+++ b/debuggerd/crash_dump.cpp
@@ -18,11 +18,11 @@
 #include <dirent.h>
 #include <fcntl.h>
 #include <stdlib.h>
-#include <sys/capability.h>
 #include <sys/prctl.h>
 #include <sys/ptrace.h>
 #include <sys/types.h>
 #include <sys/un.h>
+#include <sys/wait.h>
 #include <syscall.h>
 #include <unistd.h>
 
@@ -47,31 +47,22 @@
 #define ATRACE_TAG ATRACE_TAG_BIONIC
 #include <utils/Trace.h>
 
-#include "backtrace.h"
-#include "tombstone.h"
-#include "utility.h"
+#include <unwindstack/Regs.h>
+
+#include "libdebuggerd/backtrace.h"
+#include "libdebuggerd/tombstone.h"
+#include "libdebuggerd/utility.h"
 
 #include "debuggerd/handler.h"
-#include "protocol.h"
 #include "tombstoned/tombstoned.h"
+
+#include "protocol.h"
 #include "util.h"
 
 using android::base::unique_fd;
-using android::base::ReadFileToString;
 using android::base::StringPrintf;
-using android::base::Trim;
 
-static std::string get_process_name(pid_t pid) {
-  std::string result = "<unknown>";
-  ReadFileToString(StringPrintf("/proc/%d/cmdline", pid), &result);
-  return result;
-}
-
-static std::string get_thread_name(pid_t tid) {
-  std::string result = "<unknown>";
-  ReadFileToString(StringPrintf("/proc/%d/comm", tid), &result);
-  return Trim(result);
-}
+using unwindstack::Regs;
 
 static bool pid_contains_tid(int pid_proc_fd, pid_t tid) {
   struct stat st;
@@ -79,9 +70,27 @@
   return fstatat(pid_proc_fd, task_path.c_str(), &st, 0) == 0;
 }
 
+static pid_t get_tracer(pid_t tracee) {
+  // Check to see if the thread is being ptraced by another process.
+  android::procinfo::ProcessInfo process_info;
+  if (android::procinfo::GetProcessInfo(tracee, &process_info)) {
+    return process_info.tracer;
+  }
+  return -1;
+}
+
 // Attach to a thread, and verify that it's still a member of the given process
-static bool ptrace_seize_thread(int pid_proc_fd, pid_t tid, std::string* error) {
-  if (ptrace(PTRACE_SEIZE, tid, 0, 0) != 0) {
+static bool ptrace_seize_thread(int pid_proc_fd, pid_t tid, std::string* error, int flags = 0) {
+  if (ptrace(PTRACE_SEIZE, tid, 0, flags) != 0) {
+    if (errno == EPERM) {
+      pid_t tracer = get_tracer(tid);
+      if (tracer != -1) {
+        *error = StringPrintf("failed to attach to thread %d, already traced by %d (%s)", tid,
+                              tracer, get_process_name(tracer).c_str());
+        return false;
+      }
+    }
+
     *error = StringPrintf("failed to attach to thread %d: %s", tid, strerror(errno));
     return false;
   }
@@ -89,18 +98,43 @@
   // Make sure that the task we attached to is actually part of the pid we're dumping.
   if (!pid_contains_tid(pid_proc_fd, tid)) {
     if (ptrace(PTRACE_DETACH, tid, 0, 0) != 0) {
-      PLOG(FATAL) << "failed to detach from thread " << tid;
+      PLOG(WARNING) << "failed to detach from thread " << tid;
     }
     *error = StringPrintf("thread %d is not in process", tid);
     return false;
   }
 
-  // Put the task into ptrace-stop state.
-  if (ptrace(PTRACE_INTERRUPT, tid, 0, 0) != 0) {
-    PLOG(FATAL) << "failed to interrupt thread " << tid;
+  return true;
+}
+
+static bool wait_for_stop(pid_t tid, int* received_signal) {
+  while (true) {
+    int status;
+    pid_t result = waitpid(tid, &status, __WALL);
+    if (result != tid) {
+      PLOG(ERROR) << "waitpid failed on " << tid << " while detaching";
+      return false;
+    }
+
+    if (WIFSTOPPED(status)) {
+      if (status >> 16 == PTRACE_EVENT_STOP) {
+        *received_signal = 0;
+      } else {
+        *received_signal = WSTOPSIG(status);
+      }
+      return true;
+    }
+  }
+}
+
+// Interrupt a process and wait for it to be interrupted.
+static bool ptrace_interrupt(pid_t tid, int* received_signal) {
+  if (ptrace(PTRACE_INTERRUPT, tid, 0, 0) == 0) {
+    return wait_for_stop(tid, received_signal);
   }
 
-  return true;
+  PLOG(ERROR) << "failed to interrupt " << tid << " to detach";
+  return false;
 }
 
 static bool activity_manager_notify(pid_t pid, int signal, const std::string& amfd_data) {
@@ -150,70 +184,16 @@
   return true;
 }
 
-static void signal_handler(int) {
-  // We can't log easily, because the heap might be corrupt.
-  // Just die and let the surrounding log context explain things.
-  _exit(1);
-}
+// Globals used by the abort handler.
+static pid_t g_target_thread = -1;
+static bool g_tombstoned_connected = false;
+static unique_fd g_tombstoned_socket;
+static unique_fd g_output_fd;
 
-static void abort_handler(pid_t target, const bool tombstoned_connected,
-                          unique_fd& tombstoned_socket, unique_fd& output_fd,
-                          const char* abort_msg) {
-  // If we abort before we get an output fd, contact tombstoned to let any
-  // potential listeners know that we failed.
-  if (!tombstoned_connected) {
-    if (!tombstoned_connect(target, &tombstoned_socket, &output_fd, kDebuggerdAnyIntercept)) {
-      // We failed to connect, not much we can do.
-      LOG(ERROR) << "failed to connected to tombstoned to report failure";
-      _exit(1);
-    }
-  }
-
-  dprintf(output_fd.get(), "crash_dump failed to dump process");
-  if (target != 1) {
-    dprintf(output_fd.get(), " %d: %s\n", target, abort_msg);
-  } else {
-    dprintf(output_fd.get(), ": %s\n", abort_msg);
-  }
-
-  _exit(1);
-}
-
-static void drop_capabilities() {
-  ATRACE_CALL();
-  __user_cap_header_struct capheader;
-  memset(&capheader, 0, sizeof(capheader));
-  capheader.version = _LINUX_CAPABILITY_VERSION_3;
-  capheader.pid = 0;
-
-  __user_cap_data_struct capdata[2];
-  memset(&capdata, 0, sizeof(capdata));
-
-  if (capset(&capheader, &capdata[0]) == -1) {
-    PLOG(FATAL) << "failed to drop capabilities";
-  }
-
-  if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) != 0) {
-    PLOG(FATAL) << "failed to set PR_SET_NO_NEW_PRIVS";
-  }
-}
-
-int main(int argc, char** argv) {
-  atrace_begin(ATRACE_TAG, "before reparent");
-
-  pid_t target = getppid();
-  bool tombstoned_connected = false;
-  unique_fd tombstoned_socket;
-  unique_fd output_fd;
-
-  android::base::InitLogging(argv);
-  android::base::SetAborter([&](const char* abort_msg) {
-    abort_handler(target, tombstoned_connected, tombstoned_socket, output_fd, abort_msg);
-  });
-
+static void DefuseSignalHandlers() {
   // Don't try to dump ourselves.
   struct sigaction action = {};
-  action.sa_handler = signal_handler;
+  action.sa_handler = SIG_DFL;
   debuggerd_register_handlers(&action);
 
   sigset_t mask;
@@ -221,242 +201,374 @@
   if (sigprocmask(SIG_SETMASK, &mask, nullptr) != 0) {
     PLOG(FATAL) << "failed to set signal mask";
   }
+}
 
+static void Initialize(char** argv) {
+  android::base::InitLogging(argv);
+  android::base::SetAborter([](const char* abort_msg) {
+    // If we abort before we get an output fd, contact tombstoned to let any
+    // potential listeners know that we failed.
+    if (!g_tombstoned_connected) {
+      if (!tombstoned_connect(g_target_thread, &g_tombstoned_socket, &g_output_fd,
+                              kDebuggerdAnyIntercept)) {
+        // We failed to connect, not much we can do.
+        LOG(ERROR) << "failed to connected to tombstoned to report failure";
+        _exit(1);
+      }
+    }
+
+    dprintf(g_output_fd.get(), "crash_dump failed to dump process");
+    if (g_target_thread != 1) {
+      dprintf(g_output_fd.get(), " %d: %s\n", g_target_thread, abort_msg);
+    } else {
+      dprintf(g_output_fd.get(), ": %s\n", abort_msg);
+    }
+
+    _exit(1);
+  });
+}
+
+static void ParseArgs(int argc, char** argv, pid_t* pseudothread_tid, DebuggerdDumpType* dump_type) {
   if (argc != 4) {
-    LOG(FATAL) << "Wrong number of args: " << argc << " (expected 4)";
+    LOG(FATAL) << "wrong number of args: " << argc << " (expected 4)";
   }
 
-  pid_t main_tid;
-  pid_t pseudothread_tid;
-  int dump_type;
-
-  if (!android::base::ParseInt(argv[1], &main_tid, 1, std::numeric_limits<pid_t>::max())) {
-    LOG(FATAL) << "invalid main tid: " << argv[1];
+  if (!android::base::ParseInt(argv[1], &g_target_thread, 1, std::numeric_limits<pid_t>::max())) {
+    LOG(FATAL) << "invalid target tid: " << argv[1];
   }
 
-  if (!android::base::ParseInt(argv[2], &pseudothread_tid, 1, std::numeric_limits<pid_t>::max())) {
+  if (!android::base::ParseInt(argv[2], pseudothread_tid, 1, std::numeric_limits<pid_t>::max())) {
     LOG(FATAL) << "invalid pseudothread tid: " << argv[2];
   }
 
-  if (!android::base::ParseInt(argv[3], &dump_type, 0, 1)) {
+  int dump_type_int;
+  if (!android::base::ParseInt(argv[3], &dump_type_int, 0, 1)) {
     LOG(FATAL) << "invalid requested dump type: " << argv[3];
   }
+  *dump_type = static_cast<DebuggerdDumpType>(dump_type_int);
+}
 
-  if (target == 1) {
-    LOG(FATAL) << "target died before we could attach (received main tid = " << main_tid << ")";
+static void ReadCrashInfo(unique_fd& fd, siginfo_t* siginfo,
+                          std::unique_ptr<unwindstack::Regs>* regs, uintptr_t* abort_address) {
+  std::aligned_storage<sizeof(CrashInfo) + 1, alignof(CrashInfo)>::type buf;
+  ssize_t rc = TEMP_FAILURE_RETRY(read(fd.get(), &buf, sizeof(buf)));
+  if (rc == -1) {
+    PLOG(FATAL) << "failed to read target ucontext";
+  } else if (rc != sizeof(CrashInfo)) {
+    LOG(FATAL) << "read " << rc << " bytes when reading target crash information, expected "
+               << sizeof(CrashInfo);
   }
 
-  android::procinfo::ProcessInfo target_info;
-  if (!android::procinfo::GetProcessInfo(main_tid, &target_info)) {
-    LOG(FATAL) << "failed to fetch process info for target " << main_tid;
+  CrashInfo* crash_info = reinterpret_cast<CrashInfo*>(&buf);
+  if (crash_info->version != 1) {
+    LOG(FATAL) << "version mismatch, expected 1, received " << crash_info->version;
   }
 
-  if (main_tid != target_info.tid || target != target_info.pid) {
-    LOG(FATAL) << "target info mismatch, expected pid " << target << ", tid " << main_tid
-               << ", received pid " << target_info.pid << ", tid " << target_info.tid;
+  *siginfo = crash_info->siginfo;
+  regs->reset(Regs::CreateFromUcontext(Regs::CurrentArch(), &crash_info->ucontext));
+  *abort_address = crash_info->abort_msg_address;
+}
+
+// Wait for a process to clone and return the child's pid.
+// Note: this leaves the parent in PTRACE_EVENT_STOP.
+static pid_t wait_for_clone(pid_t pid, bool resume_child) {
+  int status;
+  pid_t result = TEMP_FAILURE_RETRY(waitpid(pid, &status, __WALL));
+  if (result == -1) {
+    PLOG(FATAL) << "failed to waitpid";
   }
 
-  // Open /proc/`getppid()` in the original process, and pass it down to the forked child.
-  std::string target_proc_path = "/proc/" + std::to_string(target);
+  if (WIFEXITED(status)) {
+    LOG(FATAL) << "traced process exited with status " << WEXITSTATUS(status);
+  } else if (WIFSIGNALED(status)) {
+    LOG(FATAL) << "traced process exited with signal " << WTERMSIG(status);
+  } else if (!WIFSTOPPED(status)) {
+    LOG(FATAL) << "process didn't stop? (status = " << status << ")";
+  }
+
+  if (status >> 8 != (SIGTRAP | (PTRACE_EVENT_CLONE << 8))) {
+    LOG(FATAL) << "process didn't stop due to PTRACE_O_TRACECLONE (status = " << status << ")";
+  }
+
+  pid_t child;
+  if (ptrace(PTRACE_GETEVENTMSG, pid, 0, &child) != 0) {
+    PLOG(FATAL) << "failed to get child pid via PTRACE_GETEVENTMSG";
+  }
+
+  int stop_signal;
+  if (!wait_for_stop(child, &stop_signal)) {
+    PLOG(FATAL) << "failed to waitpid on child";
+  }
+
+  CHECK_EQ(0, stop_signal);
+
+  if (resume_child) {
+    if (ptrace(PTRACE_CONT, child, 0, 0) != 0) {
+      PLOG(FATAL) << "failed to resume child (pid = " << child << ")";
+    }
+  }
+
+  return child;
+}
+
+static pid_t wait_for_vm_process(pid_t pseudothread_tid) {
+  // The pseudothread will double-fork, we want its grandchild.
+  pid_t intermediate = wait_for_clone(pseudothread_tid, true);
+  pid_t vm_pid = wait_for_clone(intermediate, false);
+  if (ptrace(PTRACE_DETACH, intermediate, 0, 0) != 0) {
+    PLOG(FATAL) << "failed to detach from intermediate vm process";
+  }
+
+  return vm_pid;
+}
+
+int main(int argc, char** argv) {
+  DefuseSignalHandlers();
+
+  atrace_begin(ATRACE_TAG, "before reparent");
+  pid_t target_process = getppid();
+
+  // Open /proc/`getppid()` before we daemonize.
+  std::string target_proc_path = "/proc/" + std::to_string(target_process);
   int target_proc_fd = open(target_proc_path.c_str(), O_DIRECTORY | O_RDONLY);
   if (target_proc_fd == -1) {
     PLOG(FATAL) << "failed to open " << target_proc_path;
   }
 
-  // Make sure our parent didn't die.
-  if (getppid() != target) {
-    PLOG(FATAL) << "parent died";
+  // Make sure getppid() hasn't changed.
+  if (getppid() != target_process) {
+    LOG(FATAL) << "parent died";
   }
-
   atrace_end(ATRACE_TAG);
 
   // Reparent ourselves to init, so that the signal handler can waitpid on the
   // original process to avoid leaving a zombie for non-fatal dumps.
+  // Move the input/output pipes off of stdout/stderr, out of paranoia.
+  unique_fd output_pipe(dup(STDOUT_FILENO));
+  unique_fd input_pipe(dup(STDIN_FILENO));
+
+  unique_fd fork_exit_read, fork_exit_write;
+  if (!Pipe(&fork_exit_read, &fork_exit_write)) {
+    PLOG(FATAL) << "failed to create pipe";
+  }
+
   pid_t forkpid = fork();
   if (forkpid == -1) {
     PLOG(FATAL) << "fork failed";
-  } else if (forkpid != 0) {
-    exit(0);
+  } else if (forkpid == 0) {
+    fork_exit_read.reset();
+  } else {
+    // We need the pseudothread to live until we get around to verifying the vm pid against it.
+    // The last thing it does is block on a waitpid on us, so wait until our child tells us to die.
+    fork_exit_write.reset();
+    char buf;
+    TEMP_FAILURE_RETRY(read(fork_exit_read.get(), &buf, sizeof(buf)));
+    _exit(0);
   }
 
   ATRACE_NAME("after reparent");
+  pid_t pseudothread_tid;
+  DebuggerdDumpType dump_type;
+  uintptr_t abort_address = 0;
+
+  Initialize(argv);
+  ParseArgs(argc, argv, &pseudothread_tid, &dump_type);
 
   // Die if we take too long.
   //
   // Note: processes with many threads and minidebug-info can take a bit to
   //       unwind, do not make this too small. b/62828735
-  alarm(5);
+  alarm(30);
 
-  std::string attach_error;
-
-  std::map<pid_t, std::string> threads;
-
-  {
-    ATRACE_NAME("ptrace");
-    // Seize the main thread.
-    if (!ptrace_seize_thread(target_proc_fd, main_tid, &attach_error)) {
-      LOG(FATAL) << attach_error;
-    }
-
-    // Seize the siblings.
-    {
-      std::set<pid_t> siblings;
-      if (!android::procinfo::GetProcessTids(target, &siblings)) {
-        PLOG(FATAL) << "failed to get process siblings";
-      }
-
-      // but not the already attached main thread.
-      siblings.erase(main_tid);
-      // or the handler pseudothread.
-      siblings.erase(pseudothread_tid);
-
-      for (pid_t sibling_tid : siblings) {
-        if (!ptrace_seize_thread(target_proc_fd, sibling_tid, &attach_error)) {
-          LOG(WARNING) << attach_error;
-        } else {
-          threads.emplace(sibling_tid, get_thread_name(sibling_tid));
-        }
-      }
-    }
-  }
-
-  // Collect the backtrace map, open files, and process/thread names, while we still have caps.
-  std::unique_ptr<BacktraceMap> backtrace_map;
-  {
-    ATRACE_NAME("backtrace map");
-    backtrace_map.reset(BacktraceMap::Create(main_tid));
-    if (!backtrace_map) {
-      LOG(FATAL) << "failed to create backtrace map";
-    }
-  }
+  // Get the process name (aka cmdline).
+  std::string process_name = get_process_name(g_target_thread);
 
   // Collect the list of open files.
   OpenFilesList open_files;
   {
     ATRACE_NAME("open files");
-    populate_open_files_list(target, &open_files);
+    populate_open_files_list(g_target_thread, &open_files);
   }
 
-  std::string process_name = get_process_name(main_tid);
-  threads.emplace(main_tid, get_thread_name(main_tid));
+  // In order to reduce the duration that we pause the process for, we ptrace
+  // the threads, fetch their registers and associated information, and then
+  // fork a separate process as a snapshot of the process's address space.
+  std::set<pid_t> threads;
+  if (!android::procinfo::GetProcessTids(g_target_thread, &threads)) {
+    PLOG(FATAL) << "failed to get process threads";
+  }
 
-  // Drop our capabilities now that we've attached to the threads we care about.
+  std::map<pid_t, ThreadInfo> thread_info;
+  siginfo_t siginfo;
+  std::string error;
+
+  {
+    ATRACE_NAME("ptrace");
+    for (pid_t thread : threads) {
+      // Trace the pseudothread separately, so we can use different options.
+      if (thread == pseudothread_tid) {
+        continue;
+      }
+
+      if (!ptrace_seize_thread(target_proc_fd, thread, &error)) {
+        bool fatal = thread == g_target_thread;
+        LOG(fatal ? FATAL : WARNING) << error;
+      }
+
+      ThreadInfo info;
+      info.pid = target_process;
+      info.tid = thread;
+      info.process_name = process_name;
+      info.thread_name = get_thread_name(thread);
+
+      if (!ptrace_interrupt(thread, &info.signo)) {
+        PLOG(WARNING) << "failed to ptrace interrupt thread " << thread;
+        ptrace(PTRACE_DETACH, thread, 0, 0);
+        continue;
+      }
+
+      if (thread == g_target_thread) {
+        // Read the thread's registers along with the rest of the crash info out of the pipe.
+        ReadCrashInfo(input_pipe, &siginfo, &info.registers, &abort_address);
+        info.siginfo = &siginfo;
+        info.signo = info.siginfo->si_signo;
+      } else {
+        info.registers.reset(Regs::RemoteGet(thread));
+        if (!info.registers) {
+          PLOG(WARNING) << "failed to fetch registers for thread " << thread;
+          ptrace(PTRACE_DETACH, thread, 0, 0);
+          continue;
+        }
+      }
+
+      thread_info[thread] = std::move(info);
+    }
+  }
+
+  // Trace the pseudothread with PTRACE_O_TRACECLONE and tell it to fork.
+  if (!ptrace_seize_thread(target_proc_fd, pseudothread_tid, &error, PTRACE_O_TRACECLONE)) {
+    LOG(FATAL) << "failed to seize pseudothread: " << error;
+  }
+
+  if (TEMP_FAILURE_RETRY(write(output_pipe.get(), "\1", 1)) != 1) {
+    PLOG(FATAL) << "failed to write to pseudothread";
+  }
+
+  pid_t vm_pid = wait_for_vm_process(pseudothread_tid);
+  if (ptrace(PTRACE_DETACH, pseudothread_tid, 0, 0) != 0) {
+    PLOG(FATAL) << "failed to detach from pseudothread";
+  }
+
+  // The pseudothread can die now.
+  fork_exit_write.reset();
+
+  // Defer the message until later, for readability.
+  bool wait_for_gdb = android::base::GetBoolProperty("debug.debuggerd.wait_for_gdb", false);
+  if (siginfo.si_signo == DEBUGGER_SIGNAL) {
+    wait_for_gdb = false;
+  }
+
+  // Detach from all of our attached threads before resuming.
+  for (const auto& [tid, thread] : thread_info) {
+    int resume_signal = thread.signo == DEBUGGER_SIGNAL ? 0 : thread.signo;
+    if (wait_for_gdb) {
+      resume_signal = 0;
+      if (tgkill(target_process, tid, SIGSTOP) != 0) {
+        PLOG(WARNING) << "failed to send SIGSTOP to " << tid;
+      }
+    }
+
+    LOG(DEBUG) << "detaching from thread " << tid;
+    if (ptrace(PTRACE_DETACH, tid, 0, resume_signal) != 0) {
+      PLOG(ERROR) << "failed to detach from thread " << tid;
+    }
+  }
+
+  // Drop our capabilities now that we've fetched all of the information we need.
   drop_capabilities();
 
   {
     ATRACE_NAME("tombstoned_connect");
-    const DebuggerdDumpType dump_type_enum = static_cast<DebuggerdDumpType>(dump_type);
-    LOG(INFO) << "obtaining output fd from tombstoned, type: " << dump_type_enum;
-    tombstoned_connected = tombstoned_connect(target, &tombstoned_socket, &output_fd, dump_type_enum);
+    LOG(INFO) << "obtaining output fd from tombstoned, type: " << dump_type;
+    g_tombstoned_connected =
+        tombstoned_connect(g_target_thread, &g_tombstoned_socket, &g_output_fd, dump_type);
   }
 
-  // Write a '\1' to stdout to tell the crashing process to resume.
-  // It also restores the value of PR_SET_DUMPABLE at this point.
-  if (TEMP_FAILURE_RETRY(write(STDOUT_FILENO, "\1", 1)) == -1) {
-    PLOG(ERROR) << "failed to communicate to target process";
-  }
-
-  if (tombstoned_connected) {
-    if (TEMP_FAILURE_RETRY(dup2(output_fd.get(), STDOUT_FILENO)) == -1) {
-      PLOG(ERROR) << "failed to dup2 output fd (" << output_fd.get() << ") to STDOUT_FILENO";
+  if (g_tombstoned_connected) {
+    if (TEMP_FAILURE_RETRY(dup2(g_output_fd.get(), STDOUT_FILENO)) == -1) {
+      PLOG(ERROR) << "failed to dup2 output fd (" << g_output_fd.get() << ") to STDOUT_FILENO";
     }
   } else {
     unique_fd devnull(TEMP_FAILURE_RETRY(open("/dev/null", O_RDWR)));
     TEMP_FAILURE_RETRY(dup2(devnull.get(), STDOUT_FILENO));
-    output_fd = std::move(devnull);
+    g_output_fd = std::move(devnull);
   }
 
-  LOG(INFO) << "performing dump of process " << target << " (target tid = " << main_tid << ")";
-
-  // At this point, the thread that made the request has been attached and is
-  // in ptrace-stopped state. After resumption, the triggering signal that has
-  // been queued will be delivered.
-  if (ptrace(PTRACE_CONT, main_tid, 0, 0) != 0) {
-    PLOG(ERROR) << "PTRACE_CONT(" << main_tid << ") failed";
-    exit(1);
-  }
-
-  siginfo_t siginfo = {};
-  {
-    ATRACE_NAME("wait_for_signal");
-    if (!wait_for_signal(main_tid, &siginfo)) {
-      printf("failed to wait for signal in tid %d: %s\n", main_tid, strerror(errno));
-      exit(1);
-    }
-  }
+  LOG(INFO) << "performing dump of process " << target_process << " (target tid = " << g_target_thread
+            << ")";
 
   int signo = siginfo.si_signo;
   bool fatal_signal = signo != DEBUGGER_SIGNAL;
   bool backtrace = false;
-  uintptr_t abort_address = 0;
 
-  // si_value can represent three things:
+  // si_value is special when used with DEBUGGER_SIGNAL.
   //   0: dump tombstone
   //   1: dump backtrace
-  //   everything else: abort message address (implies dump tombstone)
-  if (siginfo.si_value.sival_int == 1) {
-    backtrace = true;
-  } else if (siginfo.si_value.sival_ptr != nullptr) {
-    abort_address = reinterpret_cast<uintptr_t>(siginfo.si_value.sival_ptr);
+  if (!fatal_signal) {
+    int si_val = siginfo.si_value.sival_int;
+    if (si_val == 0) {
+      backtrace = false;
+    } else if (si_val == 1) {
+      backtrace = true;
+    } else {
+      LOG(WARNING) << "unknown si_value value " << si_val;
+    }
   }
 
   // TODO: Use seccomp to lock ourselves down.
+  std::unique_ptr<BacktraceMap> map(BacktraceMap::Create(vm_pid, false));
+  if (!map) {
+    LOG(FATAL) << "failed to create backtrace map";
+  }
+
+  std::shared_ptr<unwindstack::Memory> process_memory = map->GetProcessMemory();
+  if (!process_memory) {
+    LOG(FATAL) << "failed to get unwindstack::Memory handle";
+  }
 
   std::string amfd_data;
   if (backtrace) {
     ATRACE_NAME("dump_backtrace");
-    dump_backtrace(output_fd.get(), backtrace_map.get(), target, main_tid, process_name, threads, 0);
+    dump_backtrace(std::move(g_output_fd), map.get(), thread_info, g_target_thread);
   } else {
     ATRACE_NAME("engrave_tombstone");
-    engrave_tombstone(output_fd.get(), backtrace_map.get(), &open_files, target, main_tid,
-                      process_name, threads, abort_address, fatal_signal ? &amfd_data : nullptr);
+    engrave_tombstone(std::move(g_output_fd), map.get(), process_memory.get(), thread_info,
+                      g_target_thread, abort_address, &open_files, &amfd_data);
   }
 
-  // We don't actually need to PTRACE_DETACH, as long as our tracees aren't in
-  // group-stop state, which is true as long as no stopping signals are sent.
-
-  bool wait_for_gdb = android::base::GetBoolProperty("debug.debuggerd.wait_for_gdb", false);
-  if (!fatal_signal || siginfo.si_code == SI_USER) {
-    // Don't wait_for_gdb when the process didn't actually crash.
-    wait_for_gdb = false;
-  }
-
-  // If the process crashed or we need to send it SIGSTOP for wait_for_gdb,
-  // get it in a state where it can receive signals, and then send the relevant
-  // signal.
-  if (wait_for_gdb || fatal_signal) {
-    if (ptrace(PTRACE_INTERRUPT, main_tid, 0, 0) != 0) {
-      PLOG(ERROR) << "failed to use PTRACE_INTERRUPT on " << main_tid;
-    }
-
-    if (tgkill(target, main_tid, wait_for_gdb ? SIGSTOP : signo) != 0) {
-      PLOG(ERROR) << "failed to resend signal " << signo << " to " << main_tid;
+  if (fatal_signal) {
+    // Don't try to notify ActivityManager if it just crashed, or we might hang until timeout.
+    if (thread_info[target_process].thread_name != "system_server") {
+      activity_manager_notify(target_process, signo, amfd_data);
     }
   }
 
   if (wait_for_gdb) {
     // Use ALOGI to line up with output from engrave_tombstone.
     ALOGI(
-      "***********************************************************\n"
-      "* Process %d has been suspended while crashing.\n"
-      "* To attach gdbserver and start gdb, run this on the host:\n"
-      "*\n"
-      "*     gdbclient.py -p %d\n"
-      "*\n"
-      "***********************************************************",
-      target, main_tid);
-  }
-
-  if (fatal_signal) {
-    // Don't try to notify ActivityManager if it just crashed, or we might hang until timeout.
-    if (target_info.name != "system_server" || target_info.uid != AID_SYSTEM) {
-      activity_manager_notify(target, signo, amfd_data);
-    }
+        "***********************************************************\n"
+        "* Process %d has been suspended while crashing.\n"
+        "* To attach gdbserver and start gdb, run this on the host:\n"
+        "*\n"
+        "*     gdbclient.py -p %d\n"
+        "*\n"
+        "***********************************************************",
+        target_process, target_process);
   }
 
   // Close stdout before we notify tombstoned of completion.
   close(STDOUT_FILENO);
-  if (tombstoned_connected && !tombstoned_notify_completion(tombstoned_socket.get())) {
+  if (g_tombstoned_connected && !tombstoned_notify_completion(g_tombstoned_socket.get())) {
     LOG(ERROR) << "failed to notify tombstoned of completion";
   }
 
diff --git a/debuggerd/crasher/Android.bp b/debuggerd/crasher/Android.bp
index f73f672..7bec470 100644
--- a/debuggerd/crasher/Android.bp
+++ b/debuggerd/crasher/Android.bp
@@ -1,8 +1,7 @@
 cc_defaults {
     name: "crasher-defaults",
 
-    cppflags: [
-        "-std=gnu++14",
+    cflags: [
         "-W",
         "-Wall",
         "-Wextra",
@@ -18,7 +17,7 @@
         arm: {
             srcs: ["arm/crashglue.S"],
 
-            armv7_a_neon: {
+            neon: {
                 asflags: ["-DHAS_VFP_D32"],
             },
         },
diff --git a/debuggerd/crasher/crasher.cpp b/debuggerd/crasher/crasher.cpp
index 6970201..f0fe1d0 100644
--- a/debuggerd/crasher/crasher.cpp
+++ b/debuggerd/crasher/crasher.cpp
@@ -114,6 +114,11 @@
     return reinterpret_cast<uintptr_t>(result);
 }
 
+noinline int crash_null() {
+  int (*null_func)() = nullptr;
+  return null_func();
+}
+
 noinline int crash3(int a) {
     *reinterpret_cast<int*>(0xdead) = a;
     return a*4;
@@ -134,6 +139,14 @@
     free(buf); // GCC is smart enough to warn about this, but we're doing it deliberately.
 }
 
+noinline void leak() {
+    while (true) {
+        void* mapping =
+            mmap(nullptr, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+        static_cast<volatile char*>(mapping)[0] = 'a';
+    }
+}
+
 noinline void sigsegv_non_null() {
     int* a = (int *)(&do_action);
     *a = 42;
@@ -160,8 +173,9 @@
     fprintf(stderr, "  stack-overflow        recurse until the stack overflows\n");
     fprintf(stderr, "  nostack               crash with a NULL stack pointer\n");
     fprintf(stderr, "\n");
-    fprintf(stderr, "  heap-corruption       cause a libc abort by corrupting the heap\n");
     fprintf(stderr, "  heap-usage            cause a libc abort by abusing a heap function\n");
+    fprintf(stderr, "  call-null             cause a crash by calling through a nullptr\n");
+    fprintf(stderr, "  leak                  leak memory until we get OOM-killed\n");
     fprintf(stderr, "\n");
     fprintf(stderr, "  abort                 call abort()\n");
     fprintf(stderr, "  assert                call assert() without a function\n");
@@ -169,6 +183,8 @@
     fprintf(stderr, "  exit                  call exit(1)\n");
     fprintf(stderr, "\n");
     fprintf(stderr, "  fortify               fail a _FORTIFY_SOURCE check\n");
+    fprintf(stderr, "  fdsan_file            close a file descriptor that's owned by a FILE*\n");
+    fprintf(stderr, "  fdsan_dir             close a file descriptor that's owned by a DIR*\n");
     fprintf(stderr, "  seccomp               fail a seccomp check\n");
 #if defined(__arm__)
     fprintf(stderr, "  kuser_helper_version  call kuser_helper_version\n");
@@ -183,6 +199,7 @@
     fprintf(stderr, "  LOG-FATAL             call libbase LOG(FATAL)\n");
     fprintf(stderr, "\n");
     fprintf(stderr, "  SIGFPE                cause a SIGFPE\n");
+    fprintf(stderr, "  SIGILL                cause a SIGILL\n");
     fprintf(stderr, "  SIGSEGV               cause a SIGSEGV at address 0x0 (synonym: crash)\n");
     fprintf(stderr, "  SIGSEGV-non-null      cause a SIGSEGV at a non-zero address\n");
     fprintf(stderr, "  SIGSEGV-unmapped      mmap/munmap a region of memory and then attempt to access it\n");
@@ -221,58 +238,78 @@
 
     // Actions.
     if (!strcasecmp(arg, "SIGSEGV-non-null")) {
-        sigsegv_non_null();
+      sigsegv_non_null();
     } else if (!strcasecmp(arg, "smash-stack")) {
-        volatile int len = 128;
-        return smash_stack(&len);
+      volatile int len = 128;
+      return smash_stack(&len);
     } else if (!strcasecmp(arg, "stack-overflow")) {
-        overflow_stack(nullptr);
+      overflow_stack(nullptr);
     } else if (!strcasecmp(arg, "nostack")) {
-        crashnostack();
+      crashnostack();
     } else if (!strcasecmp(arg, "exit")) {
-        exit(1);
+      exit(1);
+    } else if (!strcasecmp(arg, "call-null")) {
+      return crash_null();
     } else if (!strcasecmp(arg, "crash") || !strcmp(arg, "SIGSEGV")) {
-        return crash(42);
+      return crash(42);
     } else if (!strcasecmp(arg, "abort")) {
-        maybe_abort();
+      maybe_abort();
     } else if (!strcasecmp(arg, "assert")) {
-        __assert("some_file.c", 123, "false");
+      __assert("some_file.c", 123, "false");
     } else if (!strcasecmp(arg, "assert2")) {
-        __assert2("some_file.c", 123, "some_function", "false");
+      __assert2("some_file.c", 123, "some_function", "false");
     } else if (!strcasecmp(arg, "fortify")) {
-        char buf[10];
-        __read_chk(-1, buf, 32, 10);
-        while (true) pause();
+      char buf[10];
+      __read_chk(-1, buf, 32, 10);
+      while (true) pause();
+    } else if (!strcasecmp(arg, "fdsan_file")) {
+      FILE* f = fopen("/dev/null", "r");
+      close(fileno(f));
+    } else if (!strcasecmp(arg, "fdsan_dir")) {
+      DIR* d = opendir("/dev/");
+      close(dirfd(d));
     } else if (!strcasecmp(arg, "LOG(FATAL)")) {
-        LOG(FATAL) << "hello " << 123;
+      LOG(FATAL) << "hello " << 123;
     } else if (!strcasecmp(arg, "LOG_ALWAYS_FATAL")) {
-        LOG_ALWAYS_FATAL("hello %s", "world");
+      LOG_ALWAYS_FATAL("hello %s", "world");
     } else if (!strcasecmp(arg, "LOG_ALWAYS_FATAL_IF")) {
-        LOG_ALWAYS_FATAL_IF(true, "hello %s", "world");
+      LOG_ALWAYS_FATAL_IF(true, "hello %s", "world");
     } else if (!strcasecmp(arg, "SIGFPE")) {
-        raise(SIGFPE);
-        return EXIT_SUCCESS;
+      raise(SIGFPE);
+      return EXIT_SUCCESS;
+    } else if (!strcasecmp(arg, "SIGILL")) {
+#if defined(__aarch64__)
+      __asm__ volatile(".word 0\n");
+#elif defined(__arm__)
+      __asm__ volatile(".word 0xe7f0def0\n");
+#elif defined(__i386__) || defined(__x86_64__)
+      __asm__ volatile("ud2\n");
+#else
+#error
+#endif
     } else if (!strcasecmp(arg, "SIGTRAP")) {
-        raise(SIGTRAP);
-        return EXIT_SUCCESS;
+      raise(SIGTRAP);
+      return EXIT_SUCCESS;
     } else if (!strcasecmp(arg, "fprintf-NULL")) {
-        fprintf_null();
+      fprintf_null();
     } else if (!strcasecmp(arg, "readdir-NULL")) {
-        readdir_null();
+      readdir_null();
     } else if (!strcasecmp(arg, "strlen-NULL")) {
-        return strlen_null();
+      return strlen_null();
     } else if (!strcasecmp(arg, "pthread_join-NULL")) {
-        return pthread_join(0, nullptr);
+      return pthread_join(0, nullptr);
     } else if (!strcasecmp(arg, "heap-usage")) {
-        abuse_heap();
+      abuse_heap();
+    } else if (!strcasecmp(arg, "leak")) {
+      leak();
     } else if (!strcasecmp(arg, "SIGSEGV-unmapped")) {
-        char* map = reinterpret_cast<char*>(mmap(nullptr, sizeof(int), PROT_READ | PROT_WRITE,
-                                                 MAP_SHARED | MAP_ANONYMOUS, -1, 0));
-        munmap(map, sizeof(int));
-        map[0] = '8';
+      char* map = reinterpret_cast<char*>(
+          mmap(nullptr, sizeof(int), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0));
+      munmap(map, sizeof(int));
+      map[0] = '8';
     } else if (!strcasecmp(arg, "seccomp")) {
-        set_seccomp_filter();
-        syscall(99999);
+      set_system_seccomp_filter();
+      syscall(99999);
 #if defined(__arm__)
     } else if (!strcasecmp(arg, "kuser_helper_version")) {
         return __kuser_helper_version;
diff --git a/debuggerd/debuggerd.cpp b/debuggerd/debuggerd.cpp
index b016e23..360ea95 100644
--- a/debuggerd/debuggerd.cpp
+++ b/debuggerd/debuggerd.cpp
@@ -20,6 +20,7 @@
 #include <string.h>
 
 #include <limits>
+#include <string_view>
 #include <thread>
 
 #include <android-base/file.h>
@@ -33,9 +34,10 @@
 using android::base::unique_fd;
 
 static void usage(int exit_code) {
-  fprintf(stderr, "usage: debuggerd [-b] PID\n");
+  fprintf(stderr, "usage: debuggerd [-bj] PID\n");
   fprintf(stderr, "\n");
   fprintf(stderr, "-b, --backtrace    just a backtrace rather than a full tombstone\n");
+  fprintf(stderr, "-j                 collect java traces\n");
   _exit(exit_code);
 }
 
@@ -58,8 +60,19 @@
 int main(int argc, char* argv[]) {
   if (argc <= 1) usage(0);
   if (argc > 3) usage(1);
-  if (argc == 3 && strcmp(argv[1], "-b") != 0 && strcmp(argv[1], "--backtrace") != 0) usage(1);
-  bool backtrace_only = argc == 3;
+
+  DebuggerdDumpType dump_type = kDebuggerdTombstone;
+
+  if (argc == 3) {
+    std::string_view flag = argv[1];
+    if (flag == "-b" || flag == "--backtrace") {
+      dump_type = kDebuggerdNativeBacktrace;
+    } else if (flag == "-j") {
+      dump_type = kDebuggerdJavaBacktrace;
+    } else {
+      usage(1);
+    }
+  }
 
   pid_t pid;
   if (!android::base::ParseInt(argv[argc - 1], &pid, 1, std::numeric_limits<pid_t>::max())) {
@@ -90,8 +103,7 @@
   }
 
   std::thread redirect_thread = spawn_redirect_thread(std::move(piperead));
-  if (!debuggerd_trigger_dump(pid, backtrace_only ? kDebuggerdNativeBacktrace : kDebuggerdTombstone,
-                              0, std::move(pipewrite))) {
+  if (!debuggerd_trigger_dump(pid, dump_type, 0, std::move(pipewrite))) {
     redirect_thread.join();
     errx(1, "failed to dump process %d", pid);
   }
diff --git a/debuggerd/debuggerd_benchmark.cpp b/debuggerd/debuggerd_benchmark.cpp
new file mode 100644
index 0000000..37ee214
--- /dev/null
+++ b/debuggerd/debuggerd_benchmark.cpp
@@ -0,0 +1,128 @@
+/*
+ * Copyright 2017, 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 <err.h>
+#include <errno.h>
+#include <sched.h>
+#include <string.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <chrono>
+#include <thread>
+
+#include <benchmark/benchmark.h>
+#include <debuggerd/client.h>
+
+using namespace std::chrono_literals;
+
+static_assert(std::chrono::high_resolution_clock::is_steady);
+
+enum class ThreadState { Starting, Started, Stopping };
+
+static void SetScheduler() {
+  struct sched_param param {
+    .sched_priority = 1,
+  };
+
+  if (sched_setscheduler(getpid(), SCHED_FIFO, &param) != 0) {
+    fprintf(stderr, "failed to set scheduler to SCHED_FIFO: %s", strerror(errno));
+  }
+}
+
+static std::chrono::duration<double> GetMaximumPause(std::atomic<ThreadState>& state) {
+  std::chrono::duration<double> max_diff(0);
+
+  const auto begin = std::chrono::high_resolution_clock::now();
+  auto last = begin;
+  state.store(ThreadState::Started);
+  while (state.load() != ThreadState::Stopping) {
+    auto now = std::chrono::high_resolution_clock::now();
+
+    auto diff = now - last;
+    if (diff > max_diff) {
+      max_diff = diff;
+    }
+
+    last = now;
+  }
+
+  return max_diff;
+}
+
+static void PerformDump() {
+  pid_t target = getpid();
+  pid_t forkpid = fork();
+  if (forkpid == -1) {
+    err(1, "fork failed");
+  } else if (forkpid != 0) {
+    int status;
+    pid_t pid = waitpid(forkpid, &status, 0);
+    if (pid == -1) {
+      err(1, "waitpid failed");
+    } else if (!WIFEXITED(status)) {
+      err(1, "child didn't exit");
+    } else if (WEXITSTATUS(status) != 0) {
+      errx(1, "child exited with non-zero status %d", WEXITSTATUS(status));
+    }
+  } else {
+    android::base::unique_fd output_fd(open("/dev/null", O_WRONLY | O_CLOEXEC));
+    if (output_fd == -1) {
+      err(1, "failed to open /dev/null");
+    }
+
+    if (!debuggerd_trigger_dump(target, kDebuggerdNativeBacktrace, 1000, std::move(output_fd))) {
+      errx(1, "failed to trigger dump");
+    }
+
+    _exit(0);
+  }
+}
+
+template <typename Fn>
+static void BM_maximum_pause_impl(benchmark::State& state, const Fn& function) {
+  SetScheduler();
+
+  for (auto _ : state) {
+    std::chrono::duration<double> max_pause;
+    std::atomic<ThreadState> thread_state(ThreadState::Starting);
+    auto thread = std::thread([&]() { max_pause = GetMaximumPause(thread_state); });
+
+    while (thread_state != ThreadState::Started) {
+      std::this_thread::sleep_for(1ms);
+    }
+
+    function();
+
+    thread_state = ThreadState::Stopping;
+    thread.join();
+
+    state.SetIterationTime(max_pause.count());
+  }
+}
+
+static void BM_maximum_pause_noop(benchmark::State& state) {
+  BM_maximum_pause_impl(state, []() {});
+}
+
+static void BM_maximum_pause_debuggerd(benchmark::State& state) {
+  BM_maximum_pause_impl(state, []() { PerformDump(); });
+}
+
+BENCHMARK(BM_maximum_pause_noop)->Iterations(128)->UseManualTime();
+BENCHMARK(BM_maximum_pause_debuggerd)->Iterations(128)->UseManualTime();
+
+BENCHMARK_MAIN();
diff --git a/debuggerd/debuggerd_test.cpp b/debuggerd/debuggerd_test.cpp
index b51fc66..dfb7a6a 100644
--- a/debuggerd/debuggerd_test.cpp
+++ b/debuggerd/debuggerd_test.cpp
@@ -16,10 +16,14 @@
 
 #include <err.h>
 #include <fcntl.h>
-#include <unistd.h>
+#include <stdlib.h>
 #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>
 
 #include <chrono>
 #include <regex>
@@ -33,10 +37,14 @@
 #include <android-base/parseint.h>
 #include <android-base/properties.h>
 #include <android-base/strings.h>
+#include <android-base/test_utils.h>
 #include <android-base/unique_fd.h>
 #include <cutils/sockets.h>
 #include <gtest/gtest.h>
 
+#include <libminijail.h>
+#include <scoped_minijail.h>
+
 #include "debuggerd/handler.h"
 #include "protocol.h"
 #include "tombstoned/tombstoned.h"
@@ -72,25 +80,13 @@
     return value;                                                  \
   }()
 
-#define ASSERT_MATCH(str, pattern)                                              \
-  do {                                                                          \
-    std::regex r((pattern));                                                    \
-    if (!std::regex_search((str), r)) {                                         \
-      FAIL() << "regex mismatch: expected " << (pattern) << " in: \n" << (str); \
-    }                                                                           \
-  } while (0)
-
-#define ASSERT_NOT_MATCH(str, pattern)                                                      \
-  do {                                                                                      \
-    std::regex r((pattern));                                                                \
-    if (std::regex_search((str), r)) {                                                      \
-      FAIL() << "regex mismatch: expected to not find " << (pattern) << " in: \n" << (str); \
-    }                                                                                       \
-  } while (0)
-
-#define ASSERT_BACKTRACE_FRAME(result, frame_name)                        \
-  ASSERT_MATCH(result, R"(#\d\d pc [0-9a-f]+\s+ /system/lib)" ARCH_SUFFIX \
-                       R"(/libc.so \()" frame_name R"(\+)")
+// Backtrace frame dump could contain:
+//   #01 pc 0001cded  /data/tmp/debuggerd_test32 (raise_debugger_signal+80)
+// or
+//   #01 pc 00022a09  /data/tmp/debuggerd_test32 (offset 0x12000) (raise_debugger_signal+80)
+#define ASSERT_BACKTRACE_FRAME(result, frame_name) \
+  ASSERT_MATCH(result,                             \
+               R"(#\d\d pc [0-9a-f]+\s+ \S+ (\(offset 0x[0-9a-f]+\) )?\()" frame_name R"(\+)");
 
 static void tombstoned_intercept(pid_t target_pid, unique_fd* intercept_fd, unique_fd* output_fd,
                                  InterceptStatus* status, DebuggerdDumpType intercept_type) {
@@ -242,6 +238,8 @@
   int status;
   pid_t pid = TIMEOUT(5, waitpid(crasher_pid, &status, 0));
   if (pid != crasher_pid) {
+    printf("failed to wait for crasher (pid %d)\n", crasher_pid);
+    sleep(100);
     FAIL() << "failed to wait for crasher: " << strerror(errno);
   }
 
@@ -296,6 +294,26 @@
   ASSERT_MATCH(result, R"(signal 11 \(SIGSEGV\), code 1 \(SEGV_MAPERR\), fault addr 0xdead)");
 }
 
+TEST_F(CrasherTest, LD_PRELOAD) {
+  int intercept_result;
+  unique_fd output_fd;
+  StartProcess([]() {
+    setenv("LD_PRELOAD", "nonexistent.so", 1);
+    *reinterpret_cast<volatile char*>(0xdead) = '1';
+  });
+
+  StartIntercept(&output_fd);
+  FinishCrasher();
+  AssertDeath(SIGSEGV);
+  FinishIntercept(&intercept_result);
+
+  ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
+
+  std::string result;
+  ConsumeFd(std::move(output_fd), &result);
+  ASSERT_MATCH(result, R"(signal 11 \(SIGSEGV\), code 1 \(SEGV_MAPERR\), fault addr 0xdead)");
+}
+
 TEST_F(CrasherTest, abort) {
   int intercept_result;
   unique_fd output_fd;
@@ -318,13 +336,12 @@
   int intercept_result;
   unique_fd output_fd;
   StartProcess([]() {
-    abort();
+    while (true) {
+      sleep(1);
+    }
   });
   StartIntercept(&output_fd);
-
-  // Wait for a bit, or we might end up killing the process before the signal
-  // handler even gets a chance to be registered.
-  std::this_thread::sleep_for(100ms);
+  FinishCrasher();
   ASSERT_EQ(0, kill(crasher_pid, SIGSEGV));
 
   AssertDeath(SIGSEGV);
@@ -334,7 +351,9 @@
 
   std::string result;
   ConsumeFd(std::move(output_fd), &result);
-  ASSERT_MATCH(result, R"(signal 11 \(SIGSEGV\), code 0 \(SI_USER\), fault addr --------)");
+  ASSERT_MATCH(
+      result,
+      R"(signal 11 \(SIGSEGV\), code 0 \(SI_USER from pid \d+, uid \d+\), fault addr --------)");
   ASSERT_MATCH(result, R"(backtrace:)");
 }
 
@@ -342,7 +361,14 @@
   int intercept_result;
   unique_fd output_fd;
   StartProcess([]() {
-    android_set_abort_message("abort message goes here");
+    // Arrived at experimentally;
+    // logd truncates at 4062.
+    // strlen("Abort message: ''") is 17.
+    // That's 4045, but we also want a NUL.
+    char buf[4045 + 1];
+    memset(buf, 'x', sizeof(buf));
+    buf[sizeof(buf) - 1] = '\0';
+    android_set_abort_message(buf);
     abort();
   });
   StartIntercept(&output_fd);
@@ -354,7 +380,7 @@
 
   std::string result;
   ConsumeFd(std::move(output_fd), &result);
-  ASSERT_MATCH(result, R"(Abort message: 'abort message goes here')");
+  ASSERT_MATCH(result, R"(Abort message: 'x{4045}')");
 }
 
 TEST_F(CrasherTest, abort_message_backtrace) {
@@ -416,19 +442,6 @@
   AssertDeath(SIGABRT);
 }
 
-// wait_for_gdb shouldn't trigger on manually sent signals.
-TEST_F(CrasherTest, wait_for_gdb_signal) {
-  if (!android::base::SetProperty(kWaitForGdbKey, "1")) {
-    FAIL() << "failed to enable wait_for_gdb";
-  }
-
-  StartProcess([]() {
-    abort();
-  });
-  ASSERT_EQ(0, kill(crasher_pid, SIGSEGV)) << strerror(errno);
-  AssertDeath(SIGSEGV);
-}
-
 TEST_F(CrasherTest, backtrace) {
   std::string result;
   int intercept_result;
@@ -569,6 +582,225 @@
   ASSERT_BACKTRACE_FRAME(result, "tgkill");
 }
 
+static const char* const kDebuggerdSeccompPolicy =
+    "/system/etc/seccomp_policy/crash_dump." ABI_STRING ".policy";
+
+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;
+  }
+
+  ScopedMinijail jail{minijail_new()};
+  if (!jail) {
+    LOG(FATAL) << "failed to create minijail";
+  }
+
+  minijail_no_new_privs(jail.get());
+  minijail_log_seccomp_filter_failures(jail.get());
+  minijail_use_seccomp_filter(jail.get());
+  minijail_parse_seccomp_filters_from_fd(jail.get(), policy_fd.release());
+
+  pid_t result = fork();
+  if (result == -1) {
+    return result;
+  } else if (result != 0) {
+    return result;
+  }
+
+  // Spawn and detach a thread that spins forever.
+  std::atomic<bool> thread_ready(false);
+  std::thread thread([&jail, &thread_ready]() {
+    minijail_enter(jail.get());
+    thread_ready = true;
+    for (;;)
+      ;
+  });
+  thread.detach();
+
+  while (!thread_ready) {
+    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;
+
+  StartProcess([]() { abort(); }, &seccomp_fork);
+
+  StartIntercept(&output_fd);
+  FinishCrasher();
+  AssertDeath(SIGABRT);
+  FinishIntercept(&intercept_result);
+  ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
+
+  std::string result;
+  ConsumeFd(std::move(output_fd), &result);
+  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;
+  siginfo.si_pid = getpid();
+  siginfo.si_uid = getuid();
+
+  if (dump_type != kDebuggerdNativeBacktrace && dump_type != kDebuggerdTombstone) {
+    PLOG(FATAL) << "invalid dump type";
+  }
+
+  siginfo.si_value.sival_int = dump_type == kDebuggerdNativeBacktrace;
+
+  if (syscall(__NR_rt_tgsigqueueinfo, getpid(), gettid(), DEBUGGER_SIGNAL, &siginfo) != 0) {
+    PLOG(ERROR) << "libdebuggerd_client: failed to send signal to self";
+    return false;
+  }
+
+  return true;
+}
+
+TEST_F(CrasherTest, seccomp_tombstone) {
+  int intercept_result;
+  unique_fd output_fd;
+
+  static const auto dump_type = kDebuggerdTombstone;
+  StartProcess(
+      []() {
+        raise_debugger_signal(dump_type);
+        _exit(0);
+      },
+      &seccomp_fork);
+
+  StartIntercept(&output_fd, dump_type);
+  FinishCrasher();
+  AssertDeath(0);
+  FinishIntercept(&intercept_result);
+  ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
+
+  std::string result;
+  ConsumeFd(std::move(output_fd), &result);
+  ASSERT_BACKTRACE_FRAME(result, "raise_debugger_signal");
+}
+
+TEST_F(CrasherTest, seccomp_backtrace) {
+  int intercept_result;
+  unique_fd output_fd;
+
+  static const auto dump_type = kDebuggerdNativeBacktrace;
+  StartProcess(
+      []() {
+        raise_debugger_signal(dump_type);
+        _exit(0);
+      },
+      &seccomp_fork);
+
+  StartIntercept(&output_fd, dump_type);
+  FinishCrasher();
+  AssertDeath(0);
+  FinishIntercept(&intercept_result);
+  ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
+
+  std::string result;
+  ConsumeFd(std::move(output_fd), &result);
+  ASSERT_BACKTRACE_FRAME(result, "raise_debugger_signal");
+}
+
+TEST_F(CrasherTest, seccomp_crash_logcat) {
+  StartProcess([]() { abort(); }, &seccomp_fork);
+  FinishCrasher();
+
+  // Make sure we don't get SIGSYS when trying to dump a crash to logcat.
+  AssertDeath(SIGABRT);
+}
+
+TEST_F(CrasherTest, competing_tracer) {
+  int intercept_result;
+  unique_fd output_fd;
+  StartProcess([]() {
+    raise(SIGABRT);
+  });
+
+  StartIntercept(&output_fd);
+
+  ASSERT_EQ(0, ptrace(PTRACE_SEIZE, crasher_pid, 0, 0));
+  FinishCrasher();
+
+  int status;
+  ASSERT_EQ(crasher_pid, waitpid(crasher_pid, &status, 0));
+  ASSERT_TRUE(WIFSTOPPED(status));
+  ASSERT_EQ(SIGABRT, WSTOPSIG(status));
+
+  ASSERT_EQ(0, ptrace(PTRACE_CONT, crasher_pid, 0, SIGABRT));
+  FinishIntercept(&intercept_result);
+  ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
+
+  std::string result;
+  ConsumeFd(std::move(output_fd), &result);
+  std::string regex = R"(failed to attach to thread \d+, already traced by )";
+  regex += std::to_string(gettid());
+  regex += R"( \(.+debuggerd_test)";
+  ASSERT_MATCH(result, regex.c_str());
+
+  ASSERT_EQ(crasher_pid, waitpid(crasher_pid, &status, 0));
+  ASSERT_TRUE(WIFSTOPPED(status));
+  ASSERT_EQ(SIGABRT, WSTOPSIG(status));
+
+  ASSERT_EQ(0, ptrace(PTRACE_DETACH, crasher_pid, 0, SIGABRT));
+  AssertDeath(SIGABRT);
+}
+
 TEST(crash_dump, zombie) {
   pid_t forkpid = fork();
 
diff --git a/debuggerd/handler/debuggerd_fallback.cpp b/debuggerd/handler/debuggerd_fallback.cpp
index 43104ec..079a574 100644
--- a/debuggerd/handler/debuggerd_fallback.cpp
+++ b/debuggerd/handler/debuggerd_fallback.cpp
@@ -36,21 +36,27 @@
 #include <unistd.h>
 
 #include <atomic>
+#include <memory>
+#include <mutex>
 
 #include <android-base/file.h>
 #include <android-base/unique_fd.h>
 #include <async_safe/log.h>
+#include <backtrace/BacktraceMap.h>
+#include <unwindstack/Memory.h>
+#include <unwindstack/Regs.h>
 
 #include "debuggerd/handler.h"
 #include "tombstoned/tombstoned.h"
 #include "util.h"
 
-#include "backtrace.h"
-#include "tombstone.h"
+#include "libdebuggerd/backtrace.h"
+#include "libdebuggerd/tombstone.h"
 
 using android::base::unique_fd;
+using unwindstack::Regs;
 
-extern "C" void __linker_enable_fallback_allocator();
+extern "C" bool __linker_enable_fallback_allocator();
 extern "C" void __linker_disable_fallback_allocator();
 
 // This is incredibly sketchy to do inside of a signal handler, especially when libbacktrace
@@ -60,14 +66,34 @@
 // This isn't the default method of dumping because it can fail in cases such as address space
 // exhaustion.
 static void debuggerd_fallback_trace(int output_fd, ucontext_t* ucontext) {
-  __linker_enable_fallback_allocator();
-  dump_backtrace_ucontext(output_fd, ucontext);
+  if (!__linker_enable_fallback_allocator()) {
+    async_safe_format_log(ANDROID_LOG_ERROR, "libc", "fallback allocator already in use");
+    return;
+  }
+
+  {
+    std::unique_ptr<Regs> regs;
+
+    ThreadInfo thread;
+    thread.pid = getpid();
+    thread.tid = gettid();
+    thread.thread_name = get_thread_name(gettid());
+    thread.registers.reset(Regs::CreateFromUcontext(Regs::CurrentArch(), ucontext));
+
+    // TODO: Create this once and store it in a global?
+    std::unique_ptr<BacktraceMap> map(BacktraceMap::Create(getpid()));
+    dump_backtrace_thread(output_fd, map.get(), thread);
+  }
   __linker_disable_fallback_allocator();
 }
 
 static void debuggerd_fallback_tombstone(int output_fd, ucontext_t* ucontext, siginfo_t* siginfo,
                                          void* abort_message) {
-  __linker_enable_fallback_allocator();
+  if (!__linker_enable_fallback_allocator()) {
+    async_safe_format_log(ANDROID_LOG_ERROR, "libc", "fallback allocator already in use");
+    return;
+  }
+
   engrave_tombstone_ucontext(output_fd, reinterpret_cast<uintptr_t>(abort_message), siginfo,
                              ucontext);
   __linker_disable_fallback_allocator();
@@ -99,7 +125,7 @@
   closedir(dir);
 }
 
-static bool forward_output(int src_fd, int dst_fd) {
+static bool forward_output(int src_fd, int dst_fd, pid_t expected_tid) {
   // Make sure the thread actually got the signal.
   struct pollfd pfd = {
     .fd = src_fd, .events = POLLIN,
@@ -110,6 +136,18 @@
     return false;
   }
 
+  pid_t tid;
+  if (TEMP_FAILURE_RETRY(read(src_fd, &tid, sizeof(tid))) != sizeof(tid)) {
+    async_safe_format_log(ANDROID_LOG_ERROR, "libc", "failed to read tid");
+    return false;
+  }
+
+  if (tid != expected_tid) {
+    async_safe_format_log(ANDROID_LOG_ERROR, "libc", "received tid %d, expected %d", tid,
+                          expected_tid);
+    return false;
+  }
+
   while (true) {
     char buf[512];
     ssize_t rc = TEMP_FAILURE_RETRY(read(src_fd, buf, sizeof(buf)));
@@ -127,16 +165,54 @@
   }
 }
 
+struct __attribute__((__packed__)) packed_thread_output {
+  int32_t tid;
+  int32_t fd;
+};
+
+static uint64_t pack_thread_fd(pid_t tid, int fd) {
+  packed_thread_output packed = {.tid = tid, .fd = fd};
+  uint64_t result;
+  static_assert(sizeof(packed) == sizeof(result));
+  memcpy(&result, &packed, sizeof(packed));
+  return result;
+}
+
+static std::pair<pid_t, int> unpack_thread_fd(uint64_t value) {
+  packed_thread_output result;
+  memcpy(&result, &value, sizeof(value));
+  return std::make_pair(result.tid, result.fd);
+}
+
 static void trace_handler(siginfo_t* info, ucontext_t* ucontext) {
-  static std::atomic<int> trace_output_fd(-1);
+  static std::atomic<uint64_t> trace_output(pack_thread_fd(-1, -1));
 
   if (info->si_value.sival_int == ~0) {
     // Asked to dump by the original signal recipient.
-    debuggerd_fallback_trace(trace_output_fd, ucontext);
+    uint64_t val = trace_output.load();
+    auto [tid, fd] = unpack_thread_fd(val);
+    if (tid != gettid()) {
+      // We received some other thread's info request?
+      async_safe_format_log(ANDROID_LOG_ERROR, "libc",
+                            "thread %d received output fd for thread %d?", gettid(), tid);
+      return;
+    }
 
-    int tmp = trace_output_fd.load();
-    trace_output_fd.store(-1);
-    close(tmp);
+    if (!trace_output.compare_exchange_strong(val, pack_thread_fd(-1, -1))) {
+      // Presumably, the timeout in forward_output expired, and the main thread moved on.
+      // If this happened, the main thread closed our fd for us, so just return.
+      async_safe_format_log(ANDROID_LOG_ERROR, "libc", "cmpxchg for thread %d failed", gettid());
+      return;
+    }
+
+    // Write our tid to the output fd to let the main thread know that we're working.
+    if (TEMP_FAILURE_RETRY(write(fd, &tid, sizeof(tid))) == sizeof(tid)) {
+      debuggerd_fallback_trace(fd, ucontext);
+    } else {
+      async_safe_format_log(ANDROID_LOG_ERROR, "libc", "failed to write to output fd");
+    }
+
+    close(fd);
     return;
   }
 
@@ -162,41 +238,56 @@
 
   // Send a signal to all of our siblings, asking them to dump their stack.
   iterate_siblings(
-    [](pid_t tid, int output_fd) {
-      // Use a pipe, to be able to detect situations where the thread gracefully exits before
-      // receiving our signal.
-      unique_fd pipe_read, pipe_write;
-      if (!Pipe(&pipe_read, &pipe_write)) {
-        async_safe_format_log(ANDROID_LOG_ERROR, "libc", "failed to create pipe: %s",
-                              strerror(errno));
-        return false;
-      }
+      [](pid_t tid, int output_fd) {
+        // Use a pipe, to be able to detect situations where the thread gracefully exits before
+        // receiving our signal.
+        unique_fd pipe_read, pipe_write;
+        if (!Pipe(&pipe_read, &pipe_write)) {
+          async_safe_format_log(ANDROID_LOG_ERROR, "libc", "failed to create pipe: %s",
+                                strerror(errno));
+          return false;
+        }
 
-      trace_output_fd.store(pipe_write.get());
+        uint64_t expected = pack_thread_fd(-1, -1);
+        if (!trace_output.compare_exchange_strong(expected,
+                                                  pack_thread_fd(tid, pipe_write.release()))) {
+          auto [tid, fd] = unpack_thread_fd(expected);
+          async_safe_format_log(ANDROID_LOG_ERROR, "libc",
+                                "thread %d is already outputting to fd %d?", tid, fd);
+          return false;
+        }
 
-      siginfo_t siginfo = {};
-      siginfo.si_code = SI_QUEUE;
-      siginfo.si_value.sival_int = ~0;
-      siginfo.si_pid = getpid();
-      siginfo.si_uid = getuid();
+        siginfo_t siginfo = {};
+        siginfo.si_code = SI_QUEUE;
+        siginfo.si_value.sival_int = ~0;
+        siginfo.si_pid = getpid();
+        siginfo.si_uid = getuid();
 
-      if (syscall(__NR_rt_tgsigqueueinfo, getpid(), tid, DEBUGGER_SIGNAL, &siginfo) != 0) {
-        async_safe_format_log(ANDROID_LOG_ERROR, "libc", "failed to send trace signal to %d: %s",
-                              tid, strerror(errno));
-        return false;
-      }
+        if (syscall(__NR_rt_tgsigqueueinfo, getpid(), tid, DEBUGGER_SIGNAL, &siginfo) != 0) {
+          async_safe_format_log(ANDROID_LOG_ERROR, "libc", "failed to send trace signal to %d: %s",
+                                tid, strerror(errno));
+          return false;
+        }
 
-      bool success = forward_output(pipe_read.get(), output_fd);
-      if (success) {
-        // The signaled thread has closed trace_output_fd already.
-        (void)pipe_write.release();
-      } else {
-        trace_output_fd.store(-1);
-      }
+        bool success = forward_output(pipe_read.get(), output_fd, tid);
+        if (!success) {
+          async_safe_format_log(ANDROID_LOG_ERROR, "libc",
+                                "timeout expired while waiting for thread %d to dump", tid);
+        }
 
-      return true;
-    },
-    output_fd.get());
+        // Regardless of whether the poll succeeds, check to see if the thread took fd ownership.
+        uint64_t post_wait = trace_output.exchange(pack_thread_fd(-1, -1));
+        if (post_wait != pack_thread_fd(-1, -1)) {
+          auto [tid, fd] = unpack_thread_fd(post_wait);
+          if (fd != -1) {
+            async_safe_format_log(ANDROID_LOG_ERROR, "libc", "closing fd %d for thread %d", fd, tid);
+            close(fd);
+          }
+        }
+
+        return true;
+      },
+      output_fd.get());
 
   dump_backtrace_footer(output_fd.get());
   tombstoned_notify_completion(tombstone_socket.get());
@@ -206,12 +297,24 @@
 }
 
 static void crash_handler(siginfo_t* info, ucontext_t* ucontext, void* abort_message) {
-  // Only allow one thread to handle a crash.
-  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;
+  // 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 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, aborting");
+    signal(SIGABRT, SIG_DFL);
+    raise(SIGABRT);
+    sigset_t sigset;
+    sigemptyset(&sigset);
+    sigaddset(&sigset, SIGABRT);
+    sigprocmask(SIG_UNBLOCK, &sigset, nullptr);
+
+    // Just in case...
+    async_safe_format_log(ANDROID_LOG_ERROR, "libc", "abort didn't exit, exiting");
+    _exit(1);
   }
 
   unique_fd tombstone_socket, output_fd;
@@ -221,11 +324,14 @@
   if (tombstoned_connected) {
     tombstoned_notify_completion(tombstone_socket.get());
   }
+
+  --lock_count;
+  crash_mutex.unlock();
 }
 
 extern "C" void debuggerd_fallback_handler(siginfo_t* info, ucontext_t* ucontext,
                                            void* abort_message) {
-  if (info->si_signo == DEBUGGER_SIGNAL) {
+  if (info->si_signo == DEBUGGER_SIGNAL && info->si_value.sival_int != 0) {
     return trace_handler(info, ucontext);
   } else {
     return crash_handler(info, ucontext, abort_message);
diff --git a/debuggerd/handler/debuggerd_handler.cpp b/debuggerd/handler/debuggerd_handler.cpp
index 55cd03e..615fb46 100644
--- a/debuggerd/handler/debuggerd_handler.cpp
+++ b/debuggerd/handler/debuggerd_handler.cpp
@@ -44,13 +44,31 @@
 #include <sys/prctl.h>
 #include <sys/socket.h>
 #include <sys/syscall.h>
+#include <sys/uio.h>
 #include <sys/un.h>
 #include <sys/wait.h>
 #include <unistd.h>
 
+#include <android-base/unique_fd.h>
 #include <async_safe/log.h>
+#include <cutils/properties.h>
+
+#include <libdebuggerd/utility.h>
 
 #include "dump_type.h"
+#include "protocol.h"
+
+using android::base::Pipe;
+
+// We muck with our fds in a 'thread' that doesn't share the same fd table.
+// Close fds in that thread with a raw close syscall instead of going through libc.
+struct FdsanBypassCloser {
+  static void Close(int fd) {
+    syscall(__NR_close, fd);
+  }
+};
+
+using unique_fd = android::base::unique_fd_impl<FdsanBypassCloser>;
 
 // see man(2) prctl, specifically the section about PR_GET_NAME
 #define MAX_TASK_NAME_LEN (16)
@@ -111,11 +129,30 @@
   va_list args;
   va_start(args, fmt);
 
-  char buf[4096];
+  char buf[256];
   async_safe_format_buffer_va_list(buf, sizeof(buf), fmt, args);
   fatal("%s: %s", buf, strerror(err));
 }
 
+static bool get_main_thread_name(char* buf, size_t len) {
+  unique_fd fd(open("/proc/self/comm", O_RDONLY | O_CLOEXEC));
+  if (fd == -1) {
+    return false;
+  }
+
+  ssize_t rc = read(fd, buf, len);
+  if (rc == -1) {
+    return false;
+  } else if (rc == 0) {
+    // Should never happen?
+    return false;
+  }
+
+  // There's a trailing newline, replace it with a NUL.
+  buf[rc - 1] = '\0';
+  return true;
+}
+
 /*
  * Writes a summary of the signal to the log file.  We do this so that, if
  * for some reason we're not able to contact debuggerd, there is still some
@@ -125,7 +162,7 @@
  * mutex is being held, so we don't want to use any libc functions that
  * could allocate memory or hold a lock.
  */
-static void log_signal_summary(int signum, const siginfo_t* info) {
+static void log_signal_summary(const siginfo_t* info) {
   char thread_name[MAX_TASK_NAME_LEN + 1];  // one more for termination
   if (prctl(PR_GET_NAME, reinterpret_cast<unsigned long>(thread_name), 0, 0, 0) != 0) {
     strcpy(thread_name, "<name unknown>");
@@ -135,61 +172,32 @@
     thread_name[MAX_TASK_NAME_LEN] = 0;
   }
 
-  if (signum == DEBUGGER_SIGNAL) {
+  if (info->si_signo == DEBUGGER_SIGNAL) {
     async_safe_format_log(ANDROID_LOG_INFO, "libc", "Requested dump for tid %d (%s)", __gettid(),
                           thread_name);
     return;
   }
 
-  const char* signal_name = "???";
-  bool has_address = false;
-  switch (signum) {
-    case SIGABRT:
-      signal_name = "SIGABRT";
-      break;
-    case SIGBUS:
-      signal_name = "SIGBUS";
-      has_address = true;
-      break;
-    case SIGFPE:
-      signal_name = "SIGFPE";
-      has_address = true;
-      break;
-    case SIGILL:
-      signal_name = "SIGILL";
-      has_address = true;
-      break;
-    case SIGSEGV:
-      signal_name = "SIGSEGV";
-      has_address = true;
-      break;
-#if defined(SIGSTKFLT)
-    case SIGSTKFLT:
-      signal_name = "SIGSTKFLT";
-      break;
-#endif
-    case SIGSYS:
-      signal_name = "SIGSYS";
-      break;
-    case SIGTRAP:
-      signal_name = "SIGTRAP";
-      break;
+  // Many signals don't have an address or sender.
+  char addr_desc[32] = "";  // ", fault addr 0x1234"
+  if (signal_has_si_addr(info)) {
+    async_safe_format_buffer(addr_desc, sizeof(addr_desc), ", fault addr %p", info->si_addr);
+  }
+  pid_t self_pid = __getpid();
+  char sender_desc[32] = {};  // " from pid 1234, uid 666"
+  if (signal_has_sender(info, self_pid)) {
+    get_signal_sender(sender_desc, sizeof(sender_desc), info);
   }
 
-  // "info" will be null if the siginfo_t information was not available.
-  // Many signals don't have an address or a code.
-  char code_desc[32];  // ", code -6"
-  char addr_desc[32];  // ", fault addr 0x1234"
-  addr_desc[0] = code_desc[0] = 0;
-  if (info != nullptr) {
-    async_safe_format_buffer(code_desc, sizeof(code_desc), ", code %d", info->si_code);
-    if (has_address) {
-      async_safe_format_buffer(addr_desc, sizeof(addr_desc), ", fault addr %p", info->si_addr);
-    }
+  char main_thread_name[MAX_TASK_NAME_LEN + 1];
+  if (!get_main_thread_name(main_thread_name, sizeof(main_thread_name))) {
+    strncpy(main_thread_name, "<unknown>", sizeof(main_thread_name));
   }
 
-  async_safe_format_log(ANDROID_LOG_FATAL, "libc", "Fatal signal %d (%s)%s%s in tid %d (%s)",
-                        signum, signal_name, code_desc, addr_desc, __gettid(), thread_name);
+  async_safe_format_log(ANDROID_LOG_FATAL, "libc",
+                        "Fatal signal %d (%s), code %d (%s%s)%s in tid %d (%s), pid %d (%s)",
+                        info->si_signo, get_signame(info), info->si_code, get_sigcode(info),
+                        sender_desc, addr_desc, __gettid(), thread_name, self_pid, main_thread_name);
 }
 
 /*
@@ -240,12 +248,44 @@
   }
 }
 
+static pid_t __fork() {
+  return clone(nullptr, nullptr, 0, nullptr);
+}
+
+// Double-clone, with CLONE_FILES to share the file descriptor table for kcmp validation.
+// Returns 0 in the orphaned child, the pid of the orphan in the original process, or -1 on failure.
+static void create_vm_process() {
+  pid_t first = clone(nullptr, nullptr, CLONE_FILES, nullptr);
+  if (first == -1) {
+    fatal_errno("failed to clone vm process");
+  } else if (first == 0) {
+    drop_capabilities();
+
+    if (clone(nullptr, nullptr, CLONE_FILES, nullptr) == -1) {
+      _exit(errno);
+    }
+
+    // Exit immediately on both sides of the fork.
+    // crash_dump is ptracing us, so it'll get to do whatever it wants in between.
+    _exit(0);
+  }
+
+  int status;
+  if (TEMP_FAILURE_RETRY(waitpid(first, &status, __WCLONE)) != first) {
+    fatal_errno("failed to waitpid in double fork");
+  } else if (!WIFEXITED(status)) {
+    fatal("intermediate process didn't exit cleanly in double fork (status = %d)", status);
+  } else if (WEXITSTATUS(status)) {
+    fatal("second clone failed: %s", strerror(WEXITSTATUS(status)));
+  }
+}
+
 struct debugger_thread_info {
-  bool crash_dump_started;
   pid_t crashing_tid;
   pid_t pseudothread_tid;
-  int signal_number;
-  siginfo_t* info;
+  siginfo_t* siginfo;
+  void* ucontext;
+  uintptr_t abort_msg;
 };
 
 // Logging and contacting debuggerd requires free file descriptors, which we might not have.
@@ -256,7 +296,8 @@
 static void* pseudothread_stack;
 
 static DebuggerdDumpType get_dump_type(const debugger_thread_info* thread_info) {
-  if (thread_info->signal_number == DEBUGGER_SIGNAL && thread_info->info->si_value.sival_int) {
+  if (thread_info->siginfo->si_signo == DEBUGGER_SIGNAL &&
+      thread_info->siginfo->si_value.sival_int) {
     return kDebuggerdNativeBacktrace;
   }
 
@@ -267,29 +308,63 @@
   debugger_thread_info* thread_info = static_cast<debugger_thread_info*>(arg);
 
   for (int i = 0; i < 1024; ++i) {
-    close(i);
+    // Don't use close to avoid bionic's file descriptor ownership checks.
+    syscall(__NR_close, i);
   }
 
   int devnull = TEMP_FAILURE_RETRY(open("/dev/null", O_RDWR));
+  if (devnull == -1) {
+    fatal_errno("failed to open /dev/null");
+  } else if (devnull != 0) {
+    fatal_errno("expected /dev/null fd to be 0, actually %d", devnull);
+  }
 
   // devnull will be 0.
-  TEMP_FAILURE_RETRY(dup2(devnull, STDOUT_FILENO));
-  TEMP_FAILURE_RETRY(dup2(devnull, STDERR_FILENO));
+  TEMP_FAILURE_RETRY(dup2(devnull, 1));
+  TEMP_FAILURE_RETRY(dup2(devnull, 2));
 
-  int pipefds[2];
-  if (pipe(pipefds) != 0) {
+  unique_fd input_read, input_write;
+  unique_fd output_read, output_write;
+  if (!Pipe(&input_read, &input_write) != 0 || !Pipe(&output_read, &output_write)) {
     fatal_errno("failed to create pipe");
   }
 
+  // ucontext_t is absurdly large on AArch64, so piece it together manually with writev.
+  uint32_t version = 1;
+  constexpr size_t expected =
+      sizeof(version) + sizeof(siginfo_t) + sizeof(ucontext_t) + sizeof(uintptr_t);
+
+  errno = 0;
+  if (fcntl(output_write.get(), F_SETPIPE_SZ, expected) < static_cast<int>(expected)) {
+    fatal_errno("failed to set pipe bufer size");
+  }
+
+  struct iovec iovs[4] = {
+      {.iov_base = &version, .iov_len = sizeof(version)},
+      {.iov_base = thread_info->siginfo, .iov_len = sizeof(siginfo_t)},
+      {.iov_base = thread_info->ucontext, .iov_len = sizeof(ucontext_t)},
+      {.iov_base = &thread_info->abort_msg, .iov_len = sizeof(uintptr_t)},
+  };
+
+  ssize_t rc = TEMP_FAILURE_RETRY(writev(output_write.get(), iovs, 4));
+  if (rc == -1) {
+    fatal_errno("failed to write crash info");
+  } else if (rc != expected) {
+    fatal("failed to write crash info, wrote %zd bytes, expected %zd", rc, expected);
+  }
+
   // Don't use fork(2) to avoid calling pthread_atfork handlers.
-  int forkpid = clone(nullptr, nullptr, 0, nullptr);
-  if (forkpid == -1) {
+  pid_t crash_dump_pid = __fork();
+  if (crash_dump_pid == -1) {
     async_safe_format_log(ANDROID_LOG_FATAL, "libc",
                           "failed to fork in debuggerd signal handler: %s", strerror(errno));
-  } else if (forkpid == 0) {
-    TEMP_FAILURE_RETRY(dup2(pipefds[1], STDOUT_FILENO));
-    close(pipefds[0]);
-    close(pipefds[1]);
+  } else if (crash_dump_pid == 0) {
+    TEMP_FAILURE_RETRY(dup2(input_write.get(), STDOUT_FILENO));
+    TEMP_FAILURE_RETRY(dup2(output_read.get(), STDIN_FILENO));
+    input_read.reset();
+    input_write.reset();
+    output_read.reset();
+    output_write.reset();
 
     raise_caps();
 
@@ -302,47 +377,56 @@
     async_safe_format_buffer(debuggerd_dump_type, sizeof(debuggerd_dump_type), "%d",
                              get_dump_type(thread_info));
 
-    execl(CRASH_DUMP_PATH, CRASH_DUMP_NAME, main_tid, pseudothread_tid, debuggerd_dump_type,
-          nullptr);
-
+    execle(CRASH_DUMP_PATH, CRASH_DUMP_NAME, main_tid, pseudothread_tid, debuggerd_dump_type,
+           nullptr, nullptr);
     fatal_errno("exec failed");
-  } else {
-    close(pipefds[1]);
-    char buf[4];
-    ssize_t rc = TEMP_FAILURE_RETRY(read(pipefds[0], &buf, sizeof(buf)));
-    if (rc == -1) {
-      async_safe_format_log(ANDROID_LOG_FATAL, "libc", "read of IPC pipe failed: %s",
-                            strerror(errno));
-    } else if (rc == 0) {
-      async_safe_format_log(ANDROID_LOG_FATAL, "libc", "crash_dump helper failed to exec");
-    } else if (rc != 1) {
-      async_safe_format_log(ANDROID_LOG_FATAL, "libc",
-                            "read of IPC pipe returned unexpected value: %zd", rc);
-    } else {
-      if (buf[0] != '\1') {
-        async_safe_format_log(ANDROID_LOG_FATAL, "libc", "crash_dump helper reported failure");
-      } else {
-        thread_info->crash_dump_started = true;
-      }
-    }
-    close(pipefds[0]);
-
-    // Don't leave a zombie child.
-    int status;
-    if (TEMP_FAILURE_RETRY(waitpid(forkpid, &status, 0)) == -1) {
-      async_safe_format_log(ANDROID_LOG_FATAL, "libc", "failed to wait for crash_dump helper: %s",
-                            strerror(errno));
-    } else if (WIFSTOPPED(status) || WIFSIGNALED(status)) {
-      async_safe_format_log(ANDROID_LOG_FATAL, "libc", "crash_dump helper crashed or stopped");
-      thread_info->crash_dump_started = false;
-    }
   }
 
-  syscall(__NR_exit, 0);
+  input_write.reset();
+  output_read.reset();
+
+  // crash_dump will ptrace and pause all of our threads, and then write to the pipe to tell
+  // us to fork off a process to read memory from.
+  char buf[4];
+  rc = TEMP_FAILURE_RETRY(read(input_read.get(), &buf, sizeof(buf)));
+  if (rc == -1) {
+    async_safe_format_log(ANDROID_LOG_FATAL, "libc", "read of IPC pipe failed: %s", strerror(errno));
+    return 1;
+  } else if (rc == 0) {
+    async_safe_format_log(ANDROID_LOG_FATAL, "libc", "crash_dump helper failed to exec");
+    return 1;
+  } else if (rc != 1) {
+    async_safe_format_log(ANDROID_LOG_FATAL, "libc",
+                          "read of IPC pipe returned unexpected value: %zd", rc);
+    return 1;
+  } else if (buf[0] != '\1') {
+    async_safe_format_log(ANDROID_LOG_FATAL, "libc", "crash_dump helper reported failure");
+    return 1;
+  }
+
+  // crash_dump is ptracing us, fork off a copy of our address space for it to use.
+  create_vm_process();
+
+  // Don't leave a zombie child.
+  int status;
+  if (TEMP_FAILURE_RETRY(waitpid(crash_dump_pid, &status, 0)) == -1) {
+    async_safe_format_log(ANDROID_LOG_FATAL, "libc", "failed to wait for crash_dump helper: %s",
+                          strerror(errno));
+  } else if (WIFSTOPPED(status) || WIFSIGNALED(status)) {
+    async_safe_format_log(ANDROID_LOG_FATAL, "libc", "crash_dump helper crashed or stopped");
+  }
+
+  if (thread_info->siginfo->si_signo != DEBUGGER_SIGNAL) {
+    // For crashes, we don't need to minimize pause latency.
+    // Wait for the dump to complete before having the process exit, to avoid being murdered by
+    // ActivityManager or init.
+    TEMP_FAILURE_RETRY(read(input_read, &buf, sizeof(buf)));
+  }
+
   return 0;
 }
 
-static void resend_signal(siginfo_t* info, bool crash_dump_started) {
+static void resend_signal(siginfo_t* info) {
   // Signals can either be fatal or nonfatal.
   // For fatal signals, crash_dump will send us the signal we crashed with
   // before resuming us, so that processes using waitpid on us will see that we
@@ -351,16 +435,6 @@
   // to deregister our signal handler for that signal before continuing.
   if (info->si_signo != DEBUGGER_SIGNAL) {
     signal(info->si_signo, SIG_DFL);
-  }
-
-  // We need to return from our signal handler so that crash_dump can see the
-  // signal via ptrace and dump the thread that crashed. However, returning
-  // does not guarantee that the signal will be thrown again, even for SIGSEGV
-  // and friends, since the signal could have been sent manually. We blocked
-  // all signals when registering the handler, so resending the signal (using
-  // rt_tgsigqueueinfo(2) to preserve SA_SIGINFO) will cause it to be delivered
-  // when our signal handler returns.
-  if (crash_dump_started || info->si_signo != DEBUGGER_SIGNAL) {
     int rc = syscall(SYS_rt_tgsigqueueinfo, __getpid(), __gettid(), info->si_signo, info);
     if (rc != 0) {
       fatal_errno("failed to resend signal during crash");
@@ -397,16 +471,21 @@
   }
 
   void* abort_message = nullptr;
-  if (g_callbacks.get_abort_message) {
+  if (signal_number != DEBUGGER_SIGNAL && g_callbacks.get_abort_message) {
     abort_message = g_callbacks.get_abort_message();
   }
 
-  if (prctl(PR_GET_NO_NEW_PRIVS, 0, 0, 0, 0) == 1) {
+  // If sival_int is ~0, it means that the fallback handler has been called
+  // once before and this function is being called again to dump the stack
+  // of a specific thread. It is possible that the prctl call might return 1,
+  // then return 0 in subsequent calls, so check the sival_int to determine if
+  // the fallback handler should be called first.
+  if (info->si_value.sival_int == ~0 || prctl(PR_GET_NO_NEW_PRIVS, 0, 0, 0, 0) == 1) {
     // This check might be racy if another thread sets NO_NEW_PRIVS, but this should be unlikely,
     // you can only set NO_NEW_PRIVS to 1, and the effect should be at worst a single missing
     // ANR trace.
     debuggerd_fallback_handler(info, static_cast<ucontext_t*>(context), abort_message);
-    resend_signal(info, false);
+    resend_signal(info);
     return;
   }
 
@@ -417,20 +496,14 @@
     return;
   }
 
-  log_signal_summary(signal_number, info);
-
-  // If this was a fatal crash, populate si_value with the abort message address if possible.
-  // Note that applications can set an abort message without aborting.
-  if (abort_message && signal_number != DEBUGGER_SIGNAL) {
-    info->si_value.sival_ptr = abort_message;
-  }
+  log_signal_summary(info);
 
   debugger_thread_info thread_info = {
-    .crash_dump_started = false,
-    .pseudothread_tid = -1,
-    .crashing_tid = __gettid(),
-    .signal_number = signal_number,
-    .info = info
+      .pseudothread_tid = -1,
+      .crashing_tid = __gettid(),
+      .siginfo = info,
+      .ucontext = context,
+      .abort_msg = reinterpret_cast<uintptr_t>(abort_message),
   };
 
   // Set PR_SET_DUMPABLE to 1, so that crash_dump can ptrace us.
@@ -439,7 +512,19 @@
     fatal_errno("failed to set dumpable");
   }
 
-  // Essentially pthread_create without CLONE_FILES (see debuggerd_dispatch_pseudothread).
+  // On kernels with yama_ptrace enabled, also allow any process to attach.
+  bool restore_orig_ptracer = true;
+  if (prctl(PR_SET_PTRACER, PR_SET_PTRACER_ANY) != 0) {
+    if (errno == EINVAL) {
+      // This kernel does not support PR_SET_PTRACER_ANY, or Yama is not enabled.
+      restore_orig_ptracer = false;
+    } else {
+      fatal_errno("failed to set traceable");
+    }
+  }
+
+  // Essentially pthread_create without CLONE_FILES, so we still work during file descriptor
+  // exhaustion.
   pid_t child_pid =
     clone(debuggerd_dispatch_pseudothread, pseudothread_stack,
           CLONE_THREAD | CLONE_SIGHAND | CLONE_VM | CLONE_CHILD_SETTID | CLONE_CHILD_CLEARTID,
@@ -451,7 +536,7 @@
   // Wait for the child to start...
   futex_wait(&thread_info.pseudothread_tid, -1);
 
-  // and then wait for it to finish.
+  // and then wait for it to terminate.
   futex_wait(&thread_info.pseudothread_tid, child_pid);
 
   // Restore PR_SET_DUMPABLE to its original value.
@@ -459,21 +544,18 @@
     fatal_errno("failed to restore dumpable");
   }
 
-  // Signals can either be fatal or nonfatal.
-  // For fatal signals, crash_dump will PTRACE_CONT us with the signal we
-  // crashed with, so that processes using waitpid on us will see that we
-  // exited with the correct exit status (e.g. so that sh will report
-  // "Segmentation fault" instead of "Killed"). For this to work, we need
-  // to deregister our signal handler for that signal before continuing.
-  if (signal_number != DEBUGGER_SIGNAL) {
-    signal(signal_number, SIG_DFL);
+  // Restore PR_SET_PTRACER to its original value.
+  if (restore_orig_ptracer && prctl(PR_SET_PTRACER, 0) != 0) {
+    fatal_errno("failed to restore traceable");
   }
 
-  resend_signal(info, thread_info.crash_dump_started);
   if (info->si_signo == DEBUGGER_SIGNAL) {
     // If the signal is fatal, don't unlock the mutex to prevent other crashing threads from
     // starting to dump right before our death.
     pthread_mutex_unlock(&crash_mutex);
+  } else {
+    // Resend the signal, so that either gdb or the parent's waitpid sees it.
+    resend_signal(info);
   }
 }
 
diff --git a/debuggerd/libdebuggerd/arm/machine.cpp b/debuggerd/libdebuggerd/arm/machine.cpp
deleted file mode 100644
index ac833ae..0000000
--- a/debuggerd/libdebuggerd/arm/machine.cpp
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- *
- * Copyright 2006, 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.
- */
-
-#define LOG_TAG "DEBUG"
-
-#include <errno.h>
-#include <stdint.h>
-#include <string.h>
-#include <sys/ptrace.h>
-
-#include <backtrace/Backtrace.h>
-#include <log/log.h>
-
-#include "machine.h"
-#include "utility.h"
-
-void dump_memory_and_code(log_t* log, Backtrace* backtrace) {
-  pt_regs regs;
-  if (ptrace(PTRACE_GETREGS, backtrace->Tid(), 0, &regs)) {
-    ALOGE("cannot get registers: %s\n", strerror(errno));
-    return;
-  }
-
-  static const char reg_names[] = "r0r1r2r3r4r5r6r7r8r9slfpipsp";
-
-  for (int reg = 0; reg < 14; reg++) {
-    dump_memory(log, backtrace, regs.uregs[reg], "memory near %.2s:", &reg_names[reg * 2]);
-  }
-
-  dump_memory(log, backtrace, static_cast<uintptr_t>(regs.ARM_pc), "code around pc:");
-
-  if (regs.ARM_pc != regs.ARM_lr) {
-    dump_memory(log, backtrace, static_cast<uintptr_t>(regs.ARM_lr), "code around lr:");
-  }
-}
-
-#define DUMP_GP_REGISTERS(log, reg_prefix)                                             \
-  _LOG(log, logtype::REGISTERS, "    r0 %08x  r1 %08x  r2 %08x  r3 %08x\n",            \
-       static_cast<uint32_t>(reg_prefix##r0), static_cast<uint32_t>(reg_prefix##r1),   \
-       static_cast<uint32_t>(reg_prefix##r2), static_cast<uint32_t>(reg_prefix##r3));  \
-  _LOG(log, logtype::REGISTERS, "    r4 %08x  r5 %08x  r6 %08x  r7 %08x\n",            \
-       static_cast<uint32_t>(reg_prefix##r4), static_cast<uint32_t>(reg_prefix##r5),   \
-       static_cast<uint32_t>(reg_prefix##r6), static_cast<uint32_t>(reg_prefix##r7));  \
-  _LOG(log, logtype::REGISTERS, "    r8 %08x  r9 %08x  sl %08x  fp %08x\n",            \
-       static_cast<uint32_t>(reg_prefix##r8), static_cast<uint32_t>(reg_prefix##r9),   \
-       static_cast<uint32_t>(reg_prefix##r10), static_cast<uint32_t>(reg_prefix##fp)); \
-  _LOG(log, logtype::REGISTERS, "    ip %08x  sp %08x  lr %08x  pc %08x  cpsr %08x\n", \
-       static_cast<uint32_t>(reg_prefix##ip), static_cast<uint32_t>(reg_prefix##sp),   \
-       static_cast<uint32_t>(reg_prefix##lr), static_cast<uint32_t>(reg_prefix##pc),   \
-       static_cast<uint32_t>(reg_prefix##cpsr))
-
-void dump_registers(log_t* log, pid_t tid) {
-  pt_regs r;
-  if (ptrace(PTRACE_GETREGS, tid, 0, &r)) {
-    ALOGE("cannot get registers: %s\n", strerror(errno));
-    return;
-  }
-
-  DUMP_GP_REGISTERS(log, r.ARM_);
-
-  user_vfp vfp_regs;
-  if (ptrace(PTRACE_GETVFPREGS, tid, 0, &vfp_regs)) {
-    ALOGE("cannot get FP registers: %s\n", strerror(errno));
-    return;
-  }
-
-  for (size_t i = 0; i < 32; i += 2) {
-    _LOG(log, logtype::FP_REGISTERS, "    d%-2d %016llx  d%-2d %016llx\n",
-         i, vfp_regs.fpregs[i], i+1, vfp_regs.fpregs[i+1]);
-  }
-  _LOG(log, logtype::FP_REGISTERS, "    scr %08lx\n", vfp_regs.fpscr);
-}
-
-void dump_registers(log_t* log, const ucontext_t* uc) {
-  DUMP_GP_REGISTERS(log, uc->uc_mcontext.arm_);
-}
diff --git a/debuggerd/libdebuggerd/arm64/machine.cpp b/debuggerd/libdebuggerd/arm64/machine.cpp
deleted file mode 100644
index fa73c99a..0000000
--- a/debuggerd/libdebuggerd/arm64/machine.cpp
+++ /dev/null
@@ -1,104 +0,0 @@
-/*
- *
- * Copyright 2014, 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.
- */
-
-#define LOG_TAG "DEBUG"
-
-#include <elf.h>
-#include <errno.h>
-#include <stdint.h>
-#include <string.h>
-#include <sys/ptrace.h>
-#include <sys/uio.h>
-
-#include <backtrace/Backtrace.h>
-#include <log/log.h>
-
-#include "machine.h"
-#include "utility.h"
-
-void dump_memory_and_code(log_t* log, Backtrace* backtrace) {
-  struct user_pt_regs regs;
-  struct iovec io;
-  io.iov_base = &regs;
-  io.iov_len = sizeof(regs);
-
-  if (ptrace(PTRACE_GETREGSET, backtrace->Tid(), reinterpret_cast<void*>(NT_PRSTATUS), &io) == -1) {
-    ALOGE("ptrace failed to get registers: %s", strerror(errno));
-    return;
-  }
-
-  for (int reg = 0; reg < 31; reg++) {
-    dump_memory(log, backtrace, regs.regs[reg], "memory near x%d:", reg);
-  }
-
-  dump_memory(log, backtrace, static_cast<uintptr_t>(regs.pc), "code around pc:");
-
-  if (regs.pc != regs.sp) {
-    dump_memory(log, backtrace, static_cast<uintptr_t>(regs.sp), "code around sp:");
-  }
-}
-
-#define DUMP_GP_REGISTERS(log)                                                                   \
-  for (int i = 0; i < 28; i += 4) {                                                              \
-    const char* fmt = "    x%-2d  %016llx  x%-2d  %016llx  x%-2d  %016llx  x%-2d  %016llx\n";    \
-    _LOG(log, logtype::REGISTERS, fmt, i, r.regs[i], i + 1, r.regs[i + 1], i + 2, r.regs[i + 2], \
-         i + 3, r.regs[i + 3]);                                                                  \
-  }                                                                                              \
-  _LOG(log, logtype::REGISTERS, "    x28  %016llx  x29  %016llx  x30  %016llx\n", r.regs[28],    \
-       r.regs[29], r.regs[30]);                                                                  \
-  _LOG(log, logtype::REGISTERS, "    sp   %016llx  pc   %016llx  pstate %016llx\n", r.sp, r.pc,  \
-       r.pstate)
-
-void dump_registers(log_t* log, pid_t tid) {
-  struct user_pt_regs r;
-  struct iovec io;
-  io.iov_base = &r;
-  io.iov_len = sizeof(r);
-
-  if (ptrace(PTRACE_GETREGSET, tid, (void*) NT_PRSTATUS, (void*) &io) == -1) {
-    ALOGE("ptrace error: %s\n", strerror(errno));
-    return;
-  }
-
-  DUMP_GP_REGISTERS(log);
-
-  struct user_fpsimd_state f;
-  io.iov_base = &f;
-  io.iov_len = sizeof(f);
-
-  if (ptrace(PTRACE_GETREGSET, tid, (void*) NT_PRFPREG, (void*) &io) == -1) {
-    ALOGE("ptrace error: %s\n", strerror(errno));
-    return;
-  }
-
-  for (int i = 0; i < 32; i += 2) {
-    _LOG(log, logtype::FP_REGISTERS,
-         "    v%-2d  %016" PRIx64 "%016" PRIx64 "  v%-2d  %016" PRIx64 "%016" PRIx64 "\n",
-         i,
-         static_cast<uint64_t>(f.vregs[i] >> 64),
-         static_cast<uint64_t>(f.vregs[i]),
-         i+1,
-         static_cast<uint64_t>(f.vregs[i+1] >> 64),
-         static_cast<uint64_t>(f.vregs[i+1]));
-  }
-  _LOG(log, logtype::FP_REGISTERS, "    fpsr %08x  fpcr %08x\n", f.fpsr, f.fpcr);
-}
-
-void dump_registers(log_t* log, const ucontext_t* ucontext) {
-  const mcontext_t& r = ucontext->uc_mcontext;
-  DUMP_GP_REGISTERS(log);
-}
diff --git a/debuggerd/libdebuggerd/backtrace.cpp b/debuggerd/libdebuggerd/backtrace.cpp
index 334d97f..f0a01f4 100644
--- a/debuggerd/libdebuggerd/backtrace.cpp
+++ b/debuggerd/libdebuggerd/backtrace.cpp
@@ -16,6 +16,8 @@
 
 #define LOG_TAG "DEBUG"
 
+#include "libdebuggerd/backtrace.h"
+
 #include <errno.h>
 #include <dirent.h>
 #include <limits.h>
@@ -28,15 +30,16 @@
 #include <time.h>
 #include <unistd.h>
 
+#include <map>
 #include <memory>
 #include <string>
 
+#include <android-base/unique_fd.h>
 #include <backtrace/Backtrace.h>
 #include <log/log.h>
 
-#include "backtrace.h"
-
-#include "utility.h"
+#include "libdebuggerd/types.h"
+#include "libdebuggerd/utility.h"
 
 static void dump_process_header(log_t* log, pid_t pid, const char* process_name) {
   time_t t = time(NULL);
@@ -56,62 +59,46 @@
   _LOG(log, logtype::BACKTRACE, "\n----- end %d -----\n", pid);
 }
 
-static void log_thread_name(log_t* log, pid_t tid, const char* thread_name) {
-  _LOG(log, logtype::BACKTRACE, "\n\"%s\" sysTid=%d\n", thread_name, tid);
-}
-
-static void dump_thread(log_t* log, BacktraceMap* map, pid_t pid, pid_t tid,
-                        const std::string& thread_name) {
-  log_thread_name(log, tid, thread_name.c_str());
-
-  std::unique_ptr<Backtrace> backtrace(Backtrace::Create(pid, tid, map));
-  if (backtrace->Unwind(0)) {
-    dump_backtrace_to_log(backtrace.get(), log, "  ");
-  } else {
-    ALOGE("Unwind failed: tid = %d: %s", tid,
-          backtrace->GetErrorString(backtrace->GetError()).c_str());
-  }
-}
-
-void dump_backtrace(int fd, BacktraceMap* map, pid_t pid, pid_t tid, const std::string& process_name,
-                    const std::map<pid_t, std::string>& threads, std::string* amfd_data) {
-  log_t log;
-  log.tfd = fd;
-  log.amfd_data = amfd_data;
-
-  dump_process_header(&log, pid, process_name.c_str());
-  dump_thread(&log, map, pid, tid, threads.find(tid)->second.c_str());
-
-  for (const auto& it : threads) {
-    pid_t thread_tid = it.first;
-    const std::string& thread_name = it.second;
-    if (thread_tid != tid) {
-      dump_thread(&log, map, pid, thread_tid, thread_name.c_str());
-    }
-  }
-
-  dump_process_footer(&log, pid);
-}
-
-void dump_backtrace_ucontext(int output_fd, ucontext_t* ucontext) {
-  pid_t pid = getpid();
-  pid_t tid = gettid();
-
+void dump_backtrace_thread(int output_fd, BacktraceMap* map, const ThreadInfo& thread) {
   log_t log;
   log.tfd = output_fd;
   log.amfd_data = nullptr;
 
-  char thread_name[16];
-  read_with_default("/proc/self/comm", thread_name, sizeof(thread_name), "<unknown>");
-  log_thread_name(&log, tid, thread_name);
+  _LOG(&log, logtype::BACKTRACE, "\n\"%s\" sysTid=%d\n", thread.thread_name.c_str(), thread.tid);
 
-  std::unique_ptr<Backtrace> backtrace(Backtrace::Create(pid, tid));
-  if (backtrace->Unwind(0, ucontext)) {
-    dump_backtrace_to_log(backtrace.get(), &log, "  ");
-  } else {
-    ALOGE("Unwind failed: tid = %d: %s", tid,
-          backtrace->GetErrorString(backtrace->GetError()).c_str());
+  std::vector<backtrace_frame_data_t> frames;
+  if (!Backtrace::Unwind(thread.registers.get(), map, &frames, 0, nullptr)) {
+    _LOG(&log, logtype::THREAD, "Unwind failed: tid = %d", thread.tid);
+    return;
   }
+
+  for (auto& frame : frames) {
+    _LOG(&log, logtype::BACKTRACE, "  %s\n", Backtrace::FormatFrameData(&frame).c_str());
+  }
+}
+
+void dump_backtrace(android::base::unique_fd output_fd, BacktraceMap* map,
+                    const std::map<pid_t, ThreadInfo>& thread_info, pid_t target_thread) {
+  log_t log;
+  log.tfd = output_fd.get();
+  log.amfd_data = nullptr;
+
+  auto target = thread_info.find(target_thread);
+  if (target == thread_info.end()) {
+    ALOGE("failed to find target thread in thread info");
+    return;
+  }
+
+  dump_process_header(&log, target->second.pid, target->second.process_name.c_str());
+
+  dump_backtrace_thread(output_fd.get(), map, target->second);
+  for (const auto& [tid, info] : thread_info) {
+    if (tid != target_thread) {
+      dump_backtrace_thread(output_fd.get(), map, info);
+    }
+  }
+
+  dump_process_footer(&log, target->second.pid);
 }
 
 void dump_backtrace_header(int output_fd) {
@@ -131,9 +118,3 @@
 
   dump_process_footer(&log, getpid());
 }
-
-void dump_backtrace_to_log(Backtrace* backtrace, log_t* log, const char* prefix) {
-  for (size_t i = 0; i < backtrace->NumFrames(); i++) {
-    _LOG(log, logtype::BACKTRACE, "%s%s\n", prefix, backtrace->FormatFrameData(i).c_str());
-  }
-}
diff --git a/debuggerd/libdebuggerd/elf_utils.cpp b/debuggerd/libdebuggerd/elf_utils.cpp
index 4e798e2..d7afc0b 100644
--- a/debuggerd/libdebuggerd/elf_utils.cpp
+++ b/debuggerd/libdebuggerd/elf_utils.cpp
@@ -16,6 +16,8 @@
 
 #define LOG_TAG "DEBUG"
 
+#include "libdebuggerd/elf_utils.h"
+
 #include <elf.h>
 #include <stdint.h>
 #include <stdlib.h>
@@ -24,30 +26,28 @@
 #include <string>
 
 #include <android-base/stringprintf.h>
-#include <backtrace/Backtrace.h>
 #include <log/log.h>
-
-#include "elf_utils.h"
+#include <unwindstack/Memory.h>
 
 #define NOTE_ALIGN(size)  (((size) + 3) & ~3)
 
 template <typename HdrType, typename PhdrType, typename NhdrType>
-static bool get_build_id(
-    Backtrace* backtrace, uintptr_t base_addr, uint8_t* e_ident, std::string* build_id) {
+static bool get_build_id(unwindstack::Memory* memory, uintptr_t base_addr, uint8_t* e_ident,
+                         std::string* build_id) {
   HdrType hdr;
 
   memcpy(&hdr.e_ident[0], e_ident, EI_NIDENT);
 
   // First read the rest of the header.
-  if (backtrace->Read(base_addr + EI_NIDENT, reinterpret_cast<uint8_t*>(&hdr) + EI_NIDENT,
-                      sizeof(HdrType) - EI_NIDENT) != sizeof(HdrType) - EI_NIDENT) {
+  if (memory->Read(base_addr + EI_NIDENT, reinterpret_cast<uint8_t*>(&hdr) + EI_NIDENT,
+                   sizeof(HdrType) - EI_NIDENT) != sizeof(HdrType) - EI_NIDENT) {
     return false;
   }
 
   for (size_t i = 0; i < hdr.e_phnum; i++) {
     PhdrType phdr;
-    if (backtrace->Read(base_addr + hdr.e_phoff + i * hdr.e_phentsize,
-                        reinterpret_cast<uint8_t*>(&phdr), sizeof(phdr)) != sizeof(phdr)) {
+    if (memory->Read(base_addr + hdr.e_phoff + i * hdr.e_phentsize,
+                     reinterpret_cast<uint8_t*>(&phdr), sizeof(phdr)) != sizeof(phdr)) {
       return false;
     }
     // Looking for the .note.gnu.build-id note.
@@ -56,7 +56,7 @@
       uintptr_t addr = base_addr + phdr.p_offset;
       while (hdr_size >= sizeof(NhdrType)) {
         NhdrType nhdr;
-        if (backtrace->Read(addr, reinterpret_cast<uint8_t*>(&nhdr), sizeof(nhdr)) != sizeof(nhdr)) {
+        if (memory->Read(addr, reinterpret_cast<uint8_t*>(&nhdr), sizeof(nhdr)) != sizeof(nhdr)) {
           return false;
         }
         addr += sizeof(nhdr);
@@ -69,7 +69,7 @@
                   nhdr.n_descsz);
             return false;
           }
-          if (backtrace->Read(addr, build_id_data, nhdr.n_descsz) != nhdr.n_descsz) {
+          if (memory->Read(addr, build_id_data, nhdr.n_descsz) != nhdr.n_descsz) {
             return false;
           }
 
@@ -95,10 +95,10 @@
   return false;
 }
 
-bool elf_get_build_id(Backtrace* backtrace, uintptr_t addr, std::string* build_id) {
+bool elf_get_build_id(unwindstack::Memory* memory, uintptr_t addr, std::string* build_id) {
   // Read and verify the elf magic number first.
   uint8_t e_ident[EI_NIDENT];
-  if (backtrace->Read(addr, e_ident, SELFMAG) != SELFMAG) {
+  if (memory->Read(addr, e_ident, SELFMAG) != SELFMAG) {
     return false;
   }
 
@@ -107,14 +107,14 @@
   }
 
   // Read the rest of EI_NIDENT.
-  if (backtrace->Read(addr + SELFMAG, e_ident + SELFMAG, EI_NIDENT - SELFMAG) != EI_NIDENT - SELFMAG) {
+  if (memory->Read(addr + SELFMAG, e_ident + SELFMAG, EI_NIDENT - SELFMAG) != EI_NIDENT - SELFMAG) {
     return false;
   }
 
   if (e_ident[EI_CLASS] == ELFCLASS32) {
-    return get_build_id<Elf32_Ehdr, Elf32_Phdr, Elf32_Nhdr>(backtrace, addr, e_ident, build_id);
+    return get_build_id<Elf32_Ehdr, Elf32_Phdr, Elf32_Nhdr>(memory, addr, e_ident, build_id);
   } else if (e_ident[EI_CLASS] == ELFCLASS64) {
-    return get_build_id<Elf64_Ehdr, Elf64_Phdr, Elf64_Nhdr>(backtrace, addr, e_ident, build_id);
+    return get_build_id<Elf64_Ehdr, Elf64_Phdr, Elf64_Nhdr>(memory, addr, e_ident, build_id);
   }
 
   return false;
diff --git a/debuggerd/libdebuggerd/include/backtrace.h b/debuggerd/libdebuggerd/include/backtrace.h
deleted file mode 100644
index fe738f1..0000000
--- a/debuggerd/libdebuggerd/include/backtrace.h
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Copyright (C) 2012 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 _DEBUGGERD_BACKTRACE_H
-#define _DEBUGGERD_BACKTRACE_H
-
-#include <sys/types.h>
-#include <sys/ucontext.h>
-
-#include <map>
-#include <string>
-
-#include "utility.h"
-
-class Backtrace;
-class BacktraceMap;
-
-// Dumps a backtrace using a format similar to what Dalvik uses so that the result
-// can be intermixed in a bug report.
-void dump_backtrace(int fd, BacktraceMap* map, pid_t pid, pid_t tid, const std::string& process_name,
-                    const std::map<pid_t, std::string>& threads, std::string* amfd_data);
-
-/* Dumps the backtrace in the backtrace data structure to the log. */
-void dump_backtrace_to_log(Backtrace* backtrace, log_t* log, const char* prefix);
-
-void dump_backtrace_ucontext(int output_fd, ucontext_t* ucontext);
-void dump_backtrace_header(int output_fd);
-void dump_backtrace_footer(int output_fd);
-
-#endif // _DEBUGGERD_BACKTRACE_H
diff --git a/debuggerd/libdebuggerd/include/elf_utils.h b/debuggerd/libdebuggerd/include/elf_utils.h
deleted file mode 100644
index 11d0a43..0000000
--- a/debuggerd/libdebuggerd/include/elf_utils.h
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Copyright (C) 2015 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 _DEBUGGERD_ELF_UTILS_H
-#define _DEBUGGERD_ELF_UTILS_H
-
-#include <stdint.h>
-#include <string>
-
-class Backtrace;
-
-bool elf_get_build_id(Backtrace*, uintptr_t, std::string*);
-
-#endif // _DEBUGGERD_ELF_UTILS_H
diff --git a/debuggerd/libdebuggerd/include/libdebuggerd/backtrace.h b/debuggerd/libdebuggerd/include/libdebuggerd/backtrace.h
new file mode 100644
index 0000000..119e59b
--- /dev/null
+++ b/debuggerd/libdebuggerd/include/libdebuggerd/backtrace.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2012 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 _DEBUGGERD_BACKTRACE_H
+#define _DEBUGGERD_BACKTRACE_H
+
+#include <sys/types.h>
+#include <sys/ucontext.h>
+
+#include <map>
+#include <string>
+
+#include <android-base/unique_fd.h>
+
+#include "types.h"
+#include "utility.h"
+
+class BacktraceMap;
+
+// Dumps a backtrace using a format similar to what Dalvik uses so that the result
+// can be intermixed in a bug report.
+void dump_backtrace(android::base::unique_fd output_fd, BacktraceMap* map,
+                    const std::map<pid_t, ThreadInfo>& thread_info, pid_t target_thread);
+
+void dump_backtrace_header(int output_fd);
+void dump_backtrace_thread(int output_fd, BacktraceMap* map, const ThreadInfo& thread);
+void dump_backtrace_footer(int output_fd);
+
+#endif // _DEBUGGERD_BACKTRACE_H
diff --git a/debuggerd/libdebuggerd/include/libdebuggerd/elf_utils.h b/debuggerd/libdebuggerd/include/libdebuggerd/elf_utils.h
new file mode 100644
index 0000000..5d0d924
--- /dev/null
+++ b/debuggerd/libdebuggerd/include/libdebuggerd/elf_utils.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2015 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 _DEBUGGERD_ELF_UTILS_H
+#define _DEBUGGERD_ELF_UTILS_H
+
+#include <stdint.h>
+#include <string>
+
+namespace unwindstack {
+class Memory;
+}
+
+bool elf_get_build_id(unwindstack::Memory*, uintptr_t, std::string*);
+
+#endif // _DEBUGGERD_ELF_UTILS_H
diff --git a/debuggerd/libdebuggerd/include/libdebuggerd/open_files_list.h b/debuggerd/libdebuggerd/include/libdebuggerd/open_files_list.h
new file mode 100644
index 0000000..4727ca4
--- /dev/null
+++ b/debuggerd/libdebuggerd/include/libdebuggerd/open_files_list.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2016 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 _DEBUGGERD_OPEN_FILES_LIST_H
+#define _DEBUGGERD_OPEN_FILES_LIST_H
+
+#include <sys/types.h>
+
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "utility.h"
+
+typedef std::vector<std::pair<int, std::string>> OpenFilesList;
+
+/* Populates the given list with open files for the given process. */
+void populate_open_files_list(pid_t pid, OpenFilesList* list);
+
+/* Dumps the open files list to the log. */
+void dump_open_files_list(log_t* log, const OpenFilesList& files, const char* prefix);
+
+#endif // _DEBUGGERD_OPEN_FILES_LIST_H
diff --git a/debuggerd/libdebuggerd/include/libdebuggerd/tombstone.h b/debuggerd/libdebuggerd/include/libdebuggerd/tombstone.h
new file mode 100644
index 0000000..be90d0f
--- /dev/null
+++ b/debuggerd/libdebuggerd/include/libdebuggerd/tombstone.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2012 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 _DEBUGGERD_TOMBSTONE_H
+#define _DEBUGGERD_TOMBSTONE_H
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <sys/types.h>
+
+#include <map>
+#include <string>
+
+#include <android-base/unique_fd.h>
+
+#include "open_files_list.h"
+#include "types.h"
+
+class BacktraceMap;
+
+/* Create and open a tombstone file for writing.
+ * Returns a writable file descriptor, or -1 with errno set appropriately.
+ * If out_path is non-null, *out_path is set to the path of the tombstone file.
+ */
+int open_tombstone(std::string* path);
+
+/* Creates a tombstone file and writes the crash dump to it. */
+void engrave_tombstone(int tombstone_fd, BacktraceMap* map, const OpenFilesList* open_files,
+                       pid_t pid, pid_t tid, const std::string& process_name,
+                       const std::map<pid_t, std::string>& threads, uint64_t abort_msg_address,
+                       std::string* amfd_data);
+
+void engrave_tombstone_ucontext(int tombstone_fd, uint64_t abort_msg_address, siginfo_t* siginfo,
+                                ucontext_t* ucontext);
+
+void engrave_tombstone(android::base::unique_fd output_fd, BacktraceMap* map,
+                       unwindstack::Memory* process_memory,
+                       const std::map<pid_t, ThreadInfo>& thread_info, pid_t target_thread,
+                       uint64_t abort_msg_address, OpenFilesList* open_files,
+                       std::string* amfd_data);
+
+#endif  // _DEBUGGERD_TOMBSTONE_H
diff --git a/debuggerd/libdebuggerd/include/libdebuggerd/types.h b/debuggerd/libdebuggerd/include/libdebuggerd/types.h
new file mode 100644
index 0000000..70583af
--- /dev/null
+++ b/debuggerd/libdebuggerd/include/libdebuggerd/types.h
@@ -0,0 +1,34 @@
+#pragma once
+
+/*
+ * Copyright (C) 2012 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 <string>
+
+#include <unwindstack/Regs.h>
+
+struct ThreadInfo {
+  std::unique_ptr<unwindstack::Regs> registers;
+  pid_t tid;
+  std::string thread_name;
+
+  pid_t pid;
+  std::string process_name;
+
+  int signo = 0;
+  siginfo_t* siginfo = nullptr;
+};
diff --git a/debuggerd/libdebuggerd/include/libdebuggerd/utility.h b/debuggerd/libdebuggerd/include/libdebuggerd/utility.h
new file mode 100644
index 0000000..7c5304e
--- /dev/null
+++ b/debuggerd/libdebuggerd/include/libdebuggerd/utility.h
@@ -0,0 +1,83 @@
+/* system/debuggerd/utility.h
+**
+** Copyright 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.
+*/
+
+#ifndef _DEBUGGERD_UTILITY_H
+#define _DEBUGGERD_UTILITY_H
+
+#include <signal.h>
+#include <stdbool.h>
+#include <sys/types.h>
+
+#include <string>
+
+#include <android-base/macros.h>
+#include <backtrace/Backtrace.h>
+
+struct log_t {
+  // Tombstone file descriptor.
+  int tfd;
+  // Data to be sent to the Activity Manager.
+  std::string* amfd_data;
+  // The tid of the thread that crashed.
+  pid_t crashed_tid;
+  // The tid of the thread we are currently working with.
+  pid_t current_tid;
+  // logd daemon crash, can block asking for logcat data, allow suppression.
+  bool should_retrieve_logcat;
+
+  log_t()
+      : tfd(-1),
+        amfd_data(nullptr),
+        crashed_tid(-1),
+        current_tid(-1),
+        should_retrieve_logcat(true) {}
+};
+
+// List of types of logs to simplify the logging decision in _LOG
+enum logtype {
+  HEADER,
+  THREAD,
+  REGISTERS,
+  FP_REGISTERS,
+  BACKTRACE,
+  MAPS,
+  MEMORY,
+  STACK,
+  LOGS,
+  OPEN_FILES
+};
+
+// Log information onto the tombstone.
+void _LOG(log_t* log, logtype ltype, const char* fmt, ...) __attribute__((format(printf, 3, 4)));
+
+namespace unwindstack {
+class Memory;
+}
+
+void dump_memory(log_t* log, unwindstack::Memory* backtrace, uint64_t addr, const std::string&);
+
+void read_with_default(const char* path, char* buf, size_t len, const char* default_value);
+
+void drop_capabilities();
+
+bool signal_has_sender(const siginfo_t*, pid_t caller_pid);
+bool signal_has_si_addr(const siginfo_t*);
+void get_signal_sender(char* buf, size_t n, const siginfo_t*);
+const char* get_signame(const siginfo_t*);
+const char* get_sigcode(const siginfo_t*);
+
+#endif // _DEBUGGERD_UTILITY_H
diff --git a/debuggerd/libdebuggerd/include/machine.h b/debuggerd/libdebuggerd/include/machine.h
deleted file mode 100644
index 5e56682..0000000
--- a/debuggerd/libdebuggerd/include/machine.h
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright (C) 2011 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 _DEBUGGERD_MACHINE_H
-#define _DEBUGGERD_MACHINE_H
-
-#include <sys/types.h>
-
-#include <backtrace/Backtrace.h>
-
-#include "utility.h"
-
-void dump_memory_and_code(log_t* log, Backtrace* backtrace);
-void dump_registers(log_t* log, pid_t tid);
-void dump_registers(log_t* log, const ucontext_t* uc);
-
-#endif // _DEBUGGERD_MACHINE_H
diff --git a/debuggerd/libdebuggerd/include/open_files_list.h b/debuggerd/libdebuggerd/include/open_files_list.h
deleted file mode 100644
index b37228d..0000000
--- a/debuggerd/libdebuggerd/include/open_files_list.h
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright (C) 2016 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 _DEBUGGERD_OPEN_FILES_LIST_H
-#define _DEBUGGERD_OPEN_FILES_LIST_H
-
-#include <sys/types.h>
-
-#include <string>
-#include <utility>
-#include <vector>
-
-#include "utility.h"
-
-typedef std::vector<std::pair<int, std::string>> OpenFilesList;
-
-/* Populates the given list with open files for the given process. */
-void populate_open_files_list(pid_t pid, OpenFilesList* list);
-
-/* Dumps the open files list to the log. */
-void dump_open_files_list_to_log(const OpenFilesList& files, log_t* log, const char* prefix);
-
-#endif // _DEBUGGERD_OPEN_FILES_LIST_H
diff --git a/debuggerd/libdebuggerd/include/tombstone.h b/debuggerd/libdebuggerd/include/tombstone.h
deleted file mode 100644
index 79743b6..0000000
--- a/debuggerd/libdebuggerd/include/tombstone.h
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright (C) 2012 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 _DEBUGGERD_TOMBSTONE_H
-#define _DEBUGGERD_TOMBSTONE_H
-
-#include <stdbool.h>
-#include <stddef.h>
-#include <sys/types.h>
-
-#include <map>
-#include <string>
-
-#include "open_files_list.h"
-
-class BacktraceMap;
-
-/* Create and open a tombstone file for writing.
- * Returns a writable file descriptor, or -1 with errno set appropriately.
- * If out_path is non-null, *out_path is set to the path of the tombstone file.
- */
-int open_tombstone(std::string* path);
-
-/* Creates a tombstone file and writes the crash dump to it. */
-void engrave_tombstone(int tombstone_fd, BacktraceMap* map, const OpenFilesList* open_files,
-                       pid_t pid, pid_t tid, const std::string& process_name,
-                       const std::map<pid_t, std::string>& threads, uintptr_t abort_msg_address,
-                       std::string* amfd_data);
-
-void engrave_tombstone_ucontext(int tombstone_fd, uintptr_t abort_msg_address, siginfo_t* siginfo,
-                                ucontext_t* ucontext);
-
-// Compatibility shim.
-__attribute__((__unused__))
-static void engrave_tombstone_ucontext(int tombstone_fd, pid_t, pid_t, uintptr_t abort_msg_address,
-                                       siginfo_t* siginfo, ucontext_t* ucontext) {
-  engrave_tombstone_ucontext(tombstone_fd, abort_msg_address, siginfo, ucontext);
-}
-
-#endif // _DEBUGGERD_TOMBSTONE_H
diff --git a/debuggerd/libdebuggerd/include/utility.h b/debuggerd/libdebuggerd/include/utility.h
deleted file mode 100644
index e5e5106..0000000
--- a/debuggerd/libdebuggerd/include/utility.h
+++ /dev/null
@@ -1,88 +0,0 @@
-/* system/debuggerd/utility.h
-**
-** Copyright 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.
-*/
-
-#ifndef _DEBUGGERD_UTILITY_H
-#define _DEBUGGERD_UTILITY_H
-
-#include <signal.h>
-#include <stdbool.h>
-#include <sys/types.h>
-
-#include <string>
-
-#include <backtrace/Backtrace.h>
-
-// Figure out the abi based on defined macros.
-#if defined(__arm__)
-#define ABI_STRING "arm"
-#elif defined(__aarch64__)
-#define ABI_STRING "arm64"
-#elif defined(__mips__) && !defined(__LP64__)
-#define ABI_STRING "mips"
-#elif defined(__mips__) && defined(__LP64__)
-#define ABI_STRING "mips64"
-#elif defined(__i386__)
-#define ABI_STRING "x86"
-#elif defined(__x86_64__)
-#define ABI_STRING "x86_64"
-#else
-#error "Unsupported ABI"
-#endif
-
-
-struct log_t{
-    // Tombstone file descriptor.
-    int tfd;
-    // Data to be sent to the Activity Manager.
-    std::string* amfd_data;
-    // The tid of the thread that crashed.
-    pid_t crashed_tid;
-    // The tid of the thread we are currently working with.
-    pid_t current_tid;
-    // logd daemon crash, can block asking for logcat data, allow suppression.
-    bool should_retrieve_logcat;
-
-    log_t()
-        : tfd(-1), amfd_data(nullptr), crashed_tid(-1), current_tid(-1),
-          should_retrieve_logcat(true) {}
-};
-
-// List of types of logs to simplify the logging decision in _LOG
-enum logtype {
-  HEADER,
-  THREAD,
-  REGISTERS,
-  FP_REGISTERS,
-  BACKTRACE,
-  MAPS,
-  MEMORY,
-  STACK,
-  LOGS,
-  OPEN_FILES
-};
-
-// Log information onto the tombstone.
-void _LOG(log_t* log, logtype ltype, const char *fmt, ...)
-        __attribute__ ((format(printf, 3, 4)));
-
-bool wait_for_signal(pid_t tid, siginfo_t* siginfo);
-
-void dump_memory(log_t* log, Backtrace* backtrace, uintptr_t addr, const char* fmt, ...);
-
-void read_with_default(const char* path, char* buf, size_t len, const char* default_value);
-
-#endif // _DEBUGGERD_UTILITY_H
diff --git a/debuggerd/libdebuggerd/mips/machine.cpp b/debuggerd/libdebuggerd/mips/machine.cpp
deleted file mode 100644
index cbf272a..0000000
--- a/debuggerd/libdebuggerd/mips/machine.cpp
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- * Copyright 2012, 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.
- */
-
-#define LOG_TAG "DEBUG"
-
-#include <errno.h>
-#include <inttypes.h>
-#include <stdint.h>
-#include <string.h>
-#include <sys/ptrace.h>
-
-#include <backtrace/Backtrace.h>
-#include <log/log.h>
-
-#include "machine.h"
-#include "utility.h"
-
-#define R(x) (static_cast<uintptr_t>(x))
-
-// If configured to do so, dump memory around *all* registers
-// for the crashing thread.
-void dump_memory_and_code(log_t* log, Backtrace* backtrace) {
-  pt_regs r;
-  if (ptrace(PTRACE_GETREGS, backtrace->Tid(), 0, &r)) {
-    ALOGE("cannot get registers: %s\n", strerror(errno));
-    return;
-  }
-
-  static const char reg_names[] = "$0atv0v1a0a1a2a3t0t1t2t3t4t5t6t7s0s1s2s3s4s5s6s7t8t9k0k1gpsps8ra";
-
-  for (int reg = 0; reg < 32; reg++) {
-    // skip uninteresting registers
-    if (reg == 0 // $0
-        || reg == 26 // $k0
-        || reg == 27 // $k1
-        || reg == 31 // $ra (done below)
-       )
-      continue;
-
-    dump_memory(log, backtrace, R(r.regs[reg]), "memory near %.2s:", &reg_names[reg * 2]);
-  }
-
-  uintptr_t pc = R(r.cp0_epc);
-  uintptr_t ra = R(r.regs[31]);
-  dump_memory(log, backtrace, pc, "code around pc:");
-  if (pc != ra) {
-    dump_memory(log, backtrace, ra, "code around ra:");
-  }
-}
-
-void dump_registers(log_t* log, pid_t tid) {
-  pt_regs r;
-  if(ptrace(PTRACE_GETREGS, tid, 0, &r)) {
-    ALOGE("cannot get registers: %s\n", strerror(errno));
-    return;
-  }
-
-  _LOG(log, logtype::REGISTERS, " zr %08" PRIxPTR "  at %08" PRIxPTR
-       "  v0 %08" PRIxPTR "  v1 %08" PRIxPTR "\n",
-       R(r.regs[0]), R(r.regs[1]), R(r.regs[2]), R(r.regs[3]));
-  _LOG(log, logtype::REGISTERS, " a0 %08" PRIxPTR "  a1 %08" PRIxPTR
-       "  a2 %08" PRIxPTR "  a3 %08" PRIxPTR "\n",
-       R(r.regs[4]), R(r.regs[5]), R(r.regs[6]), R(r.regs[7]));
-  _LOG(log, logtype::REGISTERS, " t0 %08" PRIxPTR "  t1 %08" PRIxPTR
-       "  t2 %08" PRIxPTR "  t3 %08" PRIxPTR "\n",
-       R(r.regs[8]), R(r.regs[9]), R(r.regs[10]), R(r.regs[11]));
-  _LOG(log, logtype::REGISTERS, " t4 %08" PRIxPTR "  t5 %08" PRIxPTR
-       "  t6 %08" PRIxPTR "  t7 %08" PRIxPTR "\n",
-       R(r.regs[12]), R(r.regs[13]), R(r.regs[14]), R(r.regs[15]));
-  _LOG(log, logtype::REGISTERS, " s0 %08" PRIxPTR "  s1 %08" PRIxPTR
-       "  s2 %08" PRIxPTR "  s3 %08" PRIxPTR "\n",
-       R(r.regs[16]), R(r.regs[17]), R(r.regs[18]), R(r.regs[19]));
-  _LOG(log, logtype::REGISTERS, " s4 %08" PRIxPTR "  s5 %08" PRIxPTR
-       "  s6 %08" PRIxPTR "  s7 %08" PRIxPTR "\n",
-       R(r.regs[20]), R(r.regs[21]), R(r.regs[22]), R(r.regs[23]));
-  _LOG(log, logtype::REGISTERS, " t8 %08" PRIxPTR "  t9 %08" PRIxPTR
-       "  k0 %08" PRIxPTR "  k1 %08" PRIxPTR "\n",
-       R(r.regs[24]), R(r.regs[25]), R(r.regs[26]), R(r.regs[27]));
-  _LOG(log, logtype::REGISTERS, " gp %08" PRIxPTR "  sp %08" PRIxPTR
-       "  s8 %08" PRIxPTR "  ra %08" PRIxPTR "\n",
-       R(r.regs[28]), R(r.regs[29]), R(r.regs[30]), R(r.regs[31]));
-  _LOG(log, logtype::REGISTERS, " hi %08" PRIxPTR "  lo %08" PRIxPTR
-       " bva %08" PRIxPTR " epc %08" PRIxPTR "\n",
-       R(r.hi), R(r.lo), R(r.cp0_badvaddr), R(r.cp0_epc));
-}
diff --git a/debuggerd/libdebuggerd/mips64/machine.cpp b/debuggerd/libdebuggerd/mips64/machine.cpp
deleted file mode 100644
index 0a8d532..0000000
--- a/debuggerd/libdebuggerd/mips64/machine.cpp
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- * Copyright 2014, 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.
- */
-
-#define LOG_TAG "DEBUG"
-
-#include <errno.h>
-#include <inttypes.h>
-#include <stdint.h>
-#include <string.h>
-#include <sys/ptrace.h>
-
-#include <backtrace/Backtrace.h>
-#include <log/log.h>
-
-#include "machine.h"
-#include "utility.h"
-
-#define R(x) (static_cast<uintptr_t>(x))
-
-// If configured to do so, dump memory around *all* registers
-// for the crashing thread.
-void dump_memory_and_code(log_t* log, Backtrace* backtrace) {
-  pt_regs r;
-  if (ptrace(PTRACE_GETREGS, backtrace->Tid(), 0, &r)) {
-    ALOGE("cannot get registers: %s\n", strerror(errno));
-    return;
-  }
-
-  static const char reg_names[] = "$0atv0v1a0a1a2a3a4a5a6a7t0t1t2t3s0s1s2s3s4s5s6s7t8t9k0k1gpsps8ra";
-
-  for (int reg = 0; reg < 32; reg++) {
-    // skip uninteresting registers
-    if (reg == 0 // $0
-        || reg == 26 // $k0
-        || reg == 27 // $k1
-        || reg == 31 // $ra (done below)
-       )
-      continue;
-
-    dump_memory(log, backtrace, R(r.regs[reg]), "memory near %.2s:", &reg_names[reg * 2]);
-  }
-
-  uintptr_t pc = R(r.cp0_epc);
-  uintptr_t ra = R(r.regs[31]);
-  dump_memory(log, backtrace, pc, "code around pc:");
-  if (pc != ra) {
-    dump_memory(log, backtrace, ra, "code around ra:");
-  }
-}
-
-void dump_registers(log_t* log, pid_t tid) {
-  pt_regs r;
-  if(ptrace(PTRACE_GETREGS, tid, 0, &r)) {
-    ALOGE("cannot get registers: %s\n", strerror(errno));
-    return;
-  }
-
-  _LOG(log, logtype::REGISTERS, " zr %016" PRIxPTR "  at %016" PRIxPTR
-       "  v0 %016" PRIxPTR "  v1 %016" PRIxPTR "\n",
-       R(r.regs[0]), R(r.regs[1]), R(r.regs[2]), R(r.regs[3]));
-  _LOG(log, logtype::REGISTERS, " a0 %016" PRIxPTR "  a1 %016" PRIxPTR
-       "  a2 %016" PRIxPTR "  a3 %016" PRIxPTR "\n",
-       R(r.regs[4]), R(r.regs[5]), R(r.regs[6]), R(r.regs[7]));
-  _LOG(log, logtype::REGISTERS, " a4 %016" PRIxPTR "  a5 %016" PRIxPTR
-       "  a6 %016" PRIxPTR "  a7 %016" PRIxPTR "\n",
-       R(r.regs[8]), R(r.regs[9]), R(r.regs[10]), R(r.regs[11]));
-  _LOG(log, logtype::REGISTERS, " t0 %016" PRIxPTR "  t1 %016" PRIxPTR
-       "  t2 %016" PRIxPTR "  t3 %016" PRIxPTR "\n",
-       R(r.regs[12]), R(r.regs[13]), R(r.regs[14]), R(r.regs[15]));
-  _LOG(log, logtype::REGISTERS, " s0 %016" PRIxPTR "  s1 %016" PRIxPTR
-       "  s2 %016" PRIxPTR "  s3 %016" PRIxPTR "\n",
-       R(r.regs[16]), R(r.regs[17]), R(r.regs[18]), R(r.regs[19]));
-  _LOG(log, logtype::REGISTERS, " s4 %016" PRIxPTR "  s5 %016" PRIxPTR
-       "  s6 %016" PRIxPTR "  s7 %016" PRIxPTR "\n",
-       R(r.regs[20]), R(r.regs[21]), R(r.regs[22]), R(r.regs[23]));
-  _LOG(log, logtype::REGISTERS, " t8 %016" PRIxPTR "  t9 %016" PRIxPTR
-       "  k0 %016" PRIxPTR "  k1 %016" PRIxPTR "\n",
-       R(r.regs[24]), R(r.regs[25]), R(r.regs[26]), R(r.regs[27]));
-  _LOG(log, logtype::REGISTERS, " gp %016" PRIxPTR "  sp %016" PRIxPTR
-       "  s8 %016" PRIxPTR "  ra %016" PRIxPTR "\n",
-       R(r.regs[28]), R(r.regs[29]), R(r.regs[30]), R(r.regs[31]));
-  _LOG(log, logtype::REGISTERS, " hi %016" PRIxPTR "  lo %016" PRIxPTR
-       " bva %016" PRIxPTR " epc %016" PRIxPTR "\n",
-       R(r.hi), R(r.lo), R(r.cp0_badvaddr), R(r.cp0_epc));
-}
diff --git a/debuggerd/libdebuggerd/open_files_list.cpp b/debuggerd/libdebuggerd/open_files_list.cpp
index 5c7ea70..b12703e 100644
--- a/debuggerd/libdebuggerd/open_files_list.cpp
+++ b/debuggerd/libdebuggerd/open_files_list.cpp
@@ -16,6 +16,8 @@
 
 #define LOG_TAG "DEBUG"
 
+#include "libdebuggerd/open_files_list.h"
+
 #include <dirent.h>
 #include <errno.h>
 #include <stdio.h>
@@ -31,9 +33,7 @@
 #include <android-base/file.h>
 #include <log/log.h>
 
-#include "open_files_list.h"
-
-#include "utility.h"
+#include "libdebuggerd/utility.h"
 
 void populate_open_files_list(pid_t pid, OpenFilesList* list) {
   std::string fd_dir_name = "/proc/" + std::to_string(pid) + "/fd";
@@ -61,7 +61,7 @@
   }
 }
 
-void dump_open_files_list_to_log(const OpenFilesList& files, log_t* log, const char* prefix) {
+void dump_open_files_list(log_t* log, const OpenFilesList& files, const char* prefix) {
   for (auto& file : files) {
     _LOG(log, logtype::OPEN_FILES, "%sfd %i: %s\n", prefix, file.first, file.second.c_str());
   }
diff --git a/debuggerd/libdebuggerd/test/BacktraceMock.h b/debuggerd/libdebuggerd/test/BacktraceMock.h
index 6104f7e..e7dbed7 100644
--- a/debuggerd/libdebuggerd/test/BacktraceMock.h
+++ b/debuggerd/libdebuggerd/test/BacktraceMock.h
@@ -17,15 +17,6 @@
 #ifndef _DEBUGGERD_TEST_BACKTRACE_MOCK_H
 #define _DEBUGGERD_TEST_BACKTRACE_MOCK_H
 
-#include <stdint.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/ucontext.h>
-
-#include <string>
-#include <vector>
-
-#include <backtrace/Backtrace.h>
 #include <backtrace/BacktraceMap.h>
 
 class BacktraceMapMock : public BacktraceMap {
@@ -38,69 +29,4 @@
   }
 };
 
-
-class BacktraceMock : public Backtrace {
- public:
-  explicit BacktraceMock(BacktraceMapMock* map) : Backtrace(0, 0, map) {
-    if (map_ == nullptr) {
-      abort();
-    }
-  }
-  virtual ~BacktraceMock() {}
-
-  virtual bool Unwind(size_t, ucontext_t*) { return false; }
-  virtual bool ReadWord(uintptr_t, word_t*) { return false;}
-
-  virtual std::string GetFunctionNameRaw(uintptr_t, uintptr_t*) { return ""; }
-
-  virtual size_t Read(uintptr_t addr, uint8_t* buffer, size_t bytes) {
-    size_t offset = 0;
-    if (last_read_addr_ > 0) {
-      offset = addr - last_read_addr_;
-    }
-    size_t bytes_available = buffer_.size() - offset;
-
-    if (do_partial_read_) {
-      // Do a partial read.
-      if (bytes > bytes_partial_read_) {
-        bytes = bytes_partial_read_;
-      }
-      bytes_partial_read_ -= bytes;
-      // Only support a single partial read.
-      do_partial_read_ = false;
-    } else if (bytes > bytes_available) {
-      bytes = bytes_available;
-    }
-
-    if (bytes > 0) {
-      memcpy(buffer, buffer_.data() + offset, bytes);
-    }
-
-    last_read_addr_ = addr;
-    return bytes;
-  }
-
-  void SetReadData(uint8_t* buffer, size_t bytes) {
-    buffer_.resize(bytes);
-    memcpy(buffer_.data(), buffer, bytes);
-    bytes_partial_read_ = 0;
-    do_partial_read_ = false;
-    last_read_addr_ = 0;
-  }
-
-  void SetPartialReadAmount(size_t bytes) {
-    if (bytes > buffer_.size()) {
-      abort();
-    }
-    bytes_partial_read_ = bytes;
-    do_partial_read_ = true;
-  }
-
- private:
-  std::vector<uint8_t> buffer_;
-  size_t bytes_partial_read_ = 0;
-  uintptr_t last_read_addr_ = 0;
-  bool do_partial_read_ = false;
-};
-
 #endif //  _DEBUGGERD_TEST_BACKTRACE_MOCK_H
diff --git a/debuggerd/libdebuggerd/test/dump_memory_test.cpp b/debuggerd/libdebuggerd/test/dump_memory_test.cpp
index 49f3690..be39582 100644
--- a/debuggerd/libdebuggerd/test/dump_memory_test.cpp
+++ b/debuggerd/libdebuggerd/test/dump_memory_test.cpp
@@ -19,12 +19,13 @@
 #include <memory>
 #include <string>
 
-#include <gtest/gtest.h>
 #include <android-base/file.h>
+#include <gtest/gtest.h>
+#include <unwindstack/Memory.h>
 
-#include "BacktraceMock.h"
+#include "libdebuggerd/utility.h"
+
 #include "log_fake.h"
-#include "utility.h"
 
 const char g_expected_full_dump[] =
 "\nmemory near r1:\n"
@@ -102,11 +103,59 @@
 "    123456d0 -------- -------- -------- --------  ................\n";
 #endif
 
+class MemoryMock : public unwindstack::Memory {
+ public:
+  virtual ~MemoryMock() = default;
+
+  virtual size_t Read(uint64_t addr, void* buffer, size_t bytes) override {
+    size_t offset = 0;
+    if (last_read_addr_ > 0) {
+      offset = addr - last_read_addr_;
+    }
+    size_t bytes_available = buffer_.size() - offset;
+
+    if (partial_read_) {
+      bytes = std::min(bytes, bytes_partial_read_);
+      bytes_partial_read_ -= bytes;
+      partial_read_ = bytes_partial_read_;
+    } else if (bytes > bytes_available) {
+      bytes = bytes_available;
+    }
+
+    if (bytes > 0) {
+      memcpy(buffer, buffer_.data() + offset, bytes);
+    }
+
+    last_read_addr_ = addr;
+    return bytes;
+  }
+
+  void SetReadData(uint8_t* buffer, size_t bytes) {
+    buffer_.resize(bytes);
+    memcpy(buffer_.data(), buffer, bytes);
+    bytes_partial_read_ = 0;
+    last_read_addr_ = 0;
+  }
+
+  void SetPartialReadAmount(size_t bytes) {
+    if (bytes > buffer_.size()) {
+      abort();
+    }
+    partial_read_ = true;
+    bytes_partial_read_ = bytes;
+  }
+
+ private:
+  std::vector<uint8_t> buffer_;
+  bool partial_read_ = false;
+  size_t bytes_partial_read_ = 0;
+  uintptr_t last_read_addr_ = 0;
+};
+
 class DumpMemoryTest : public ::testing::Test {
  protected:
   virtual void SetUp() {
-    map_mock_.reset(new BacktraceMapMock());
-    backtrace_mock_.reset(new BacktraceMock(map_mock_.get()));
+    memory_mock_ = std::make_unique<MemoryMock>();
 
     char tmp_file[256];
     const char data_template[] = "/data/local/tmp/debuggerd_memory_testXXXXXX";
@@ -137,10 +186,10 @@
     if (log_.tfd >= 0) {
       close(log_.tfd);
     }
+    memory_mock_.reset();
   }
 
-  std::unique_ptr<BacktraceMapMock> map_mock_;
-  std::unique_ptr<BacktraceMock> backtrace_mock_;
+  std::unique_ptr<MemoryMock> memory_mock_;
 
   log_t log_;
 };
@@ -150,9 +199,9 @@
   for (size_t i = 0; i < sizeof(buffer); i++) {
     buffer[i] = i;
   }
-  backtrace_mock_->SetReadData(buffer, sizeof(buffer));
+  memory_mock_->SetReadData(buffer, sizeof(buffer));
 
-  dump_memory(&log_, backtrace_mock_.get(), 0x12345678, "memory near %.2s:", "r1");
+  dump_memory(&log_, memory_mock_.get(), 0x12345678, "memory near r1");
 
   std::string tombstone_contents;
   ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
@@ -169,10 +218,10 @@
   for (size_t i = 0; i < sizeof(buffer); i++) {
     buffer[i] = i;
   }
-  backtrace_mock_->SetReadData(buffer, sizeof(buffer));
-  backtrace_mock_->SetPartialReadAmount(96);
+  memory_mock_->SetReadData(buffer, sizeof(buffer));
+  memory_mock_->SetPartialReadAmount(96);
 
-  dump_memory(&log_, backtrace_mock_.get(), 0x12345679, "memory near %.2s:", "r1");
+  dump_memory(&log_, memory_mock_.get(), 0x12345679, "memory near r1");
 
   std::string tombstone_contents;
   ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
@@ -189,9 +238,9 @@
   for (size_t i = 0; i < sizeof(buffer); i++) {
     buffer[i] = i;
   }
-  backtrace_mock_->SetReadData(buffer, sizeof(buffer));
+  memory_mock_->SetReadData(buffer, sizeof(buffer));
 
-  dump_memory(&log_, backtrace_mock_.get(), 0x12345679, "memory near %.2s:", "r1");
+  dump_memory(&log_, memory_mock_.get(), 0x12345679, "memory near r1");
 
   std::string tombstone_contents;
   ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
@@ -204,7 +253,7 @@
 }
 
 TEST_F(DumpMemoryTest, memory_unreadable) {
-  dump_memory(&log_, backtrace_mock_.get(), 0xa2345678, "memory near pc:");
+  dump_memory(&log_, memory_mock_.get(), 0xa2345678, "memory near pc");
 
   std::string tombstone_contents;
   ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
@@ -258,9 +307,9 @@
   for (size_t i = 0; i < sizeof(buffer); i++) {
     buffer[i] = i;
   }
-  backtrace_mock_->SetReadData(buffer, sizeof(buffer));
+  memory_mock_->SetReadData(buffer, sizeof(buffer));
 
-  dump_memory(&log_, backtrace_mock_.get(), 0x12345600, "memory near pc:");
+  dump_memory(&log_, memory_mock_.get(), 0x12345600, "memory near pc");
 
   std::string tombstone_contents;
   ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
@@ -277,10 +326,10 @@
   for (size_t i = 0; i < sizeof(buffer); i++) {
     buffer[i] = i;
   }
-  backtrace_mock_->SetReadData(buffer, sizeof(buffer));
-  backtrace_mock_->SetPartialReadAmount(102);
+  memory_mock_->SetReadData(buffer, sizeof(buffer));
+  memory_mock_->SetPartialReadAmount(102);
 
-  dump_memory(&log_, backtrace_mock_.get(), 0x12345600, "memory near pc:");
+  dump_memory(&log_, memory_mock_.get(), 0x12345600, "memory near pc");
 
   std::string tombstone_contents;
   ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
@@ -302,10 +351,10 @@
   for (size_t i = 0; i < sizeof(buffer); i++) {
     buffer[i] = i;
   }
-  backtrace_mock_->SetReadData(buffer, sizeof(buffer));
-  backtrace_mock_->SetPartialReadAmount(45);
+  memory_mock_->SetReadData(buffer, sizeof(buffer));
+  memory_mock_->SetPartialReadAmount(45);
 
-  dump_memory(&log_, backtrace_mock_.get(), 0x12345600, "memory near pc:");
+  dump_memory(&log_, memory_mock_.get(), 0x12345600, "memory near pc");
 
   std::string tombstone_contents;
   ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
@@ -329,9 +378,9 @@
 TEST_F(DumpMemoryTest, address_low_fence) {
   uint8_t buffer[256];
   memset(buffer, 0, sizeof(buffer));
-  backtrace_mock_->SetReadData(buffer, sizeof(buffer));
+  memory_mock_->SetReadData(buffer, sizeof(buffer));
 
-  dump_memory(&log_, backtrace_mock_.get(), 0x1000, "memory near %.2s:", "r1");
+  dump_memory(&log_, memory_mock_.get(), 0x1000, "memory near r1");
 
   std::string tombstone_contents;
   ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
@@ -383,9 +432,9 @@
 TEST_F(DumpMemoryTest, memory_address_too_low) {
   uint8_t buffer[256];
   memset(buffer, 0, sizeof(buffer));
-  backtrace_mock_->SetReadData(buffer, sizeof(buffer));
+  memory_mock_->SetReadData(buffer, sizeof(buffer));
 
-  dump_memory(&log_, backtrace_mock_.get(), 0, "memory near %.2s:", "r1");
+  dump_memory(&log_, memory_mock_.get(), 0, "memory near r1");
 
   std::string tombstone_contents;
   ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
@@ -400,16 +449,16 @@
 TEST_F(DumpMemoryTest, memory_address_too_high) {
   uint8_t buffer[256];
   memset(buffer, 0, sizeof(buffer));
-  backtrace_mock_->SetReadData(buffer, sizeof(buffer));
+  memory_mock_->SetReadData(buffer, sizeof(buffer));
 
 #if defined(__LP64__)
-  dump_memory(&log_, backtrace_mock_.get(), 0x4000000000000000UL, "memory near %.2s:", "r1");
-  dump_memory(&log_, backtrace_mock_.get(), 0x4000000000000000UL - 32, "memory near %.2s:", "r1");
-  dump_memory(&log_, backtrace_mock_.get(), 0x4000000000000000UL - 216, "memory near %.2s:", "r1");
+  dump_memory(&log_, memory_mock_.get(), 0x4000000000000000UL, "memory near r1");
+  dump_memory(&log_, memory_mock_.get(), 0x4000000000000000UL - 32, "memory near r1");
+  dump_memory(&log_, memory_mock_.get(), 0x4000000000000000UL - 216, "memory near r1");
 #else
-  dump_memory(&log_, backtrace_mock_.get(), 0xffff0000, "memory near %.2s:", "r1");
-  dump_memory(&log_, backtrace_mock_.get(), 0xffff0000 - 32, "memory near %.2s:", "r1");
-  dump_memory(&log_, backtrace_mock_.get(), 0xffff0000 - 220, "memory near %.2s:", "r1");
+  dump_memory(&log_, memory_mock_.get(), 0xffff0000, "memory near r1");
+  dump_memory(&log_, memory_mock_.get(), 0xffff0000 - 32, "memory near r1");
+  dump_memory(&log_, memory_mock_.get(), 0xffff0000 - 220, "memory near r1");
 #endif
 
   std::string tombstone_contents;
@@ -425,12 +474,12 @@
 TEST_F(DumpMemoryTest, memory_address_would_overflow) {
   uint8_t buffer[256];
   memset(buffer, 0, sizeof(buffer));
-  backtrace_mock_->SetReadData(buffer, sizeof(buffer));
+  memory_mock_->SetReadData(buffer, sizeof(buffer));
 
 #if defined(__LP64__)
-  dump_memory(&log_, backtrace_mock_.get(), 0xfffffffffffffff0, "memory near %.2s:", "r1");
+  dump_memory(&log_, memory_mock_.get(), 0xfffffffffffffff0, "memory near r1");
 #else
-  dump_memory(&log_, backtrace_mock_.get(), 0xfffffff0, "memory near %.2s:", "r1");
+  dump_memory(&log_, memory_mock_.get(), 0xfffffff0, "memory near r1");
 #endif
 
   std::string tombstone_contents;
@@ -448,12 +497,12 @@
   for (size_t i = 0; i < sizeof(buffer); i++) {
     buffer[i] = i;
   }
-  backtrace_mock_->SetReadData(buffer, sizeof(buffer));
+  memory_mock_->SetReadData(buffer, sizeof(buffer));
 
 #if defined(__LP64__)
-  dump_memory(&log_, backtrace_mock_.get(), 0x4000000000000000UL - 224, "memory near %.2s:", "r4");
+  dump_memory(&log_, memory_mock_.get(), 0x4000000000000000UL - 224, "memory near r4");
 #else
-  dump_memory(&log_, backtrace_mock_.get(), 0xffff0000 - 224, "memory near %.2s:", "r4");
+  dump_memory(&log_, memory_mock_.get(), 0xffff0000 - 224, "memory near r4");
 #endif
 
   std::string tombstone_contents;
@@ -508,12 +557,12 @@
   for (size_t i = 0; i < sizeof(buffer); i++) {
     buffer[i] = i;
   }
-  backtrace_mock_->SetReadData(buffer, sizeof(buffer));
-  backtrace_mock_->SetPartialReadAmount(0);
+  memory_mock_->SetReadData(buffer, sizeof(buffer));
+  memory_mock_->SetPartialReadAmount(0);
 
   size_t page_size = sysconf(_SC_PAGE_SIZE);
   uintptr_t addr = 0x10000020 + page_size - 120;
-  dump_memory(&log_, backtrace_mock_.get(), addr, "memory near %.2s:", "r4");
+  dump_memory(&log_, memory_mock_.get(), addr, "memory near r4");
 
   std::string tombstone_contents;
   ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
@@ -567,12 +616,12 @@
   for (size_t i = 0; i < sizeof(buffer); i++) {
     buffer[i] = i;
   }
-  backtrace_mock_->SetReadData(buffer, sizeof(buffer));
-  backtrace_mock_->SetPartialReadAmount(0);
+  memory_mock_->SetReadData(buffer, sizeof(buffer));
+  memory_mock_->SetPartialReadAmount(0);
 
   size_t page_size = sysconf(_SC_PAGE_SIZE);
   uintptr_t addr = 0x10000020 + page_size - 192;
-  dump_memory(&log_, backtrace_mock_.get(), addr, "memory near %.2s:", "r4");
+  dump_memory(&log_, memory_mock_.get(), addr, "memory near r4");
 
   std::string tombstone_contents;
   ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
@@ -626,11 +675,11 @@
   for (size_t i = 0; i < sizeof(buffer); i++) {
     buffer[i] = i;
   }
-  backtrace_mock_->SetReadData(buffer, sizeof(buffer));
-  backtrace_mock_->SetPartialReadAmount(0);
+  memory_mock_->SetReadData(buffer, sizeof(buffer));
+  memory_mock_->SetPartialReadAmount(0);
 
   uintptr_t addr = 0x10000020;
-  dump_memory(&log_, backtrace_mock_.get(), addr, "memory near %.2s:", "r4");
+  dump_memory(&log_, memory_mock_.get(), addr, "memory near r4");
 
   std::string tombstone_contents;
   ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
@@ -684,13 +733,13 @@
   for (size_t i = 0; i < sizeof(buffer); i++) {
     buffer[i] = i;
   }
-  backtrace_mock_->SetReadData(buffer, sizeof(buffer));
-  backtrace_mock_->SetPartialReadAmount(0);
+  memory_mock_->SetReadData(buffer, sizeof(buffer));
+  memory_mock_->SetPartialReadAmount(0);
 
   size_t page_size = sysconf(_SC_PAGE_SIZE);
   uintptr_t addr = 0x10000020 + page_size - 256;
 
-  dump_memory(&log_, backtrace_mock_.get(), addr, "memory near %.2s:", "r4");
+  dump_memory(&log_, memory_mock_.get(), addr, "memory near r4");
 
   std::string tombstone_contents;
   ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
diff --git a/debuggerd/libdebuggerd/test/elf_fake.cpp b/debuggerd/libdebuggerd/test/elf_fake.cpp
index bb52b59..9b8281a 100644
--- a/debuggerd/libdebuggerd/test/elf_fake.cpp
+++ b/debuggerd/libdebuggerd/test/elf_fake.cpp
@@ -14,11 +14,15 @@
  * limitations under the License.
  */
 
+#include "elf_fake.h"
+
 #include <stdint.h>
 
 #include <string>
 
-class Backtrace;
+namespace unwindstack {
+class Memory;
+}
 
 std::string g_build_id;
 
@@ -26,7 +30,7 @@
   g_build_id = build_id;
 }
 
-bool elf_get_build_id(Backtrace*, uintptr_t, std::string* build_id) {
+bool elf_get_build_id(unwindstack::Memory*, uintptr_t, std::string* build_id) {
   if (g_build_id != "") {
     *build_id = g_build_id;
     return true;
diff --git a/debuggerd/libdebuggerd/test/log_fake.cpp b/debuggerd/libdebuggerd/test/log_fake.cpp
index 3336bcb..68f4013 100644
--- a/debuggerd/libdebuggerd/test/log_fake.cpp
+++ b/debuggerd/libdebuggerd/test/log_fake.cpp
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#include "log_fake.h"
+
 #include <errno.h>
 #include <stdarg.h>
 
diff --git a/debuggerd/libdebuggerd/test/open_files_list_test.cpp b/debuggerd/libdebuggerd/test/open_files_list_test.cpp
index 85e0695..acac72c 100644
--- a/debuggerd/libdebuggerd/test/open_files_list_test.cpp
+++ b/debuggerd/libdebuggerd/test/open_files_list_test.cpp
@@ -24,7 +24,7 @@
 
 #include "android-base/test_utils.h"
 
-#include "open_files_list.h"
+#include "libdebuggerd/open_files_list.h"
 
 // Check that we can produce a list of open files for the current process, and
 // that it includes a known open file.
diff --git a/debuggerd/libdebuggerd/test/property_fake.cpp b/debuggerd/libdebuggerd/test/property_fake.cpp
deleted file mode 100644
index 02069f1..0000000
--- a/debuggerd/libdebuggerd/test/property_fake.cpp
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright (C) 2015 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 <string.h>
-
-#include <string>
-#include <unordered_map>
-
-#include <sys/system_properties.h>
-
-std::unordered_map<std::string, std::string> g_properties;
-
-extern "C" int property_set(const char* name, const char* value) {
-  if (g_properties.count(name) != 0) {
-    g_properties.erase(name);
-  }
-  g_properties[name] = value;
-  return 0;
-}
-
-extern "C" int property_get(const char* key, char* value, const char* default_value) {
-  if (g_properties.count(key) == 0) {
-    if (default_value == nullptr) {
-      return 0;
-    }
-    strncpy(value, default_value, PROP_VALUE_MAX-1);
-  } else {
-    strncpy(value, g_properties[key].c_str(), PROP_VALUE_MAX-1);
-  }
-  value[PROP_VALUE_MAX-1] = '\0';
-  return strlen(value);
-}
diff --git a/debuggerd/libdebuggerd/test/ptrace_fake.cpp b/debuggerd/libdebuggerd/test/ptrace_fake.cpp
deleted file mode 100644
index f40cbd4..0000000
--- a/debuggerd/libdebuggerd/test/ptrace_fake.cpp
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright (C) 2015 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 <errno.h>
-#include <signal.h>
-#include <stdarg.h>
-#include <sys/ptrace.h>
-
-#include <string>
-
-#include "ptrace_fake.h"
-
-siginfo_t g_fake_si = {.si_signo = 0};
-
-void ptrace_set_fake_getsiginfo(const siginfo_t& si) {
-  g_fake_si = si;
-}
-
-#if !defined(__BIONIC__)
-extern "C" long ptrace_fake(enum __ptrace_request request, ...) {
-#else
-extern "C" long ptrace_fake(int request, ...) {
-#endif
-  if (request == PTRACE_GETSIGINFO) {
-    if (g_fake_si.si_signo == 0) {
-      errno = EFAULT;
-      return -1;
-    }
-
-    va_list ap;
-    va_start(ap, request);
-    va_arg(ap, int);
-    va_arg(ap, int);
-    siginfo_t* si = va_arg(ap, siginfo*);
-    va_end(ap);
-    *si = g_fake_si;
-    return 0;
-  }
-  return -1;
-}
diff --git a/debuggerd/libdebuggerd/test/ptrace_fake.h b/debuggerd/libdebuggerd/test/ptrace_fake.h
deleted file mode 100644
index fdbb663..0000000
--- a/debuggerd/libdebuggerd/test/ptrace_fake.h
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * Copyright (C) 2015 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 _DEBUGGERD_TEST_PTRACE_FAKE_H
-#define _DEBUGGERD_TEST_PTRACE_FAKE_H
-
-#include <signal.h>
-
-void ptrace_set_fake_getsiginfo(const siginfo_t&);
-
-#endif // _DEBUGGERD_TEST_PTRACE_FAKE_H
diff --git a/debuggerd/libdebuggerd/test/tombstone_test.cpp b/debuggerd/libdebuggerd/test/tombstone_test.cpp
index 325210d..421ce43 100644
--- a/debuggerd/libdebuggerd/test/tombstone_test.cpp
+++ b/debuggerd/libdebuggerd/test/tombstone_test.cpp
@@ -19,20 +19,16 @@
 #include <memory>
 #include <string>
 
-#include <gtest/gtest.h>
 #include <android-base/file.h>
+#include <android-base/properties.h>
+#include <gtest/gtest.h>
 
-#include "utility.h"
+#include "libdebuggerd/utility.h"
 
 #include "BacktraceMock.h"
 #include "elf_fake.h"
 #include "host_signal_fixup.h"
 #include "log_fake.h"
-#include "ptrace_fake.h"
-
-// In order to test this code, we need to include the tombstone.cpp code.
-// Including it, also allows us to override the ptrace function.
-#define ptrace ptrace_fake
 
 #include "tombstone.cpp"
 
@@ -49,7 +45,6 @@
  protected:
   virtual void SetUp() {
     map_mock_.reset(new BacktraceMapMock());
-    backtrace_mock_.reset(new BacktraceMock(map_mock_.get()));
 
     char tmp_file[256];
     const char data_template[] = "/data/local/tmp/debuggerd_memory_testXXXXXX";
@@ -76,11 +71,6 @@
 
     resetLogs();
     elf_set_fake_build_id("");
-    siginfo_t si;
-    memset(&si, 0, sizeof(si));
-    si.si_signo = SIGABRT;
-    si.si_code = SI_KERNEL;
-    ptrace_set_fake_getsiginfo(si);
   }
 
   virtual void TearDown() {
@@ -90,7 +80,6 @@
   }
 
   std::unique_ptr<BacktraceMapMock> map_mock_;
-  std::unique_ptr<BacktraceMock> backtrace_mock_;
 
   log_t log_;
   std::string amfd_data_;
@@ -107,13 +96,13 @@
 #endif
   map_mock_->AddMap(map);
 
-  dump_all_maps(backtrace_mock_.get(), map_mock_.get(), &log_, 100);
+  dump_all_maps(&log_, map_mock_.get(), nullptr, 0);
 
   std::string tombstone_contents;
   ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
   ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
   const char* expected_dump = \
-"\nmemory map:\n"
+"\nmemory map (1 entry):\n"
 #if defined(__LP64__)
 "    12345678'9abcd000-12345678'9abdefff ---         0     12000\n";
 #else
@@ -142,13 +131,13 @@
   map_mock_->AddMap(map);
 
   elf_set_fake_build_id("abcdef1234567890abcdef1234567890");
-  dump_all_maps(backtrace_mock_.get(), map_mock_.get(), &log_, 100);
+  dump_all_maps(&log_, map_mock_.get(), nullptr, 0);
 
   std::string tombstone_contents;
   ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
   ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
   const char* expected_dump = \
-"\nmemory map:\n"
+"\nmemory map (1 entry):\n"
 #if defined(__LP64__)
 "    12345678'9abcd000-12345678'9abdefff r--         0     12000  /system/lib/libfake.so (BuildId: abcdef1234567890abcdef1234567890)\n";
 #else
@@ -181,13 +170,13 @@
   map_mock_->AddMap(map);
 
   elf_set_fake_build_id("abcdef1234567890abcdef1234567890");
-  dump_all_maps(backtrace_mock_.get(), map_mock_.get(), &log_, 100);
+  dump_all_maps(&log_, map_mock_.get(), nullptr, 0);
 
   std::string tombstone_contents;
   ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
   ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
   const char* expected_dump = \
-"\nmemory map:\n"
+"\nmemory map (2 entries):\n"
 #if defined(__LP64__)
 "    12345678'9abcd000-12345678'9abdefff -w-         0     12000\n"
 "    12345678'9abcd000-12345678'9abdefff -w-         0     12000  /system/lib/libfake.so\n";
@@ -220,44 +209,44 @@
   map.start = 0xa434000;
   map.end = 0xa435000;
   map.offset = 0x1000;
-  map.load_base = 0xd000;
+  map.load_bias = 0xd000;
   map.flags = PROT_WRITE;
   map_mock_->AddMap(map);
 
   map.start = 0xa534000;
   map.end = 0xa535000;
   map.offset = 0x3000;
-  map.load_base = 0x2000;
+  map.load_bias = 0x2000;
   map.flags = PROT_EXEC;
   map_mock_->AddMap(map);
 
   map.start = 0xa634000;
   map.end = 0xa635000;
   map.offset = 0;
-  map.load_base = 0;
+  map.load_bias = 0;
   map.flags = PROT_READ | PROT_WRITE | PROT_EXEC;
   map.name = "/system/lib/fake.so";
   map_mock_->AddMap(map);
 
-  dump_all_maps(backtrace_mock_.get(), map_mock_.get(), &log_, 100);
+  dump_all_maps(&log_, map_mock_.get(), nullptr, 0);
 
   std::string tombstone_contents;
   ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
   ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
-  const char* expected_dump = \
-"\nmemory map:\n"
+  const char* expected_dump =
+      "\nmemory map (5 entries):\n"
 #if defined(__LP64__)
-"    00000000'0a234000-00000000'0a234fff ---         0      1000\n"
-"    00000000'0a334000-00000000'0a334fff r--      f000      1000\n"
-"    00000000'0a434000-00000000'0a434fff -w-      1000      1000  (load base 0xd000)\n"
-"    00000000'0a534000-00000000'0a534fff --x      3000      1000  (load base 0x2000)\n"
-"    00000000'0a634000-00000000'0a634fff rwx         0      1000  /system/lib/fake.so\n";
+      "    00000000'0a234000-00000000'0a234fff ---         0      1000\n"
+      "    00000000'0a334000-00000000'0a334fff r--      f000      1000\n"
+      "    00000000'0a434000-00000000'0a434fff -w-      1000      1000  (load bias 0xd000)\n"
+      "    00000000'0a534000-00000000'0a534fff --x      3000      1000  (load bias 0x2000)\n"
+      "    00000000'0a634000-00000000'0a634fff rwx         0      1000  /system/lib/fake.so\n";
 #else
-"    0a234000-0a234fff ---         0      1000\n"
-"    0a334000-0a334fff r--      f000      1000\n"
-"    0a434000-0a434fff -w-      1000      1000  (load base 0xd000)\n"
-"    0a534000-0a534fff --x      3000      1000  (load base 0x2000)\n"
-"    0a634000-0a634fff rwx         0      1000  /system/lib/fake.so\n";
+      "    0a234000-0a234fff ---         0      1000\n"
+      "    0a334000-0a334fff r--      f000      1000\n"
+      "    0a434000-0a434fff -w-      1000      1000  (load bias 0xd000)\n"
+      "    0a534000-0a534fff --x      3000      1000  (load bias 0x2000)\n"
+      "    0a634000-0a634fff rwx         0      1000  /system/lib/fake.so\n";
 #endif
   ASSERT_STREQ(expected_dump, tombstone_contents.c_str());
 
@@ -274,48 +263,42 @@
   map.start = 0xa434000;
   map.end = 0xa435000;
   map.offset = 0x1000;
-  map.load_base = 0xd000;
+  map.load_bias = 0xd000;
   map.flags = PROT_WRITE;
   map_mock_->AddMap(map);
 
   map.start = 0xa534000;
   map.end = 0xa535000;
   map.offset = 0x3000;
-  map.load_base = 0x2000;
+  map.load_bias = 0x2000;
   map.flags = PROT_EXEC;
   map_mock_->AddMap(map);
 
   map.start = 0xa634000;
   map.end = 0xa635000;
   map.offset = 0;
-  map.load_base = 0;
+  map.load_bias = 0;
   map.flags = PROT_READ | PROT_WRITE | PROT_EXEC;
   map.name = "/system/lib/fake.so";
   map_mock_->AddMap(map);
 
-  siginfo_t si;
-  memset(&si, 0, sizeof(si));
-  si.si_signo = SIGBUS;
-  si.si_code = SI_KERNEL;
-  si.si_addr = reinterpret_cast<void*>(0x1000);
-  ptrace_set_fake_getsiginfo(si);
-  dump_all_maps(backtrace_mock_.get(), map_mock_.get(), &log_, 100);
+  dump_all_maps(&log_, map_mock_.get(), nullptr, 0x1000);
 
   std::string tombstone_contents;
   ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
   ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
-  const char* expected_dump = \
-"\nmemory map: (fault address prefixed with --->)\n"
+  const char* expected_dump =
+      "\nmemory map (3 entries):\n"
 #if defined(__LP64__)
-"--->Fault address falls at 00000000'00001000 before any mapped regions\n"
-"    00000000'0a434000-00000000'0a434fff -w-      1000      1000  (load base 0xd000)\n"
-"    00000000'0a534000-00000000'0a534fff --x      3000      1000  (load base 0x2000)\n"
-"    00000000'0a634000-00000000'0a634fff rwx         0      1000  /system/lib/fake.so\n";
+      "--->Fault address falls at 00000000'00001000 before any mapped regions\n"
+      "    00000000'0a434000-00000000'0a434fff -w-      1000      1000  (load bias 0xd000)\n"
+      "    00000000'0a534000-00000000'0a534fff --x      3000      1000  (load bias 0x2000)\n"
+      "    00000000'0a634000-00000000'0a634fff rwx         0      1000  /system/lib/fake.so\n";
 #else
-"--->Fault address falls at 00001000 before any mapped regions\n"
-"    0a434000-0a434fff -w-      1000      1000  (load base 0xd000)\n"
-"    0a534000-0a534fff --x      3000      1000  (load base 0x2000)\n"
-"    0a634000-0a634fff rwx         0      1000  /system/lib/fake.so\n";
+      "--->Fault address falls at 00001000 before any mapped regions\n"
+      "    0a434000-0a434fff -w-      1000      1000  (load bias 0xd000)\n"
+      "    0a534000-0a534fff --x      3000      1000  (load bias 0x2000)\n"
+      "    0a634000-0a634fff rwx         0      1000  /system/lib/fake.so\n";
 #endif
   ASSERT_STREQ(expected_dump, tombstone_contents.c_str());
 
@@ -332,48 +315,42 @@
   map.start = 0xa434000;
   map.end = 0xa435000;
   map.offset = 0x1000;
-  map.load_base = 0xd000;
+  map.load_bias = 0xd000;
   map.flags = PROT_WRITE;
   map_mock_->AddMap(map);
 
   map.start = 0xa534000;
   map.end = 0xa535000;
   map.offset = 0x3000;
-  map.load_base = 0x2000;
+  map.load_bias = 0x2000;
   map.flags = PROT_EXEC;
   map_mock_->AddMap(map);
 
   map.start = 0xa634000;
   map.end = 0xa635000;
   map.offset = 0;
-  map.load_base = 0;
+  map.load_bias = 0;
   map.flags = PROT_READ | PROT_WRITE | PROT_EXEC;
   map.name = "/system/lib/fake.so";
   map_mock_->AddMap(map);
 
-  siginfo_t si;
-  memset(&si, 0, sizeof(si));
-  si.si_signo = SIGBUS;
-  si.si_code = SI_KERNEL;
-  si.si_addr = reinterpret_cast<void*>(0xa533000);
-  ptrace_set_fake_getsiginfo(si);
-  dump_all_maps(backtrace_mock_.get(), map_mock_.get(), &log_, 100);
+  dump_all_maps(&log_, map_mock_.get(), nullptr, 0xa533000);
 
   std::string tombstone_contents;
   ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
   ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
-  const char* expected_dump = \
-"\nmemory map: (fault address prefixed with --->)\n"
+  const char* expected_dump =
+      "\nmemory map (3 entries): (fault address prefixed with --->)\n"
 #if defined(__LP64__)
-"    00000000'0a434000-00000000'0a434fff -w-      1000      1000  (load base 0xd000)\n"
-"--->Fault address falls at 00000000'0a533000 between mapped regions\n"
-"    00000000'0a534000-00000000'0a534fff --x      3000      1000  (load base 0x2000)\n"
-"    00000000'0a634000-00000000'0a634fff rwx         0      1000  /system/lib/fake.so\n";
+      "    00000000'0a434000-00000000'0a434fff -w-      1000      1000  (load bias 0xd000)\n"
+      "--->Fault address falls at 00000000'0a533000 between mapped regions\n"
+      "    00000000'0a534000-00000000'0a534fff --x      3000      1000  (load bias 0x2000)\n"
+      "    00000000'0a634000-00000000'0a634fff rwx         0      1000  /system/lib/fake.so\n";
 #else
-"    0a434000-0a434fff -w-      1000      1000  (load base 0xd000)\n"
-"--->Fault address falls at 0a533000 between mapped regions\n"
-"    0a534000-0a534fff --x      3000      1000  (load base 0x2000)\n"
-"    0a634000-0a634fff rwx         0      1000  /system/lib/fake.so\n";
+      "    0a434000-0a434fff -w-      1000      1000  (load bias 0xd000)\n"
+      "--->Fault address falls at 0a533000 between mapped regions\n"
+      "    0a534000-0a534fff --x      3000      1000  (load bias 0x2000)\n"
+      "    0a634000-0a634fff rwx         0      1000  /system/lib/fake.so\n";
 #endif
   ASSERT_STREQ(expected_dump, tombstone_contents.c_str());
 
@@ -390,46 +367,40 @@
   map.start = 0xa434000;
   map.end = 0xa435000;
   map.offset = 0x1000;
-  map.load_base = 0xd000;
+  map.load_bias = 0xd000;
   map.flags = PROT_WRITE;
   map_mock_->AddMap(map);
 
   map.start = 0xa534000;
   map.end = 0xa535000;
   map.offset = 0x3000;
-  map.load_base = 0x2000;
+  map.load_bias = 0x2000;
   map.flags = PROT_EXEC;
   map_mock_->AddMap(map);
 
   map.start = 0xa634000;
   map.end = 0xa635000;
   map.offset = 0;
-  map.load_base = 0;
+  map.load_bias = 0;
   map.flags = PROT_READ | PROT_WRITE | PROT_EXEC;
   map.name = "/system/lib/fake.so";
   map_mock_->AddMap(map);
 
-  siginfo_t si;
-  memset(&si, 0, sizeof(si));
-  si.si_signo = SIGBUS;
-  si.si_code = SI_KERNEL;
-  si.si_addr = reinterpret_cast<void*>(0xa534040);
-  ptrace_set_fake_getsiginfo(si);
-  dump_all_maps(backtrace_mock_.get(), map_mock_.get(), &log_, 100);
+  dump_all_maps(&log_, map_mock_.get(), nullptr, 0xa534040);
 
   std::string tombstone_contents;
   ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
   ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
-  const char* expected_dump = \
-"\nmemory map: (fault address prefixed with --->)\n"
+  const char* expected_dump =
+      "\nmemory map (3 entries): (fault address prefixed with --->)\n"
 #if defined(__LP64__)
-"    00000000'0a434000-00000000'0a434fff -w-      1000      1000  (load base 0xd000)\n"
-"--->00000000'0a534000-00000000'0a534fff --x      3000      1000  (load base 0x2000)\n"
-"    00000000'0a634000-00000000'0a634fff rwx         0      1000  /system/lib/fake.so\n";
+      "    00000000'0a434000-00000000'0a434fff -w-      1000      1000  (load bias 0xd000)\n"
+      "--->00000000'0a534000-00000000'0a534fff --x      3000      1000  (load bias 0x2000)\n"
+      "    00000000'0a634000-00000000'0a634fff rwx         0      1000  /system/lib/fake.so\n";
 #else
-"    0a434000-0a434fff -w-      1000      1000  (load base 0xd000)\n"
-"--->0a534000-0a534fff --x      3000      1000  (load base 0x2000)\n"
-"    0a634000-0a634fff rwx         0      1000  /system/lib/fake.so\n";
+      "    0a434000-0a434fff -w-      1000      1000  (load bias 0xd000)\n"
+      "--->0a534000-0a534fff --x      3000      1000  (load bias 0x2000)\n"
+      "    0a634000-0a634fff rwx         0      1000  /system/lib/fake.so\n";
 #endif
   ASSERT_STREQ(expected_dump, tombstone_contents.c_str());
 
@@ -446,52 +417,47 @@
   map.start = 0xa434000;
   map.end = 0xa435000;
   map.offset = 0x1000;
-  map.load_base = 0xd000;
+  map.load_bias = 0xd000;
   map.flags = PROT_WRITE;
   map_mock_->AddMap(map);
 
   map.start = 0xa534000;
   map.end = 0xa535000;
   map.offset = 0x3000;
-  map.load_base = 0x2000;
+  map.load_bias = 0x2000;
   map.flags = PROT_EXEC;
   map_mock_->AddMap(map);
 
   map.start = 0xa634000;
   map.end = 0xa635000;
   map.offset = 0;
-  map.load_base = 0;
+  map.load_bias = 0;
   map.flags = PROT_READ | PROT_WRITE | PROT_EXEC;
   map.name = "/system/lib/fake.so";
   map_mock_->AddMap(map);
 
-  siginfo_t si;
-  memset(&si, 0, sizeof(si));
-  si.si_signo = SIGBUS;
-  si.si_code = SI_KERNEL;
 #if defined(__LP64__)
-  si.si_addr = reinterpret_cast<void*>(0x12345a534040UL);
+  uint64_t addr = 0x12345a534040UL;
 #else
-  si.si_addr = reinterpret_cast<void*>(0xf534040UL);
+  uint64_t addr = 0xf534040UL;
 #endif
-  ptrace_set_fake_getsiginfo(si);
-  dump_all_maps(backtrace_mock_.get(), map_mock_.get(), &log_, 100);
+  dump_all_maps(&log_, map_mock_.get(), nullptr, addr);
 
   std::string tombstone_contents;
   ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
   ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
-  const char* expected_dump = \
-"\nmemory map: (fault address prefixed with --->)\n"
+  const char* expected_dump =
+      "\nmemory map (3 entries): (fault address prefixed with --->)\n"
 #if defined(__LP64__)
-"    00000000'0a434000-00000000'0a434fff -w-      1000      1000  (load base 0xd000)\n"
-"    00000000'0a534000-00000000'0a534fff --x      3000      1000  (load base 0x2000)\n"
-"    00000000'0a634000-00000000'0a634fff rwx         0      1000  /system/lib/fake.so\n"
-"--->Fault address falls at 00001234'5a534040 after any mapped regions\n";
+      "    00000000'0a434000-00000000'0a434fff -w-      1000      1000  (load bias 0xd000)\n"
+      "    00000000'0a534000-00000000'0a534fff --x      3000      1000  (load bias 0x2000)\n"
+      "    00000000'0a634000-00000000'0a634fff rwx         0      1000  /system/lib/fake.so\n"
+      "--->Fault address falls at 00001234'5a534040 after any mapped regions\n";
 #else
-"    0a434000-0a434fff -w-      1000      1000  (load base 0xd000)\n"
-"    0a534000-0a534fff --x      3000      1000  (load base 0x2000)\n"
-"    0a634000-0a634fff rwx         0      1000  /system/lib/fake.so\n"
-"--->Fault address falls at 0f534040 after any mapped regions\n";
+      "    0a434000-0a434fff -w-      1000      1000  (load bias 0xd000)\n"
+      "    0a534000-0a534fff --x      3000      1000  (load bias 0x2000)\n"
+      "    0a634000-0a634fff rwx         0      1000  /system/lib/fake.so\n"
+      "--->Fault address falls at 0f534040 after any mapped regions\n";
 #endif
   ASSERT_STREQ(expected_dump, tombstone_contents.c_str());
 
@@ -502,124 +468,6 @@
   ASSERT_STREQ("", getFakeLogPrint().c_str());
 }
 
-TEST_F(TombstoneTest, multiple_maps_getsiginfo_fail) {
-  backtrace_map_t map;
-
-  map.start = 0xa434000;
-  map.end = 0xa435000;
-  map.offset = 0x1000;
-  map.load_base = 0xd000;
-  map.flags = PROT_WRITE;
-  map_mock_->AddMap(map);
-
-  siginfo_t si;
-  memset(&si, 0, sizeof(si));
-  ptrace_set_fake_getsiginfo(si);
-  dump_all_maps(backtrace_mock_.get(), map_mock_.get(), &log_, 100);
-
-  std::string tombstone_contents;
-  ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
-  ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
-  const char* expected_dump = \
-"\nmemory map:\n"
-#if defined(__LP64__)
-"    00000000'0a434000-00000000'0a434fff -w-      1000      1000  (load base 0xd000)\n";
-#else
-"    0a434000-0a434fff -w-      1000      1000  (load base 0xd000)\n";
-#endif
-  ASSERT_STREQ(expected_dump, tombstone_contents.c_str());
-
-  ASSERT_STREQ("", amfd_data_.c_str());
-
-  // Verify that the log buf is empty, and no error messages.
-  ASSERT_STREQ("", getFakeLogBuf().c_str());
-  ASSERT_STREQ("6 DEBUG Cannot get siginfo for 100: Bad address\n\n", getFakeLogPrint().c_str());
-}
-
-TEST_F(TombstoneTest, multiple_maps_check_signal_has_si_addr) {
-  backtrace_map_t map;
-
-  map.start = 0xa434000;
-  map.end = 0xa435000;
-  map.flags = PROT_WRITE;
-  map_mock_->AddMap(map);
-
-  for (int i = 1; i < 255; i++) {
-    ASSERT_TRUE(ftruncate(log_.tfd, 0) == 0);
-    ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
-
-    siginfo_t si;
-    memset(&si, 0, sizeof(si));
-    si.si_signo = i;
-    si.si_code = SI_KERNEL;
-    si.si_addr = reinterpret_cast<void*>(0x1000);
-    ptrace_set_fake_getsiginfo(si);
-    dump_all_maps(backtrace_mock_.get(), map_mock_.get(), &log_, 100);
-
-    std::string tombstone_contents;
-    ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
-    ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
-    bool has_addr = false;
-    switch (si.si_signo) {
-    case SIGBUS:
-    case SIGFPE:
-    case SIGILL:
-    case SIGSEGV:
-    case SIGTRAP:
-      has_addr = true;
-      break;
-    }
-
-    const char* expected_addr_dump = \
-"\nmemory map: (fault address prefixed with --->)\n"
-#if defined(__LP64__)
-"--->Fault address falls at 00000000'00001000 before any mapped regions\n"
-"    00000000'0a434000-00000000'0a434fff -w-         0      1000\n";
-#else
-"--->Fault address falls at 00001000 before any mapped regions\n"
-"    0a434000-0a434fff -w-         0      1000\n";
-#endif
-    const char* expected_dump = \
-"\nmemory map:\n"
-#if defined(__LP64__)
-"    00000000'0a434000-00000000'0a434fff -w-         0      1000\n";
-#else
-"    0a434000-0a434fff -w-         0      1000\n";
-#endif
-    if (has_addr) {
-      ASSERT_STREQ(expected_addr_dump, tombstone_contents.c_str())
-        << "Signal " << si.si_signo << " expected to include an address.";
-    } else {
-      ASSERT_STREQ(expected_dump, tombstone_contents.c_str())
-        << "Signal " << si.si_signo << " is not expected to include an address.";
-    }
-
-    ASSERT_STREQ("", amfd_data_.c_str());
-
-    // Verify that the log buf is empty, and no error messages.
-    ASSERT_STREQ("", getFakeLogBuf().c_str());
-    ASSERT_STREQ("", getFakeLogPrint().c_str());
-  }
-}
-
-TEST_F(TombstoneTest, dump_signal_info_error) {
-  siginfo_t si;
-  memset(&si, 0, sizeof(si));
-  ptrace_set_fake_getsiginfo(si);
-
-  dump_signal_info(&log_, 123);
-
-  std::string tombstone_contents;
-  ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
-  ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
-  ASSERT_STREQ("", tombstone_contents.c_str());
-
-  ASSERT_STREQ("", getFakeLogBuf().c_str());
-  ASSERT_STREQ("6 DEBUG cannot get siginfo: Bad address\n\n", getFakeLogPrint().c_str());
-
-  ASSERT_STREQ("", amfd_data_.c_str());
-}
-
 TEST_F(TombstoneTest, dump_log_file_error) {
   log_.should_retrieve_logcat = true;
   dump_log_file(&log_, 123, "/fake/filename", 10);
@@ -639,7 +487,10 @@
 TEST_F(TombstoneTest, dump_header_info) {
   dump_header_info(&log_);
 
-  std::string expected = "Build fingerprint: 'unknown'\nRevision: 'unknown'\n";
+  std::string expected = android::base::StringPrintf(
+      "Build fingerprint: '%s'\nRevision: '%s'\n",
+      android::base::GetProperty("ro.build.fingerprint", "unknown").c_str(),
+      android::base::GetProperty("ro.revision", "unknown").c_str());
   expected += android::base::StringPrintf("ABI: '%s'\n", ABI_STRING);
   ASSERT_STREQ(expected.c_str(), amfd_data_.c_str());
 }
diff --git a/debuggerd/libdebuggerd/tombstone.cpp b/debuggerd/libdebuggerd/tombstone.cpp
index 996d714..0b8a936 100644
--- a/debuggerd/libdebuggerd/tombstone.cpp
+++ b/debuggerd/libdebuggerd/tombstone.cpp
@@ -16,6 +16,8 @@
 
 #define LOG_TAG "DEBUG"
 
+#include "libdebuggerd/tombstone.h"
+
 #include <dirent.h>
 #include <errno.h>
 #include <fcntl.h>
@@ -33,259 +35,120 @@
 #include <string>
 
 #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>
 #include <android/log.h>
 #include <backtrace/Backtrace.h>
 #include <backtrace/BacktraceMap.h>
-#include <cutils/properties.h>
 #include <log/log.h>
 #include <log/logprint.h>
 #include <private/android_filesystem_config.h>
+#include <unwindstack/Memory.h>
+#include <unwindstack/Regs.h>
 
+// Needed to get DEBUGGER_SIGNAL.
 #include "debuggerd/handler.h"
 
-#include "backtrace.h"
-#include "elf_utils.h"
-#include "machine.h"
-#include "open_files_list.h"
-#include "tombstone.h"
+#include "libdebuggerd/backtrace.h"
+#include "libdebuggerd/elf_utils.h"
+#include "libdebuggerd/open_files_list.h"
+#include "libdebuggerd/utility.h"
 
+using android::base::GetBoolProperty;
+using android::base::GetProperty;
 using android::base::StringPrintf;
+using android::base::unique_fd;
+
+using unwindstack::Memory;
+using unwindstack::Regs;
+
+using namespace std::literals::string_literals;
 
 #define STACK_WORDS 16
 
-#define MAX_TOMBSTONES  10
-#define TOMBSTONE_DIR   "/data/tombstones"
-#define TOMBSTONE_TEMPLATE (TOMBSTONE_DIR"/tombstone_%02d")
-
-static bool signal_has_si_addr(int si_signo, int si_code) {
-  // Manually sent signals won't have si_addr.
-  if (si_code == SI_USER || si_code == SI_QUEUE || si_code == SI_TKILL) {
-    return false;
-  }
-
-  switch (si_signo) {
-    case SIGBUS:
-    case SIGFPE:
-    case SIGILL:
-    case SIGSEGV:
-    case SIGTRAP:
-      return true;
-    default:
-      return false;
-  }
-}
-
-static const char* get_signame(int sig) {
-  switch (sig) {
-    case SIGABRT: return "SIGABRT";
-    case SIGBUS: return "SIGBUS";
-    case SIGFPE: return "SIGFPE";
-    case SIGILL: return "SIGILL";
-    case SIGSEGV: return "SIGSEGV";
-#if defined(SIGSTKFLT)
-    case SIGSTKFLT: return "SIGSTKFLT";
-#endif
-    case SIGSTOP: return "SIGSTOP";
-    case SIGSYS: return "SIGSYS";
-    case SIGTRAP: return "SIGTRAP";
-    case DEBUGGER_SIGNAL: return "<debuggerd signal>";
-    default: return "?";
-  }
-}
-
-static const char* get_sigcode(int signo, int code) {
-  // Try the signal-specific codes...
-  switch (signo) {
-    case SIGILL:
-      switch (code) {
-        case ILL_ILLOPC: return "ILL_ILLOPC";
-        case ILL_ILLOPN: return "ILL_ILLOPN";
-        case ILL_ILLADR: return "ILL_ILLADR";
-        case ILL_ILLTRP: return "ILL_ILLTRP";
-        case ILL_PRVOPC: return "ILL_PRVOPC";
-        case ILL_PRVREG: return "ILL_PRVREG";
-        case ILL_COPROC: return "ILL_COPROC";
-        case ILL_BADSTK: return "ILL_BADSTK";
-      }
-      static_assert(NSIGILL == ILL_BADSTK, "missing ILL_* si_code");
-      break;
-    case SIGBUS:
-      switch (code) {
-        case BUS_ADRALN: return "BUS_ADRALN";
-        case BUS_ADRERR: return "BUS_ADRERR";
-        case BUS_OBJERR: return "BUS_OBJERR";
-        case BUS_MCEERR_AR: return "BUS_MCEERR_AR";
-        case BUS_MCEERR_AO: return "BUS_MCEERR_AO";
-      }
-      static_assert(NSIGBUS == BUS_MCEERR_AO, "missing BUS_* si_code");
-      break;
-    case SIGFPE:
-      switch (code) {
-        case FPE_INTDIV: return "FPE_INTDIV";
-        case FPE_INTOVF: return "FPE_INTOVF";
-        case FPE_FLTDIV: return "FPE_FLTDIV";
-        case FPE_FLTOVF: return "FPE_FLTOVF";
-        case FPE_FLTUND: return "FPE_FLTUND";
-        case FPE_FLTRES: return "FPE_FLTRES";
-        case FPE_FLTINV: return "FPE_FLTINV";
-        case FPE_FLTSUB: return "FPE_FLTSUB";
-      }
-      static_assert(NSIGFPE == FPE_FLTSUB, "missing FPE_* si_code");
-      break;
-    case SIGSEGV:
-      switch (code) {
-        case SEGV_MAPERR: return "SEGV_MAPERR";
-        case SEGV_ACCERR: return "SEGV_ACCERR";
-#if defined(SEGV_BNDERR)
-        case SEGV_BNDERR: return "SEGV_BNDERR";
-#endif
-#if defined(SEGV_PKUERR)
-        case SEGV_PKUERR: return "SEGV_PKUERR";
-#endif
-      }
-#if defined(SEGV_PKUERR)
-      static_assert(NSIGSEGV == SEGV_PKUERR, "missing SEGV_* si_code");
-#elif defined(SEGV_BNDERR)
-      static_assert(NSIGSEGV == SEGV_BNDERR, "missing SEGV_* si_code");
-#else
-      static_assert(NSIGSEGV == SEGV_ACCERR, "missing SEGV_* si_code");
-#endif
-      break;
-#if defined(SYS_SECCOMP) // Our glibc is too old, and we build this for the host too.
-    case SIGSYS:
-      switch (code) {
-        case SYS_SECCOMP: return "SYS_SECCOMP";
-      }
-      static_assert(NSIGSYS == SYS_SECCOMP, "missing SYS_* si_code");
-      break;
-#endif
-    case SIGTRAP:
-      switch (code) {
-        case TRAP_BRKPT: return "TRAP_BRKPT";
-        case TRAP_TRACE: return "TRAP_TRACE";
-        case TRAP_BRANCH: return "TRAP_BRANCH";
-        case TRAP_HWBKPT: return "TRAP_HWBKPT";
-      }
-      if ((code & 0xff) == SIGTRAP) {
-        switch ((code >> 8) & 0xff) {
-          case PTRACE_EVENT_FORK:
-            return "PTRACE_EVENT_FORK";
-          case PTRACE_EVENT_VFORK:
-            return "PTRACE_EVENT_VFORK";
-          case PTRACE_EVENT_CLONE:
-            return "PTRACE_EVENT_CLONE";
-          case PTRACE_EVENT_EXEC:
-            return "PTRACE_EVENT_EXEC";
-          case PTRACE_EVENT_VFORK_DONE:
-            return "PTRACE_EVENT_VFORK_DONE";
-          case PTRACE_EVENT_EXIT:
-            return "PTRACE_EVENT_EXIT";
-          case PTRACE_EVENT_SECCOMP:
-            return "PTRACE_EVENT_SECCOMP";
-          case PTRACE_EVENT_STOP:
-            return "PTRACE_EVENT_STOP";
-        }
-      }
-      static_assert(NSIGTRAP == TRAP_HWBKPT, "missing TRAP_* si_code");
-      break;
-  }
-  // Then the other codes...
-  switch (code) {
-    case SI_USER: return "SI_USER";
-    case SI_KERNEL: return "SI_KERNEL";
-    case SI_QUEUE: return "SI_QUEUE";
-    case SI_TIMER: return "SI_TIMER";
-    case SI_MESGQ: return "SI_MESGQ";
-    case SI_ASYNCIO: return "SI_ASYNCIO";
-    case SI_SIGIO: return "SI_SIGIO";
-    case SI_TKILL: return "SI_TKILL";
-    case SI_DETHREAD: return "SI_DETHREAD";
-  }
-  // Then give up...
-  return "?";
-}
-
 static void dump_header_info(log_t* log) {
-  char fingerprint[PROPERTY_VALUE_MAX];
-  char revision[PROPERTY_VALUE_MAX];
+  auto fingerprint = GetProperty("ro.build.fingerprint", "unknown");
+  auto revision = GetProperty("ro.revision", "unknown");
 
-  property_get("ro.build.fingerprint", fingerprint, "unknown");
-  property_get("ro.revision", revision, "unknown");
-
-  _LOG(log, logtype::HEADER, "Build fingerprint: '%s'\n", fingerprint);
-  _LOG(log, logtype::HEADER, "Revision: '%s'\n", revision);
+  _LOG(log, logtype::HEADER, "Build fingerprint: '%s'\n", fingerprint.c_str());
+  _LOG(log, logtype::HEADER, "Revision: '%s'\n", revision.c_str());
   _LOG(log, logtype::HEADER, "ABI: '%s'\n", ABI_STRING);
 }
 
-static void dump_probable_cause(log_t* log, const siginfo_t& si) {
+static void dump_probable_cause(log_t* log, const siginfo_t* si) {
   std::string cause;
-  if (si.si_signo == SIGSEGV && si.si_code == SEGV_MAPERR) {
-    if (si.si_addr < reinterpret_cast<void*>(4096)) {
+  if (si->si_signo == SIGSEGV && si->si_code == SEGV_MAPERR) {
+    if (si->si_addr < reinterpret_cast<void*>(4096)) {
       cause = StringPrintf("null pointer dereference");
-    } else if (si.si_addr == reinterpret_cast<void*>(0xffff0ffc)) {
+    } else if (si->si_addr == reinterpret_cast<void*>(0xffff0ffc)) {
       cause = "call to kuser_helper_version";
-    } else if (si.si_addr == reinterpret_cast<void*>(0xffff0fe0)) {
+    } else if (si->si_addr == reinterpret_cast<void*>(0xffff0fe0)) {
       cause = "call to kuser_get_tls";
-    } else if (si.si_addr == reinterpret_cast<void*>(0xffff0fc0)) {
+    } else if (si->si_addr == reinterpret_cast<void*>(0xffff0fc0)) {
       cause = "call to kuser_cmpxchg";
-    } else if (si.si_addr == reinterpret_cast<void*>(0xffff0fa0)) {
+    } else if (si->si_addr == reinterpret_cast<void*>(0xffff0fa0)) {
       cause = "call to kuser_memory_barrier";
-    } else if (si.si_addr == reinterpret_cast<void*>(0xffff0f60)) {
+    } else if (si->si_addr == reinterpret_cast<void*>(0xffff0f60)) {
       cause = "call to kuser_cmpxchg64";
     }
-  } else if (si.si_signo == SIGSYS && si.si_code == SYS_SECCOMP) {
-    cause = StringPrintf("seccomp prevented call to disallowed %s system call %d",
-                         ABI_STRING, si.si_syscall);
+  } else if (si->si_signo == SIGSYS && si->si_code == SYS_SECCOMP) {
+    cause = StringPrintf("seccomp prevented call to disallowed %s system call %d", ABI_STRING,
+                         si->si_syscall);
   }
 
   if (!cause.empty()) _LOG(log, logtype::HEADER, "Cause: %s\n", cause.c_str());
 }
 
-static void dump_signal_info(log_t* log, const siginfo_t* siginfo) {
-  const siginfo_t& si = *siginfo;
-  char addr_desc[32]; // ", fault addr 0x1234"
-  if (signal_has_si_addr(si.si_signo, si.si_code)) {
-    snprintf(addr_desc, sizeof(addr_desc), "%p", si.si_addr);
+static void dump_signal_info(log_t* log, const ThreadInfo& thread_info, Memory* process_memory) {
+  char addr_desc[64];  // ", fault addr 0x1234"
+  if (signal_has_si_addr(thread_info.siginfo)) {
+    void* addr = thread_info.siginfo->si_addr;
+    if (thread_info.siginfo->si_signo == SIGILL) {
+      uint32_t instruction = {};
+      process_memory->Read(reinterpret_cast<uint64_t>(addr), &instruction, sizeof(instruction));
+      snprintf(addr_desc, sizeof(addr_desc), "%p (*pc=%#08x)", addr, instruction);
+    } else {
+      snprintf(addr_desc, sizeof(addr_desc), "%p", addr);
+    }
   } else {
     snprintf(addr_desc, sizeof(addr_desc), "--------");
   }
 
-  _LOG(log, logtype::HEADER, "signal %d (%s), code %d (%s), fault addr %s\n", si.si_signo,
-       get_signame(si.si_signo), si.si_code, get_sigcode(si.si_signo, si.si_code), addr_desc);
-
-  dump_probable_cause(log, si);
-}
-
-static void dump_signal_info(log_t* log, pid_t tid) {
-  siginfo_t si;
-  memset(&si, 0, sizeof(si));
-  if (ptrace(PTRACE_GETSIGINFO, tid, 0, &si) == -1) {
-    ALOGE("cannot get siginfo: %s\n", strerror(errno));
-    return;
+  char sender_desc[32] = {};  // " from pid 1234, uid 666"
+  if (signal_has_sender(thread_info.siginfo, thread_info.pid)) {
+    get_signal_sender(sender_desc, sizeof(sender_desc), thread_info.siginfo);
   }
 
-  dump_signal_info(log, &si);
+  _LOG(log, logtype::HEADER, "signal %d (%s), code %d (%s%s), fault addr %s\n",
+       thread_info.siginfo->si_signo, get_signame(thread_info.siginfo),
+       thread_info.siginfo->si_code, get_sigcode(thread_info.siginfo), sender_desc, addr_desc);
+
+  dump_probable_cause(log, thread_info.siginfo);
 }
 
-static void dump_thread_info(log_t* log, pid_t pid, pid_t tid, const char* process_name,
-                             const char* thread_name) {
+static void dump_thread_info(log_t* log, const ThreadInfo& thread_info) {
   // Blacklist logd, logd.reader, logd.writer, logd.auditd, logd.control ...
   // TODO: Why is this controlled by thread name?
-  if (strcmp(thread_name, "logd") == 0 || strncmp(thread_name, "logd.", 4) == 0) {
+  if (thread_info.thread_name == "logd" ||
+      android::base::StartsWith(thread_info.thread_name, "logd.")) {
     log->should_retrieve_logcat = false;
   }
 
-  _LOG(log, logtype::HEADER, "pid: %d, tid: %d, name: %s  >>> %s <<<\n", pid, tid, thread_name,
-       process_name);
+  _LOG(log, logtype::HEADER, "pid: %d, tid: %d, name: %s  >>> %s <<<\n", thread_info.pid,
+       thread_info.tid, thread_info.thread_name.c_str(), thread_info.process_name.c_str());
 }
 
-static void dump_stack_segment(
-    Backtrace* backtrace, log_t* log, uintptr_t* sp, size_t words, int label) {
+static void dump_stack_segment(log_t* log, BacktraceMap* backtrace_map, Memory* process_memory,
+                               uint64_t* sp, size_t words, int label) {
   // Read the data all at once.
   word_t stack_data[words];
-  size_t bytes_read = backtrace->Read(*sp, reinterpret_cast<uint8_t*>(&stack_data[0]), sizeof(word_t) * words);
+
+  // TODO: Do we need to word align this for crashes caused by a misaligned sp?
+  //       The process_vm_readv implementation of Memory should handle this appropriately?
+  size_t bytes_read = process_memory->Read(*sp, stack_data, sizeof(word_t) * words);
   words = bytes_read / sizeof(word_t);
   std::string line;
   for (size_t i = 0; i < words; i++) {
@@ -296,18 +159,19 @@
     } else {
       line += "     ";
     }
-    line += StringPrintf("%" PRIPTR "  %" PRIPTR, *sp, stack_data[i]);
+    line += StringPrintf("%" PRIPTR "  %" PRIPTR, *sp, static_cast<uint64_t>(stack_data[i]));
 
     backtrace_map_t map;
-    backtrace->FillInMap(stack_data[i], &map);
-    if (BacktraceMap::IsValid(map) && !map.name.empty()) {
-      line += "  " + map.name;
-      uintptr_t offset = 0;
-      std::string func_name(backtrace->GetFunctionName(stack_data[i], &offset, &map));
+    backtrace_map->FillIn(stack_data[i], &map);
+    std::string map_name{map.Name()};
+    if (BacktraceMap::IsValid(map) && !map_name.empty()) {
+      line += "  " + map_name;
+      uint64_t offset = 0;
+      std::string func_name = backtrace_map->GetFunctionName(stack_data[i], &offset);
       if (!func_name.empty()) {
         line += " (" + func_name;
         if (offset) {
-          line += StringPrintf("+%" PRIuPTR, offset);
+          line += StringPrintf("+%" PRIu64, offset);
         }
         line += ')';
       }
@@ -318,36 +182,38 @@
   }
 }
 
-static void dump_stack(Backtrace* backtrace, log_t* log) {
+static void dump_stack(log_t* log, BacktraceMap* backtrace_map, Memory* process_memory,
+                       std::vector<backtrace_frame_data_t>& frames) {
   size_t first = 0, last;
-  for (size_t i = 0; i < backtrace->NumFrames(); i++) {
-    const backtrace_frame_data_t* frame = backtrace->GetFrame(i);
-    if (frame->sp) {
+  for (size_t i = 0; i < frames.size(); i++) {
+    const backtrace_frame_data_t& frame = frames[i];
+    if (frame.sp) {
       if (!first) {
         first = i+1;
       }
       last = i;
     }
   }
+
   if (!first) {
     return;
   }
   first--;
 
   // Dump a few words before the first frame.
-  word_t sp = backtrace->GetFrame(first)->sp - STACK_WORDS * sizeof(word_t);
-  dump_stack_segment(backtrace, log, &sp, STACK_WORDS, -1);
+  uint64_t sp = frames[first].sp - STACK_WORDS * sizeof(word_t);
+  dump_stack_segment(log, backtrace_map, process_memory, &sp, STACK_WORDS, -1);
 
   // Dump a few words from all successive frames.
   // Only log the first 3 frames, put the rest in the tombstone.
   for (size_t i = first; i <= last; i++) {
-    const backtrace_frame_data_t* frame = backtrace->GetFrame(i);
+    const backtrace_frame_data_t* frame = &frames[i];
     if (sp != frame->sp) {
       _LOG(log, logtype::STACK, "         ........  ........\n");
       sp = frame->sp;
     }
     if (i == last) {
-      dump_stack_segment(backtrace, log, &sp, STACK_WORDS, i);
+      dump_stack_segment(log, backtrace_map, process_memory, &sp, STACK_WORDS, i);
       if (sp < frame->sp + frame->stack_size) {
         _LOG(log, logtype::STACK, "         ........  ........\n");
       }
@@ -358,119 +224,117 @@
       } else if (words > STACK_WORDS) {
         words = STACK_WORDS;
       }
-      dump_stack_segment(backtrace, log, &sp, words, i);
+      dump_stack_segment(log, backtrace_map, process_memory, &sp, words, i);
     }
   }
 }
 
-static std::string get_addr_string(uintptr_t addr) {
+static std::string get_addr_string(uint64_t addr) {
   std::string addr_str;
 #if defined(__LP64__)
   addr_str = StringPrintf("%08x'%08x",
                           static_cast<uint32_t>(addr >> 32),
                           static_cast<uint32_t>(addr & 0xffffffff));
 #else
-  addr_str = StringPrintf("%08x", addr);
+  addr_str = StringPrintf("%08x", static_cast<uint32_t>(addr));
 #endif
   return addr_str;
 }
 
-static void dump_abort_message(Backtrace* backtrace, log_t* log, uintptr_t address) {
+static void dump_abort_message(log_t* log, Memory* process_memory, uint64_t address) {
   if (address == 0) {
     return;
   }
 
-  address += sizeof(size_t);  // Skip the buffer length.
-
-  char msg[512];
-  memset(msg, 0, sizeof(msg));
-  char* p = &msg[0];
-  while (p < &msg[sizeof(msg)]) {
-    word_t data;
-    size_t len = sizeof(word_t);
-    if (!backtrace->ReadWord(address, &data)) {
-      break;
-    }
-    address += sizeof(word_t);
-
-    while (len > 0 && (*p++ = (data >> (sizeof(word_t) - len) * 8) & 0xff) != 0) {
-      len--;
-    }
+  size_t length;
+  if (!process_memory->ReadFully(address, &length, sizeof(length))) {
+    _LOG(log, logtype::HEADER, "Failed to read abort message header: %s\n", strerror(errno));
+    return;
   }
-  msg[sizeof(msg) - 1] = '\0';
 
-  _LOG(log, logtype::HEADER, "Abort message: '%s'\n", msg);
+  // The length field includes the length of the length field itself.
+  if (length < sizeof(size_t)) {
+    _LOG(log, logtype::HEADER, "Abort message header malformed: claimed length = %zd\n", length);
+    return;
+  }
+
+  length -= sizeof(size_t);
+
+  // The abort message should be null terminated already, but reserve a spot for NUL just in case.
+  std::vector<char> msg(length + 1);
+  if (!process_memory->ReadFully(address + sizeof(length), &msg[0], length)) {
+    _LOG(log, logtype::HEADER, "Failed to read abort message: %s\n", strerror(errno));
+    return;
+  }
+
+  _LOG(log, logtype::HEADER, "Abort message: '%s'\n", &msg[0]);
 }
 
-static void dump_all_maps(Backtrace* backtrace, BacktraceMap* map, log_t* log, pid_t tid) {
-  bool print_fault_address_marker = false;
-  uintptr_t addr = 0;
-  siginfo_t si;
-  memset(&si, 0, sizeof(si));
-  if (ptrace(PTRACE_GETSIGINFO, tid, 0, &si) != -1) {
-    print_fault_address_marker = signal_has_si_addr(si.si_signo, si.si_code);
-    addr = reinterpret_cast<uintptr_t>(si.si_addr);
-  } else {
-    ALOGE("Cannot get siginfo for %d: %s\n", tid, strerror(errno));
-  }
+static void dump_all_maps(log_t* log, BacktraceMap* map, Memory* process_memory, uint64_t addr) {
+  bool print_fault_address_marker = addr;
 
   ScopedBacktraceMapIteratorLock lock(map);
-  _LOG(log, logtype::MAPS, "\n");
-  if (!print_fault_address_marker) {
-    _LOG(log, logtype::MAPS, "memory map:\n");
-  } else {
-    _LOG(log, logtype::MAPS, "memory map: (fault address prefixed with --->)\n");
-    if (map->begin() != map->end() && addr < map->begin()->start) {
-      _LOG(log, logtype::MAPS, "--->Fault address falls at %s before any mapped regions\n",
+  _LOG(log, logtype::MAPS,
+       "\n"
+       "memory map (%zu entr%s):",
+       map->size(), map->size() == 1 ? "y" : "ies");
+  if (print_fault_address_marker) {
+    if (map->begin() != map->end() && addr < (*map->begin())->start) {
+      _LOG(log, logtype::MAPS, "\n--->Fault address falls at %s before any mapped regions\n",
            get_addr_string(addr).c_str());
       print_fault_address_marker = false;
+    } else {
+      _LOG(log, logtype::MAPS, " (fault address prefixed with --->)\n");
     }
+  } else {
+    _LOG(log, logtype::MAPS, "\n");
   }
 
   std::string line;
-  for (BacktraceMap::const_iterator it = map->begin(); it != map->end(); ++it) {
+  for (auto it = map->begin(); it != map->end(); ++it) {
+    const backtrace_map_t* entry = *it;
     line = "    ";
     if (print_fault_address_marker) {
-      if (addr < it->start) {
+      if (addr < entry->start) {
         _LOG(log, logtype::MAPS, "--->Fault address falls at %s between mapped regions\n",
              get_addr_string(addr).c_str());
         print_fault_address_marker = false;
-      } else if (addr >= it->start && addr < it->end) {
+      } else if (addr >= entry->start && addr < entry->end) {
         line = "--->";
         print_fault_address_marker = false;
       }
     }
-    line += get_addr_string(it->start) + '-' + get_addr_string(it->end - 1) + ' ';
-    if (it->flags & PROT_READ) {
+    line += get_addr_string(entry->start) + '-' + get_addr_string(entry->end - 1) + ' ';
+    if (entry->flags & PROT_READ) {
       line += 'r';
     } else {
       line += '-';
     }
-    if (it->flags & PROT_WRITE) {
+    if (entry->flags & PROT_WRITE) {
       line += 'w';
     } else {
       line += '-';
     }
-    if (it->flags & PROT_EXEC) {
+    if (entry->flags & PROT_EXEC) {
       line += 'x';
     } else {
       line += '-';
     }
-    line += StringPrintf("  %8" PRIxPTR "  %8" PRIxPTR, it->offset, it->end - it->start);
+    line += StringPrintf("  %8" PRIx64 "  %8" PRIx64, entry->offset, entry->end - entry->start);
     bool space_needed = true;
-    if (it->name.length() > 0) {
+    if (entry->name.length() > 0) {
       space_needed = false;
-      line += "  " + it->name;
+      line += "  " + entry->name;
       std::string build_id;
-      if ((it->flags & PROT_READ) && elf_get_build_id(backtrace, it->start, &build_id)) {
+      if ((entry->flags & PROT_READ) && elf_get_build_id(process_memory, entry->start, &build_id)) {
         line += " (BuildId: " + build_id + ")";
       }
     }
-    if (it->load_base != 0) {
+    if (entry->load_bias != 0) {
       if (space_needed) {
         line += ' ';
       }
-      line += StringPrintf(" (load base 0x%" PRIxPTR ")", it->load_base);
+      line += StringPrintf(" (load bias 0x%" PRIx64 ")", entry->load_bias);
     }
     _LOG(log, logtype::MAPS, "%s\n", line.c_str());
   }
@@ -480,50 +344,126 @@
   }
 }
 
-static void dump_backtrace_and_stack(Backtrace* backtrace, log_t* log) {
-  if (backtrace->NumFrames()) {
-    _LOG(log, logtype::BACKTRACE, "\nbacktrace:\n");
-    dump_backtrace_to_log(backtrace, log, "    ");
-
-    _LOG(log, logtype::STACK, "\nstack:\n");
-    dump_stack(backtrace, log);
+void dump_backtrace(log_t* log, std::vector<backtrace_frame_data_t>& frames, const char* prefix) {
+  for (auto& frame : frames) {
+    _LOG(log, logtype::BACKTRACE, "%s%s\n", prefix, Backtrace::FormatFrameData(&frame).c_str());
   }
 }
 
-// Weak noop implementation, real implementations are in <arch>/machine.cpp.
-__attribute__((weak)) void dump_registers(log_t* log, const ucontext_t*) {
-  _LOG(log, logtype::REGISTERS, "    register dumping unimplemented on this architecture");
+static void print_register_row(log_t* log,
+                               const std::vector<std::pair<std::string, uint64_t>>& registers) {
+  std::string output;
+  for (auto& [name, value] : registers) {
+    output += android::base::StringPrintf("  %-3s %0*" PRIx64, name.c_str(),
+                                          static_cast<int>(2 * sizeof(void*)),
+                                          static_cast<uint64_t>(value));
+  }
+
+  _LOG(log, logtype::REGISTERS, "  %s\n", output.c_str());
 }
 
-static void dump_thread(log_t* log, pid_t pid, pid_t tid, const std::string& process_name,
-                        const std::string& thread_name, BacktraceMap* map,
-                        uintptr_t abort_msg_address, bool primary_thread) {
-  log->current_tid = tid;
+void dump_registers(log_t* log, Regs* regs) {
+  // Split lr/sp/pc into their own special row.
+  static constexpr size_t column_count = 4;
+  std::vector<std::pair<std::string, uint64_t>> current_row;
+  std::vector<std::pair<std::string, uint64_t>> special_row;
+
+#if defined(__arm__) || defined(__aarch64__)
+  static constexpr const char* special_registers[] = {"ip", "lr", "sp", "pc"};
+#elif defined(__i386__)
+  static constexpr const char* special_registers[] = {"ebp", "esp", "eip"};
+#elif defined(__x86_64__)
+  static constexpr const char* special_registers[] = {"rbp", "rsp", "rip"};
+#else
+  static constexpr const char* special_registers[] = {};
+#endif
+
+  regs->IterateRegisters([log, &current_row, &special_row](const char* name, uint64_t value) {
+    auto row = &current_row;
+    for (const char* special_name : special_registers) {
+      if (strcmp(special_name, name) == 0) {
+        row = &special_row;
+        break;
+      }
+    }
+
+    row->emplace_back(name, value);
+    if (current_row.size() == column_count) {
+      print_register_row(log, current_row);
+      current_row.clear();
+    }
+  });
+
+  if (!current_row.empty()) {
+    print_register_row(log, current_row);
+  }
+
+  print_register_row(log, special_row);
+}
+
+void dump_memory_and_code(log_t* log, BacktraceMap* map, Memory* memory, Regs* regs) {
+  regs->IterateRegisters([log, map, memory](const char* reg_name, uint64_t reg_value) {
+    std::string label{"memory near "s + reg_name};
+    if (map) {
+      backtrace_map_t map_info;
+      map->FillIn(reg_value, &map_info);
+      std::string map_name{map_info.Name()};
+      if (!map_name.empty()) label += " (" + map_info.Name() + ")";
+    }
+    dump_memory(log, memory, reg_value, label);
+  });
+}
+
+static bool dump_thread(log_t* log, BacktraceMap* map, Memory* process_memory,
+                        const ThreadInfo& thread_info, uint64_t abort_msg_address,
+                        bool primary_thread) {
+  UNUSED(process_memory);
+  log->current_tid = thread_info.tid;
   if (!primary_thread) {
     _LOG(log, logtype::THREAD, "--- --- --- --- --- --- --- --- --- --- --- --- --- --- --- ---\n");
   }
-  dump_thread_info(log, pid, tid, process_name.c_str(), thread_name.c_str());
-  dump_signal_info(log, tid);
+  dump_thread_info(log, thread_info);
 
-  std::unique_ptr<Backtrace> backtrace(Backtrace::Create(pid, tid, map));
-  if (primary_thread) {
-    dump_abort_message(backtrace.get(), log, abort_msg_address);
-  }
-  dump_registers(log, tid);
-  if (backtrace->Unwind(0)) {
-    dump_backtrace_and_stack(backtrace.get(), log);
-  } else {
-    ALOGE("Unwind failed: pid = %d, tid = %d", pid, tid);
+  if (thread_info.siginfo) {
+    dump_signal_info(log, thread_info, process_memory);
   }
 
   if (primary_thread) {
-    dump_memory_and_code(log, backtrace.get());
+    dump_abort_message(log, process_memory, abort_msg_address);
+  }
+
+  dump_registers(log, thread_info.registers.get());
+
+  // Unwind will mutate the registers, so make a copy first.
+  std::unique_ptr<Regs> regs_copy(thread_info.registers->Clone());
+  std::vector<backtrace_frame_data_t> frames;
+  if (!Backtrace::Unwind(regs_copy.get(), map, &frames, 0, nullptr)) {
+    _LOG(log, logtype::THREAD, "Failed to unwind");
+    return false;
+  }
+
+  if (!frames.empty()) {
+    _LOG(log, logtype::BACKTRACE, "\nbacktrace:\n");
+    dump_backtrace(log, frames, "    ");
+
+    _LOG(log, logtype::STACK, "\nstack:\n");
+    dump_stack(log, map, process_memory, frames);
+  }
+
+  if (primary_thread) {
+    dump_memory_and_code(log, map, process_memory, thread_info.registers.get());
     if (map) {
-      dump_all_maps(backtrace.get(), map, log, tid);
+      uint64_t addr = 0;
+      siginfo_t* si = thread_info.siginfo;
+      if (signal_has_si_addr(si)) {
+        addr = reinterpret_cast<uint64_t>(si->si_addr);
+      }
+      dump_all_maps(log, map, process_memory, addr);
     }
   }
 
   log->current_tid = log->crashed_tid;
+  return true;
 }
 
 // Reads the contents of the specified log device, filters out the entries
@@ -532,10 +472,9 @@
 // If "tail" is non-zero, log the last "tail" number of lines.
 static EventTagMap* g_eventTagMap = NULL;
 
-static void dump_log_file(
-    log_t* log, pid_t pid, const char* filename, unsigned int tail) {
+static void dump_log_file(log_t* log, pid_t pid, const char* filename, unsigned int tail) {
   bool first = true;
-  struct logger_list* logger_list;
+  logger_list* logger_list;
 
   if (!log->should_retrieve_logcat) {
     return;
@@ -549,11 +488,9 @@
     return;
   }
 
-  struct log_msg log_entry;
-
   while (true) {
+    log_msg log_entry;
     ssize_t actual = android_logger_list_read(logger_list, &log_entry);
-    struct logger_entry* entry;
 
     if (actual < 0) {
       if (actual == -EINTR) {
@@ -576,8 +513,6 @@
     // high-frequency debug diagnostics should just be written to
     // the tombstone file.
 
-    entry = &log_entry.entry_v1;
-
     if (first) {
       _LOG(log, logtype::LOGS, "--------- %slog %s\n",
         tail ? "tail end of " : "", filename);
@@ -588,19 +523,8 @@
     //
     // We want to display it in the same format as "logcat -v threadtime"
     // (although in this case the pid is redundant).
-    static const char* kPrioChars = "!.VDIWEFS";
-    unsigned hdr_size = log_entry.entry.hdr_size;
-    if (!hdr_size) {
-      hdr_size = sizeof(log_entry.entry_v1);
-    }
-    if ((hdr_size < sizeof(log_entry.entry_v1)) ||
-        (hdr_size > sizeof(log_entry.entry))) {
-      continue;
-    }
-    char* msg = reinterpret_cast<char*>(log_entry.buf) + hdr_size;
-
     char timeBuf[32];
-    time_t sec = static_cast<time_t>(entry->sec);
+    time_t sec = static_cast<time_t>(log_entry.entry.sec);
     struct tm tmBuf;
     struct tm* ptm;
     ptm = localtime_r(&sec, &tmBuf);
@@ -608,17 +532,23 @@
 
     if (log_entry.id() == LOG_ID_EVENTS) {
       if (!g_eventTagMap) {
-        g_eventTagMap = android_openEventTagMap(NULL);
+        g_eventTagMap = android_openEventTagMap(nullptr);
       }
       AndroidLogEntry e;
       char buf[512];
-      android_log_processBinaryLogBuffer(entry, &e, g_eventTagMap, buf, sizeof(buf));
-      _LOG(log, logtype::LOGS, "%s.%03d %5d %5d %c %-8.*s: %s\n",
-         timeBuf, entry->nsec / 1000000, entry->pid, entry->tid,
-         'I', (int)e.tagLen, e.tag, e.message);
+      if (android_log_processBinaryLogBuffer(&log_entry.entry_v1, &e, g_eventTagMap, buf,
+                                             sizeof(buf)) == 0) {
+        _LOG(log, logtype::LOGS, "%s.%03d %5d %5d %c %-8.*s: %s\n", timeBuf,
+             log_entry.entry.nsec / 1000000, log_entry.entry.pid, log_entry.entry.tid, 'I',
+             (int)e.tagLen, e.tag, e.message);
+      }
       continue;
     }
 
+    char* msg = log_entry.msg();
+    if (msg == nullptr) {
+      continue;
+    }
     unsigned char prio = msg[0];
     char* tag = msg + 1;
     msg = tag + strlen(tag) + 1;
@@ -629,20 +559,21 @@
       *nl-- = '\0';
     }
 
+    static const char* kPrioChars = "!.VDIWEFS";
     char prioChar = (prio < strlen(kPrioChars) ? kPrioChars[prio] : '?');
 
     // Look for line breaks ('\n') and display each text line
     // on a separate line, prefixed with the header, like logcat does.
     do {
       nl = strchr(msg, '\n');
-      if (nl) {
+      if (nl != nullptr) {
         *nl = '\0';
         ++nl;
       }
 
-      _LOG(log, logtype::LOGS, "%s.%03d %5d %5d %c %-8s: %s\n",
-         timeBuf, entry->nsec / 1000000, entry->pid, entry->tid,
-         prioChar, tag, msg);
+      _LOG(log, logtype::LOGS, "%s.%03d %5d %5d %c %-8s: %s\n", timeBuf,
+           log_entry.entry.nsec / 1000000, log_entry.entry.pid, log_entry.entry.tid, prioChar, tag,
+           msg);
     } while ((msg = nl));
   }
 
@@ -652,112 +583,16 @@
 // Dumps the logs generated by the specified pid to the tombstone, from both
 // "system" and "main" log devices.  Ideally we'd interleave the output.
 static void dump_logs(log_t* log, pid_t pid, unsigned int tail) {
+  if (pid == getpid()) {
+    // Cowardly refuse to dump logs while we're running in-process.
+    return;
+  }
+
   dump_log_file(log, pid, "system", tail);
   dump_log_file(log, pid, "main", tail);
 }
 
-// Dumps all information about the specified pid to the tombstone.
-static void dump_crash(log_t* log, BacktraceMap* map, const OpenFilesList* open_files, pid_t pid,
-                       pid_t tid, const std::string& process_name,
-                       const std::map<pid_t, std::string>& threads, uintptr_t abort_msg_address) {
-  // don't copy log messages to tombstone unless this is a dev device
-  char value[PROPERTY_VALUE_MAX];
-  property_get("ro.debuggable", value, "0");
-  bool want_logs = (value[0] == '1');
-
-  _LOG(log, logtype::HEADER,
-       "*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***\n");
-  dump_header_info(log);
-  dump_thread(log, pid, tid, process_name, threads.find(tid)->second, map, abort_msg_address, true);
-  if (want_logs) {
-    dump_logs(log, pid, 5);
-  }
-
-  for (const auto& it : threads) {
-    pid_t thread_tid = it.first;
-    const std::string& thread_name = it.second;
-
-    if (thread_tid != tid) {
-      dump_thread(log, pid, thread_tid, process_name, thread_name, map, 0, false);
-    }
-  }
-
-  if (open_files) {
-    _LOG(log, logtype::OPEN_FILES, "\nopen files:\n");
-    dump_open_files_list_to_log(*open_files, log, "    ");
-  }
-
-  if (want_logs) {
-    dump_logs(log, pid, 0);
-  }
-}
-
-// open_tombstone - find an available tombstone slot, if any, of the
-// form tombstone_XX where XX is 00 to MAX_TOMBSTONES-1, inclusive. If no
-// file is available, we reuse the least-recently-modified file.
-int open_tombstone(std::string* out_path) {
-  // In a single pass, find an available slot and, in case none
-  // exist, find and record the least-recently-modified file.
-  char path[128];
-  int fd = -1;
-  int oldest = -1;
-  struct stat oldest_sb;
-  for (int i = 0; i < MAX_TOMBSTONES; i++) {
-    snprintf(path, sizeof(path), TOMBSTONE_TEMPLATE, i);
-
-    struct stat sb;
-    if (stat(path, &sb) == 0) {
-      if (oldest < 0 || sb.st_mtime < oldest_sb.st_mtime) {
-        oldest = i;
-        oldest_sb.st_mtime = sb.st_mtime;
-      }
-      continue;
-    }
-    if (errno != ENOENT) continue;
-
-    fd = open(path, O_CREAT | O_EXCL | O_WRONLY | O_NOFOLLOW | O_CLOEXEC, 0600);
-    if (fd < 0) continue;  // raced ?
-
-    if (out_path) {
-      *out_path = path;
-    }
-    fchown(fd, AID_SYSTEM, AID_SYSTEM);
-    return fd;
-  }
-
-  if (oldest < 0) {
-    ALOGE("debuggerd: failed to find a valid tombstone, default to using tombstone 0.\n");
-    oldest = 0;
-  }
-
-  // we didn't find an available file, so we clobber the oldest one
-  snprintf(path, sizeof(path), TOMBSTONE_TEMPLATE, oldest);
-  fd = open(path, O_CREAT | O_TRUNC | O_WRONLY | O_NOFOLLOW | O_CLOEXEC, 0600);
-  if (fd < 0) {
-    ALOGE("debuggerd: failed to open tombstone file '%s': %s\n", path, strerror(errno));
-    return -1;
-  }
-
-  if (out_path) {
-    *out_path = path;
-  }
-  fchown(fd, AID_SYSTEM, AID_SYSTEM);
-  return fd;
-}
-
-void engrave_tombstone(int tombstone_fd, BacktraceMap* map, const OpenFilesList* open_files,
-                       pid_t pid, pid_t tid, const std::string& process_name,
-                       const std::map<pid_t, std::string>& threads, uintptr_t abort_msg_address,
-                       std::string* amfd_data) {
-  log_t log;
-  log.current_tid = tid;
-  log.crashed_tid = tid;
-  log.tfd = tombstone_fd;
-  log.amfd_data = amfd_data;
-  dump_crash(&log, map, open_files, pid, tid, process_name, threads, abort_msg_address);
-}
-
-void engrave_tombstone_ucontext(int tombstone_fd, uintptr_t abort_msg_address, siginfo_t* siginfo,
+void engrave_tombstone_ucontext(int tombstone_fd, uint64_t abort_msg_address, siginfo_t* siginfo,
                                 ucontext_t* ucontext) {
   pid_t pid = getpid();
   pid_t tid = gettid();
@@ -774,19 +609,69 @@
   read_with_default("/proc/self/comm", thread_name, sizeof(thread_name), "<unknown>");
   read_with_default("/proc/self/cmdline", process_name, sizeof(process_name), "<unknown>");
 
+  std::unique_ptr<Regs> regs(Regs::CreateFromUcontext(Regs::CurrentArch(), ucontext));
+
+  std::map<pid_t, ThreadInfo> threads;
+  threads[gettid()] = ThreadInfo{
+      .registers = std::move(regs),
+      .tid = tid,
+      .thread_name = thread_name,
+      .pid = pid,
+      .process_name = process_name,
+      .siginfo = siginfo,
+  };
+
+  std::unique_ptr<BacktraceMap> backtrace_map(BacktraceMap::Create(getpid(), false));
+  if (!backtrace_map) {
+    ALOGE("failed to create backtrace map");
+    _exit(1);
+  }
+
+  std::shared_ptr<Memory> process_memory = backtrace_map->GetProcessMemory();
+  engrave_tombstone(unique_fd(dup(tombstone_fd)), backtrace_map.get(), process_memory.get(),
+                    threads, tid, abort_msg_address, nullptr, nullptr);
+}
+
+void engrave_tombstone(unique_fd output_fd, BacktraceMap* map, Memory* process_memory,
+                       const std::map<pid_t, ThreadInfo>& threads, pid_t target_thread,
+                       uint64_t abort_msg_address, OpenFilesList* open_files,
+                       std::string* amfd_data) {
+  // don't copy log messages to tombstone unless this is a dev device
+  bool want_logs = android::base::GetBoolProperty("ro.debuggable", false);
+
+  log_t log;
+  log.current_tid = target_thread;
+  log.crashed_tid = target_thread;
+  log.tfd = output_fd.get();
+  log.amfd_data = amfd_data;
+
   _LOG(&log, logtype::HEADER, "*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***\n");
   dump_header_info(&log);
-  dump_thread_info(&log, pid, tid, thread_name, process_name);
-  dump_signal_info(&log, siginfo);
 
-  std::unique_ptr<Backtrace> backtrace(Backtrace::Create(pid, tid));
-  dump_abort_message(backtrace.get(), &log, abort_msg_address);
-  dump_registers(&log, ucontext);
+  auto it = threads.find(target_thread);
+  if (it == threads.end()) {
+    LOG(FATAL) << "failed to find target thread";
+  }
+  dump_thread(&log, map, process_memory, it->second, abort_msg_address, true);
 
-  // TODO: Dump registers from the ucontext.
-  if (backtrace->Unwind(0, ucontext)) {
-    dump_backtrace_and_stack(backtrace.get(), &log);
-  } else {
-    ALOGE("Unwind failed: pid = %d, tid = %d", pid, tid);
+  if (want_logs) {
+    dump_logs(&log, it->second.pid, 50);
+  }
+
+  for (auto& [tid, thread_info] : threads) {
+    if (tid == target_thread) {
+      continue;
+    }
+
+    dump_thread(&log, map, process_memory, thread_info, 0, false);
+  }
+
+  if (open_files) {
+    _LOG(&log, logtype::OPEN_FILES, "\nopen files:\n");
+    dump_open_files_list(&log, *open_files, "    ");
+  }
+
+  if (want_logs) {
+    dump_logs(&log, it->second.pid, 0);
   }
 }
diff --git a/debuggerd/libdebuggerd/utility.cpp b/debuggerd/libdebuggerd/utility.cpp
index 7f450e6..8bdc02f 100644
--- a/debuggerd/libdebuggerd/utility.cpp
+++ b/debuggerd/libdebuggerd/utility.cpp
@@ -16,11 +16,13 @@
 
 #define LOG_TAG "DEBUG"
 
-#include "utility.h"
+#include "libdebuggerd/utility.h"
 
 #include <errno.h>
 #include <signal.h>
 #include <string.h>
+#include <sys/capability.h>
+#include <sys/prctl.h>
 #include <sys/ptrace.h>
 #include <sys/uio.h>
 #include <sys/wait.h>
@@ -34,7 +36,9 @@
 #include <android-base/strings.h>
 #include <android-base/unique_fd.h>
 #include <backtrace/Backtrace.h>
+#include <debuggerd/handler.h>
 #include <log/log.h>
+#include <unwindstack/Memory.h>
 
 using android::base::unique_fd;
 
@@ -70,25 +74,22 @@
                       && (log->crashed_tid == log->current_tid);
   static bool write_to_kmsg = should_write_to_kmsg();
 
-  char buf[512];
+  std::string msg;
   va_list ap;
   va_start(ap, fmt);
-  vsnprintf(buf, sizeof(buf), fmt, ap);
+  android::base::StringAppendV(&msg, fmt, ap);
   va_end(ap);
 
-  size_t len = strlen(buf);
-  if (len <= 0) {
-    return;
-  }
+  if (msg.empty()) return;
 
   if (write_to_tombstone) {
-    TEMP_FAILURE_RETRY(write(log->tfd, buf, len));
+    TEMP_FAILURE_RETRY(write(log->tfd, msg.c_str(), msg.size()));
   }
 
   if (write_to_logcat) {
-    __android_log_buf_write(LOG_ID_CRASH, ANDROID_LOG_FATAL, LOG_TAG, buf);
+    __android_log_buf_write(LOG_ID_CRASH, ANDROID_LOG_FATAL, LOG_TAG, msg.c_str());
     if (log->amfd_data != nullptr) {
-      *log->amfd_data += buf;
+      *log->amfd_data += msg;
     }
 
     if (write_to_kmsg) {
@@ -96,11 +97,11 @@
       if (kmsg_fd.get() >= 0) {
         // Our output might contain newlines which would otherwise be handled by the android logger.
         // Split the lines up ourselves before sending to the kernel logger.
-        if (buf[len - 1] == '\n') {
-          buf[len - 1] = '\0';
+        if (msg.back() == '\n') {
+          msg.back() = '\0';
         }
 
-        std::vector<std::string> fragments = android::base::Split(buf, "\n");
+        std::vector<std::string> fragments = android::base::Split(msg, "\n");
         for (const std::string& fragment : fragments) {
           static constexpr char prefix[] = "<3>DEBUG: ";
           struct iovec iov[3];
@@ -117,40 +118,10 @@
   }
 }
 
-bool wait_for_signal(pid_t tid, siginfo_t* siginfo) {
-  while (true) {
-    int status;
-    pid_t n = TEMP_FAILURE_RETRY(waitpid(tid, &status, __WALL));
-    if (n == -1) {
-      ALOGE("waitpid failed: tid %d, %s", tid, strerror(errno));
-      return false;
-    } else if (n == tid) {
-      if (WIFSTOPPED(status)) {
-        if (ptrace(PTRACE_GETSIGINFO, tid, nullptr, siginfo) != 0) {
-          ALOGE("PTRACE_GETSIGINFO failed: %s", strerror(errno));
-          return false;
-        }
-        return true;
-      } else {
-        ALOGE("unexpected waitpid response: n=%d, status=%08x\n", n, status);
-        // This is the only circumstance under which we can allow a detach
-        // to fail with ESRCH, which indicates the tid has exited.
-        return false;
-      }
-    }
-  }
-}
-
 #define MEMORY_BYTES_TO_DUMP 256
 #define MEMORY_BYTES_PER_LINE 16
 
-void dump_memory(log_t* log, Backtrace* backtrace, uintptr_t addr, const char* fmt, ...) {
-  std::string log_msg;
-  va_list ap;
-  va_start(ap, fmt);
-  android::base::StringAppendV(&log_msg, fmt, ap);
-  va_end(ap);
-
+void dump_memory(log_t* log, unwindstack::Memory* memory, uint64_t addr, const std::string& label) {
   // Align the address to sizeof(long) and start 32 bytes before the address.
   addr &= ~(sizeof(long) - 1);
   if (addr >= 4128) {
@@ -167,19 +138,19 @@
     return;
   }
 
-  _LOG(log, logtype::MEMORY, "\n%s\n", log_msg.c_str());
+  _LOG(log, logtype::MEMORY, "\n%s:\n", label.c_str());
 
   // Dump 256 bytes
   uintptr_t data[MEMORY_BYTES_TO_DUMP/sizeof(uintptr_t)];
   memset(data, 0, MEMORY_BYTES_TO_DUMP);
-  size_t bytes = backtrace->Read(addr, reinterpret_cast<uint8_t*>(data), sizeof(data));
+  size_t bytes = memory->Read(addr, reinterpret_cast<uint8_t*>(data), sizeof(data));
   if (bytes % sizeof(uintptr_t) != 0) {
     // This should never happen, but just in case.
     ALOGE("Bytes read %zu, is not a multiple of %zu", bytes, sizeof(uintptr_t));
     bytes &= ~(sizeof(uintptr_t) - 1);
   }
 
-  uintptr_t start = 0;
+  uint64_t start = 0;
   bool skip_2nd_read = false;
   if (bytes == 0) {
     // In this case, we might want to try another read at the beginning of
@@ -199,8 +170,8 @@
     // into a readable map. Only requires one extra read because a map has
     // to contain at least one page, and the total number of bytes to dump
     // is smaller than a page.
-    size_t bytes2 = backtrace->Read(addr + start + bytes, reinterpret_cast<uint8_t*>(data) + bytes,
-                                    sizeof(data) - bytes - start);
+    size_t bytes2 = memory->Read(addr + start + bytes, reinterpret_cast<uint8_t*>(data) + bytes,
+                                 sizeof(data) - bytes - start);
     bytes += bytes2;
     if (bytes2 > 0 && bytes % sizeof(uintptr_t) != 0) {
       // This should never happen, but we'll try and continue any way.
@@ -226,7 +197,7 @@
     std::string ascii;
     for (size_t i = 0; i < MEMORY_BYTES_PER_LINE / sizeof(uintptr_t); i++) {
       if (current >= start && current + sizeof(uintptr_t) <= total_bytes) {
-        android::base::StringAppendF(&logline, " %" PRIPTR, *data_ptr);
+        android::base::StringAppendF(&logline, " %" PRIPTR, static_cast<uint64_t>(*data_ptr));
 
         // Fill out the ascii string from the data.
         uint8_t* ptr = reinterpret_cast<uint8_t*>(data_ptr);
@@ -264,3 +235,189 @@
   }
   strcpy(buf, default_value);
 }
+
+void drop_capabilities() {
+  __user_cap_header_struct capheader;
+  memset(&capheader, 0, sizeof(capheader));
+  capheader.version = _LINUX_CAPABILITY_VERSION_3;
+  capheader.pid = 0;
+
+  __user_cap_data_struct capdata[2];
+  memset(&capdata, 0, sizeof(capdata));
+
+  if (capset(&capheader, &capdata[0]) == -1) {
+    PLOG(FATAL) << "failed to drop capabilities";
+  }
+
+  if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) != 0) {
+    PLOG(FATAL) << "failed to set PR_SET_NO_NEW_PRIVS";
+  }
+}
+
+bool signal_has_si_addr(const siginfo_t* si) {
+  // Manually sent signals won't have si_addr.
+  if (si->si_code == SI_USER || si->si_code == SI_QUEUE || si->si_code == SI_TKILL) {
+    return false;
+  }
+
+  switch (si->si_signo) {
+    case SIGBUS:
+    case SIGFPE:
+    case SIGILL:
+    case SIGSEGV:
+    case SIGTRAP:
+      return true;
+    default:
+      return false;
+  }
+}
+
+bool signal_has_sender(const siginfo_t* si, pid_t caller_pid) {
+  return SI_FROMUSER(si) && (si->si_pid != 0) && (si->si_pid != caller_pid);
+}
+
+void get_signal_sender(char* buf, size_t n, const siginfo_t* si) {
+  snprintf(buf, n, " from pid %d, uid %d", si->si_pid, si->si_uid);
+}
+
+const char* get_signame(const siginfo_t* si) {
+  switch (si->si_signo) {
+    case SIGABRT: return "SIGABRT";
+    case SIGBUS: return "SIGBUS";
+    case SIGFPE: return "SIGFPE";
+    case SIGILL: return "SIGILL";
+    case SIGSEGV: return "SIGSEGV";
+    case SIGSTKFLT: return "SIGSTKFLT";
+    case SIGSTOP: return "SIGSTOP";
+    case SIGSYS: return "SIGSYS";
+    case SIGTRAP: return "SIGTRAP";
+    case DEBUGGER_SIGNAL: return "<debuggerd signal>";
+    default: return "?";
+  }
+}
+
+const char* get_sigcode(const siginfo_t* si) {
+  // Try the signal-specific codes...
+  switch (si->si_signo) {
+    case SIGILL:
+      switch (si->si_code) {
+        case ILL_ILLOPC: return "ILL_ILLOPC";
+        case ILL_ILLOPN: return "ILL_ILLOPN";
+        case ILL_ILLADR: return "ILL_ILLADR";
+        case ILL_ILLTRP: return "ILL_ILLTRP";
+        case ILL_PRVOPC: return "ILL_PRVOPC";
+        case ILL_PRVREG: return "ILL_PRVREG";
+        case ILL_COPROC: return "ILL_COPROC";
+        case ILL_BADSTK: return "ILL_BADSTK";
+        case ILL_BADIADDR:
+          return "ILL_BADIADDR";
+        case __ILL_BREAK:
+          return "ILL_BREAK";
+        case __ILL_BNDMOD:
+          return "ILL_BNDMOD";
+      }
+      static_assert(NSIGILL == __ILL_BNDMOD, "missing ILL_* si_code");
+      break;
+    case SIGBUS:
+      switch (si->si_code) {
+        case BUS_ADRALN: return "BUS_ADRALN";
+        case BUS_ADRERR: return "BUS_ADRERR";
+        case BUS_OBJERR: return "BUS_OBJERR";
+        case BUS_MCEERR_AR: return "BUS_MCEERR_AR";
+        case BUS_MCEERR_AO: return "BUS_MCEERR_AO";
+      }
+      static_assert(NSIGBUS == BUS_MCEERR_AO, "missing BUS_* si_code");
+      break;
+    case SIGFPE:
+      switch (si->si_code) {
+        case FPE_INTDIV: return "FPE_INTDIV";
+        case FPE_INTOVF: return "FPE_INTOVF";
+        case FPE_FLTDIV: return "FPE_FLTDIV";
+        case FPE_FLTOVF: return "FPE_FLTOVF";
+        case FPE_FLTUND: return "FPE_FLTUND";
+        case FPE_FLTRES: return "FPE_FLTRES";
+        case FPE_FLTINV: return "FPE_FLTINV";
+        case FPE_FLTSUB: return "FPE_FLTSUB";
+        case __FPE_DECOVF:
+          return "FPE_DECOVF";
+        case __FPE_DECDIV:
+          return "FPE_DECDIV";
+        case __FPE_DECERR:
+          return "FPE_DECERR";
+        case __FPE_INVASC:
+          return "FPE_INVASC";
+        case __FPE_INVDEC:
+          return "FPE_INVDEC";
+        case FPE_FLTUNK:
+          return "FPE_FLTUNK";
+        case FPE_CONDTRAP:
+          return "FPE_CONDTRAP";
+      }
+      static_assert(NSIGFPE == FPE_CONDTRAP, "missing FPE_* si_code");
+      break;
+    case SIGSEGV:
+      switch (si->si_code) {
+        case SEGV_MAPERR: return "SEGV_MAPERR";
+        case SEGV_ACCERR: return "SEGV_ACCERR";
+        case SEGV_BNDERR: return "SEGV_BNDERR";
+        case SEGV_PKUERR: return "SEGV_PKUERR";
+        case SEGV_ACCADI:
+          return "SEGV_ACCADI";
+        case SEGV_ADIDERR:
+          return "SEGV_ADIDERR";
+        case SEGV_ADIPERR:
+          return "SEGV_ADIPERR";
+      }
+      static_assert(NSIGSEGV == SEGV_ADIPERR, "missing SEGV_* si_code");
+      break;
+    case SIGSYS:
+      switch (si->si_code) {
+        case SYS_SECCOMP: return "SYS_SECCOMP";
+      }
+      static_assert(NSIGSYS == SYS_SECCOMP, "missing SYS_* si_code");
+      break;
+    case SIGTRAP:
+      switch (si->si_code) {
+        case TRAP_BRKPT: return "TRAP_BRKPT";
+        case TRAP_TRACE: return "TRAP_TRACE";
+        case TRAP_BRANCH: return "TRAP_BRANCH";
+        case TRAP_HWBKPT: return "TRAP_HWBKPT";
+      }
+      if ((si->si_code & 0xff) == SIGTRAP) {
+        switch ((si->si_code >> 8) & 0xff) {
+          case PTRACE_EVENT_FORK:
+            return "PTRACE_EVENT_FORK";
+          case PTRACE_EVENT_VFORK:
+            return "PTRACE_EVENT_VFORK";
+          case PTRACE_EVENT_CLONE:
+            return "PTRACE_EVENT_CLONE";
+          case PTRACE_EVENT_EXEC:
+            return "PTRACE_EVENT_EXEC";
+          case PTRACE_EVENT_VFORK_DONE:
+            return "PTRACE_EVENT_VFORK_DONE";
+          case PTRACE_EVENT_EXIT:
+            return "PTRACE_EVENT_EXIT";
+          case PTRACE_EVENT_SECCOMP:
+            return "PTRACE_EVENT_SECCOMP";
+          case PTRACE_EVENT_STOP:
+            return "PTRACE_EVENT_STOP";
+        }
+      }
+      static_assert(NSIGTRAP == TRAP_HWBKPT, "missing TRAP_* si_code");
+      break;
+  }
+  // Then the other codes...
+  switch (si->si_code) {
+    case SI_USER: return "SI_USER";
+    case SI_KERNEL: return "SI_KERNEL";
+    case SI_QUEUE: return "SI_QUEUE";
+    case SI_TIMER: return "SI_TIMER";
+    case SI_MESGQ: return "SI_MESGQ";
+    case SI_ASYNCIO: return "SI_ASYNCIO";
+    case SI_SIGIO: return "SI_SIGIO";
+    case SI_TKILL: return "SI_TKILL";
+    case SI_DETHREAD: return "SI_DETHREAD";
+  }
+  // Then give up...
+  return "?";
+}
diff --git a/debuggerd/libdebuggerd/x86/machine.cpp b/debuggerd/libdebuggerd/x86/machine.cpp
deleted file mode 100644
index af10817..0000000
--- a/debuggerd/libdebuggerd/x86/machine.cpp
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright 2006, 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.
- */
-
-#define LOG_TAG "DEBUG"
-
-#include <errno.h>
-#include <stdint.h>
-#include <string.h>
-#include <sys/ptrace.h>
-
-#include <backtrace/Backtrace.h>
-#include <log/log.h>
-
-#include "machine.h"
-#include "utility.h"
-
-void dump_memory_and_code(log_t* log, Backtrace* backtrace) {
-  struct pt_regs r;
-  if (ptrace(PTRACE_GETREGS, backtrace->Tid(), 0, &r) == -1) {
-    ALOGE("cannot get registers: %s\n", strerror(errno));
-    return;
-  }
-
-  dump_memory(log, backtrace, static_cast<uintptr_t>(r.eax), "memory near eax:");
-  dump_memory(log, backtrace, static_cast<uintptr_t>(r.ebx), "memory near ebx:");
-  dump_memory(log, backtrace, static_cast<uintptr_t>(r.ecx), "memory near ecx:");
-  dump_memory(log, backtrace, static_cast<uintptr_t>(r.edx), "memory near edx:");
-  dump_memory(log, backtrace, static_cast<uintptr_t>(r.esi), "memory near esi:");
-  dump_memory(log, backtrace, static_cast<uintptr_t>(r.edi), "memory near edi:");
-
-  dump_memory(log, backtrace, static_cast<uintptr_t>(r.eip), "code around eip:");
-}
-
-void dump_registers(log_t* log, pid_t tid) {
-  struct pt_regs r;
-  if (ptrace(PTRACE_GETREGS, tid, 0, &r) == -1) {
-    ALOGE("cannot get registers: %s\n", strerror(errno));
-    return;
-  }
-
-  _LOG(log, logtype::REGISTERS, "    eax %08lx  ebx %08lx  ecx %08lx  edx %08lx\n",
-       r.eax, r.ebx, r.ecx, r.edx);
-  _LOG(log, logtype::REGISTERS, "    esi %08lx  edi %08lx\n",
-       r.esi, r.edi);
-  _LOG(log, logtype::REGISTERS, "    xcs %08x  xds %08x  xes %08x  xfs %08x  xss %08x\n",
-       r.xcs, r.xds, r.xes, r.xfs, r.xss);
-  _LOG(log, logtype::REGISTERS, "    eip %08lx  ebp %08lx  esp %08lx  flags %08lx\n",
-       r.eip, r.ebp, r.esp, r.eflags);
-}
diff --git a/debuggerd/libdebuggerd/x86_64/machine.cpp b/debuggerd/libdebuggerd/x86_64/machine.cpp
deleted file mode 100644
index bf2c2b4..0000000
--- a/debuggerd/libdebuggerd/x86_64/machine.cpp
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
-** Copyright 2013, 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.
-*/
-
-#define LOG_TAG "DEBUG"
-
-#include <errno.h>
-#include <stdint.h>
-#include <string.h>
-#include <sys/ptrace.h>
-#include <sys/user.h>
-
-#include <backtrace/Backtrace.h>
-#include <log/log.h>
-
-#include "machine.h"
-#include "utility.h"
-
-void dump_memory_and_code(log_t* log, Backtrace* backtrace) {
-  struct user_regs_struct r;
-  if (ptrace(PTRACE_GETREGS, backtrace->Tid(), 0, &r) == -1) {
-    ALOGE("cannot get registers: %s\n", strerror(errno));
-    return;
-  }
-
-  dump_memory(log, backtrace, static_cast<uintptr_t>(r.rax), "memory near rax:");
-  dump_memory(log, backtrace, static_cast<uintptr_t>(r.rbx), "memory near rbx:");
-  dump_memory(log, backtrace, static_cast<uintptr_t>(r.rcx), "memory near rcx:");
-  dump_memory(log, backtrace, static_cast<uintptr_t>(r.rdx), "memory near rdx:");
-  dump_memory(log, backtrace, static_cast<uintptr_t>(r.rsi), "memory near rsi:");
-  dump_memory(log, backtrace, static_cast<uintptr_t>(r.rdi), "memory near rdi:");
-
-  dump_memory(log, backtrace, static_cast<uintptr_t>(r.rip), "code around rip:");
-}
-
-void dump_registers(log_t* log, pid_t tid) {
-  struct user_regs_struct r;
-  if (ptrace(PTRACE_GETREGS, tid, 0, &r) == -1) {
-    ALOGE("cannot get registers: %s\n", strerror(errno));
-    return;
-  }
-
-  _LOG(log, logtype::REGISTERS, "    rax %016lx  rbx %016lx  rcx %016lx  rdx %016lx\n",
-       r.rax, r.rbx, r.rcx, r.rdx);
-  _LOG(log, logtype::REGISTERS, "    rsi %016lx  rdi %016lx\n",
-       r.rsi, r.rdi);
-  _LOG(log, logtype::REGISTERS, "    r8  %016lx  r9  %016lx  r10 %016lx  r11 %016lx\n",
-       r.r8, r.r9, r.r10, r.r11);
-  _LOG(log, logtype::REGISTERS, "    r12 %016lx  r13 %016lx  r14 %016lx  r15 %016lx\n",
-       r.r12, r.r13, r.r14, r.r15);
-  _LOG(log, logtype::REGISTERS, "    cs  %016lx  ss  %016lx\n",
-       r.cs, r.ss);
-  _LOG(log, logtype::REGISTERS, "    rip %016lx  rbp %016lx  rsp %016lx  eflags %016lx\n",
-       r.rip, r.rbp, r.rsp, r.eflags);
-}
diff --git a/debuggerd/protocol.h b/debuggerd/protocol.h
index 7e1961e..6903b0e 100644
--- a/debuggerd/protocol.h
+++ b/debuggerd/protocol.h
@@ -16,7 +16,10 @@
 
 #pragma once
 
+#include <signal.h>
 #include <stdint.h>
+#include <sys/ucontext.h>
+#include <unistd.h>
 
 #include "dump_type.h"
 
@@ -76,3 +79,11 @@
   InterceptStatus status;
   char error_message[127];  // always null-terminated
 };
+
+// Sent from handler to crash_dump via pipe.
+struct __attribute__((__packed__)) CrashInfo {
+  uint32_t version;  // must be 1.
+  siginfo_t siginfo;
+  ucontext_t ucontext;
+  uintptr_t abort_msg_address;
+};
diff --git a/debuggerd/seccomp_policy/crash_dump.arm.policy b/debuggerd/seccomp_policy/crash_dump.arm.policy
new file mode 100644
index 0000000..254330d
--- /dev/null
+++ b/debuggerd/seccomp_policy/crash_dump.arm.policy
@@ -0,0 +1,37 @@
+read: 1
+write: 1
+exit: 1
+rt_sigreturn: 1
+sigreturn: 1
+exit_group: 1
+clock_gettime: 1
+gettimeofday: 1
+futex: 1
+getrandom: 1
+getpid: 1
+gettid: 1
+ppoll: 1
+pipe2: 1
+openat: 1
+dup: 1
+close: 1
+lseek: 1
+getdents64: 1
+faccessat: 1
+recvmsg: 1
+process_vm_readv: 1
+tgkill: 1
+rt_sigprocmask: 1
+rt_sigaction: 1
+rt_tgsigqueueinfo: 1
+prctl: arg0 == PR_GET_NO_NEW_PRIVS || arg0 == 0x53564d41
+madvise: 1
+mprotect: arg2 in 0x1|0x2
+munmap: 1
+getuid32: 1
+fstat64: 1
+mmap2: arg2 in 0x1|0x2
+geteuid32: 1
+getgid32: 1
+getegid32: 1
+getgroups32: 1
diff --git a/debuggerd/seccomp_policy/crash_dump.arm64.policy b/debuggerd/seccomp_policy/crash_dump.arm64.policy
new file mode 100644
index 0000000..9b3ef09
--- /dev/null
+++ b/debuggerd/seccomp_policy/crash_dump.arm64.policy
@@ -0,0 +1,36 @@
+read: 1
+write: 1
+exit: 1
+rt_sigreturn: 1
+exit_group: 1
+clock_gettime: 1
+gettimeofday: 1
+futex: 1
+getrandom: 1
+getpid: 1
+gettid: 1
+ppoll: 1
+pipe2: 1
+openat: 1
+dup: 1
+close: 1
+lseek: 1
+getdents64: 1
+faccessat: 1
+recvmsg: 1
+process_vm_readv: 1
+tgkill: 1
+rt_sigprocmask: 1
+rt_sigaction: 1
+rt_tgsigqueueinfo: 1
+prctl: arg0 == PR_GET_NO_NEW_PRIVS || arg0 == 0x53564d41
+madvise: 1
+mprotect: arg2 in 0x1|0x2
+munmap: 1
+getuid: 1
+fstat: 1
+mmap: arg2 in 0x1|0x2
+geteuid: 1
+getgid: 1
+getegid: 1
+getgroups: 1
diff --git a/debuggerd/seccomp_policy/crash_dump.policy.def b/debuggerd/seccomp_policy/crash_dump.policy.def
new file mode 100644
index 0000000..2ef31b0
--- /dev/null
+++ b/debuggerd/seccomp_policy/crash_dump.policy.def
@@ -0,0 +1,71 @@
+// SECCOMP_MODE_STRICT
+read: 1
+write: 1
+exit: 1
+rt_sigreturn: 1
+#if !defined(__LP64__)
+sigreturn: 1
+#endif
+
+exit_group: 1
+clock_gettime: 1
+gettimeofday: 1
+futex: 1
+getrandom: 1
+getpid: 1
+gettid: 1
+
+ppoll: 1
+pipe2: 1
+openat: 1
+dup: 1
+close: 1
+lseek: 1
+getdents64: 1
+faccessat: 1
+recvmsg: 1
+
+process_vm_readv: 1
+
+tgkill: 1
+rt_sigprocmask: 1
+rt_sigaction: 1
+rt_tgsigqueueinfo: 1
+
+#define PR_SET_VMA 0x53564d41
+prctl: arg0 == PR_GET_NO_NEW_PRIVS || arg0 == PR_SET_VMA
+
+#if 0
+libminijail on vendor partitions older than P does not have constants from <sys/mman.h>.
+Define the values of PROT_READ and PROT_WRITE ourselves to maintain backwards compatibility.
+#else
+#define PROT_READ 0x1
+#define PROT_WRITE 0x2
+#endif
+
+madvise: 1
+mprotect: arg2 in PROT_READ|PROT_WRITE
+munmap: 1
+
+#if defined(__LP64__)
+getuid: 1
+fstat: 1
+mmap: arg2 in PROT_READ|PROT_WRITE
+#else
+getuid32: 1
+fstat64: 1
+mmap2: arg2 in PROT_READ|PROT_WRITE
+#endif
+
+// Needed for logging.
+#if defined(__LP64__)
+geteuid: 1
+getgid: 1
+getegid: 1
+getgroups: 1
+#else
+geteuid32: 1
+getgid32: 1
+getegid32: 1
+getgroups32: 1
+#endif
diff --git a/debuggerd/seccomp_policy/crash_dump.x86.policy b/debuggerd/seccomp_policy/crash_dump.x86.policy
new file mode 100644
index 0000000..254330d
--- /dev/null
+++ b/debuggerd/seccomp_policy/crash_dump.x86.policy
@@ -0,0 +1,37 @@
+read: 1
+write: 1
+exit: 1
+rt_sigreturn: 1
+sigreturn: 1
+exit_group: 1
+clock_gettime: 1
+gettimeofday: 1
+futex: 1
+getrandom: 1
+getpid: 1
+gettid: 1
+ppoll: 1
+pipe2: 1
+openat: 1
+dup: 1
+close: 1
+lseek: 1
+getdents64: 1
+faccessat: 1
+recvmsg: 1
+process_vm_readv: 1
+tgkill: 1
+rt_sigprocmask: 1
+rt_sigaction: 1
+rt_tgsigqueueinfo: 1
+prctl: arg0 == PR_GET_NO_NEW_PRIVS || arg0 == 0x53564d41
+madvise: 1
+mprotect: arg2 in 0x1|0x2
+munmap: 1
+getuid32: 1
+fstat64: 1
+mmap2: arg2 in 0x1|0x2
+geteuid32: 1
+getgid32: 1
+getegid32: 1
+getgroups32: 1
diff --git a/debuggerd/seccomp_policy/crash_dump.x86_64.policy b/debuggerd/seccomp_policy/crash_dump.x86_64.policy
new file mode 100644
index 0000000..9b3ef09
--- /dev/null
+++ b/debuggerd/seccomp_policy/crash_dump.x86_64.policy
@@ -0,0 +1,36 @@
+read: 1
+write: 1
+exit: 1
+rt_sigreturn: 1
+exit_group: 1
+clock_gettime: 1
+gettimeofday: 1
+futex: 1
+getrandom: 1
+getpid: 1
+gettid: 1
+ppoll: 1
+pipe2: 1
+openat: 1
+dup: 1
+close: 1
+lseek: 1
+getdents64: 1
+faccessat: 1
+recvmsg: 1
+process_vm_readv: 1
+tgkill: 1
+rt_sigprocmask: 1
+rt_sigaction: 1
+rt_tgsigqueueinfo: 1
+prctl: arg0 == PR_GET_NO_NEW_PRIVS || arg0 == 0x53564d41
+madvise: 1
+mprotect: arg2 in 0x1|0x2
+munmap: 1
+getuid: 1
+fstat: 1
+mmap: arg2 in 0x1|0x2
+geteuid: 1
+getgid: 1
+getegid: 1
+getgroups: 1
diff --git a/debuggerd/seccomp_policy/generate.sh b/debuggerd/seccomp_policy/generate.sh
new file mode 100755
index 0000000..8c58b05
--- /dev/null
+++ b/debuggerd/seccomp_policy/generate.sh
@@ -0,0 +1,10 @@
+#!/bin/bash
+
+set -ex
+
+cd "$(dirname "$0")"
+CPP='cpp -undef -E -P crash_dump.policy.def'
+$CPP -D__arm__ -o crash_dump.arm.policy
+$CPP -D__aarch64__ -D__LP64__ -o crash_dump.arm64.policy
+$CPP -D__i386__ -o crash_dump.x86.policy
+$CPP -D__x86_64__ -D__LP64__ -o crash_dump.x86_64.policy
diff --git a/debuggerd/signal_sender.cpp b/debuggerd/signal_sender.cpp
deleted file mode 100644
index 42a8e77..0000000
--- a/debuggerd/signal_sender.cpp
+++ /dev/null
@@ -1,183 +0,0 @@
-/*
- * Copyright (C) 2016 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.
- */
-
-#define LOG_TAG "debuggerd-signal"
-
-#include <errno.h>
-#include <pthread.h>
-#include <signal.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/socket.h>
-#include <sys/syscall.h>
-#include <sys/types.h>
-#include <sys/wait.h>
-#include <unistd.h>
-
-#include <log/log.h>
-
-#include "signal_sender.h"
-
-static int signal_fd = -1;
-static pid_t signal_pid;
-struct signal_message {
-  pid_t pid;
-  pid_t tid;
-  int signal;
-};
-
-static void set_signal_sender_process_name() {
-#if defined(__LP64__)
-  static constexpr char long_process_name[] = "debuggerd64:signaller";
-  static constexpr char short_process_name[] = "debuggerd64:sig";
-  static_assert(sizeof(long_process_name) <= sizeof("/system/bin/debuggerd64"), "");
-#else
-  static constexpr char long_process_name[] = "debuggerd:signaller";
-  static constexpr char short_process_name[] = "debuggerd:sig";
-  static_assert(sizeof(long_process_name) <= sizeof("/system/bin/debuggerd"), "");
-#endif
-
-  // pthread_setname_np has a maximum length of 16 chars, including null terminator.
-  static_assert(sizeof(short_process_name) <= 16, "");
-  pthread_setname_np(pthread_self(), short_process_name);
-
-  char* progname = const_cast<char*>(getprogname());
-  if (strlen(progname) <= strlen(long_process_name)) {
-    ALOGE("debuggerd: unexpected progname %s", progname);
-    return;
-  }
-
-  memset(progname, 0, strlen(progname));
-  strcpy(progname, long_process_name);
-}
-
-// Fork a process to send signals for the worker processes to use after they've dropped privileges.
-bool start_signal_sender() {
-  if (signal_pid != 0) {
-    ALOGE("debuggerd: attempted to start signal sender multiple times");
-    return false;
-  }
-
-  int sfd[2];
-  if (socketpair(AF_UNIX, SOCK_SEQPACKET | SOCK_CLOEXEC, 0, sfd) != 0) {
-    ALOGE("debuggerd: failed to create socketpair for signal sender: %s", strerror(errno));
-    return false;
-  }
-
-  pid_t parent = getpid();
-  pid_t fork_pid = fork();
-  if (fork_pid == -1) {
-    ALOGE("debuggerd: failed to initialize signal sender: fork failed: %s", strerror(errno));
-    return false;
-  } else if (fork_pid == 0) {
-    close(sfd[1]);
-
-    set_signal_sender_process_name();
-
-    while (true) {
-      signal_message msg;
-      int rc = TEMP_FAILURE_RETRY(read(sfd[0], &msg, sizeof(msg)));
-      if (rc < 0) {
-        ALOGE("debuggerd: signal sender failed to read from socket");
-        break;
-      } else if (rc != sizeof(msg)) {
-        ALOGE("debuggerd: signal sender read unexpected number of bytes: %d", rc);
-        break;
-      }
-
-      // Report success after sending a signal
-      int err = 0;
-      if (msg.tid > 0) {
-        if (syscall(SYS_tgkill, msg.pid, msg.tid, msg.signal) != 0) {
-          err = errno;
-        }
-      } else {
-        if (kill(msg.pid, msg.signal) != 0) {
-          err = errno;
-        }
-      }
-
-      if (TEMP_FAILURE_RETRY(write(sfd[0], &err, sizeof(err))) < 0) {
-        ALOGE("debuggerd: signal sender failed to write: %s", strerror(errno));
-      }
-    }
-
-    // Our parent proably died, but if not, kill them.
-    if (getppid() == parent) {
-      kill(parent, SIGKILL);
-    }
-    _exit(1);
-  } else {
-    close(sfd[0]);
-    signal_fd = sfd[1];
-    signal_pid = fork_pid;
-    return true;
-  }
-}
-
-bool stop_signal_sender() {
-  if (signal_pid <= 0) {
-    return false;
-  }
-
-  if (kill(signal_pid, SIGKILL) != 0) {
-    ALOGE("debuggerd: failed to kill signal sender: %s", strerror(errno));
-    return false;
-  }
-
-  close(signal_fd);
-  signal_fd = -1;
-
-  int status;
-  waitpid(signal_pid, &status, 0);
-  signal_pid = 0;
-
-  return true;
-}
-
-bool send_signal(pid_t pid, pid_t tid, int signal) {
-  if (signal_fd == -1) {
-    ALOGE("debuggerd: attempted to send signal before signal sender was started");
-    errno = EHOSTUNREACH;
-    return false;
-  }
-
-  signal_message msg = {.pid = pid, .tid = tid, .signal = signal };
-  if (TEMP_FAILURE_RETRY(write(signal_fd, &msg, sizeof(msg))) < 0) {
-    ALOGE("debuggerd: failed to send message to signal sender: %s", strerror(errno));
-    errno = EHOSTUNREACH;
-    return false;
-  }
-
-  int response;
-  ssize_t rc = TEMP_FAILURE_RETRY(read(signal_fd, &response, sizeof(response)));
-  if (rc == 0) {
-    ALOGE("debuggerd: received EOF from signal sender");
-    errno = EHOSTUNREACH;
-    return false;
-  } else if (rc < 0) {
-    ALOGE("debuggerd: failed to receive response from signal sender: %s", strerror(errno));
-    errno = EHOSTUNREACH;
-    return false;
-  }
-
-  if (response == 0) {
-    return true;
-  }
-
-  errno = response;
-  return false;
-}
diff --git a/debuggerd/tombstoned/intercept_manager.cpp b/debuggerd/tombstoned/intercept_manager.cpp
index 24960bc..c446dbb 100644
--- a/debuggerd/tombstoned/intercept_manager.cpp
+++ b/debuggerd/tombstoned/intercept_manager.cpp
@@ -185,8 +185,8 @@
 }
 
 InterceptManager::InterceptManager(event_base* base, int intercept_socket) : base(base) {
-  this->listener = evconnlistener_new(base, intercept_accept_cb, this, -1, LEV_OPT_CLOSE_ON_FREE,
-                                      intercept_socket);
+  this->listener = evconnlistener_new(base, intercept_accept_cb, this, LEV_OPT_CLOSE_ON_FREE,
+                                      /* backlog */ -1, intercept_socket);
 }
 
 bool InterceptManager::GetIntercept(pid_t pid, DebuggerdDumpType dump_type,
diff --git a/debuggerd/tombstoned/tombstoned.cpp b/debuggerd/tombstoned/tombstoned.cpp
index 93c7fb5..15ae406 100644
--- a/debuggerd/tombstoned/tombstoned.cpp
+++ b/debuggerd/tombstoned/tombstoned.cpp
@@ -61,10 +61,11 @@
 struct Crash {
   ~Crash() { event_free(crash_event); }
 
-  unique_fd crash_fd;
+  std::string crash_tombstone_path;
+  unique_fd crash_tombstone_fd;
+  unique_fd crash_socket_fd;
   pid_t crash_pid;
   event* crash_event = nullptr;
-  std::string crash_path;
 
   DebuggerdDumpType crash_type;
 };
@@ -109,24 +110,29 @@
     return &queue;
   }
 
-  std::pair<unique_fd, std::string> get_output() {
-    unique_fd result;
-    std::string file_name = StringPrintf("%s%02d", file_name_prefix_.c_str(), next_artifact_);
-
-    // Unlink and create the file, instead of using O_TRUNC, to avoid two processes
-    // interleaving their output in case we ever get into that situation.
-    if (unlinkat(dir_fd_, file_name.c_str(), 0) != 0 && errno != ENOENT) {
-      PLOG(FATAL) << "failed to unlink tombstone at " << dir_path_ << "/" << file_name;
-    }
-
-    result.reset(openat(dir_fd_, file_name.c_str(),
-                        O_CREAT | O_EXCL | O_WRONLY | O_APPEND | O_CLOEXEC, 0640));
+  std::pair<std::string, unique_fd> get_output() {
+    std::string path;
+    unique_fd result(openat(dir_fd_, ".", O_WRONLY | O_APPEND | O_TMPFILE | O_CLOEXEC, 0640));
     if (result == -1) {
-      PLOG(FATAL) << "failed to create tombstone at " << dir_path_ << "/" << file_name;
-    }
+      // We might not have O_TMPFILE. Try creating with an arbitrary filename instead.
+      static size_t counter = 0;
+      std::string tmp_filename = StringPrintf(".temporary%zu", counter++);
+      result.reset(openat(dir_fd_, tmp_filename.c_str(),
+                          O_WRONLY | O_APPEND | O_CREAT | O_TRUNC | O_CLOEXEC, 0640));
+      if (result == -1) {
+        PLOG(FATAL) << "failed to create temporary tombstone in " << dir_path_;
+      }
 
+      path = StringPrintf("%s/%s", dir_path_.c_str(), tmp_filename.c_str());
+    }
+    return std::make_pair(std::move(path), std::move(result));
+  }
+
+  std::string get_next_artifact_path() {
+    std::string file_name =
+        StringPrintf("%s/%s%02d", dir_path_.c_str(), file_name_prefix_.c_str(), next_artifact_);
     next_artifact_ = (next_artifact_ + 1) % max_artifacts_;
-    return {std::move(result), dir_path_ + "/" + file_name};
+    return file_name;
   }
 
   bool maybe_enqueue_crash(Crash* crash) {
@@ -203,14 +209,17 @@
 
 static void perform_request(Crash* crash) {
   unique_fd output_fd;
-  if (!intercept_manager->GetIntercept(crash->crash_pid, crash->crash_type, &output_fd)) {
-    std::tie(output_fd, crash->crash_path) = CrashQueue::for_crash(crash)->get_output();
+  bool intercepted =
+      intercept_manager->GetIntercept(crash->crash_pid, crash->crash_type, &output_fd);
+  if (!intercepted) {
+    std::tie(crash->crash_tombstone_path, output_fd) = CrashQueue::for_crash(crash)->get_output();
+    crash->crash_tombstone_fd.reset(dup(output_fd.get()));
   }
 
   TombstonedCrashPacket response = {
     .packet_type = CrashPacketType::kPerformDump
   };
-  ssize_t rc = send_fd(crash->crash_fd, &response, sizeof(response), std::move(output_fd));
+  ssize_t rc = send_fd(crash->crash_socket_fd, &response, sizeof(response), std::move(output_fd));
   if (rc == -1) {
     PLOG(WARNING) << "failed to send response to CrashRequest";
     goto fail;
@@ -222,7 +231,7 @@
     struct timeval timeout = { 10, 0 };
 
     event_base* base = event_get_base(crash->crash_event);
-    event_assign(crash->crash_event, base, crash->crash_fd, EV_TIMEOUT | EV_READ,
+    event_assign(crash->crash_event, base, crash->crash_socket_fd, EV_TIMEOUT | EV_READ,
                  crash_completed_cb, crash);
     event_add(crash->crash_event, &timeout);
   }
@@ -243,7 +252,7 @@
   // and only native crashes on the native socket.
   struct timeval timeout = { 1, 0 };
   event* crash_event = event_new(base, sockfd, EV_TIMEOUT | EV_READ, crash_request_cb, crash);
-  crash->crash_fd.reset(sockfd);
+  crash->crash_socket_fd.reset(sockfd);
   crash->crash_event = crash_event;
   event_add(crash_event, &timeout);
 }
@@ -342,14 +351,37 @@
     goto fail;
   }
 
-  if (!crash->crash_path.empty()) {
-    if (crash->crash_type == kDebuggerdJavaBacktrace) {
-      LOG(ERROR) << "Traces for pid " << crash->crash_pid << " written to: " << crash->crash_path;
+  if (crash->crash_tombstone_fd != -1) {
+    std::string fd_path = StringPrintf("/proc/self/fd/%d", crash->crash_tombstone_fd.get());
+    std::string tombstone_path = CrashQueue::for_crash(crash)->get_next_artifact_path();
+
+    // linkat doesn't let us replace a file, so we need to unlink first.
+    int rc = unlink(tombstone_path.c_str());
+    if (rc != 0 && errno != ENOENT) {
+      PLOG(ERROR) << "failed to unlink tombstone at " << tombstone_path;
+      goto fail;
+    }
+
+    rc = linkat(AT_FDCWD, fd_path.c_str(), AT_FDCWD, tombstone_path.c_str(), AT_SYMLINK_FOLLOW);
+    if (rc != 0) {
+      PLOG(ERROR) << "failed to link tombstone";
     } else {
-      // NOTE: Several tools parse this log message to figure out where the
-      // tombstone associated with a given native crash was written. Any changes
-      // to this message must be carefully considered.
-      LOG(ERROR) << "Tombstone written to: " << crash->crash_path;
+      if (crash->crash_type == kDebuggerdJavaBacktrace) {
+        LOG(ERROR) << "Traces for pid " << crash->crash_pid << " written to: " << tombstone_path;
+      } else {
+        // NOTE: Several tools parse this log message to figure out where the
+        // tombstone associated with a given native crash was written. Any changes
+        // to this message must be carefully considered.
+        LOG(ERROR) << "Tombstone written to: " << tombstone_path;
+      }
+    }
+
+    // If we don't have O_TMPFILE, we need to clean up after ourselves.
+    if (!crash->crash_tombstone_path.empty()) {
+      rc = unlink(crash->crash_tombstone_path.c_str());
+      if (rc != 0) {
+        PLOG(ERROR) << "failed to unlink temporary tombstone at " << crash->crash_tombstone_path;
+      }
     }
   }
 
@@ -389,8 +421,9 @@
 
   intercept_manager = new InterceptManager(base, intercept_socket);
 
-  evconnlistener* tombstone_listener = evconnlistener_new(
-      base, crash_accept_cb, CrashQueue::for_tombstones(), -1, LEV_OPT_CLOSE_ON_FREE, crash_socket);
+  evconnlistener* tombstone_listener =
+      evconnlistener_new(base, crash_accept_cb, CrashQueue::for_tombstones(), LEV_OPT_CLOSE_ON_FREE,
+                         -1 /* backlog */, crash_socket);
   if (!tombstone_listener) {
     LOG(FATAL) << "failed to create evconnlistener for tombstones.";
   }
@@ -402,8 +435,9 @@
     }
 
     evutil_make_socket_nonblocking(java_trace_socket);
-    evconnlistener* java_trace_listener = evconnlistener_new(
-        base, crash_accept_cb, CrashQueue::for_anrs(), -1, LEV_OPT_CLOSE_ON_FREE, java_trace_socket);
+    evconnlistener* java_trace_listener =
+        evconnlistener_new(base, crash_accept_cb, CrashQueue::for_anrs(), LEV_OPT_CLOSE_ON_FREE,
+                           -1 /* backlog */, java_trace_socket);
     if (!java_trace_listener) {
       LOG(FATAL) << "failed to create evconnlistener for java traces.";
     }
diff --git a/debuggerd/util.cpp b/debuggerd/util.cpp
index c6a997b..50c5efc 100644
--- a/debuggerd/util.cpp
+++ b/debuggerd/util.cpp
@@ -18,8 +18,12 @@
 
 #include <sys/socket.h>
 
+#include <string>
 #include <utility>
 
+#include <android-base/file.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
 #include <android-base/unique_fd.h>
 #include <cutils/sockets.h>
 #include "protocol.h"
@@ -87,12 +91,14 @@
   return result;
 }
 
-bool Pipe(unique_fd* read, unique_fd* write) {
-  int pipefds[2];
-  if (pipe(pipefds) != 0) {
-    return false;
-  }
-  read->reset(pipefds[0]);
-  write->reset(pipefds[1]);
-  return true;
+std::string get_process_name(pid_t pid) {
+  std::string result = "<unknown>";
+  android::base::ReadFileToString(android::base::StringPrintf("/proc/%d/cmdline", pid), &result);
+  return result;
+}
+
+std::string get_thread_name(pid_t tid) {
+  std::string result = "<unknown>";
+  android::base::ReadFileToString(android::base::StringPrintf("/proc/%d/comm", tid), &result);
+  return android::base::Trim(result);
 }
diff --git a/debuggerd/util.h b/debuggerd/util.h
index 6051714..8260b44 100644
--- a/debuggerd/util.h
+++ b/debuggerd/util.h
@@ -16,6 +16,8 @@
 
 #pragma once
 
+#include <string>
+
 #include <sys/cdefs.h>
 #include <sys/types.h>
 
@@ -43,4 +45,5 @@
 ssize_t recv_fd(int sockfd, void* _Nonnull data, size_t len,
                 android::base::unique_fd* _Nullable out_fd);
 
-bool Pipe(android::base::unique_fd* read, android::base::unique_fd* write);
+std::string get_process_name(pid_t pid);
+std::string get_thread_name(pid_t tid);
diff --git a/demangle/Android.bp b/demangle/Android.bp
index e55c886..fd79cf8 100644
--- a/demangle/Android.bp
+++ b/demangle/Android.bp
@@ -24,23 +24,30 @@
         "-Werror",
         "-Wextra",
     ],
+
+    target: {
+        linux_bionic: {
+            enabled: true,
+        },
+    },
 }
 
 cc_library {
     name: "libdemangle",
     defaults: ["libdemangle_defaults"],
     vendor_available: true,
+    recovery_available: true,
 
     srcs: [
         "Demangler.cpp",
     ],
 
     local_include_dirs: [
-       "include",
+        "include",
     ],
 
     export_include_dirs: [
-       "include",
+        "include",
     ],
 }
 
@@ -72,4 +79,9 @@
     shared_libs: [
         "libdemangle",
     ],
+
+    test_suites: ["device-tests"],
+    required: [
+        "libdemangle",
+    ],
 }
diff --git a/demangle/Android.mk b/demangle/Android.mk
index e3cfc2a..d8082a9 100644
--- a/demangle/Android.mk
+++ b/demangle/Android.mk
@@ -19,7 +19,6 @@
 include $(CLEAR_VARS)
 
 LOCAL_MODULE := demangle_fuzzer
-LOCAL_MODULE_TAGS := optional
 LOCAL_SRC_FILES := \
     Demangler.cpp \
     demangle_fuzzer.cpp \
diff --git a/demangle/DemangleTest.cpp b/demangle/DemangleTest.cpp
index f56a9be..1787031 100644
--- a/demangle/DemangleTest.cpp
+++ b/demangle/DemangleTest.cpp
@@ -334,6 +334,29 @@
   // Template within templates.
   ASSERT_EQ("one::two<three<char, int>>", demangler.Parse("_ZN3one3twoIN5threeIciEEEE"));
   ASSERT_EQ("one::two<three<char, four<int>>>", demangler.Parse("_ZN3one3twoIN5threeIcN4fourIiEEEEEE"));
+
+  ASSERT_EQ("one<char>", demangler.Parse("_Z3oneIcE"));
+  ASSERT_EQ("one<void>", demangler.Parse("_Z3oneIvE"));
+  ASSERT_EQ("one<void*>", demangler.Parse("_Z3oneIPvE"));
+  ASSERT_EQ("one<void const>", demangler.Parse("_Z3oneIKvE"));
+  ASSERT_EQ("one<char, int, bool>", demangler.Parse("_Z3oneIcibE"));
+  ASSERT_EQ("one(two<three>)", demangler.Parse("_Z3one3twoIN5threeEE"));
+  ASSERT_EQ("one<char, int, two::three>", demangler.Parse("_Z3oneIciN3two5threeEE"));
+  // Template within templates.
+  ASSERT_EQ("one(two<three<char, int>>)", demangler.Parse("_Z3one3twoIN5threeIciEEE"));
+  ASSERT_EQ("one(two<three<char, four<int>>>)",
+            demangler.Parse("_Z3one3twoIN5threeIcN4fourIiEEEEE"));
+}
+
+TEST(DemangleTest, TemplateFunctionWithReturnType) {
+  Demangler demangler;
+
+  ASSERT_EQ("char one<int>(char)", demangler.Parse("_Z3oneIiEcc"));
+  ASSERT_EQ("void one<int>()", demangler.Parse("_Z3oneIiEvv"));
+  ASSERT_EQ("char one<int>()", demangler.Parse("_Z3oneIiEcv"));
+  ASSERT_EQ("char one<int>(void, void)", demangler.Parse("_Z3oneIiEcvv"));
+  ASSERT_EQ("char one<int>()", demangler.Parse("_ZN3oneIiEEcv"));
+  ASSERT_EQ("char one<int>(void, void)", demangler.Parse("_ZN3oneIiEEcvv"));
 }
 
 TEST(DemangleTest, TemplateArguments) {
@@ -410,6 +433,28 @@
             demangler.Parse("_ZN3one3two5three4fourINS_4fiveEED2EPS3_"));
 }
 
+TEST(DemangleTest, TemplateSubstitution) {
+  Demangler demangler;
+
+  ASSERT_EQ("void one<int, double>(int)", demangler.Parse("_ZN3oneIidEEvT_"));
+  ASSERT_EQ("void one<int, double>(double)", demangler.Parse("_ZN3oneIidEEvT0_"));
+  ASSERT_EQ("void one<int, double, char, void>(char)", demangler.Parse("_ZN3oneIidcvEEvT1_"));
+
+  ASSERT_EQ("void one<int, double>(int)", demangler.Parse("_Z3oneIidEvT_"));
+  ASSERT_EQ("void one<int, double>(double)", demangler.Parse("_Z3oneIidEvT0_"));
+  ASSERT_EQ("void one<int, double, char, void>(char)", demangler.Parse("_Z3oneIidcvEvT1_"));
+
+  ASSERT_EQ("void one<a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r>(l)",
+            demangler.Parse("_ZN3oneI1a1b1c1d1e1f1g1h1i1j1k1l1m1n1o1p1q1rEEvT10_"));
+  ASSERT_EQ("void one<a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r>(m)",
+            demangler.Parse("_ZN3oneI1a1b1c1d1e1f1g1h1i1j1k1l1m1n1o1p1q1rEEvT11_"));
+
+  ASSERT_EQ("void one<a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r>(l)",
+            demangler.Parse("_Z3oneI1a1b1c1d1e1f1g1h1i1j1k1l1m1n1o1p1q1rEvT10_"));
+  ASSERT_EQ("void one<a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r>(m)",
+            demangler.Parse("_Z3oneI1a1b1c1d1e1f1g1h1i1j1k1l1m1n1o1p1q1rEvT11_"));
+}
+
 TEST(DemangleTest, StringTooLong) {
   Demangler demangler;
 
@@ -428,6 +473,65 @@
   ASSERT_EQ("_ZN3one3twoEDa", demangler.Parse("_ZN3one3twoEDa", 12));
 }
 
+TEST(DemangleTest, BooleanLiterals) {
+  Demangler demangler;
+
+  ASSERT_EQ("one<true>", demangler.Parse("_ZN3oneILb1EEE"));
+  ASSERT_EQ("one<false>", demangler.Parse("_ZN3oneILb0EEE"));
+  ASSERT_EQ("one<false, true>", demangler.Parse("_ZN3oneILb0ELb1EEE"));
+
+  ASSERT_EQ("one<true>", demangler.Parse("_Z3oneILb1EE"));
+  ASSERT_EQ("one<false>", demangler.Parse("_Z3oneILb0EE"));
+  ASSERT_EQ("one<false, true>", demangler.Parse("_Z3oneILb0ELb1EE"));
+
+  ASSERT_EQ("one(two<three<four>, false, true>)",
+            demangler.Parse("_ZN3oneE3twoI5threeI4fourELb0ELb1EE"));
+}
+
+TEST(DemangleTest, non_virtual_thunk) {
+  Demangler demangler;
+
+  ASSERT_EQ("non-virtual thunk to one", demangler.Parse("_ZThn0_N3oneE"));
+  ASSERT_EQ("non-virtual thunk to two", demangler.Parse("_ZThn0_3two"));
+  ASSERT_EQ("non-virtual thunk to three", demangler.Parse("_ZTh0_5three"));
+  ASSERT_EQ("non-virtual thunk to four", demangler.Parse("_ZTh_4four"));
+  ASSERT_EQ("non-virtual thunk to five", demangler.Parse("_ZTh0123456789_4five"));
+  ASSERT_EQ("non-virtual thunk to six", demangler.Parse("_ZThn0123456789_3six"));
+
+  ASSERT_EQ("_ZThn0N3oneE", demangler.Parse("_ZThn0N3oneE"));
+  ASSERT_EQ("_ZThn03two", demangler.Parse("_ZThn03two"));
+  ASSERT_EQ("_ZTh05three", demangler.Parse("_ZTh05three"));
+  ASSERT_EQ("_ZTh4four", demangler.Parse("_ZTh4four"));
+  ASSERT_EQ("_ZTh01234567894five", demangler.Parse("_ZTh01234567894five"));
+  ASSERT_EQ("_ZThn01234567893six", demangler.Parse("_ZThn01234567893six"));
+  ASSERT_EQ("_ZT_N3oneE", demangler.Parse("_ZT_N3oneE"));
+  ASSERT_EQ("_ZT0_N3oneE", demangler.Parse("_ZT0_N3oneE"));
+  ASSERT_EQ("_ZTH_N3oneE", demangler.Parse("_ZTH_N3oneE"));
+}
+
+TEST(DemangleTest, r_value_reference) {
+  Demangler demangler;
+  ASSERT_EQ(
+      "android::SurfaceComposerClient::Transaction::merge(android::SurfaceComposerClient::"
+      "Transaction&&)",
+      demangler.Parse("_ZN7android21SurfaceComposerClient11Transaction5mergeEOS1_"));
+}
+
+TEST(DemangleTest, initial_St) {
+  Demangler demangler;
+  EXPECT_EQ("std::state", demangler.Parse("_ZSt5state"));
+  EXPECT_EQ("std::_In::ward", demangler.Parse("_ZNSt3_In4wardE"));
+  EXPECT_EQ("std::__terminate(void (*)())", demangler.Parse("_ZSt11__terminatePFvvE"));
+}
+
+TEST(DemangleTest, cfi) {
+  Demangler demangler;
+  EXPECT_EQ("nfa_sys_ptim_timer_update(tPTIM_CB*)",
+            demangler.Parse("_Z25nfa_sys_ptim_timer_updateP8tPTIM_CB"));
+  EXPECT_EQ("nfa_sys_ptim_timer_update(tPTIM_CB*) [clone .cfi]",
+            demangler.Parse("_Z25nfa_sys_ptim_timer_updateP8tPTIM_CB.cfi"));
+}
+
 TEST(DemangleTest, demangle) {
   std::string str;
 
diff --git a/demangle/Demangler.cpp b/demangle/Demangler.cpp
index c0a96aa..7a3aa81 100644
--- a/demangle/Demangler.cpp
+++ b/demangle/Demangler.cpp
@@ -347,6 +347,33 @@
   return name + 1;
 }
 
+const char* Demangler::ParseT(const char* name) {
+  if (template_saves_.empty()) {
+    return nullptr;
+  }
+
+  if (*name == '_') {
+    last_save_name_ = false;
+    AppendCurrent(template_saves_[0]);
+    return name + 1;
+  }
+
+  // Need to get the total number.
+  char* end;
+  unsigned long int index = strtoul(name, &end, 10) + 1;
+  if (name == end || *end != '_') {
+    return nullptr;
+  }
+
+  if (index >= template_saves_.size()) {
+    return nullptr;
+  }
+
+  last_save_name_ = false;
+  AppendCurrent(template_saves_[index]);
+  return end + 1;
+}
+
 const char* Demangler::ParseFunctionName(const char* name) {
   if (*name == 'E') {
     if (parse_funcs_.empty()) {
@@ -361,7 +388,7 @@
       saves_.pop_back();
     }
 
-    function_name_ = cur_state_.str;
+    function_name_ += cur_state_.str;
     while (!cur_state_.suffixes.empty()) {
       function_suffix_ += cur_state_.suffixes.back();
       cur_state_.suffixes.pop_back();
@@ -371,9 +398,28 @@
     return name + 1;
   }
 
+  if (*name == 'I') {
+    state_stack_.push(cur_state_);
+    cur_state_.Clear();
+
+    parse_funcs_.push_back(parse_func_);
+    parse_func_ = &Demangler::ParseFunctionNameTemplate;
+    return name + 1;
+  }
+
   return ParseComplexString(name);
 }
 
+const char* Demangler::ParseFunctionNameTemplate(const char* name) {
+  if (*name == 'E' && name[1] == 'E') {
+    // Only consider this a template with saves if it is right before
+    // the end of the name.
+    template_found_ = true;
+    template_saves_ = cur_state_.args;
+  }
+  return ParseTemplateArgumentsComplex(name);
+}
+
 const char* Demangler::ParseComplexArgument(const char* name) {
   if (*name == 'E') {
     if (parse_funcs_.empty()) {
@@ -534,6 +580,10 @@
     }
     return name + 1;
 
+  case 'O':
+    cur_state_.suffixes.push_back("&&");
+    return name + 1;
+
   case 'K':
   case 'V': {
     const char* suffix;
@@ -655,11 +705,37 @@
         cur_state_.str.clear();
       }
       return name;
+    } else if (strcmp(name, ".cfi") == 0) {
+      function_suffix_ += " [clone .cfi]";
+      return name + 4;
     }
   }
   return nullptr;
 }
 
+const char* Demangler::ParseTemplateLiteral(const char* name) {
+  if (*name == 'E') {
+    parse_func_ = parse_funcs_.back();
+    parse_funcs_.pop_back();
+    return name + 1;
+  }
+  // Only understand boolean values with 0 or 1.
+  if (*name == 'b') {
+    name++;
+    if (*name == '0') {
+      AppendArgument("false");
+      cur_state_.str.clear();
+    } else if (*name == '1') {
+      AppendArgument("true");
+      cur_state_.str.clear();
+    } else {
+      return nullptr;
+    }
+    return name + 1;
+  }
+  return nullptr;
+}
+
 const char* Demangler::ParseTemplateArgumentsComplex(const char* name) {
   if (*name == 'E') {
     if (parse_funcs_.empty()) {
@@ -667,10 +743,17 @@
     }
     parse_func_ = parse_funcs_.back();
     parse_funcs_.pop_back();
+
     FinalizeTemplate();
     Save(cur_state_.str, false);
     return name + 1;
+  } else if (*name == 'L') {
+    // Literal value for a template.
+    parse_funcs_.push_back(parse_func_);
+    parse_func_ = &Demangler::ParseTemplateLiteral;
+    return name + 1;
   }
+
   return ParseArguments(name);
 }
 
@@ -685,17 +768,71 @@
     AppendArgument(cur_state_.str);
     cur_state_.str.clear();
     return name + 1;
+  } else if (*name == 'L') {
+    // Literal value for a template.
+    parse_funcs_.push_back(parse_func_);
+    parse_func_ = &Demangler::ParseTemplateLiteral;
+    return name + 1;
   }
+
   return ParseArguments(name);
 }
 
+const char* Demangler::ParseFunctionTemplateArguments(const char* name) {
+  if (*name == 'E') {
+    parse_func_ = parse_funcs_.back();
+    parse_funcs_.pop_back();
+
+    function_name_ += '<' + GetArgumentsString() + '>';
+    template_found_ = true;
+    template_saves_ = cur_state_.args;
+    cur_state_.Clear();
+    return name + 1;
+  }
+  return ParseTemplateArgumentsComplex(name);
+}
+
 const char* Demangler::FindFunctionName(const char* name) {
+  if (*name == 'T') {
+    // non-virtual thunk, verify that it matches one of these patterns:
+    //   Thn[0-9]+_
+    //   Th[0-9]+_
+    //   Thn_
+    //   Th_
+    name++;
+    if (*name != 'h') {
+      return nullptr;
+    }
+    name++;
+    if (*name == 'n') {
+      name++;
+    }
+    while (std::isdigit(*name)) {
+      name++;
+    }
+    if (*name != '_') {
+      return nullptr;
+    }
+    function_name_ = "non-virtual thunk to ";
+    return name + 1;
+  }
+
   if (*name == 'N') {
-    parse_funcs_.push_back(&Demangler::ParseArguments);
+    parse_funcs_.push_back(&Demangler::ParseArgumentsAtTopLevel);
     parse_func_ = &Demangler::ParseFunctionName;
     return name + 1;
   }
 
+  if (*name == 'S') {
+    name++;
+    if (*name == 't') {
+      function_name_ = "std::";
+      name++;
+    } else {
+      return nullptr;
+    }
+  }
+
   if (std::isdigit(*name)) {
     name = GetStringFromLength(name, &function_name_);
   } else if (*name == 'L' && std::isdigit(name[1])) {
@@ -704,11 +841,35 @@
     name = AppendOperatorString(name);
     function_name_ = cur_state_.str;
   }
-  parse_func_ = &Demangler::ParseArguments;
   cur_state_.Clear();
+
+  // Check for a template argument, which will still be part of the function
+  // name.
+  if (name != nullptr && *name == 'I') {
+    parse_funcs_.push_back(&Demangler::ParseArgumentsAtTopLevel);
+    parse_func_ = &Demangler::ParseFunctionTemplateArguments;
+    return name + 1;
+  }
+  parse_func_ = &Demangler::ParseArgumentsAtTopLevel;
   return name;
 }
 
+const char* Demangler::ParseArgumentsAtTopLevel(const char* name) {
+  // At the top level is the only place where T is allowed.
+  if (*name == 'T') {
+    name++;
+    name = ParseT(name);
+    if (name == nullptr) {
+      return nullptr;
+    }
+    AppendArgument(cur_state_.str);
+    cur_state_.str.clear();
+    return name;
+  }
+
+  return Demangler::ParseArguments(name);
+}
+
 std::string Demangler::Parse(const char* name, size_t max_length) {
   if (name[0] == '\0' || name[0] != '_' || name[1] == '\0' || name[1] != 'Z') {
     // Name is not mangled.
@@ -729,6 +890,21 @@
     return name;
   }
 
+  std::string return_type;
+  if (template_found_) {
+    // Only a single argument with a template is not allowed.
+    if (cur_state_.args.size() == 1) {
+      return name;
+    }
+
+    // If there are at least two arguments, this template has a return type.
+    if (cur_state_.args.size() > 1) {
+      // The first argument will be the return value.
+      return_type = cur_state_.args[0] + ' ';
+      cur_state_.args.erase(cur_state_.args.begin());
+    }
+  }
+
   std::string arg_str;
   if (cur_state_.args.size() == 1 && cur_state_.args[0] == "void") {
     // If the only argument is void, then don't print any args.
@@ -739,7 +915,7 @@
       arg_str = '(' + arg_str + ')';
     }
   }
-  return function_name_ + arg_str + function_suffix_;
+  return return_type + function_name_ + arg_str + function_suffix_;
 }
 
 std::string demangle(const char* name) {
diff --git a/demangle/Demangler.h b/demangle/Demangler.h
index 3bd4f3c..3b7d44e 100644
--- a/demangle/Demangler.h
+++ b/demangle/Demangler.h
@@ -39,6 +39,7 @@
   std::string GetArgumentsString();
   void FinalizeTemplate();
   const char* ParseS(const char* name);
+  const char* ParseT(const char* name);
   const char* AppendOperatorString(const char* name);
   void Save(const std::string& str, bool is_name);
 
@@ -50,17 +51,21 @@
     first_save_.clear();
     cur_state_.Clear();
     saves_.clear();
+    template_saves_.clear();
     while (!state_stack_.empty()) {
       state_stack_.pop();
     }
     last_save_name_ = false;
+    template_found_ = false;
   }
 
   using parse_func_type = const char* (Demangler::*)(const char*);
   parse_func_type parse_func_;
   std::vector<parse_func_type> parse_funcs_;
   std::vector<std::string> saves_;
+  std::vector<std::string> template_saves_;
   bool last_save_name_;
+  bool template_found_;
 
   std::string function_name_;
   std::string function_suffix_;
@@ -89,11 +94,15 @@
   // Parsing functions.
   const char* ParseComplexString(const char* name);
   const char* ParseComplexArgument(const char* name);
+  const char* ParseArgumentsAtTopLevel(const char* name);
   const char* ParseArguments(const char* name);
   const char* ParseTemplateArguments(const char* name);
   const char* ParseTemplateArgumentsComplex(const char* name);
+  const char* ParseTemplateLiteral(const char* name);
   const char* ParseFunctionArgument(const char* name);
   const char* ParseFunctionName(const char* name);
+  const char* ParseFunctionNameTemplate(const char* name);
+  const char* ParseFunctionTemplateArguments(const char* name);
   const char* FindFunctionName(const char* name);
   const char* Fail(const char*) { return nullptr; }
 
diff --git a/demangle/OWNERS b/demangle/OWNERS
new file mode 100644
index 0000000..6f7e4a3
--- /dev/null
+++ b/demangle/OWNERS
@@ -0,0 +1 @@
+cferris@google.com
diff --git a/diagnose_usb/Android.bp b/diagnose_usb/Android.bp
new file mode 100644
index 0000000..6bee28c
--- /dev/null
+++ b/diagnose_usb/Android.bp
@@ -0,0 +1,14 @@
+cc_library_static {
+    name: "libdiagnose_usb",
+    cflags: ["-Wall", "-Wextra", "-Werror"],
+    host_supported: true,
+    recovery_available: 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/diagnose_usb/diagnose_usb.cpp b/diagnose_usb/diagnose_usb.cpp
new file mode 100644
index 0000000..5695ece
--- /dev/null
+++ b/diagnose_usb/diagnose_usb.cpp
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2015 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 "diagnose_usb.h"
+
+#include <errno.h>
+#include <unistd.h>
+
+#include <string>
+
+#include <android-base/stringprintf.h>
+
+#if defined(__linux__)
+#include <grp.h>
+#include <pwd.h>
+#endif
+
+static const char kPermissionsHelpUrl[] = "http://developer.android.com/tools/device.html";
+
+// 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__) && !defined(__BIONIC__)
+    errno = 0;
+    group* plugdev_group = getgrnam("plugdev");
+
+    if (plugdev_group == nullptr) {
+        if (errno != 0) {
+            perror("failed to read plugdev group info");
+        }
+        // We can't give any generally useful advice here, just let the caller print the help URL.
+        return "";
+    }
+
+    // getgroups(2) indicates that the GNU group_member(3) may not check the egid so we check it
+    // additionally just to be sure.
+    if (group_member(plugdev_group->gr_gid) || getegid() == plugdev_group->gr_gid) {
+        // The user is in plugdev so the problem is likely with the udev rules.
+        return "user in plugdev group; are your udev rules wrong?";
+    }
+    passwd* pwd = getpwuid(getuid());
+    return android::base::StringPrintf("user %s is not in the plugdev group",
+                                       pwd ? pwd->pw_name : "?");
+#else
+    return "";
+#endif
+}
+
+// Short help text must be a single line, and will look something like:
+//
+//   no permissions (reason); see [URL]
+std::string UsbNoPermissionsShortHelpText() {
+    std::string help_text = "no permissions";
+
+    std::string problem(GetUdevProblem());
+    if (!problem.empty()) help_text += " (" + problem + ")";
+
+    return android::base::StringPrintf("%s; see [%s]", help_text.c_str(), kPermissionsHelpUrl);
+}
+
+// Long help text can span multiple lines but doesn't currently provide more detailed information:
+//
+//   insufficient permissions for device: reason
+//   See [URL] for more information
+std::string UsbNoPermissionsLongHelpText() {
+    std::string header = "insufficient permissions for device";
+
+    std::string problem(GetUdevProblem());
+    if (!problem.empty()) header += ": " + problem;
+
+    return android::base::StringPrintf("%s\nSee [%s] for more information", header.c_str(),
+                                       kPermissionsHelpUrl);
+}
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.bp b/fastboot/Android.bp
new file mode 100644
index 0000000..2b4a954
--- /dev/null
+++ b/fastboot/Android.bp
@@ -0,0 +1,56 @@
+cc_library_host_static {
+    name: "libfastboot2",
+
+    //host_supported: true,
+
+    compile_multilib: "first",
+    srcs: [
+      "bootimg_utils.cpp",
+      "fs.cpp",
+      "socket.cpp",
+      "tcp.cpp",
+      "udp.cpp",
+      "util.cpp",
+      "fastboot_driver.cpp",
+    ],
+
+    static_libs: [
+      "libziparchive",
+      "libsparse",
+      "libutils",
+      "liblog",
+      "libz",
+      "libdiagnose_usb",
+      "libbase",
+      "libcutils",
+      "libgtest",
+      "libgtest_main",
+      "libbase",
+      "libadb_host"
+    ],
+
+    header_libs: [
+      "bootimg_headers"
+    ],
+
+    export_header_lib_headers: [
+      "bootimg_headers"
+    ],
+
+
+    target: {
+      linux: {
+        srcs: ["usb_linux.cpp"],
+      },
+    },
+
+    cflags: [
+      "-Wall",
+      "-Wextra",
+      "-Werror",
+      "-Wunreachable-code",
+    ],
+
+    export_include_dirs: ["."],
+
+}
diff --git a/fastboot/Android.mk b/fastboot/Android.mk
index 80def73..60c338c 100644
--- a/fastboot/Android.mk
+++ b/fastboot/Android.mk
@@ -16,48 +16,17 @@
 
 include $(LOCAL_PATH)/../platform_tools_tool_version.mk
 
-include $(CLEAR_VARS)
-
-LOCAL_CFLAGS += -DFASTBOOT_VERSION="\"$(tool_version)\""
-
-LOCAL_C_INCLUDES := \
-  $(LOCAL_PATH)/../adb \
-  $(LOCAL_PATH)/../mkbootimg \
-  $(LOCAL_PATH)/../../extras/f2fs_utils \
-
-LOCAL_SRC_FILES := \
-    bootimg_utils.cpp \
-    engine.cpp \
-    fastboot.cpp \
-    fs.cpp\
-    protocol.cpp \
-    socket.cpp \
-    tcp.cpp \
-    udp.cpp \
-    util.cpp \
-
-LOCAL_MODULE := fastboot
-LOCAL_MODULE_TAGS := debug
-LOCAL_MODULE_HOST_OS := darwin linux windows
-LOCAL_CFLAGS += -Wall -Wextra -Werror -Wunreachable-code
-
-LOCAL_SRC_FILES_linux := usb_linux.cpp
-LOCAL_STATIC_LIBRARIES_linux := libselinux
-
-LOCAL_SRC_FILES_darwin := usb_osx.cpp
-LOCAL_STATIC_LIBRARIES_darwin := libselinux
-LOCAL_LDLIBS_darwin := -lpthread -framework CoreFoundation -framework IOKit -framework Carbon
-LOCAL_CFLAGS_darwin := -Wno-unused-parameter
-
-LOCAL_SRC_FILES_windows := usb_windows.cpp
-LOCAL_STATIC_LIBRARIES_windows := AdbWinApi
-LOCAL_REQUIRED_MODULES_windows := AdbWinApi AdbWinUsbApi
-LOCAL_LDLIBS_windows := -lws2_32
-LOCAL_C_INCLUDES_windows := development/host/windows/usb/api
-
-LOCAL_STATIC_LIBRARIES := \
+fastboot_cflags := -Wall -Wextra -Werror -Wunreachable-code
+fastboot_cflags += -DFASTBOOT_VERSION="\"$(tool_version)\""
+fastboot_cflags_darwin := -Wno-unused-parameter
+fastboot_ldlibs_darwin := -lpthread -framework CoreFoundation -framework IOKit -framework Carbon
+fastboot_ldlibs_windows := -lws2_32
+# Don't add anything here, we don't want additional shared dependencies
+# on the host fastboot tool, and shared libraries that link against libc++
+# will violate ODR.
+fastboot_shared_libs :=
+fastboot_static_libs := \
     libziparchive \
-    libext4_utils \
     libsparse \
     libutils \
     liblog \
@@ -67,66 +36,104 @@
     libcutils \
     libgtest_host \
 
-# libf2fs_dlutils_host will dlopen("libf2fs_fmt_host_dyn")
-LOCAL_CFLAGS_linux := -DUSE_F2FS
-LOCAL_LDFLAGS_linux := -ldl -rdynamic -Wl,-rpath,.
-LOCAL_REQUIRED_MODULES_linux := libf2fs_fmt_host_dyn
-# The following libf2fs_* are from system/extras/f2fs_utils,
-# and do not use code in external/f2fs-tools.
-LOCAL_STATIC_LIBRARIES_linux += libf2fs_utils_host libf2fs_ioutils_host libf2fs_dlutils_host
+fastboot_stl := libc++_static
 
-LOCAL_CXX_STL := libc++_static
+#
+# Build host libfastboot.
+#
 
-# Don't add anything here, we don't want additional shared dependencies
-# on the host fastboot tool, and shared libraries that link against libc++
-# will violate ODR
-LOCAL_SHARED_LIBRARIES :=
-
-include $(BUILD_HOST_EXECUTABLE)
-
-my_dist_files := $(LOCAL_BUILT_MODULE)
-ifeq ($(HOST_OS),linux)
-my_dist_files += $(HOST_LIBRARY_PATH)/libf2fs_fmt_host_dyn$(HOST_SHLIB_SUFFIX)
-endif
-$(call dist-for-goals,dist_files sdk win_sdk,$(my_dist_files))
-ifdef HOST_CROSS_OS
-# Archive fastboot.exe for win_sdk build.
-$(call dist-for-goals,win_sdk,$(ALL_MODULES.host_cross_fastboot.BUILT))
-endif
-my_dist_files :=
-
-ifeq ($(HOST_OS),linux)
 include $(CLEAR_VARS)
-LOCAL_SRC_FILES := usbtest.cpp usb_linux.cpp util.cpp
-LOCAL_MODULE := usbtest
-LOCAL_CFLAGS := -Werror
-LOCAL_STATIC_LIBRARIES := libbase
-include $(BUILD_HOST_EXECUTABLE)
-endif
-
-# fastboot_test
-# =========================================================
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := fastboot_test
+LOCAL_MODULE := libfastboot
 LOCAL_MODULE_HOST_OS := darwin linux windows
 
 LOCAL_SRC_FILES := \
+    bootimg_utils.cpp \
+    engine.cpp \
+    fastboot.cpp \
+    fs.cpp \
     socket.cpp \
+    tcp.cpp \
+    udp.cpp \
+    util.cpp \
+    fastboot_driver.cpp \
+
+LOCAL_SRC_FILES_darwin := usb_osx.cpp
+LOCAL_SRC_FILES_linux := usb_linux.cpp
+LOCAL_SRC_FILES_windows := usb_windows.cpp
+
+LOCAL_C_INCLUDES_windows := development/host/windows/usb/api
+LOCAL_CFLAGS := $(fastboot_cflags)
+LOCAL_CFLAGS_darwin := $(fastboot_cflags_darwin)
+LOCAL_CXX_STL := $(fastboot_stl)
+LOCAL_HEADER_LIBRARIES := bootimg_headers
+LOCAL_LDLIBS_darwin := $(fastboot_ldlibs_darwin)
+LOCAL_LDLIBS_windows := $(fastboot_ldlibs_windows)
+LOCAL_SHARED_LIBRARIES := $(fastboot_shared_libs)
+LOCAL_STATIC_LIBRARIES := $(fastboot_static_libs)
+include $(BUILD_HOST_STATIC_LIBRARY)
+
+#
+# Build host fastboot / fastboot.exe
+#
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := fastboot
+LOCAL_MODULE_HOST_OS := darwin linux windows
+
+LOCAL_CFLAGS := $(fastboot_cflags)
+LOCAL_CFLAGS_darwin := $(fastboot_cflags_darwin)
+LOCAL_CXX_STL := $(fastboot_stl)
+LOCAL_HEADER_LIBRARIES := bootimg_headers
+LOCAL_LDLIBS_darwin := $(fastboot_ldlibs_darwin)
+LOCAL_LDLIBS_windows := $(fastboot_ldlibs_windows)
+LOCAL_REQUIRED_MODULES := mke2fs make_f2fs
+LOCAL_REQUIRED_MODULES_darwin := e2fsdroid mke2fs.conf sload_f2fs
+LOCAL_REQUIRED_MODULES_linux := e2fsdroid mke2fs.conf sload_f2fs
+LOCAL_REQUIRED_MODULES_windows := AdbWinUsbApi
+LOCAL_SRC_FILES := main.cpp
+LOCAL_SHARED_LIBRARIES := $(fastboot_shared_libs)
+LOCAL_SHARED_LIBRARIES_windows := AdbWinApi
+LOCAL_STATIC_LIBRARIES := libfastboot $(fastboot_static_libs)
+include $(BUILD_HOST_EXECUTABLE)
+
+#
+# Package fastboot-related executables.
+#
+
+my_dist_files := $(HOST_OUT_EXECUTABLES)/fastboot
+my_dist_files += $(HOST_OUT_EXECUTABLES)/mke2fs
+my_dist_files += $(HOST_OUT_EXECUTABLES)/e2fsdroid
+my_dist_files += $(HOST_OUT_EXECUTABLES)/make_f2fs
+my_dist_files += $(HOST_OUT_EXECUTABLES)/sload_f2fs
+$(call dist-for-goals,dist_files sdk win_sdk,$(my_dist_files))
+ifdef HOST_CROSS_OS
+$(call dist-for-goals,dist_files sdk win_sdk,$(ALL_MODULES.host_cross_fastboot.BUILT))
+endif
+my_dist_files :=
+
+#
+# Build host fastboot_test.
+#
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := fastboot_test
+LOCAL_MODULE_HOST_OS := darwin linux windows
+LOCAL_MODULE_HOST_CROSS_ARCH := x86 # Avoid trying to build for win64.
+
+LOCAL_SRC_FILES := \
+    fastboot_test.cpp \
     socket_mock.cpp \
     socket_test.cpp \
-    tcp.cpp \
     tcp_test.cpp \
-    udp.cpp \
     udp_test.cpp \
 
-LOCAL_STATIC_LIBRARIES := libbase libcutils
-
-LOCAL_CFLAGS += -Wall -Wextra -Werror -Wunreachable-code
-
-LOCAL_LDLIBS_darwin := -lpthread -framework CoreFoundation -framework IOKit -framework Carbon
-LOCAL_CFLAGS_darwin := -Wno-unused-parameter
-
-LOCAL_LDLIBS_windows := -lws2_32
-
+LOCAL_CFLAGS := $(fastboot_cflags)
+LOCAL_CFLAGS_darwin := $(fastboot_cflags_darwin)
+LOCAL_CXX_STL := $(fastboot_stl)
+LOCAL_HEADER_LIBRARIES := bootimg_headers
+LOCAL_LDLIBS_darwin := $(fastboot_ldlibs_darwin)
+LOCAL_LDLIBS_windows := $(fastboot_ldlibs_windows)
+LOCAL_SHARED_LIBRARIES := $(fastboot_shared_libs)
+LOCAL_SHARED_LIBRARIES_windows := AdbWinApi
+LOCAL_STATIC_LIBRARIES := libfastboot $(fastboot_static_libs)
 include $(BUILD_HOST_NATIVE_TEST)
diff --git a/fastboot/OWNERS b/fastboot/OWNERS
new file mode 100644
index 0000000..2088ae3
--- /dev/null
+++ b/fastboot/OWNERS
@@ -0,0 +1,4 @@
+dpursell@google.com
+enh@google.com
+jmgao@google.com
+tomcherry@google.com
diff --git a/fastboot/bootimg_utils.cpp b/fastboot/bootimg_utils.cpp
index c1028ef..1152007 100644
--- a/fastboot/bootimg_utils.cpp
+++ b/fastboot/bootimg_utils.cpp
@@ -28,50 +28,50 @@
 
 #include "bootimg_utils.h"
 
+#include "util.h"
+
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 
-void bootimg_set_cmdline(boot_img_hdr* h, const char* cmdline)
-{
-    strcpy((char*) h->cmdline, cmdline);
+void bootimg_set_cmdline(boot_img_hdr_v1* h, const std::string& cmdline) {
+    if (cmdline.size() >= sizeof(h->cmdline)) die("command line too large: %zu", cmdline.size());
+    strcpy(reinterpret_cast<char*>(h->cmdline), cmdline.c_str());
 }
 
-boot_img_hdr* mkbootimg(void* kernel, int64_t kernel_size, off_t kernel_offset,
-                        void* ramdisk, int64_t ramdisk_size, off_t ramdisk_offset,
-                        void* second, int64_t second_size, off_t second_offset,
-                        size_t page_size, size_t base, off_t tags_offset,
-                        int64_t* bootimg_size)
-{
-    size_t page_mask = page_size - 1;
+boot_img_hdr_v1* mkbootimg(void* kernel, int64_t kernel_size, void* ramdisk, int64_t ramdisk_size,
+                           void* second, int64_t second_size, size_t base,
+                           const boot_img_hdr_v1& src, int64_t* bootimg_size) {
+    const size_t page_mask = src.page_size - 1;
 
+    int64_t header_actual = (sizeof(boot_img_hdr_v1) + page_mask) & (~page_mask);
     int64_t kernel_actual = (kernel_size + page_mask) & (~page_mask);
     int64_t ramdisk_actual = (ramdisk_size + page_mask) & (~page_mask);
     int64_t second_actual = (second_size + page_mask) & (~page_mask);
 
-    *bootimg_size = page_size + kernel_actual + ramdisk_actual + second_actual;
+    *bootimg_size = header_actual + kernel_actual + ramdisk_actual + second_actual;
 
-    boot_img_hdr* hdr = reinterpret_cast<boot_img_hdr*>(calloc(*bootimg_size, 1));
-    if (hdr == nullptr) {
-        return hdr;
-    }
+    boot_img_hdr_v1* hdr = reinterpret_cast<boot_img_hdr_v1*>(calloc(*bootimg_size, 1));
+    if (hdr == nullptr) die("couldn't allocate boot image: %" PRId64 " bytes", *bootimg_size);
 
+    *hdr = src;
     memcpy(hdr->magic, BOOT_MAGIC, BOOT_MAGIC_SIZE);
 
     hdr->kernel_size =  kernel_size;
     hdr->ramdisk_size = ramdisk_size;
     hdr->second_size =  second_size;
 
-    hdr->kernel_addr =  base + kernel_offset;
-    hdr->ramdisk_addr = base + ramdisk_offset;
-    hdr->second_addr =  base + second_offset;
-    hdr->tags_addr =    base + tags_offset;
+    hdr->kernel_addr += base;
+    hdr->ramdisk_addr += base;
+    hdr->second_addr += base;
+    hdr->tags_addr += base;
 
-    hdr->page_size =    page_size;
+    if (hdr->header_version != 0) {
+        hdr->header_size = sizeof(boot_img_hdr_v1);
+    }
 
-    memcpy(hdr->magic + page_size, kernel, kernel_size);
-    memcpy(hdr->magic + page_size + kernel_actual, ramdisk, ramdisk_size);
-    memcpy(hdr->magic + page_size + kernel_actual + ramdisk_actual, second, second_size);
-
+    memcpy(hdr->magic + hdr->page_size, kernel, kernel_size);
+    memcpy(hdr->magic + hdr->page_size + kernel_actual, ramdisk, ramdisk_size);
+    memcpy(hdr->magic + hdr->page_size + kernel_actual + ramdisk_actual, second, second_size);
     return hdr;
 }
diff --git a/fastboot/bootimg_utils.h b/fastboot/bootimg_utils.h
index fcc8662..fe805b0 100644
--- a/fastboot/bootimg_utils.h
+++ b/fastboot/bootimg_utils.h
@@ -26,18 +26,15 @@
  * SUCH DAMAGE.
  */
 
-#ifndef _FASTBOOT_BOOTIMG_UTILS_H_
-#define _FASTBOOT_BOOTIMG_UTILS_H_
+#pragma once
 
 #include <bootimg.h>
 #include <inttypes.h>
 #include <sys/types.h>
 
-void bootimg_set_cmdline(boot_img_hdr* h, const char* cmdline);
-boot_img_hdr* mkbootimg(void* kernel, int64_t kernel_size, off_t kernel_offset,
-                        void* ramdisk, int64_t ramdisk_size, off_t ramdisk_offset,
-                        void* second, int64_t second_size, off_t second_offset,
-                        size_t page_size, size_t base, off_t tags_offset,
-                        int64_t* bootimg_size);
+#include <string>
 
-#endif
+boot_img_hdr_v1* mkbootimg(void* kernel, int64_t kernel_size, void* ramdisk, int64_t ramdisk_size,
+                           void* second, int64_t second_size, size_t base,
+                           const boot_img_hdr_v1& src, int64_t* bootimg_size);
+void bootimg_set_cmdline(boot_img_hdr_v1* h, const std::string& cmdline);
diff --git a/fastboot/constants.h b/fastboot/constants.h
new file mode 100644
index 0000000..5e7e955
--- /dev/null
+++ b/fastboot/constants.h
@@ -0,0 +1,51 @@
+/*
+ * 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.
+ */
+#pragma once
+
+#define FB_CMD_GETVAR "getvar"
+#define FB_CMD_DOWNLOAD "download"
+#define FB_CMD_UPLOAD "upload"
+#define FB_CMD_VERIFY "verify"
+#define FB_CMD_FLASH "flash"
+#define FB_CMD_ERASE "erase"
+#define FB_CMD_BOOT "boot"
+#define FB_CMD_SET_ACTIVE "set_active"
+#define FB_CMD_CONTINUE "continue"
+#define FB_CMD_REBOOT "reboot"
+#define FB_CMD_SHUTDOWN "shutdown"
+#define FB_CMD_REBOOT_BOOTLOADER "reboot-bootloader"
+#define FB_CMD_POWERDOWN "powerdown"
+
+#define RESPONSE_OKAY "OKAY"
+#define RESPONSE_FAIL "FAIL"
+#define RESPONSE_DATA "DATA"
+#define RESPONSE_INFO "INFO"
+
+#define FB_COMMAND_SZ 64
+#define FB_RESPONSE_SZ 64
+
+#define FB_VAR_VERSION "version"
+#define FB_VAR_VERSION_BOOTLOADER "version-bootloader"
+#define FB_VAR_VERSION_BASEBAND "version-baseband"
+#define FB_VAR_PRODUCT "product"
+#define FB_VAR_SERIALNO "serialno"
+#define FB_VAR_SECURE "secure"
+#define FB_VAR_UNLOCKED "unlocked"
+#define FB_VAR_CURRENT_SLOT "current-slot"
+#define FB_VAR_MAX_DOWNLOAD_SIZE "max-download-size"
+#define FB_VAR_HAS_SLOT "has-slot"
+#define FB_VAR_SLOT_COUNT "slot-count"
+#define FB_VAR_PARTITION_SIZE "partition-size"
diff --git a/fastboot/engine.cpp b/fastboot/engine.cpp
index 7e10cc9..63ee2af 100644
--- a/fastboot/engine.cpp
+++ b/fastboot/engine.cpp
@@ -25,9 +25,7 @@
  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  */
-
-#include "fastboot.h"
-#include "fs.h"
+#include "engine.h"
 
 #include <errno.h>
 #include <stdarg.h>
@@ -38,150 +36,123 @@
 #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
+#include "constants.h"
+#include "transport.h"
 
-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;
 
-bool fb_getvar(Transport* transport, const std::string& key, std::string* value) {
-    std::string cmd = "getvar:";
-    cmd += key;
+    int fd = -1;
 
-    char buf[FB_RESPONSE_SZ + 1];
-    memset(buf, 0, sizeof(buf));
-    if (fb_command_response(transport, cmd.c_str(), buf)) {
-      return false;
-    }
-    *value = buf;
-    return true;
+    int (*func)(Action& a, int status, const char* resp) = nullptr;
+
+    double start = -1;
+};
+
+static std::vector<std::unique_ptr<Action>> action_list;
+static fastboot::FastBootDriver* fb = nullptr;
+
+void fb_init(fastboot::FastBootDriver& fbi) {
+    fb = &fbi;
+    auto cb = [](std::string& info) { fprintf(stderr, "(bootloader) %s\n", info.c_str()); };
+    fb->SetInfoCallback(cb);
 }
 
-static int cb_default(Action* a, int status, const char* resp) {
+const std::string fb_get_error() {
+    return fb->Error();
+}
+
+bool fb_getvar(const std::string& key, std::string* value) {
+    return !fb->GetVar(key, value);
+}
+
+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 (%d) exceeds maximum size (%d)", 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, FB_CMD_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, FB_CMD_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' (%u 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, FB_CMD_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' (%u 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, FB_CMD_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 (%u 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, FB_CMD_FLASH ":" + partition);
+    b.msg = android::base::StringPrintf("Writing sparse '%s' %zu/%zu", partition.c_str(), current,
+                                        total);
 }
 
 static int match(const char* str, const char** value, unsigned count) {
@@ -205,212 +176,184 @@
     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, FB_CMD_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, FB_CMD_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, FB_CMD_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, FB_CMD_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' (%u 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() {
     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, "%-50s ", a->msg.c_str());
+            verbose("\n");
         }
         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() : "");
+            char* cbuf = static_cast<char*>(a->data);
+            status = fb->Download(cbuf, a->size);
+            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 = fb->Download(a->fd, a->size);
+            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 = fb->RawCommand(a->cmd);
+            status = a->func(*a, status, status ? fb_get_error().c_str() : "");
             if (status) break;
         } else if (a->op == OP_QUERY) {
-            status = fb_command_response(transport, a->cmd, resp);
-            status = a->func(a, status, status ? fb_get_error().c_str() : resp);
+            std::string resp;
+            status = fb->RawCommand(a->cmd, &resp);
+            status = a->func(*a, status, status ? fb_get_error().c_str() : resp.c_str());
             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`.
+            fprintf(stderr, "\n");
         } 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 = fb->Download(reinterpret_cast<sparse_file*>(a->data));
+            status = a->func(*a, status, status ? fb_get_error().c_str() : "");
             if (status) break;
         } else if (a->op == OP_WAIT_FOR_DISCONNECT) {
-            transport->WaitForDisconnect();
+            fb->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 = fb->Upload(reinterpret_cast<const char*>(a->data));
+            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/engine.h b/fastboot/engine.h
new file mode 100644
index 0000000..74aaa6a
--- /dev/null
+++ b/fastboot/engine.h
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <inttypes.h>
+#include <stdlib.h>
+
+#include <string>
+
+#include <bootimg.h>
+#include "fastboot_driver.h"
+#include "util.h"
+
+#include "constants.h"
+
+class Transport;
+struct sparse_file;
+
+const std::string fb_get_error();
+
+//#define FB_COMMAND_SZ (fastboot::FB_COMMAND_SZ)
+//#define FB_RESPONSE_SZ (fastboot::FB_RESPONSE_SZ)
+
+/* engine.c - high level command queue engine */
+
+void fb_init(fastboot::FastBootDriver& fbi);
+
+bool fb_getvar(const std::string& key, std::string* value);
+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 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();
+void fb_set_active(const std::string& slot);
+
+/* Current product */
+extern char cur_product[FB_RESPONSE_SZ + 1];
+
+class FastBootTool {
+  public:
+    int Main(int argc, char* argv[]);
+
+    void ParseOsPatchLevel(boot_img_hdr_v1*, const char*);
+    void ParseOsVersion(boot_img_hdr_v1*, const char*);
+};
diff --git a/fastboot/fastboot.bash b/fastboot/fastboot.bash
new file mode 100644
index 0000000..cb1d354
--- /dev/null
+++ b/fastboot/fastboot.bash
@@ -0,0 +1,182 @@
+# /* vim: set ai ts=4 ft=sh: */
+#
+# Copyright 2017, 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.
+#
+
+_fastboot() {
+    if ! check_type "$1" >/dev/null; then
+        return
+    fi
+
+    if check_type _init_completion >/dev/null; then
+        _init_completion || return
+    fi
+
+    local where i cur serial
+    COMPREPLY=()
+
+    serial="${ANDROID_SERIAL:-none}"
+    where=OPTIONS
+    for ((i=1; i <= COMP_CWORD; i++)); do
+        cur="${COMP_WORDS[i]}"
+        case "${cur}" in
+            -s)
+                where=OPT_SERIAL
+                ;;
+            --slot)
+                where=OPT_SLOT
+                ;;
+            -*)
+                where=OPTIONS
+                ;;
+            *)
+                if [[ $where == OPT_SERIAL ]]; then
+                    where=OPT_SERIAL_ARG
+                    serial=${cur}
+                elif [[ $where == OPT_SLOT ]]; then
+                    where=OPT_SLOT_ARG
+                else
+                    where=COMMAND
+                    break
+                fi
+                ;;
+        esac
+    done
+
+    if [[ $where == COMMAND && $i -ge $COMP_CWORD ]]; then
+        where=OPTIONS
+    fi
+
+    OPTIONS="-a -c --disable-verification --disable-verity -h --help -s --set-active --skip-secondary --skip-reboot --slot -u --version -w"
+    COMMAND="continue devices erase flash flashall flashing format getvar get_staged help oem reboot stage update"
+
+    case $where in
+        OPTIONS|OPT_SERIAL)
+            COMPREPLY=( $(compgen -W "$OPTIONS $COMMAND" -- "$cur") )
+            ;;
+        OPT_SERIAL_ARG)
+            local devices=$(command fastboot devices 2> /dev/null | awk '{ print $1 }')
+            COMPREPLY=( $(compgen -W "${devices}" -- ${cur}) )
+            ;;
+        OPT_SLOT_ARG)
+            local slots="a all b other"
+            COMPREPLY=( $(compgen -W "${slots}" -- ${cur}) )
+            ;;
+        COMMAND)
+            if [[ $i -eq $COMP_CWORD ]]; then
+                COMPREPLY=( $(compgen -W "$COMMAND" -- "$cur") )
+            else
+                i=$((i+1))
+                case "${cur}" in
+                    flash)
+                        _fastboot_cmd_flash "$serial" $i
+                        ;;
+                    reboot)
+                        if [[ $COMP_CWORD == $i ]]; then
+                            args="bootloader"
+                            COMPREPLY=( $(compgen -W "${args}" -- "${COMP_WORDS[i]}") )
+                        fi
+                        ;;
+                    update)
+                        _fastboot_cmd_update "$serial" $i
+                        ;;
+                esac
+            fi
+            ;;
+    esac
+
+    return 0
+}
+
+_fastboot_cmd_flash() {
+    local serial i cur
+    local partitions
+
+    serial=$1
+    i=$2
+
+    cur="${COMP_WORDS[COMP_CWORD]}"
+    if [[ $i -eq $COMP_CWORD ]]; then
+        partitions="boot bootloader dtbo modem odm oem product radio recovery system vbmeta vendor"
+        COMPREPLY=( $(compgen -W "$partitions" -- $cur) )
+    else
+        _fastboot_util_complete_local_file "${cur}" '!*.img'
+    fi
+}
+
+_fastboot_cmd_update() {
+    local serial i cur
+
+    serial=$1
+    i=$2
+
+    cur="${COMP_WORDS[COMP_CWORD]}"
+
+    _fastboot_util_complete_local_file "${cur}" '!*.zip'
+}
+
+_fastboot_util_complete_local_file() {
+    local file xspec i j IFS=$'\n'
+    local -a dirs files
+
+    file=$1
+    xspec=$2
+
+    # Since we're probably doing file completion here, don't add a space after.
+    if [[ $(check_type compopt) == "builtin" ]]; then
+        compopt -o plusdirs
+        if [[ "${xspec}" == "" ]]; then
+            COMPREPLY=( ${COMPREPLY[@]:-} $(compgen -f -- "${cur}") )
+        else
+            compopt +o filenames
+            COMPREPLY=( ${COMPREPLY[@]:-} $(compgen -f -X "${xspec}" -- "${cur}") )
+        fi
+    else
+        # Work-around for shells with no compopt
+
+        dirs=( $(compgen -d -- "${cur}" ) )
+
+        if [[ "${xspec}" == "" ]]; then
+            files=( ${COMPREPLY[@]:-} $(compgen -f -- "${cur}") )
+        else
+            files=( ${COMPREPLY[@]:-} $(compgen -f -X "${xspec}" -- "${cur}") )
+        fi
+
+        COMPREPLY=( $(
+            for i in "${files[@]}"; do
+                local skip=
+                for j in "${dirs[@]}"; do
+                    if [[ $i == $j ]]; then
+                        skip=1
+                        break
+                    fi
+                done
+                [[ -n $skip ]] || printf "%s\n" "$i"
+            done
+        ))
+
+        COMPREPLY=( ${COMPREPLY[@]:-} $(
+            for i in "${dirs[@]}"; do
+                printf "%s/\n" "$i"
+            done
+        ))
+    fi
+}
+
+if [[ $(check_type compopt) == "builtin" ]]; then
+    complete -F _fastboot fastboot
+else
+    complete -o nospace -F _fastboot fastboot
+fi
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index 271ca95..321e6ba 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -60,7 +60,7 @@
 
 #include "bootimg_utils.h"
 #include "diagnose_usb.h"
-#include "fastboot.h"
+#include "engine.h"
 #include "fs.h"
 #include "tcp.h"
 #include "transport.h"
@@ -76,22 +76,21 @@
 char cur_product[FB_RESPONSE_SZ + 1];
 
 static const char* serial = nullptr;
-static const char* cmdline = nullptr;
-static unsigned short vendor_id = 0;
-static int long_listing = 0;
+
+static bool g_long_listing = false;
 // Don't resparse files in too-big chunks.
 // libsparse will support INT_MAX, but this results in large allocations, so
 // let's keep it at 1GB to avoid memory pressure on the host.
 static constexpr int64_t RESPARSE_LIMIT = 1 * 1024 * 1024 * 1024;
-static int64_t sparse_limit = -1;
+static uint64_t sparse_limit = 0;
 static int64_t target_sparse_limit = -1;
 
-static unsigned page_size = 2048;
-static unsigned base_addr      = 0x10000000;
-static unsigned kernel_offset  = 0x00008000;
-static unsigned ramdisk_offset = 0x01000000;
-static unsigned second_offset  = 0x00f00000;
-static unsigned tags_offset    = 0x00000100;
+static unsigned g_base_addr = 0x10000000;
+static boot_img_hdr_v1 g_boot_img_hdr = {};
+static std::string g_cmdline;
+
+static bool g_disable_verity = false;
+static bool g_disable_verification = false;
 
 static const std::string convert_fbe_marker_filename("convert_fbe");
 
@@ -115,18 +114,26 @@
     bool is_optional;
     bool is_secondary;
 } images[] = {
-    // clang-format off
+        // clang-format off
     { "boot",     "boot.img",         "boot.sig",     "boot",     false, false },
     { 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 },
+    { "product-services",
+                  "product-services.img",
+                                      "product-services.sig",
+                                                      "product-services",
+                                                                  true,  false },
     { "recovery", "recovery.img",     "recovery.sig", "recovery", true,  false },
+    { "super",    "super.img",        "super.sig",    "super",    true,  false },
     { "system",   "system.img",       "system.sig",   "system",   false, false },
     { nullptr,    "system_other.img", "system.sig",   "system",   true,  true  },
     { "vbmeta",   "vbmeta.img",       "vbmeta.sig",   "vbmeta",   true,  false },
     { "vendor",   "vendor.img",       "vendor.sig",   "vendor",   true,  false },
     { nullptr,    "vendor_other.img", "vendor.sig",   "vendor",   true,  true  },
-    // clang-format on
+        // clang-format on
 };
 
 static std::string find_item_given_name(const char* img_name) {
@@ -188,11 +195,6 @@
 }
 
 static int match_fastboot_with_serial(usb_ifc_info* info, const char* local_serial) {
-    // Require a matching vendor id if the user specified one with -i.
-    if (vendor_id != 0 && info->dev_vendor != vendor_id) {
-        return -1;
-    }
-
     if (info->ifc_class != 0xff || info->ifc_subclass != 0x42 || info->ifc_protocol != 0x03) {
         return -1;
     }
@@ -218,7 +220,7 @@
             serial = "????????????";
         }
         // output compatible with "adb devices"
-        if (!long_listing) {
+        if (!g_long_listing) {
             printf("%s\tfastboot", serial.c_str());
         } else {
             printf("%-22s fastboot", serial.c_str());
@@ -233,7 +235,7 @@
 // Opens a new Transport connected to a device. If |serial| is non-null it will be used to identify
 // a specific device, otherwise the first USB device found will be used.
 //
-// If |serial| is non-null but invalid, this prints an error message to stderr and returns nullptr.
+// If |serial| is non-null but invalid, this exits.
 // Otherwise it blocks until the target is available.
 //
 // The returned Transport is a singleton, so multiple calls to this function will return the same
@@ -265,9 +267,7 @@
         if (net_address != nullptr) {
             std::string error;
             if (!android::base::ParseNetAddress(net_address, &host, &port, nullptr, &error)) {
-                fprintf(stderr, "error: Invalid network address '%s': %s\n", net_address,
-                        error.c_str());
-                return nullptr;
+                die("invalid network address '%s': %s\n", net_address, error.c_str());
             }
         }
     }
@@ -322,128 +322,103 @@
 static int show_help() {
     // clang-format off
     fprintf(stdout,
-/*           1234567890123456789012345678901234567890123456789012345678901234567890123456 */
-            "usage: fastboot [ <option> ] <command>\n"
+//                    1         2         3         4         5         6         7         8
+//           12345678901234567890123456789012345678901234567890123456789012345678901234567890
+            "usage: fastboot [OPTION...] COMMAND...\n"
             "\n"
-            "commands:\n"
-            "  update <filename>                        Reflash device from update.zip.\n"
-            "                                           Sets the flashed slot as active.\n"
-            "  flashall                                 Flash boot, system, vendor, and --\n"
-            "                                           if found -- recovery. If the device\n"
-            "                                           supports slots, the slot that has\n"
-            "                                           been flashed to is set as active.\n"
-            "                                           Secondary images may be flashed to\n"
-            "                                           an inactive slot.\n"
-            "  flash <partition> [ <filename> ]         Write a file to a flash partition.\n"
-            "  flashing lock                            Locks the device. Prevents flashing.\n"
-            "  flashing unlock                          Unlocks the device. Allows flashing\n"
-            "                                           any partition except\n"
-            "                                           bootloader-related partitions.\n"
-            "  flashing lock_critical                   Prevents flashing bootloader-related\n"
-            "                                           partitions.\n"
-            "  flashing unlock_critical                 Enables flashing bootloader-related\n"
-            "                                           partitions.\n"
-            "  flashing get_unlock_ability              Queries bootloader to see if the\n"
-            "                                           device is unlocked.\n"
-            "  flashing get_unlock_bootloader_nonce     Queries the bootloader to get the\n"
-            "                                           unlock nonce.\n"
-            "  flashing unlock_bootloader <request>     Issue unlock bootloader using request.\n"
-            "  flashing lock_bootloader                 Locks the bootloader to prevent\n"
-            "                                           bootloader version rollback.\n"
-            "  erase <partition>                        Erase a flash partition.\n"
-            "  format[:[<fs type>][:[<size>]] <partition>\n"
-            "                                           Format a flash partition. Can\n"
-            "                                           override the fs type and/or size\n"
-            "                                           the bootloader reports.\n"
-            "  getvar <variable>                        Display a bootloader variable.\n"
-            "  set_active <slot>                        Sets the active slot. If slots are\n"
-            "                                           not supported, this does nothing.\n"
-            "  boot <kernel> [ <ramdisk> [ <second> ] ] Download and boot kernel.\n"
-            "  flash:raw <bootable-partition> <kernel> [ <ramdisk> [ <second> ] ]\n"
-            "                                           Create bootimage and flash it.\n"
-            "  devices [-l]                             List all connected devices [with\n"
-            "                                           device paths].\n"
-            "  continue                                 Continue with autoboot.\n"
-            "  reboot [bootloader|emergency]            Reboot device [into bootloader or emergency mode].\n"
-            "  reboot-bootloader                        Reboot device into bootloader.\n"
-            "  oem <parameter1> ... <parameterN>        Executes oem specific command.\n"
-            "  stage <infile>                           Sends contents of <infile> to stage for\n"
-            "                                           the next command. Supported only on\n"
-            "                                           Android Things devices.\n"
-            "  get_staged <outfile>                     Receives data to <outfile> staged by the\n"
-            "                                           last command. Supported only on Android\n"
-            "                                           Things devices.\n"
-            "  help                                     Show this help message.\n"
+            "flashing:\n"
+            " update ZIP                 Flash all partitions from an update.zip package.\n"
+            " flashall                   Flash all partitions from $ANDROID_PRODUCT_OUT.\n"
+            "                            On A/B devices, flashed slot is set as active.\n"
+            "                            Secondary images may be flashed to inactive slot.\n"
+            " flash PARTITION [FILENAME] Flash given partition, using the image from\n"
+            "                            $ANDROID_PRODUCT_OUT if no filename is given.\n"
+            "\n"
+            "basics:\n"
+            " devices [-l]               List devices in bootloader (-l: with device paths).\n"
+            " getvar NAME                Display given bootloader variable.\n"
+            " reboot [bootloader]        Reboot device.\n"
+            "\n"
+            "locking/unlocking:\n"
+            " flashing lock|unlock       Lock/unlock partitions for flashing\n"
+            " flashing lock_critical|unlock_critical\n"
+            "                            Lock/unlock 'critical' bootloader partitions.\n"
+            " flashing get_unlock_ability\n"
+            "                            Check whether unlocking is allowed (1) or not(0).\n"
+            "\n"
+            "advanced:\n"
+            " erase PARTITION            Erase a flash partition.\n"
+            " format[:FS_TYPE[:SIZE]] PARTITION\n"
+            "                            Format a flash partition.\n"
+            " set_active SLOT            Set the active slot.\n"
+            " oem [COMMAND...]           Execute OEM-specific command.\n"
+            "\n"
+            "boot image:\n"
+            " boot KERNEL [RAMDISK [SECOND]]\n"
+            "                            Download and boot kernel from RAM.\n"
+            " flash:raw PARTITION KERNEL [RAMDISK [SECOND]]\n"
+            "                            Create boot image and flash it.\n"
+            " --cmdline CMDLINE          Override kernel command line.\n"
+            " --base ADDRESS             Set kernel base address (default: 0x10000000).\n"
+            " --kernel-offset            Set kernel offset (default: 0x00008000).\n"
+            " --ramdisk-offset           Set ramdisk offset (default: 0x01000000).\n"
+            " --tags-offset              Set tags offset (default: 0x00000100).\n"
+            " --page-size BYTES          Set flash page size (default: 2048).\n"
+            " --header-version VERSION   Set boot image header version.\n"
+            " --os-version MAJOR[.MINOR[.PATCH]]\n"
+            "                            Set boot image OS version (default: 0.0.0).\n"
+            " --os-patch-level YYYY-MM-DD\n"
+            "                            Set boot image OS security patch level.\n"
+            // TODO: still missing: `second_addr`, `name`, `id`, `recovery_dtbo_*`.
+            "\n"
+            // TODO: what device(s) used this? is there any documentation?
+            //" continue                               Continue with autoboot.\n"
+            //"\n"
+            "Android Things:\n"
+            " stage IN_FILE              Sends given file to stage for the next command.\n"
+            " get_staged OUT_FILE        Writes data staged by the last command to a file.\n"
             "\n"
             "options:\n"
-            "  -w                                       Erase userdata and cache (and format\n"
-            "                                           if supported by partition type).\n"
-            "  -u                                       Do not erase partition before\n"
-            "                                           formatting.\n"
-            "  -s <specific device>                     Specify a device. For USB, provide either\n"
-            "                                           a serial number or path to device port.\n"
-            "                                           For ethernet, provide an address in the\n"
-            "                                           form <protocol>:<hostname>[:port] where\n"
-            "                                           <protocol> is either tcp or udp.\n"
-            "  -c <cmdline>                             Override kernel commandline.\n"
-            "  -i <vendor id>                           Specify a custom USB vendor id.\n"
-            "  -b, --base <base_addr>                   Specify a custom kernel base\n"
-            "                                           address (default: 0x10000000).\n"
-            "  --kernel-offset                          Specify a custom kernel offset.\n"
-            "                                           (default: 0x00008000)\n"
-            "  --ramdisk-offset                         Specify a custom ramdisk offset.\n"
-            "                                           (default: 0x01000000)\n"
-            "  --tags-offset                            Specify a custom tags offset.\n"
-            "                                           (default: 0x00000100)\n"
-            "  -n, --page-size <page size>              Specify the nand page size\n"
-            "                                           (default: 2048).\n"
-            "  -S <size>[K|M|G]                         Automatically sparse files greater\n"
-            "                                           than 'size'. 0 to disable.\n"
-            "  --slot <slot>                            Specify slot name to be used if the\n"
-            "                                           device supports slots. All operations\n"
-            "                                           on partitions that support slots will\n"
-            "                                           be done on the slot specified.\n"
-            "                                           'all' can be given to refer to all slots.\n"
-            "                                           'other' can be given to refer to a\n"
-            "                                           non-current slot. If this flag is not\n"
-            "                                           used, slotted partitions will default\n"
-            "                                           to the current active slot.\n"
-            "  -a, --set-active[=<slot>]                Sets the active slot. If no slot is\n"
-            "                                           provided, this will default to the value\n"
-            "                                           given by --slot. If slots are not\n"
-            "                                           supported, this does nothing. This will\n"
-            "                                           run after all non-reboot commands.\n"
-            "  --skip-secondary                         Will not flash secondary slots when\n"
-            "                                           performing a flashall or update. This\n"
-            "                                           will preserve data on other slots.\n"
-            "  --skip-reboot                            Will not reboot the device when\n"
-            "                                           performing commands that normally\n"
-            "                                           trigger a reboot.\n"
+            " -w                         Wipe userdata.\n"
+            " -s SERIAL                  Specify a USB device.\n"
+            " -s tcp|udp:HOST[:PORT]     Specify a network device.\n"
+            " -S SIZE[K|M|G]             Break into sparse files no larger than SIZE.\n"
+            " --slot SLOT                Use SLOT; 'all' for both slots, 'other' for\n"
+            "                            non-current slot (default: current active slot).\n"
+            " --set-active[=SLOT]        Sets the active slot before rebooting.\n"
+            " --skip-secondary           Don't flash secondary slots in flashall/update.\n"
+            " --skip-reboot              Don't reboot device after flashing.\n"
+            " --disable-verity           Sets disable-verity when flashing vbmeta.\n"
+            " --disable-verification     Sets disable-verification when flashing vbmeta.\n"
 #if !defined(_WIN32)
-            "  --wipe-and-use-fbe                       On devices which support it,\n"
-            "                                           erase userdata and cache, and\n"
-            "                                           enable file-based encryption\n"
+            " --wipe-and-use-fbe         Enable file-based encryption, wiping userdata.\n"
 #endif
-            "  --unbuffered                             Do not buffer input or output.\n"
-            "  --version                                Display version.\n"
-            "  -h, --help                               show this message.\n"
+            // TODO: remove --unbuffered?
+            " --unbuffered               Don't buffer input or output.\n"
+            " --verbose, -v              Verbose output.\n"
+            " --version                  Display version.\n"
+            " --help, -h                 Show this message.\n"
         );
     // clang-format off
     return 0;
 }
 
 static void* load_bootable_image(const std::string& kernel, const std::string& ramdisk,
-                                 const std::string& second_stage, int64_t* sz,
-                                 const char* cmdline) {
+                                 const std::string& second_stage, int64_t* sz) {
     int64_t ksize;
     void* kdata = load_file(kernel.c_str(), &ksize);
-    if (kdata == nullptr) die("cannot load '%s': %s\n", kernel.c_str(), strerror(errno));
+    if (kdata == nullptr) die("cannot load '%s': %s", kernel.c_str(), strerror(errno));
 
     // Is this actually a boot image?
+    if (ksize < static_cast<int64_t>(sizeof(boot_img_hdr_v1))) {
+        die("cannot load '%s': too short", kernel.c_str());
+    }
     if (!memcmp(kdata, BOOT_MAGIC, BOOT_MAGIC_SIZE)) {
-        if (cmdline) bootimg_set_cmdline((boot_img_hdr*) kdata, cmdline);
+        if (!g_cmdline.empty()) {
+            bootimg_set_cmdline(reinterpret_cast<boot_img_hdr_v1*>(kdata), g_cmdline);
+        }
 
-        if (!ramdisk.empty()) die("cannot boot a boot.img *and* ramdisk\n");
+        if (!ramdisk.empty()) die("cannot boot a boot.img *and* ramdisk");
 
         *sz = ksize;
         return kdata;
@@ -453,54 +428,43 @@
     int64_t rsize = 0;
     if (!ramdisk.empty()) {
         rdata = load_file(ramdisk.c_str(), &rsize);
-        if (rdata == nullptr) die("cannot load '%s': %s\n", ramdisk.c_str(), strerror(errno));
+        if (rdata == nullptr) die("cannot load '%s': %s", ramdisk.c_str(), strerror(errno));
     }
 
     void* sdata = nullptr;
     int64_t ssize = 0;
     if (!second_stage.empty()) {
         sdata = load_file(second_stage.c_str(), &ssize);
-        if (sdata == nullptr) die("cannot load '%s': %s\n", second_stage.c_str(), strerror(errno));
+        if (sdata == nullptr) die("cannot load '%s': %s", second_stage.c_str(), strerror(errno));
     }
 
     fprintf(stderr,"creating boot image...\n");
-    int64_t bsize = 0;
-    void* bdata = mkbootimg(kdata, ksize, kernel_offset,
-                      rdata, rsize, ramdisk_offset,
-                      sdata, ssize, second_offset,
-                      page_size, base_addr, tags_offset, &bsize);
-    if (bdata == nullptr) die("failed to create boot.img\n");
+    boot_img_hdr_v1* bdata = mkbootimg(kdata, ksize, rdata, rsize, sdata, ssize,
+                                       g_base_addr, g_boot_img_hdr, sz);
+    if (bdata == nullptr) die("failed to create boot.img");
 
-    if (cmdline) bootimg_set_cmdline((boot_img_hdr*) bdata, cmdline);
-    fprintf(stderr, "creating boot image - %" PRId64 " bytes\n", bsize);
-    *sz = bsize;
+    if (!g_cmdline.empty()) bootimg_set_cmdline(bdata, g_cmdline);
+    fprintf(stderr, "creating boot image - %" PRId64 " bytes\n", *sz);
 
     return bdata;
 }
 
-static void* unzip_file(ZipArchiveHandle zip, const char* entry_name, int64_t* sz) {
+static void* unzip_to_memory(ZipArchiveHandle zip, const char* entry_name, int64_t* sz) {
     ZipString zip_entry_name(entry_name);
     ZipEntry zip_entry;
     if (FindEntry(zip, zip_entry_name, &zip_entry) != 0) {
         fprintf(stderr, "archive does not contain '%s'\n", entry_name);
-        return 0;
+        return nullptr;
     }
 
     *sz = zip_entry.uncompressed_length;
 
-    fprintf(stderr, "extracting %s (%" PRId64 " MB)...\n", entry_name, *sz / 1024 / 1024);
+    fprintf(stderr, "extracting %s (%" PRId64 " MB) to RAM...\n", entry_name, *sz / 1024 / 1024);
     uint8_t* data = reinterpret_cast<uint8_t*>(malloc(zip_entry.uncompressed_length));
-    if (data == nullptr) {
-        fprintf(stderr, "failed to allocate %" PRId64 " bytes for '%s'\n", *sz, entry_name);
-        return 0;
-    }
+    if (data == nullptr) die("failed to allocate %" PRId64 " bytes for '%s'", *sz, entry_name);
 
     int error = ExtractToMemory(zip, &zip_entry, data, zip_entry.uncompressed_length);
-    if (error != 0) {
-        fprintf(stderr, "failed to extract '%s': %s\n", entry_name, ErrorCodeString(error));
-        free(data);
-        return 0;
-    }
+    if (error != 0) die("failed to extract '%s': %s", entry_name, ErrorCodeString(error));
 
     return data;
 }
@@ -517,14 +481,12 @@
     char temp_path[PATH_MAX];
     DWORD nchars = GetTempPath(sizeof(temp_path), temp_path);
     if (nchars == 0 || nchars >= sizeof(temp_path)) {
-        fprintf(stderr, "GetTempPath failed, error %ld\n", GetLastError());
-        return nullptr;
+        die("GetTempPath failed, error %ld", GetLastError());
     }
 
     char filename[PATH_MAX];
     if (GetTempFileName(temp_path, "fastboot", 0, filename) == 0) {
-        fprintf(stderr, "GetTempFileName failed, error %ld\n", GetLastError());
-        return nullptr;
+        die("GetTempFileName failed, error %ld", GetLastError());
     }
 
     return fopen(filename, "w+bTD");
@@ -533,11 +495,10 @@
 #define tmpfile win32_tmpfile
 
 static std::string make_temporary_directory() {
-    fprintf(stderr, "make_temporary_directory not supported under Windows, sorry!");
-    return "";
+    die("make_temporary_directory not supported under Windows, sorry!");
 }
 
-static int make_temporary_fd() {
+static int make_temporary_fd(const char* /*what*/) {
     // TODO: reimplement to avoid leaking a FILE*.
     return fileno(tmpfile());
 }
@@ -553,18 +514,18 @@
 static std::string make_temporary_directory() {
     std::string result(make_temporary_template());
     if (mkdtemp(&result[0]) == nullptr) {
-        fprintf(stderr, "Unable to create temporary directory: %s\n", strerror(errno));
-        return "";
+        die("unable to create temporary directory with template %s: %s",
+            result.c_str(), strerror(errno));
     }
     return result;
 }
 
-static int make_temporary_fd() {
+static int make_temporary_fd(const char* what) {
     std::string path_template(make_temporary_template());
     int fd = mkstemp(&path_template[0]);
     if (fd == -1) {
-        fprintf(stderr, "Unable to create temporary file: %s\n", strerror(errno));
-        return -1;
+        die("failed to create temporary file for %s with template %s: %s\n",
+            path_template.c_str(), what, strerror(errno));
     }
     unlink(path_template.c_str());
     return fd;
@@ -574,16 +535,11 @@
 
 static std::string create_fbemarker_tmpdir() {
     std::string dir = make_temporary_directory();
-    if (dir.empty()) {
-        fprintf(stderr, "Unable to create local temp directory for FBE marker\n");
-        return "";
-    }
     std::string marker_file = dir + "/" + convert_fbe_marker_filename;
     int fd = open(marker_file.c_str(), O_CREAT | O_WRONLY | O_CLOEXEC, 0666);
     if (fd == -1) {
-        fprintf(stderr, "Unable to create FBE marker file %s locally: %d, %s\n",
-            marker_file.c_str(), errno, strerror(errno));
-        return "";
+        die("unable to create FBE marker file %s locally: %s",
+            marker_file.c_str(), strerror(errno));
     }
     close(fd);
     return dir;
@@ -604,12 +560,7 @@
 }
 
 static int unzip_to_file(ZipArchiveHandle zip, const char* entry_name) {
-    unique_fd fd(make_temporary_fd());
-    if (fd == -1) {
-        fprintf(stderr, "failed to create temporary file for '%s': %s\n",
-                entry_name, strerror(errno));
-        return -1;
-    }
+    unique_fd fd(make_temporary_fd(entry_name));
 
     ZipString zip_entry_name(entry_name);
     ZipEntry zip_entry;
@@ -618,40 +569,48 @@
         return -1;
     }
 
-    fprintf(stderr, "extracting %s (%" PRIu32 " MB)...\n", entry_name,
+    fprintf(stderr, "extracting %s (%" PRIu32 " MB) to disk...", entry_name,
             zip_entry.uncompressed_length / 1024 / 1024);
+    double start = now();
     int error = ExtractEntryToFile(zip, &zip_entry, fd);
     if (error != 0) {
-        fprintf(stderr, "failed to extract '%s': %s\n", entry_name, ErrorCodeString(error));
-        return -1;
+        die("\nfailed to extract '%s': %s", entry_name, ErrorCodeString(error));
     }
 
-    lseek(fd, 0, SEEK_SET);
-    // TODO: We're leaking 'fp' here.
+    if (lseek(fd, 0, SEEK_SET) != 0) {
+        die("\nlseek on extracted file '%s' failed: %s", entry_name, strerror(errno));
+    }
+
+    fprintf(stderr, " took %.3fs\n", now() - start);
+
     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(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;
@@ -660,19 +619,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(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;
@@ -680,87 +666,67 @@
         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(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(data);
             data = s;
         } else {
             s++;
         }
     }
+    if (fb_execute_queue()) 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("--------------------------------------------");
 }
 
-static struct sparse_file **load_sparse_files(int fd, int max_size)
-{
+static struct sparse_file** load_sparse_files(int fd, int64_t max_size) {
     struct sparse_file* s = sparse_file_import_auto(fd, false, true);
-    if (!s) {
-        die("cannot sparse read file\n");
+    if (!s) die("cannot sparse read file");
+
+    if (max_size <= 0 || max_size > std::numeric_limits<uint32_t>::max()) {
+      die("invalid max size %" PRId64, max_size);
     }
 
     int files = sparse_file_resparse(s, max_size, nullptr, 0);
-    if (files < 0) {
-        die("Failed to resparse\n");
-    }
+    if (files < 0) die("Failed to resparse");
 
     sparse_file** out_s = reinterpret_cast<sparse_file**>(calloc(sizeof(struct sparse_file *), files + 1));
-    if (!out_s) {
-        die("Failed to allocate sparse file array\n");
-    }
+    if (!out_s) die("Failed to allocate sparse file array");
 
     files = sparse_file_resparse(s, max_size, out_s, files);
-    if (files < 0) {
-        die("Failed to resparse\n");
-    }
+    if (files < 0) die("Failed to resparse");
 
     return out_s;
 }
 
-static int64_t get_target_sparse_limit(Transport* transport) {
+static int64_t get_target_sparse_limit() {
     std::string max_download_size;
-    if (!fb_getvar(transport, "max-download-size", &max_download_size) ||
-            max_download_size.empty()) {
-        fprintf(stderr, "target didn't report max-download-size\n");
+    if (!fb_getvar("max-download-size", &max_download_size) ||
+        max_download_size.empty()) {
+        verbose("target didn't report max-download-size");
         return 0;
     }
 
@@ -772,22 +738,17 @@
         fprintf(stderr, "couldn't parse max-download-size '%s'\n", max_download_size.c_str());
         return 0;
     }
-    if (limit > 0) {
-        fprintf(stderr, "target reported max download size of %" PRId64 " bytes\n", limit);
-    }
+    if (limit > 0) verbose("target reported max download size of %" PRId64 " bytes", limit);
     return limit;
 }
 
-static int64_t get_sparse_limit(Transport* transport, int64_t size) {
-    int64_t limit;
-
-    if (sparse_limit == 0) {
-        return 0;
-    } else if (sparse_limit > 0) {
-        limit = sparse_limit;
-    } else {
+static int64_t get_sparse_limit(int64_t size) {
+    int64_t limit = sparse_limit;
+    if (limit == 0) {
+        // Unlimited, so see what the target device's limit is.
+        // TODO: shouldn't we apply this limit even if you've used -S?
         if (target_sparse_limit == -1) {
-            target_sparse_limit = get_target_sparse_limit(transport);
+            target_sparse_limit = get_target_sparse_limit();
         }
         if (target_sparse_limit > 0) {
             limit = target_sparse_limit;
@@ -803,25 +764,14 @@
     return 0;
 }
 
-// Until we get lazy inode table init working in make_ext4fs, we need to
-// erase partitions of type ext4 before flashing a filesystem so no stale
-// inodes are left lying around.  Otherwise, e2fsck gets very upset.
-static bool needs_erase(Transport* transport, const char* partition) {
-    std::string partition_type;
-    if (!fb_getvar(transport, std::string("partition-type:") + partition, &partition_type)) {
-        return false;
-    }
-    return partition_type == "ext4";
-}
-
-static bool load_buf_fd(Transport* transport, int fd, struct fastboot_buffer* buf) {
+static bool load_buf_fd(int fd, struct fastboot_buffer* buf) {
     int64_t sz = get_file_size(fd);
     if (sz == -1) {
         return false;
     }
 
     lseek64(fd, 0, SEEK_SET);
-    int64_t limit = get_sparse_limit(transport, sz);
+    int64_t limit = get_sparse_limit(sz);
     if (limit) {
         sparse_file** s = load_sparse_files(fd, limit);
         if (s == nullptr) {
@@ -839,7 +789,7 @@
     return true;
 }
 
-static bool load_buf(Transport* transport, const char* fname, struct fastboot_buffer* buf) {
+static bool load_buf(const char* fname, struct fastboot_buffer* buf) {
     unique_fd fd(TEMP_FAILURE_RETRY(open(fname, O_RDONLY | O_BINARY)));
 
     if (fd == -1) {
@@ -855,13 +805,54 @@
         return false;
     }
 
-    return load_buf_fd(transport, fd.release(), buf);
+    return load_buf_fd(fd.release(), buf);
 }
 
-static void flash_buf(const char *pname, struct fastboot_buffer *buf)
+static void rewrite_vbmeta_buffer(struct fastboot_buffer* buf) {
+    // Buffer needs to be at least the size of the VBMeta struct which
+    // is 256 bytes.
+    if (buf->sz < 256) {
+        return;
+    }
+
+    int fd = make_temporary_fd("vbmeta rewriting");
+
+    std::string data;
+    if (!android::base::ReadFdToString(buf->fd, &data)) {
+        die("Failed reading from vbmeta");
+    }
+
+    // There's a 32-bit big endian |flags| field at offset 120 where
+    // bit 0 corresponds to disable-verity and bit 1 corresponds to
+    // disable-verification.
+    //
+    // See external/avb/libavb/avb_vbmeta_image.h for the layout of
+    // the VBMeta struct.
+    if (g_disable_verity) {
+        data[123] |= 0x01;
+    }
+    if (g_disable_verification) {
+        data[123] |= 0x02;
+    }
+
+    if (!android::base::WriteStringToFd(data, fd)) {
+        die("Failed writing to modified vbmeta");
+    }
+    close(buf->fd);
+    buf->fd = fd;
+    lseek(fd, 0, SEEK_SET);
+}
+
+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) &&
+        (partition == "vbmeta" || partition == "vbmeta_a" || partition == "vbmeta_b")) {
+        rewrite_vbmeta_buffer(buf);
+    }
+
     switch (buf->type) {
         case FB_BUFFER_SPARSE: {
             std::vector<std::pair<sparse_file*, int64_t>> sparse_files;
@@ -874,63 +865,35 @@
 
             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);
     }
 }
 
-static std::string get_current_slot(Transport* transport)
-{
+static std::string get_current_slot() {
     std::string current_slot;
-    if (fb_getvar(transport, "current-slot", &current_slot)) {
-        if (current_slot == "_a") return "a"; // Legacy support
-        if (current_slot == "_b") return "b"; // Legacy support
-        return current_slot;
-    }
-    return "";
+    if (!fb_getvar("current-slot", &current_slot)) return "";
+    return current_slot;
 }
 
-// Legacy support
-static std::vector<std::string> get_suffixes_obsolete(Transport* transport) {
-    std::vector<std::string> suffixes;
-    std::string suffix_list;
-    if (!fb_getvar(transport, "slot-suffixes", &suffix_list)) {
-        return suffixes;
-    }
-    suffixes = android::base::Split(suffix_list, ",");
-    // Unfortunately some devices will return an error message in the
-    // guise of a valid value. If we only see only one suffix, it's probably
-    // not real.
-    if (suffixes.size() == 1) {
-        suffixes.clear();
-    }
-    return suffixes;
-}
-
-// Legacy support
-static bool supports_AB_obsolete(Transport* transport) {
-  return !get_suffixes_obsolete(transport).empty();
-}
-
-static int get_slot_count(Transport* transport) {
+static int get_slot_count() {
     std::string var;
-    int count;
-    if (!fb_getvar(transport, "slot-count", &var)) {
-        if (supports_AB_obsolete(transport)) return 2; // Legacy support
+    int count = 0;
+    if (!fb_getvar("slot-count", &var) || !android::base::ParseInt(var, &count)) {
+        return 0;
     }
-    if (!android::base::ParseInt(var, &count)) return 0;
     return count;
 }
 
-static bool supports_AB(Transport* transport) {
-  return get_slot_count(transport) >= 2;
+static bool supports_AB() {
+  return get_slot_count() >= 2;
 }
 
 // Given a current slot, this returns what the 'other' slot is.
@@ -941,42 +904,40 @@
     return std::string(1, next);
 }
 
-static std::string get_other_slot(Transport* transport, const std::string& current_slot) {
-    return get_other_slot(current_slot, get_slot_count(transport));
+static std::string get_other_slot(const std::string& current_slot) {
+    return get_other_slot(current_slot, get_slot_count());
 }
 
-static std::string get_other_slot(Transport* transport, int count) {
-    return get_other_slot(get_current_slot(transport), count);
+static std::string get_other_slot(int count) {
+    return get_other_slot(get_current_slot(), count);
 }
 
-static std::string get_other_slot(Transport* transport) {
-    return get_other_slot(get_current_slot(transport), get_slot_count(transport));
+static std::string get_other_slot() {
+    return get_other_slot(get_current_slot(), get_slot_count());
 }
 
-static std::string verify_slot(Transport* transport, const std::string& slot_name, bool allow_all) {
+static std::string verify_slot(const std::string& slot_name, bool allow_all) {
     std::string slot = slot_name;
-    if (slot == "_a") slot = "a"; // Legacy support
-    if (slot == "_b") slot = "b"; // Legacy support
     if (slot == "all") {
         if (allow_all) {
             return "all";
         } else {
-            int count = get_slot_count(transport);
+            int count = get_slot_count();
             if (count > 0) {
                 return "a";
             } else {
-                die("No known slots.");
+                die("No known slots");
             }
         }
     }
 
-    int count = get_slot_count(transport);
-    if (count == 0) die("Device does not support slots.\n");
+    int count = get_slot_count();
+    if (count == 0) die("Device does not support slots");
 
     if (slot == "other") {
-        std::string other = get_other_slot(transport, count);
+        std::string other = get_other_slot( count);
         if (other == "") {
-           die("No known slots.");
+           die("No known slots");
         }
         return other;
     }
@@ -991,24 +952,24 @@
     exit(1);
 }
 
-static std::string verify_slot(Transport* transport, const std::string& slot) {
-   return verify_slot(transport, slot, true);
+static std::string verify_slot(const std::string& slot) {
+   return verify_slot(slot, true);
 }
 
-static void do_for_partition(Transport* transport, const std::string& part, const std::string& slot,
+static void do_for_partition(const std::string& part, const std::string& slot,
                              const std::function<void(const std::string&)>& func, bool force_slot) {
     std::string has_slot;
     std::string current_slot;
 
-    if (!fb_getvar(transport, "has-slot:" + part, &has_slot)) {
+    if (!fb_getvar("has-slot:" + part, &has_slot)) {
         /* If has-slot is not supported, the answer is no. */
         has_slot = "no";
     }
     if (has_slot == "yes") {
         if (slot == "") {
-            current_slot = get_current_slot(transport);
+            current_slot = get_current_slot();
             if (current_slot == "") {
-                die("Failed to identify current slot.\n");
+                die("Failed to identify current slot");
             }
             func(part + "_" + current_slot);
         } else {
@@ -1028,30 +989,30 @@
  * partition names. If force_slot is true, it will fail if a slot is specified, and the given
  * partition does not support slots.
  */
-static void do_for_partitions(Transport* transport, const std::string& part, const std::string& slot,
+static void do_for_partitions(const std::string& part, const std::string& slot,
                               const std::function<void(const std::string&)>& func, bool force_slot) {
     std::string has_slot;
 
     if (slot == "all") {
-        if (!fb_getvar(transport, "has-slot:" + part, &has_slot)) {
-            die("Could not check if partition %s has slot.", part.c_str());
+        if (!fb_getvar("has-slot:" + part, &has_slot)) {
+            die("Could not check if partition %s has slot %s", part.c_str(), slot.c_str());
         }
         if (has_slot == "yes") {
-            for (int i=0; i < get_slot_count(transport); i++) {
-                do_for_partition(transport, part, std::string(1, (char)(i + 'a')), func, force_slot);
+            for (int i=0; i < get_slot_count(); i++) {
+                do_for_partition(part, std::string(1, (char)(i + 'a')), func, force_slot);
             }
         } else {
-            do_for_partition(transport, part, "", func, force_slot);
+            do_for_partition(part, "", func, force_slot);
         }
     } else {
-        do_for_partition(transport, part, slot, func, force_slot);
+        do_for_partition(part, slot, func, force_slot);
     }
 }
 
-static void do_flash(Transport* transport, const char* pname, const char* fname) {
+static void do_flash(const char* pname, const char* fname) {
     struct fastboot_buffer buf;
 
-    if (!load_buf(transport, fname, &buf)) {
+    if (!load_buf(fname, &buf)) {
         die("cannot load '%s': %s", fname, strerror(errno));
     }
     flash_buf(pname, &buf);
@@ -1059,7 +1020,7 @@
 
 static void do_update_signature(ZipArchiveHandle zip, const char* filename) {
     int64_t sz;
-    void* data = unzip_file(zip, filename, &sz);
+    void* data = unzip_to_memory(zip, filename, &sz);
     if (data == nullptr) return;
     fb_queue_download("signature", data, sz);
     fb_queue_command("signature", "installing signature");
@@ -1067,26 +1028,20 @@
 
 // Sets slot_override as the active slot. If slot_override is blank,
 // set current slot as active instead. This clears slot-unbootable.
-static void set_active(Transport* transport, const std::string& slot_override) {
-    std::string separator = "";
-    if (!supports_AB(transport)) {
-        if (supports_AB_obsolete(transport)) {
-            separator = "_"; // Legacy support
-        } else {
-            return;
-        }
-    }
+static void set_active(const std::string& slot_override) {
+    if (!supports_AB()) return;
+
     if (slot_override != "") {
-        fb_set_active((separator + slot_override).c_str());
+        fb_set_active(slot_override);
     } else {
-        std::string current_slot = get_current_slot(transport);
+        std::string current_slot = get_current_slot();
         if (current_slot != "") {
-            fb_set_active((separator + current_slot).c_str());
+            fb_set_active(current_slot);
         }
     }
 }
 
-static void do_update(Transport* transport, const char* filename, const std::string& slot_override, bool erase_first, bool skip_secondary) {
+static void do_update(const char* filename, const std::string& slot_override, bool skip_secondary) {
     queue_info_dump();
 
     fb_queue_query_save("product", cur_product, sizeof(cur_product));
@@ -1094,28 +1049,26 @@
     ZipArchiveHandle zip;
     int error = OpenArchive(filename, &zip);
     if (error != 0) {
-        CloseArchive(zip);
         die("failed to open zip file '%s': %s", filename, ErrorCodeString(error));
     }
 
     int64_t sz;
-    void* data = unzip_file(zip, "android-info.txt", &sz);
+    void* data = unzip_to_memory(zip, "android-info.txt", &sz);
     if (data == nullptr) {
-        CloseArchive(zip);
         die("update package '%s' has no android-info.txt", filename);
     }
 
-    setup_requirements(reinterpret_cast<char*>(data), sz);
+    check_requirements(reinterpret_cast<char*>(data), sz);
 
     std::string secondary;
     if (!skip_secondary) {
         if (slot_override != "") {
-            secondary = get_other_slot(transport, slot_override);
+            secondary = get_other_slot(slot_override);
         } else {
-            secondary = get_other_slot(transport);
+            secondary = get_other_slot();
         }
         if (secondary == "") {
-            if (supports_AB(transport)) {
+            if (supports_AB()) {
                 fprintf(stderr, "Warning: Could not determine slot for secondary images. Ignoring.\n");
             }
             skip_secondary = true;
@@ -1134,36 +1087,34 @@
         int fd = unzip_to_file(zip, images[i].img_name);
         if (fd == -1) {
             if (images[i].is_optional) {
-                continue;
+                continue; // An optional file is missing, so ignore it.
             }
-            CloseArchive(zip);
-            exit(1); // unzip_to_file already explained why.
+            die("non-optional file %s missing", images[i].img_name);
         }
+
         fastboot_buffer buf;
-        if (!load_buf_fd(transport, fd, &buf)) {
+        if (!load_buf_fd(fd, &buf)) {
             die("cannot load %s from flash: %s", images[i].img_name, strerror(errno));
         }
 
-        auto update = [&](const std::string &partition) {
+        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());
-            }
             flash_buf(partition.c_str(), &buf);
             /* not closing the fd here since the sparse code keeps the fd around
              * but hasn't mmaped data yet. The temporary file will get cleaned up when the
              * program exits.
              */
         };
-        do_for_partitions(transport, images[i].part_name, slot, update, false);
+        do_for_partitions(images[i].part_name, slot, update, false);
+    }
+
+    if (slot_override == "all") {
+        set_active("a");
+    } else {
+        set_active(slot_override);
     }
 
     CloseArchive(zip);
-    if (slot_override == "all") {
-        set_active(transport, "a");
-    } else {
-        set_active(transport, slot_override);
-    }
 }
 
 static void do_send_signature(const std::string& fn) {
@@ -1180,7 +1131,7 @@
     fb_queue_command("signature", "installing signature");
 }
 
-static void do_flashall(Transport* transport, const std::string& slot_override, int erase_first, bool skip_secondary) {
+static void do_flashall(const std::string& slot_override, bool skip_secondary) {
     std::string fname;
     queue_info_dump();
 
@@ -1193,17 +1144,17 @@
     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(reinterpret_cast<char*>(data), sz);
 
     std::string secondary;
     if (!skip_secondary) {
         if (slot_override != "") {
-            secondary = get_other_slot(transport, slot_override);
+            secondary = get_other_slot(slot_override);
         } else {
-            secondary = get_other_slot(transport);
+            secondary = get_other_slot();
         }
         if (secondary == "") {
-            if (supports_AB(transport)) {
+            if (supports_AB()) {
                 fprintf(stderr, "Warning: Could not determine slot for secondary images. Ignoring.\n");
             }
             skip_secondary = true;
@@ -1220,25 +1171,22 @@
         if (!slot) continue;
         fname = find_item_given_name(images[i].img_name);
         fastboot_buffer buf;
-        if (!load_buf(transport, fname.c_str(), &buf)) {
+        if (!load_buf(fname.c_str(), &buf)) {
             if (images[i].is_optional) continue;
-            die("could not load '%s': %s\n", images[i].img_name, strerror(errno));
+            die("could not load '%s': %s", images[i].img_name, strerror(errno));
         }
 
         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());
-            }
             flash_buf(partition.c_str(), &buf);
         };
-        do_for_partitions(transport, images[i].part_name, slot, flashall, false);
+        do_for_partitions(images[i].part_name, slot, flashall, false);
     }
 
     if (slot_override == "all") {
-        set_active(transport, "a");
+        set_active("a");
     } else {
-        set_active(transport, slot_override);
+        set_active(slot_override);
     }
 }
 
@@ -1249,18 +1197,6 @@
     return result;
 }
 
-static void do_bypass_unlock_command(std::vector<std::string>* args) {
-    if (args->empty()) syntax_error("missing unlock_bootloader request");
-
-    std::string filename = next_arg(args);
-
-    int64_t sz;
-    void* data = load_file(filename.c_str(), &sz);
-    if (data == nullptr) die("could not load '%s': %s", filename.c_str(), strerror(errno));
-    fb_queue_download("unlock_message", data, sz);
-    fb_queue_command("flashing unlock_bootloader", "unlocking bootloader");
-}
-
 static void do_oem_command(const std::string& cmd, std::vector<std::string>* args) {
     if (args->empty()) syntax_error("empty oem command");
 
@@ -1268,48 +1204,7 @@
     while (!args->empty()) {
         command += " " + next_arg(args);
     }
-    fb_queue_command(command.c_str(), "");
-}
-
-static int64_t parse_num(const char *arg)
-{
-    char *endptr;
-    unsigned long long num;
-
-    num = strtoull(arg, &endptr, 0);
-    if (endptr == arg) {
-        return -1;
-    }
-
-    if (*endptr == 'k' || *endptr == 'K') {
-        if (num >= (-1ULL) / 1024) {
-            return -1;
-        }
-        num *= 1024LL;
-        endptr++;
-    } else if (*endptr == 'm' || *endptr == 'M') {
-        if (num >= (-1ULL) / (1024 * 1024)) {
-            return -1;
-        }
-        num *= 1024LL * 1024LL;
-        endptr++;
-    } else if (*endptr == 'g' || *endptr == 'G') {
-        if (num >= (-1ULL) / (1024 * 1024 * 1024)) {
-            return -1;
-        }
-        num *= 1024LL * 1024LL * 1024LL;
-        endptr++;
-    }
-
-    if (*endptr != '\0') {
-        return -1;
-    }
-
-    if (num > INT64_MAX) {
-        return -1;
-    }
-
-    return num;
+    fb_queue_command(command, "");
 }
 
 static std::string fb_fix_numeric_var(std::string var) {
@@ -1321,10 +1216,10 @@
     return var;
 }
 
-static unsigned fb_get_flash_block_size(Transport* transport, std::string name) {
+static unsigned fb_get_flash_block_size(std::string name) {
     std::string sizeString;
-    if (!fb_getvar(transport, name.c_str(), &sizeString)) {
-        /* This device does not report flash block sizes, so return 0 */
+    if (!fb_getvar(name, &sizeString) || sizeString.empty()) {
+        // This device does not report flash block sizes, so return 0.
         return 0;
     }
     sizeString = fb_fix_numeric_var(sizeString);
@@ -1334,16 +1229,15 @@
         fprintf(stderr, "Couldn't parse %s '%s'.\n", name.c_str(), sizeString.c_str());
         return 0;
     }
-    if (size < 4096 || (size & (size - 1)) != 0) {
-        fprintf(stderr, "Invalid %s %u: must be a power of 2 and at least 4096.\n",
-                name.c_str(), size);
+    if ((size & (size - 1)) != 0) {
+        fprintf(stderr, "Invalid %s %u: must be a power of 2.\n", name.c_str(), size);
         return 0;
     }
     return size;
 }
 
-static void fb_perform_format(Transport* transport,
-                              const char* partition, int skip_if_not_supported,
+static void fb_perform_format(
+                              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;
@@ -1362,26 +1256,26 @@
         limit = sparse_limit;
     }
 
-    if (!fb_getvar(transport, std::string("partition-type:") + partition, &partition_type)) {
+    if (!fb_getvar("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("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;
     }
@@ -1406,12 +1300,12 @@
     }
 
     unsigned eraseBlkSize, logicalBlkSize;
-    eraseBlkSize = fb_get_flash_block_size(transport, "erase-block-size");
-    logicalBlkSize = fb_get_flash_block_size(transport, "logical-block-size");
+    eraseBlkSize = fb_get_flash_block_size("erase-block-size");
+    logicalBlkSize = fb_get_flash_block_size("logical-block-size");
 
     if (fs_generator_generate(gen, output.path, size, initial_dir,
             eraseBlkSize, logicalBlkSize)) {
-        fprintf(stderr, "Cannot generate image: %s\n", strerror(errno));
+        die("Cannot generate image for %s", partition.c_str());
         return;
     }
 
@@ -1420,7 +1314,7 @@
         fprintf(stderr, "Cannot open generated image: %s\n", strerror(errno));
         return;
     }
-    if (!load_buf_fd(transport, fd.release(), &buf)) {
+    if (!load_buf_fd(fd.release(), &buf)) {
         fprintf(stderr, "Cannot read image: %s\n", strerror(errno));
         return;
     }
@@ -1435,16 +1329,13 @@
     fprintf(stderr, "FAILED (%s)\n", fb_get_error().c_str());
 }
 
-int main(int argc, char **argv)
-{
+int FastBootTool::Main(int argc, char* argv[]) {
     bool wants_wipe = false;
     bool wants_reboot = false;
     bool wants_reboot_bootloader = false;
-    bool wants_reboot_emergency = false;
     bool skip_reboot = false;
     bool wants_set_active = false;
     bool skip_secondary = false;
-    bool erase_first = true;
     bool set_fbe_marker = false;
     void *data;
     int64_t sz;
@@ -1452,24 +1343,32 @@
     std::string slot_override;
     std::string next_active;
 
+    g_boot_img_hdr.kernel_addr = 0x00008000;
+    g_boot_img_hdr.ramdisk_addr = 0x01000000;
+    g_boot_img_hdr.second_addr = 0x00f00000;
+    g_boot_img_hdr.tags_addr = 0x00000100;
+    g_boot_img_hdr.page_size = 2048;
+
     const struct option longopts[] = {
-        {"base", required_argument, 0, 'b'},
-        {"kernel_offset", required_argument, 0, 'k'},
-        {"kernel-offset", required_argument, 0, 'k'},
-        {"page_size", required_argument, 0, 'n'},
-        {"page-size", required_argument, 0, 'n'},
-        {"ramdisk_offset", required_argument, 0, 'r'},
-        {"ramdisk-offset", required_argument, 0, 'r'},
-        {"tags_offset", required_argument, 0, 't'},
-        {"tags-offset", required_argument, 0, 't'},
+        {"base", required_argument, 0, 0},
+        {"cmdline", required_argument, 0, 0},
+        {"disable-verification", no_argument, 0, 0},
+        {"disable-verity", no_argument, 0, 0},
+        {"header-version", required_argument, 0, 0},
         {"help", no_argument, 0, 'h'},
-        {"unbuffered", no_argument, 0, 0},
-        {"version", no_argument, 0, 0},
-        {"slot", required_argument, 0, 0},
-        {"set_active", optional_argument, 0, 'a'},
+        {"kernel-offset", required_argument, 0, 0},
+        {"os-patch-level", required_argument, 0, 0},
+        {"os-version", required_argument, 0, 0},
+        {"page-size", required_argument, 0, 0},
+        {"ramdisk-offset", required_argument, 0, 0},
         {"set-active", optional_argument, 0, 'a'},
-        {"skip-secondary", no_argument, 0, 0},
         {"skip-reboot", no_argument, 0, 0},
+        {"skip-secondary", no_argument, 0, 0},
+        {"slot", required_argument, 0, 0},
+        {"tags-offset", required_argument, 0, 0},
+        {"unbuffered", no_argument, 0, 0},
+        {"verbose", no_argument, 0, 'v'},
+        {"version", no_argument, 0, 0},
 #if !defined(_WIN32)
         {"wipe-and-use-fbe", no_argument, 0, 0},
 #endif
@@ -1478,96 +1377,84 @@
 
     serial = getenv("ANDROID_SERIAL");
 
-    while (1) {
-        int c = getopt_long(argc, argv, "wub:k:n:r:t:s:S:lc:i:m:ha::", longopts, &longindex);
-        if (c < 0) {
-            break;
-        }
-        /* Alphabetical cases */
-        switch (c) {
-        case 'a':
-            wants_set_active = true;
-            if (optarg)
-                next_active = optarg;
-            break;
-        case 'b':
-            base_addr = strtoul(optarg, 0, 16);
-            break;
-        case 'c':
-            cmdline = optarg;
-            break;
-        case 'h':
-            return show_help();
-        case 'i': {
-                char *endptr = nullptr;
-                unsigned long val;
-
-                val = strtoul(optarg, &endptr, 0);
-                if (!endptr || *endptr != '\0' || (val & ~0xffff))
-                    die("invalid vendor id '%s'", optarg);
-                vendor_id = (unsigned short)val;
-                break;
-            }
-        case 'k':
-            kernel_offset = strtoul(optarg, 0, 16);
-            break;
-        case 'l':
-            long_listing = 1;
-            break;
-        case 'n':
-            page_size = (unsigned)strtoul(optarg, nullptr, 0);
-            if (!page_size) die("invalid page size");
-            break;
-        case 'r':
-            ramdisk_offset = strtoul(optarg, 0, 16);
-            break;
-        case 't':
-            tags_offset = strtoul(optarg, 0, 16);
-            break;
-        case 's':
-            serial = optarg;
-            break;
-        case 'S':
-            sparse_limit = parse_num(optarg);
-            if (sparse_limit < 0) {
-                    die("invalid sparse limit");
-            }
-            break;
-        case 'u':
-            erase_first = false;
-            break;
-        case 'w':
-            wants_wipe = true;
-            break;
-        case '?':
-            return 1;
-        case 0:
-            if (strcmp("unbuffered", longopts[longindex].name) == 0) {
+    int c;
+    while ((c = getopt_long(argc, argv, "a::hls:S:vw", longopts, &longindex)) != -1) {
+        if (c == 0) {
+            std::string name{longopts[longindex].name};
+            if (name == "base") {
+                g_base_addr = strtoul(optarg, 0, 16);
+            } else if (name == "cmdline") {
+                g_cmdline = optarg;
+            } else if (name == "disable-verification") {
+                g_disable_verification = true;
+            } else if (name == "disable-verity") {
+                g_disable_verity = true;
+            } else if (name == "header-version") {
+                g_boot_img_hdr.header_version = strtoul(optarg, nullptr, 0);
+            } else if (name == "kernel-offset") {
+                g_boot_img_hdr.kernel_addr = strtoul(optarg, 0, 16);
+            } else if (name == "os-patch-level") {
+                ParseOsPatchLevel(&g_boot_img_hdr, optarg);
+            } else if (name == "os-version") {
+                ParseOsVersion(&g_boot_img_hdr, optarg);
+            } else if (name == "page-size") {
+                g_boot_img_hdr.page_size = strtoul(optarg, nullptr, 0);
+                if (g_boot_img_hdr.page_size == 0) die("invalid page size");
+            } else if (name == "ramdisk-offset") {
+                g_boot_img_hdr.ramdisk_addr = strtoul(optarg, 0, 16);
+            } else if (name == "skip-reboot") {
+                skip_reboot = true;
+            } else if (name == "skip-secondary") {
+                skip_secondary = true;
+            } else if (name == "slot") {
+                slot_override = optarg;
+            } else if (name == "tags-offset") {
+                g_boot_img_hdr.tags_addr = strtoul(optarg, 0, 16);
+            } else if (name == "unbuffered") {
                 setvbuf(stdout, nullptr, _IONBF, 0);
                 setvbuf(stderr, nullptr, _IONBF, 0);
-            } else if (strcmp("version", longopts[longindex].name) == 0) {
+            } else if (name == "version") {
                 fprintf(stdout, "fastboot version %s\n", FASTBOOT_VERSION);
                 fprintf(stdout, "Installed as %s\n", android::base::GetExecutablePath().c_str());
                 return 0;
-            } else if (strcmp("slot", longopts[longindex].name) == 0) {
-                slot_override = std::string(optarg);
-            } else if (strcmp("skip-secondary", longopts[longindex].name) == 0 ) {
-                skip_secondary = true;
-            } else if (strcmp("skip-reboot", longopts[longindex].name) == 0 ) {
-                skip_reboot = true;
 #if !defined(_WIN32)
-            } else if (strcmp("wipe-and-use-fbe", longopts[longindex].name) == 0) {
+            } else if (name == "wipe-and-use-fbe") {
                 wants_wipe = true;
                 set_fbe_marker = true;
 #endif
             } else {
-                fprintf(stderr, "Internal error in options processing for %s\n",
-                    longopts[longindex].name);
-                return 1;
+                die("unknown option %s", longopts[longindex].name);
             }
-            break;
-        default:
-            abort();
+        } else {
+            switch (c) {
+                case 'a':
+                    wants_set_active = true;
+                    if (optarg) next_active = optarg;
+                    break;
+                case 'h':
+                    return show_help();
+                case 'l':
+                    g_long_listing = true;
+                    break;
+                case 's':
+                    serial = optarg;
+                    break;
+                case 'S':
+                    if (!android::base::ParseByteCount(optarg, &sparse_limit)) {
+                        die("invalid sparse limit %s", optarg);
+                    }
+                    break;
+                case 'v':
+                    set_verbose();
+                    break;
+                case 'w':
+                    wants_wipe = true;
+                    break;
+                case '?':
+                    return 1;
+                default:
+                    abort();
+            }
         }
     }
 
@@ -1589,24 +1476,25 @@
     if (transport == nullptr) {
         return 1;
     }
+    fastboot::FastBootDriver fb(transport);
+    fb_init(fb);
 
-    if (!supports_AB(transport) && supports_AB_obsolete(transport)) {
-        fprintf(stderr, "Warning: Device A/B support is outdated. Bootloader update required.\n");
-    }
-    if (slot_override != "") slot_override = verify_slot(transport, slot_override);
-    if (next_active != "") next_active = verify_slot(transport, next_active, false);
+    const double start = now();
+
+    if (slot_override != "") slot_override = verify_slot(slot_override);
+    if (next_active != "") next_active = verify_slot(next_active, false);
 
     if (wants_set_active) {
         if (next_active == "") {
             if (slot_override == "") {
                 std::string current_slot;
-                if (fb_getvar(transport, "current-slot", &current_slot)) {
-                    next_active = verify_slot(transport, current_slot, false);
+                if (fb_getvar("current-slot", &current_slot)) {
+                    next_active = verify_slot(current_slot, false);
                 } else {
                     wants_set_active = false;
                 }
             } else {
-                next_active = verify_slot(transport, slot_override, false);
+                next_active = verify_slot(slot_override, false);
             }
         }
     }
@@ -1617,21 +1505,21 @@
 
         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) {
                 std::string partition_type;
-                if (fb_getvar(transport, std::string("partition-type:") + partition,
+                if (fb_getvar(std::string("partition-type:") + partition,
                               &partition_type) &&
                     fs_get_generator(partition_type) != nullptr) {
                     fprintf(stderr, "******** Did you mean to fastboot format this %s partition?\n",
                             partition_type.c_str());
                 }
 
-                fb_queue_erase(partition.c_str());
+                fb_queue_erase(partition);
             };
-            do_for_partitions(transport, partition, slot_override, erase, true);
+            do_for_partitions(partition, slot_override, erase, true);
         } else if (android::base::StartsWith(command, "format")) {
             // Parsing for: "format[:[type][:[size]]]"
             // Some valid things:
@@ -1649,18 +1537,14 @@
             std::string partition = next_arg(&args);
 
             auto format = [&](const std::string& partition) {
-                if (erase_first && needs_erase(transport, partition.c_str())) {
-                    fb_queue_erase(partition.c_str());
-                }
-                fb_perform_format(transport, partition.c_str(), 0, type_override, size_override,
-                                  "");
+                fb_perform_format(partition, 0, type_override, size_override, "");
             };
-            do_for_partitions(transport, partition.c_str(), slot_override, format, true);
+            do_for_partitions(partition.c_str(), slot_override, format, true);
         } else if (command == "signature") {
             std::string filename = next_arg(&args);
             data = load_file(filename.c_str(), &sz);
             if (data == nullptr) die("could not load '%s': %s", filename.c_str(), strerror(errno));
-            if (sz != 256) die("signature must be 256 bytes");
+            if (sz != 256) die("signature must be 256 bytes (got %" PRId64 ")", sz);
             fb_queue_download("signature", data, sz);
             fb_queue_command("signature", "installing signature");
         } else if (command == "reboot") {
@@ -1671,9 +1555,6 @@
                 if (what == "bootloader") {
                     wants_reboot = false;
                     wants_reboot_bootloader = true;
-                } else if (what == "emergency") {
-                    wants_reboot = false;
-                    wants_reboot_emergency = true;
                 } else {
                     syntax_error("unknown reboot target %s", what.c_str());
                 }
@@ -1691,7 +1572,7 @@
             std::string second_stage;
             if (!args.empty()) second_stage = next_arg(&args);
 
-            data = load_bootable_image(kernel, ramdisk, second_stage, &sz, cmdline);
+            data = load_bootable_image(kernel, ramdisk, second_stage, &sz);
             fb_queue_download("boot.img", data, sz);
             fb_queue_command("boot", "booting");
         } else if (command == "flash") {
@@ -1706,12 +1587,9 @@
             if (fname.empty()) die("cannot determine image filename for '%s'", pname.c_str());
 
             auto flash = [&](const std::string &partition) {
-                if (erase_first && needs_erase(transport, partition.c_str())) {
-                    fb_queue_erase(partition.c_str());
-                }
-                do_flash(transport, partition.c_str(), fname.c_str());
+                do_flash(partition.c_str(), fname.c_str());
             };
-            do_for_partitions(transport, pname.c_str(), slot_override, flash, true);
+            do_for_partitions(pname.c_str(), slot_override, flash, true);
         } else if (command == "flash:raw") {
             std::string partition = next_arg(&args);
             std::string kernel = next_arg(&args);
@@ -1720,17 +1598,17 @@
             std::string second_stage;
             if (!args.empty()) second_stage = next_arg(&args);
 
-            data = load_bootable_image(kernel, ramdisk, second_stage, &sz, cmdline);
+            data = load_bootable_image(kernel, ramdisk, second_stage, &sz);
             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);
+            do_for_partitions(partition, slot_override, flashraw, true);
         } else if (command == "flashall") {
             if (slot_override == "all") {
                 fprintf(stderr, "Warning: slot set to 'all'. Secondary slots will not be flashed.\n");
-                do_flashall(transport, slot_override, erase_first, true);
+                do_flashall(slot_override, true);
             } else {
-                do_flashall(transport, slot_override, erase_first, skip_secondary);
+                do_flashall(slot_override, skip_secondary);
             }
             wants_reboot = true;
         } else if (command == "update") {
@@ -1742,32 +1620,22 @@
             if (!args.empty()) {
                 filename = next_arg(&args);
             }
-            do_update(transport, filename.c_str(), slot_override, erase_first,
-                      skip_secondary || slot_all);
+            do_update(filename.c_str(), slot_override, skip_secondary || slot_all);
             wants_reboot = true;
         } else if (command == "set_active") {
-            std::string slot = verify_slot(transport, next_arg(&args), false);
-
-            // Legacy support: verify_slot() removes leading underscores, we need to put them back
-            // in for old bootloaders. Legacy bootloaders do not have the slot-count variable but
-            // do have slot-suffixes.
-            std::string var;
-            if (!fb_getvar(transport, "slot-count", &var) &&
-                    fb_getvar(transport, "slot-suffixes", &var)) {
-                slot = "_" + slot;
-            }
-            fb_set_active(slot.c_str());
+            std::string slot = verify_slot(next_arg(&args), false);
+            fb_set_active(slot);
         } else if (command == "stage") {
             std::string filename = next_arg(&args);
 
             struct fastboot_buffer buf;
-            if (!load_buf(transport, filename.c_str(), &buf) || buf.type != FB_BUFFER_FD) {
+            if (!load_buf(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") {
@@ -1776,12 +1644,8 @@
             } else if (args.size() == 1 && (args[0] == "unlock" || args[0] == "lock" ||
                                             args[0] == "unlock_critical" ||
                                             args[0] == "lock_critical" ||
-                                            args[0] == "get_unlock_ability" ||
-                                            args[0] == "get_unlock_bootloader_nonce" ||
-                                            args[0] == "lock_bootloader")) {
+                                            args[0] == "get_unlock_ability")) {
                 do_oem_command("flashing", &args);
-            } else if (args.size() == 2 && args[0] == "unlock_bootloader") {
-                do_bypass_unlock_command(&args);
             } else {
                 syntax_error("unknown 'flashing' command %s", args[0].c_str());
             }
@@ -1791,29 +1655,24 @@
     }
 
     if (wants_wipe) {
-        fprintf(stderr, "wiping userdata...\n");
-        fb_queue_erase("userdata");
-        if (set_fbe_marker) {
-            fprintf(stderr, "setting FBE marker...\n");
-            std::string initial_userdata_dir = create_fbemarker_tmpdir();
-            if (initial_userdata_dir.empty()) {
-                return 1;
+        std::vector<std::string> partitions = { "userdata", "cache", "metadata" };
+        for (const auto& partition : partitions) {
+            std::string partition_type;
+            if (!fb_getvar(std::string{"partition-type:"} + partition, &partition_type)) continue;
+            if (partition_type.empty()) continue;
+            fb_queue_erase(partition);
+            if (partition == "userdata" && set_fbe_marker) {
+                fprintf(stderr, "setting FBE marker on initial userdata...\n");
+                std::string initial_userdata_dir = create_fbemarker_tmpdir();
+                fb_perform_format(partition, 1, "", "", initial_userdata_dir);
+                delete_fbemarker_tmpdir(initial_userdata_dir);
+            } else {
+                fb_perform_format(partition, 1, "", "", "");
             }
-            fb_perform_format(transport, "userdata", 1, "", "", initial_userdata_dir);
-            delete_fbemarker_tmpdir(initial_userdata_dir);
-        } else {
-            fb_perform_format(transport, "userdata", 1, "", "", "");
-        }
-
-        std::string cache_type;
-        if (fb_getvar(transport, "partition-type:cache", &cache_type) && !cache_type.empty()) {
-            fprintf(stderr, "wiping cache...\n");
-            fb_queue_erase("cache");
-            fb_perform_format(transport, "cache", 1, "", "", "");
         }
     }
     if (wants_set_active) {
-        fb_set_active(next_active.c_str());
+        fb_set_active(next_active);
     }
     if (wants_reboot && !skip_reboot) {
         fb_queue_reboot();
@@ -1821,10 +1680,32 @@
     } else if (wants_reboot_bootloader) {
         fb_queue_command("reboot-bootloader", "rebooting into bootloader");
         fb_queue_wait_for_disconnect();
-    } else if (wants_reboot_emergency) {
-        fb_queue_command("reboot-emergency", "rebooting into emergency download (EDL) mode");
-        fb_queue_wait_for_disconnect();
     }
 
-    return fb_execute_queue(transport) ? EXIT_FAILURE : EXIT_SUCCESS;
+    int status = fb_execute_queue() ? EXIT_FAILURE : EXIT_SUCCESS;
+    fprintf(stderr, "Finished. Total time: %.3fs\n", (now() - start));
+    return status;
+}
+
+void FastBootTool::ParseOsPatchLevel(boot_img_hdr_v1* hdr, const char* arg) {
+    unsigned year, month, day;
+    if (sscanf(arg, "%u-%u-%u", &year, &month, &day) != 3) {
+        syntax_error("OS patch level should be YYYY-MM-DD: %s", arg);
+    }
+    if (year < 2000 || year >= 2128) syntax_error("year out of range: %d", year);
+    if (month < 1 || month > 12) syntax_error("month out of range: %d", month);
+    hdr->SetOsPatchLevel(year, month);
+}
+
+void FastBootTool::ParseOsVersion(boot_img_hdr_v1* hdr, const char* arg) {
+    unsigned major = 0, minor = 0, patch = 0;
+    std::vector<std::string> versions = android::base::Split(arg, ".");
+    if (versions.size() < 1 || versions.size() > 3 ||
+        (versions.size() >= 1 && !android::base::ParseUint(versions[0], &major)) ||
+        (versions.size() >= 2 && !android::base::ParseUint(versions[1], &minor)) ||
+        (versions.size() == 3 && !android::base::ParseUint(versions[2], &patch)) ||
+        (major > 0x7f || minor > 0x7f || patch > 0x7f)) {
+        syntax_error("bad OS version: %s", arg);
+    }
+    hdr->SetOsVersion(major, minor, patch);
 }
diff --git a/fastboot/fastboot.h b/fastboot/fastboot.h
deleted file mode 100644
index e3c60ae..0000000
--- a/fastboot/fastboot.h
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *  * Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- *  * Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in
- *    the documentation and/or other materials provided with the
- *    distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
- * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
- * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
- * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
- * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
- * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#ifndef _FASTBOOT_H_
-#define _FASTBOOT_H_
-
-#include <inttypes.h>
-#include <stdlib.h>
-
-#include <string>
-
-#include "transport.h"
-
-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);
-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);
-int64_t fb_upload_data(Transport* transport, const char* outfile);
-const std::string fb_get_error();
-
-#define FB_COMMAND_SZ 64
-#define FB_RESPONSE_SZ 64
-
-/* 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_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_wait_for_disconnect(void);
-int64_t fb_execute_queue(Transport* transport);
-void fb_set_active(const char *slot);
-
-/* util stuff */
-double now();
-char *mkmsg(const char *fmt, ...);
-__attribute__((__noreturn__)) void die(const char *fmt, ...);
-
-/* Current product */
-extern char cur_product[FB_RESPONSE_SZ + 1];
-
-#endif
diff --git a/fastboot/fastboot_driver.cpp b/fastboot/fastboot_driver.cpp
new file mode 100644
index 0000000..aabc620
--- /dev/null
+++ b/fastboot/fastboot_driver.cpp
@@ -0,0 +1,537 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+#include "fastboot_driver.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <algorithm>
+#include <chrono>
+#include <fstream>
+#include <memory>
+#include <regex>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+#include <android-base/unique_fd.h>
+#include <utils/FileMap.h>
+#include "fastboot_driver.h"
+#include "transport.h"
+
+namespace fastboot {
+
+/*************************** PUBLIC *******************************/
+FastBootDriver::FastBootDriver(Transport* transport, std::function<void(std::string&)> info,
+                               bool no_checks)
+    : transport(transport) {
+    info_cb_ = info;
+    disable_checks_ = no_checks;
+}
+
+RetCode FastBootDriver::Boot(std::string* response, std::vector<std::string>* info) {
+    return RawCommand(Commands::BOOT, response, info);
+}
+
+RetCode FastBootDriver::Continue(std::string* response, std::vector<std::string>* info) {
+    return RawCommand(Commands::CONTINUE, response, info);
+}
+
+RetCode FastBootDriver::Erase(const std::string& part, std::string* response,
+                              std::vector<std::string>* info) {
+    return RawCommand(Commands::ERASE + part, response, info);
+}
+
+RetCode FastBootDriver::Flash(const std::string& part, std::string* response,
+                              std::vector<std::string>* info) {
+    return RawCommand(Commands::FLASH + part, response, info);
+}
+
+RetCode FastBootDriver::GetVar(const std::string& key, std::string* val,
+                               std::vector<std::string>* info) {
+    return RawCommand(Commands::GET_VAR + key, val, info);
+}
+
+RetCode FastBootDriver::GetVarAll(std::vector<std::string>* response) {
+    std::string tmp;
+    return GetVar("all", &tmp, response);
+}
+
+RetCode FastBootDriver::Powerdown(std::string* response, std::vector<std::string>* info) {
+    return RawCommand(Commands::POWERDOWN, response, info);
+}
+
+RetCode FastBootDriver::Reboot(std::string* response, std::vector<std::string>* info) {
+    return RawCommand(Commands::REBOOT, response, info);
+}
+
+RetCode FastBootDriver::SetActive(const std::string& part, std::string* response,
+                                  std::vector<std::string>* info) {
+    return RawCommand(Commands::SET_ACTIVE + part, response, info);
+}
+
+RetCode FastBootDriver::Verify(uint32_t num, std::string* response, std::vector<std::string>* info) {
+    std::string cmd = android::base::StringPrintf("%s%08" PRIx32, Commands::VERIFY.c_str(), num);
+    return RawCommand(cmd, response, info);
+}
+
+RetCode FastBootDriver::FlashPartition(const std::string& part, const std::vector<char>& data) {
+    RetCode ret;
+    if ((ret = Download(data))) {
+        return ret;
+    }
+    return RawCommand(Commands::FLASH + part);
+}
+
+RetCode FastBootDriver::FlashPartition(const std::string& part, int fd, uint32_t sz) {
+    RetCode ret;
+    if ((ret = Download(fd, sz))) {
+        return ret;
+    }
+    return RawCommand(Commands::FLASH + part);
+}
+
+RetCode FastBootDriver::FlashPartition(const std::string& part, sparse_file* s) {
+    RetCode ret;
+    if ((ret = Download(s))) {
+        return ret;
+    }
+    return RawCommand(Commands::FLASH + part);
+}
+
+RetCode FastBootDriver::Partitions(std::vector<std::tuple<std::string, uint32_t>>* parts) {
+    std::vector<std::string> all;
+    RetCode ret;
+    if ((ret = GetVarAll(&all))) {
+        return ret;
+    }
+
+    std::regex reg("partition-size[[:s:]]*:[[:s:]]*([[:w:]]+)[[:s:]]*:[[:s:]]*0x([[:d:]]+)");
+    std::smatch sm;
+
+    for (auto& s : all) {
+        if (std::regex_match(s, sm, reg)) {
+            std::string m1(sm[1]);
+            std::string m2(sm[2]);
+            uint32_t tmp = strtol(m2.c_str(), 0, 16);
+            parts->push_back(std::make_tuple(m1, tmp));
+        }
+    }
+    return SUCCESS;
+}
+
+RetCode FastBootDriver::Require(const std::string& var, const std::vector<std::string>& allowed,
+                                bool* reqmet, bool invert) {
+    *reqmet = invert;
+    RetCode ret;
+    std::string response;
+    if ((ret = GetVar(var, &response))) {
+        return ret;
+    }
+
+    // Now check if we have a match
+    for (const auto s : allowed) {
+        // If it ends in *, and starting substring match
+        if (response == s || (s.length() && s.back() == '*' &&
+                              !response.compare(0, s.length() - 1, s, 0, s.length() - 1))) {
+            *reqmet = !invert;
+            break;
+        }
+    }
+
+    return SUCCESS;
+}
+
+RetCode FastBootDriver::Download(int fd, size_t size, std::string* response,
+                                 std::vector<std::string>* info) {
+    RetCode ret;
+
+    if ((size <= 0 || size > MAX_DOWNLOAD_SIZE) && !disable_checks_) {
+        error_ = "File is too large to download";
+        return BAD_ARG;
+    }
+
+    uint32_t u32size = static_cast<uint32_t>(size);
+    if ((ret = DownloadCommand(u32size, response, info))) {
+        return ret;
+    }
+
+    // Write the buffer
+    if ((ret = SendBuffer(fd, size))) {
+        return ret;
+    }
+
+    // Wait for response
+    return HandleResponse(response, info);
+}
+
+RetCode FastBootDriver::Download(const std::vector<char>& buf, std::string* response,
+                                 std::vector<std::string>* info) {
+    return Download(buf.data(), buf.size(), response, info);
+}
+
+RetCode FastBootDriver::Download(const char* buf, uint32_t size, std::string* response,
+                                 std::vector<std::string>* info) {
+    RetCode ret;
+    error_ = "";
+    if ((size == 0 || size > MAX_DOWNLOAD_SIZE) && !disable_checks_) {
+        error_ = "Buffer is too large or 0 bytes";
+        return BAD_ARG;
+    }
+
+    if ((ret = DownloadCommand(size, response, info))) {
+        return ret;
+    }
+
+    // Write the buffer
+    if ((ret = SendBuffer(buf, size))) {
+        return ret;
+    }
+
+    // Wait for response
+    return HandleResponse(response, info);
+}
+
+RetCode FastBootDriver::Download(sparse_file* s, std::string* response,
+                                 std::vector<std::string>* info) {
+    error_ = "";
+    int64_t size = sparse_file_len(s, true, false);
+    if (size <= 0 || size > MAX_DOWNLOAD_SIZE) {
+        error_ = "Sparse file is too large or invalid";
+        return BAD_ARG;
+    }
+
+    RetCode ret;
+    uint32_t u32size = static_cast<uint32_t>(size);
+    if ((ret = DownloadCommand(u32size, response, info))) {
+        return ret;
+    }
+
+    struct SparseCBPrivate {
+        FastBootDriver* self;
+        std::vector<char> tpbuf;
+    } cb_priv;
+    cb_priv.self = this;
+
+    auto cb = [](void* priv, const void* buf, size_t len) -> int {
+        SparseCBPrivate* data = static_cast<SparseCBPrivate*>(priv);
+        const char* cbuf = static_cast<const char*>(buf);
+        return data->self->SparseWriteCallback(data->tpbuf, cbuf, len);
+    };
+
+    if (sparse_file_callback(s, true, false, cb, &cb_priv) < 0) {
+        error_ = "Error reading sparse file";
+        return IO_ERROR;
+    }
+
+    // Now flush
+    if (cb_priv.tpbuf.size() && (ret = SendBuffer(cb_priv.tpbuf))) {
+        return ret;
+    }
+
+    return HandleResponse(response, info);
+}
+
+RetCode FastBootDriver::Upload(const std::string& outfile, std::string* response,
+                               std::vector<std::string>* info) {
+    RetCode ret;
+    int dsize;
+    if ((ret = RawCommand(Commands::UPLOAD, response, info, &dsize)) || dsize == 0) {
+        error_ = "Upload request failed";
+        return ret;
+    }
+
+    std::vector<char> data;
+    data.resize(dsize);
+
+    if ((ret = ReadBuffer(data))) {
+        return ret;
+    }
+
+    std::ofstream ofs;
+    ofs.open(outfile, std::ofstream::out | std::ofstream::binary);
+    if (ofs.fail()) {
+        error_ = android::base::StringPrintf("Failed to open '%s'", outfile.c_str());
+        return IO_ERROR;
+    }
+    ofs.write(data.data(), data.size());
+    if (ofs.fail() || ofs.bad()) {
+        error_ = android::base::StringPrintf("Writing to '%s' failed", outfile.c_str());
+        return IO_ERROR;
+    }
+    ofs.close();
+
+    return HandleResponse(response, info);
+}
+
+// Helpers
+void FastBootDriver::SetInfoCallback(std::function<void(std::string&)> info) {
+    info_cb_ = info;
+}
+
+const std::string FastBootDriver::RCString(RetCode rc) {
+    switch (rc) {
+        case SUCCESS:
+            return std::string("Success");
+
+        case BAD_ARG:
+            return std::string("Invalid Argument");
+
+        case IO_ERROR:
+            return std::string("I/O Error");
+
+        case BAD_DEV_RESP:
+            return std::string("Invalid Device Response");
+
+        case DEVICE_FAIL:
+            return std::string("Device Error");
+
+        case TIMEOUT:
+            return std::string("Timeout");
+
+        default:
+            return std::string("Unknown Error");
+    }
+}
+
+std::string FastBootDriver::Error() {
+    return error_;
+}
+
+RetCode FastBootDriver::WaitForDisconnect() {
+    return transport->WaitForDisconnect() ? IO_ERROR : SUCCESS;
+}
+
+/****************************** PROTECTED *************************************/
+RetCode FastBootDriver::RawCommand(const std::string& cmd, std::string* response,
+                                   std::vector<std::string>* info, int* dsize) {
+    error_ = "";  // Clear any pending error
+    if (cmd.size() > FB_COMMAND_SZ && !disable_checks_) {
+        error_ = "Command length to RawCommand() is too long";
+        return BAD_ARG;
+    }
+
+    if (transport->Write(cmd.c_str(), cmd.size()) != static_cast<int>(cmd.size())) {
+        error_ = ErrnoStr("Write to device failed");
+        return IO_ERROR;
+    }
+
+    // Read the response
+    return HandleResponse(response, info, dsize);
+}
+
+RetCode FastBootDriver::DownloadCommand(uint32_t size, std::string* response,
+                                        std::vector<std::string>* info) {
+    std::string cmd(android::base::StringPrintf("%s%08" PRIx32, Commands::DOWNLOAD.c_str(), size));
+    RetCode ret;
+    if ((ret = RawCommand(cmd, response, info))) {
+        return ret;
+    }
+    return SUCCESS;
+}
+
+RetCode FastBootDriver::HandleResponse(std::string* response, std::vector<std::string>* info,
+                                       int* dsize) {
+    char status[FB_RESPONSE_SZ + 1];
+    auto start = std::chrono::system_clock::now();
+
+    auto set_response = [response](std::string s) {
+        if (response) *response = std::move(s);
+    };
+    auto add_info = [info](std::string s) {
+        if (info) info->push_back(std::move(s));
+    };
+
+    // erase response
+    set_response("");
+    while ((std::chrono::system_clock::now() - start) < std::chrono::seconds(RESP_TIMEOUT)) {
+        int r = transport->Read(status, FB_RESPONSE_SZ);
+        if (r < 0) {
+            error_ = ErrnoStr("Status read failed");
+            return IO_ERROR;
+        }
+
+        status[r] = '\0';  // Need the null terminator
+        std::string input(status);
+        if (android::base::StartsWith(input, "INFO")) {
+            std::string tmp = input.substr(strlen("INFO"));
+            info_cb_(tmp);
+            add_info(std::move(tmp));
+        } else if (android::base::StartsWith(input, "OKAY")) {
+            set_response(input.substr(strlen("OKAY")));
+            return SUCCESS;
+        } else if (android::base::StartsWith(input, "FAIL")) {
+            error_ = android::base::StringPrintf("remote: '%s'", status + strlen("FAIL"));
+            set_response(input.substr(strlen("FAIL")));
+            return DEVICE_FAIL;
+        } else if (android::base::StartsWith(input, "DATA")) {
+            std::string tmp = input.substr(strlen("DATA"));
+            uint32_t num = strtol(tmp.c_str(), 0, 16);
+            if (num > MAX_DOWNLOAD_SIZE) {
+                error_ = android::base::StringPrintf("Data size too large (%d)", num);
+                return BAD_DEV_RESP;
+            }
+            if (dsize) *dsize = num;
+            set_response(std::move(tmp));
+            return SUCCESS;
+        } else {
+            error_ = android::base::StringPrintf("Device sent unknown status code: %s", status);
+            return BAD_DEV_RESP;
+        }
+
+    }  // End of while loop
+
+    return TIMEOUT;
+}
+
+std::string FastBootDriver::ErrnoStr(const std::string& msg) {
+    return android::base::StringPrintf("%s (%s)", msg.c_str(), strerror(errno));
+}
+
+const std::string FastBootDriver::Commands::BOOT = "boot";
+const std::string FastBootDriver::Commands::CONTINUE = "continue";
+const std::string FastBootDriver::Commands::DOWNLOAD = "download:";
+const std::string FastBootDriver::Commands::ERASE = "erase:";
+const std::string FastBootDriver::Commands::FLASH = "flash:";
+const std::string FastBootDriver::Commands::GET_VAR = "getvar:";
+const std::string FastBootDriver::Commands::POWERDOWN = "powerdown";
+const std::string FastBootDriver::Commands::REBOOT = "reboot";
+const std::string FastBootDriver::Commands::SET_ACTIVE = "set_active:";
+const std::string FastBootDriver::Commands::UPLOAD = "upload";
+const std::string FastBootDriver::Commands::VERIFY = "verify:";
+
+/******************************* PRIVATE **************************************/
+RetCode FastBootDriver::SendBuffer(int fd, size_t size) {
+    static constexpr uint32_t MAX_MAP_SIZE = 512 * 1024 * 1024;
+    off64_t offset = 0;
+    uint32_t remaining = size;
+    RetCode ret;
+
+    while (remaining) {
+        // Memory map the file
+        android::FileMap filemap;
+        size_t len = std::min(remaining, MAX_MAP_SIZE);
+
+        if (!filemap.create(NULL, fd, offset, len, true)) {
+            error_ = "Creating filemap failed";
+            return IO_ERROR;
+        }
+
+        if ((ret = SendBuffer(filemap.getDataPtr(), len))) {
+            return ret;
+        }
+
+        remaining -= len;
+        offset += len;
+    }
+
+    return SUCCESS;
+}
+
+RetCode FastBootDriver::SendBuffer(const std::vector<char>& buf) {
+    // Write the buffer
+    return SendBuffer(buf.data(), buf.size());
+}
+
+RetCode FastBootDriver::SendBuffer(const void* buf, size_t size) {
+    if (!size) {
+        return SUCCESS;
+    }
+
+    // Write the buffer
+    ssize_t tmp = transport->Write(buf, size);
+
+    if (tmp < 0) {
+        error_ = ErrnoStr("Write to device failed in SendBuffer()");
+        return IO_ERROR;
+    } else if (static_cast<size_t>(tmp) != size) {
+        error_ = android::base::StringPrintf("Failed to write all %zu bytes", size);
+
+        return IO_ERROR;
+    }
+
+    return SUCCESS;
+}
+
+RetCode FastBootDriver::ReadBuffer(std::vector<char>& buf) {
+    // Read the buffer
+    return ReadBuffer(buf.data(), buf.size());
+}
+
+RetCode FastBootDriver::ReadBuffer(void* buf, size_t size) {
+    // Read the buffer
+    ssize_t tmp = transport->Read(buf, size);
+
+    if (tmp < 0) {
+        error_ = ErrnoStr("Read from device failed in ReadBuffer()");
+        return IO_ERROR;
+    } else if (static_cast<size_t>(tmp) != size) {
+        error_ = android::base::StringPrintf("Failed to read all %zu bytes", size);
+        return IO_ERROR;
+    }
+
+    return SUCCESS;
+}
+
+int FastBootDriver::SparseWriteCallback(std::vector<char>& tpbuf, const char* data, size_t len) {
+    size_t total = 0;
+    size_t to_write = std::min(TRANSPORT_CHUNK_SIZE - tpbuf.size(), len);
+
+    // Handle the residual
+    tpbuf.insert(tpbuf.end(), data, data + to_write);
+    if (tpbuf.size() < TRANSPORT_CHUNK_SIZE) {  // Nothing enough to send rn
+        return 0;
+    }
+
+    if (SendBuffer(tpbuf)) {
+        error_ = ErrnoStr("Send failed in SparseWriteCallback()");
+        return -1;
+    }
+    tpbuf.clear();
+    total += to_write;
+
+    // Now we need to send a multiple of chunk size
+    size_t nchunks = (len - total) / TRANSPORT_CHUNK_SIZE;
+    size_t nbytes = TRANSPORT_CHUNK_SIZE * nchunks;
+    if (SendBuffer(data + total, nbytes)) {
+        error_ = ErrnoStr("Send failed in SparseWriteCallback()");
+        return -1;
+    }
+    total += nbytes;
+
+    if (len - total > 0) {  // We have residual data to save for next time
+        tpbuf.assign(data + total, data + len);
+    }
+
+    return 0;
+}
+
+}  // End namespace fastboot
diff --git a/fastboot/fastboot_driver.h b/fastboot/fastboot_driver.h
new file mode 100644
index 0000000..9fdd317
--- /dev/null
+++ b/fastboot/fastboot_driver.h
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+#pragma once
+#include <cstdlib>
+#include <deque>
+#include <limits>
+#include <string>
+#include <vector>
+
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <bootimg.h>
+#include <inttypes.h>
+#include <sparse/sparse.h>
+#include "transport.h"
+
+class Transport;
+
+namespace fastboot {
+
+static constexpr int FB_COMMAND_SZ = 64;
+static constexpr int FB_RESPONSE_SZ = 64;
+
+enum RetCode : int {
+    SUCCESS = 0,
+    BAD_ARG,
+    IO_ERROR,
+    BAD_DEV_RESP,
+    DEVICE_FAIL,
+    TIMEOUT,
+};
+
+class FastBootDriver {
+  public:
+    static constexpr int RESP_TIMEOUT = 10;  // 10 seconds
+    static constexpr uint32_t MAX_DOWNLOAD_SIZE = std::numeric_limits<uint32_t>::max();
+    static constexpr size_t TRANSPORT_CHUNK_SIZE = 1024;
+
+    FastBootDriver(Transport* transport,
+                   std::function<void(std::string&)> info = [](std::string&) {},
+                   bool no_checks = false);
+
+    RetCode Boot(std::string* response = nullptr, std::vector<std::string>* info = nullptr);
+    RetCode Continue(std::string* response = nullptr, std::vector<std::string>* info = nullptr);
+    RetCode Download(int fd, size_t size, std::string* response = nullptr,
+                     std::vector<std::string>* info = nullptr);
+    RetCode Download(const std::vector<char>& buf, std::string* response = nullptr,
+                     std::vector<std::string>* info = nullptr);
+    // This will be removed after fastboot is modified to use a vector
+    RetCode Download(const char* buf, uint32_t size, std::string* response = nullptr,
+                     std::vector<std::string>* info = nullptr);
+    RetCode Download(sparse_file* s, std::string* response = nullptr,
+                     std::vector<std::string>* info = nullptr);
+    RetCode Erase(const std::string& part, std::string* response = nullptr,
+                  std::vector<std::string>* info = nullptr);
+    RetCode Flash(const std::string& part, std::string* response = nullptr,
+                  std::vector<std::string>* info = nullptr);
+    RetCode GetVar(const std::string& key, std::string* val,
+                   std::vector<std::string>* info = nullptr);
+    RetCode GetVarAll(std::vector<std::string>* response);
+    RetCode Powerdown(std::string* response = nullptr, std::vector<std::string>* info = nullptr);
+    RetCode Reboot(std::string* response = nullptr, std::vector<std::string>* info = nullptr);
+    RetCode SetActive(const std::string& part, std::string* response = nullptr,
+                      std::vector<std::string>* info = nullptr);
+    RetCode Upload(const std::string& outfile, std::string* response = nullptr,
+                   std::vector<std::string>* info = nullptr);
+    RetCode Verify(uint32_t num, std::string* response = nullptr,
+                   std::vector<std::string>* info = nullptr);
+
+    /* HIGHER LEVEL COMMANDS -- Composed of the commands above */
+    RetCode FlashPartition(const std::string& part, const std::vector<char>& data);
+    RetCode FlashPartition(const std::string& part, int fd, uint32_t sz);
+    RetCode FlashPartition(const std::string& part, sparse_file* s);
+
+    RetCode Partitions(std::vector<std::tuple<std::string, uint32_t>>* parts);
+    RetCode Require(const std::string& var, const std::vector<std::string>& allowed, bool* reqmet,
+                    bool invert = false);
+
+    /* HELPERS */
+    void SetInfoCallback(std::function<void(std::string&)> info);
+    const std::string RCString(RetCode rc);
+    std::string Error();
+    RetCode WaitForDisconnect();
+
+    // This is temporarily public for engine.cpp
+    RetCode RawCommand(const std::string& cmd, std::string* response = nullptr,
+                       std::vector<std::string>* info = nullptr, int* dsize = nullptr);
+
+  protected:
+    RetCode DownloadCommand(uint32_t size, std::string* response = nullptr,
+                            std::vector<std::string>* info = nullptr);
+    RetCode HandleResponse(std::string* response = nullptr,
+                           std::vector<std::string>* info = nullptr, int* dsize = nullptr);
+
+    std::string ErrnoStr(const std::string& msg);
+
+    // More like a namespace...
+    struct Commands {
+        static const std::string BOOT;
+        static const std::string CONTINUE;
+        static const std::string DOWNLOAD;
+        static const std::string ERASE;
+        static const std::string FLASH;
+        static const std::string GET_VAR;
+        static const std::string POWERDOWN;
+        static const std::string REBOOT;
+        static const std::string SET_ACTIVE;
+        static const std::string UPLOAD;
+        static const std::string VERIFY;
+    };
+
+    Transport* const transport;
+
+  private:
+    RetCode SendBuffer(int fd, size_t size);
+    RetCode SendBuffer(const std::vector<char>& buf);
+    RetCode SendBuffer(const void* buf, size_t size);
+
+    RetCode ReadBuffer(std::vector<char>& buf);
+    RetCode ReadBuffer(void* buf, size_t size);
+
+    int SparseWriteCallback(std::vector<char>& tpbuf, const char* data, size_t len);
+
+    std::string error_;
+    std::function<void(std::string&)> info_cb_;
+    bool disable_checks_;
+};
+
+}  // namespace fastboot
diff --git a/fastboot/fastboot_test.cpp b/fastboot/fastboot_test.cpp
new file mode 100644
index 0000000..43201fa
--- /dev/null
+++ b/fastboot/fastboot_test.cpp
@@ -0,0 +1,61 @@
+/*
+ * 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 "engine.h"
+
+#include <gtest/gtest.h>
+
+TEST(FastBoot, ParseOsPatchLevel) {
+    FastBootTool fb;
+    boot_img_hdr_v1 hdr;
+
+    hdr = {};
+    fb.ParseOsPatchLevel(&hdr, "2018-01-05");
+    ASSERT_EQ(2018U, 2000U + ((hdr.os_version >> 4) & 0x7f));
+    ASSERT_EQ(1U, ((hdr.os_version >> 0) & 0xf));
+
+    EXPECT_DEATH(fb.ParseOsPatchLevel(&hdr, "2018"), "should be YYYY-MM-DD");
+    EXPECT_DEATH(fb.ParseOsPatchLevel(&hdr, "2018-01"), "should be YYYY-MM-DD");
+    EXPECT_DEATH(fb.ParseOsPatchLevel(&hdr, "2128-01-05"), "year out of range");
+    EXPECT_DEATH(fb.ParseOsPatchLevel(&hdr, "2018-13-05"), "month out of range");
+}
+
+TEST(FastBoot, ParseOsVersion) {
+    FastBootTool fb;
+    boot_img_hdr_v1 hdr;
+
+    hdr = {};
+    fb.ParseOsVersion(&hdr, "1.2.3");
+    ASSERT_EQ(1U, ((hdr.os_version >> 25) & 0x7f));
+    ASSERT_EQ(2U, ((hdr.os_version >> 18) & 0x7f));
+    ASSERT_EQ(3U, ((hdr.os_version >> 11) & 0x7f));
+
+    fb.ParseOsVersion(&hdr, "1.2");
+    ASSERT_EQ(1U, ((hdr.os_version >> 25) & 0x7f));
+    ASSERT_EQ(2U, ((hdr.os_version >> 18) & 0x7f));
+    ASSERT_EQ(0U, ((hdr.os_version >> 11) & 0x7f));
+
+    fb.ParseOsVersion(&hdr, "1");
+    ASSERT_EQ(1U, ((hdr.os_version >> 25) & 0x7f));
+    ASSERT_EQ(0U, ((hdr.os_version >> 18) & 0x7f));
+    ASSERT_EQ(0U, ((hdr.os_version >> 11) & 0x7f));
+
+    EXPECT_DEATH(fb.ParseOsVersion(&hdr, ""), "bad OS version");
+    EXPECT_DEATH(fb.ParseOsVersion(&hdr, "1.2.3.4"), "bad OS version");
+    EXPECT_DEATH(fb.ParseOsVersion(&hdr, "128.2.3"), "bad OS version");
+    EXPECT_DEATH(fb.ParseOsVersion(&hdr, "1.128.3"), "bad OS version");
+    EXPECT_DEATH(fb.ParseOsVersion(&hdr, "1.2.128"), "bad OS version");
+}
diff --git a/fastboot/fs.cpp b/fastboot/fs.cpp
index 99ca7dd..cc1714f 100644
--- a/fastboot/fs.cpp
+++ b/fastboot/fs.cpp
@@ -1,7 +1,5 @@
 #include "fs.h"
 
-#include "fastboot.h"
-#include "make_f2fs.h"
 
 #include <errno.h>
 #include <fcntl.h>
@@ -10,47 +8,193 @@
 #include <string.h>
 #include <sys/stat.h>
 #include <sys/types.h>
+#ifndef WIN32
+#include <sys/wait.h>
+#else
+#include <tchar.h>
+#include <windows.h>
+#endif
 #include <unistd.h>
+#include <vector>
 
+#include <android-base/errors.h>
+#include <android-base/file.h>
+#include <android-base/macros.h>
+#include <android-base/stringprintf.h>
 #include <android-base/unique_fd.h>
-#include <ext4_utils/make_ext4fs.h>
-#include <sparse/sparse.h>
 
+using android::base::GetExecutableDirectory;
+using android::base::StringPrintf;
 using android::base::unique_fd;
 
-static int generate_ext4_image(const char* fileName, long long partSize, const std::string& initial_dir,
-                                       unsigned eraseBlkSize, unsigned logicalBlkSize)
-{
-    unique_fd fd(open(fileName, O_CREAT | O_RDWR | O_TRUNC, S_IRUSR | S_IWUSR));
-    if (fd == -1) {
-        fprintf(stderr, "Unable to open output file for EXT4 filesystem: %s\n", strerror(errno));
+#ifdef WIN32
+static int exec_cmd(const char* path, const char** argv, const char** envp) {
+    std::string cmd;
+    int i = 0;
+    while (argv[i] != nullptr) {
+        cmd += argv[i++];
+        cmd += " ";
+    }
+    cmd = cmd.substr(0, cmd.size() - 1);
+
+    STARTUPINFO si;
+    PROCESS_INFORMATION pi;
+    DWORD exit_code = 0;
+
+    ZeroMemory(&si, sizeof(si));
+    si.cb = sizeof(si);
+    ZeroMemory(&pi, sizeof(pi));
+
+    std::string env_str;
+    if (envp != nullptr) {
+        while (*envp != nullptr) {
+            env_str += std::string(*envp) + std::string("\0", 1);
+            envp++;
+        }
+    }
+
+    if (!CreateProcessA(nullptr,                         // No module name (use command line)
+                        const_cast<char*>(cmd.c_str()),  // Command line
+                        nullptr,                         // Process handle not inheritable
+                        nullptr,                         // Thread handle not inheritable
+                        FALSE,                           // Set handle inheritance to FALSE
+                        0,                               // No creation flags
+                        env_str.empty() ? nullptr : LPSTR(env_str.c_str()),
+                        nullptr,  // Use parent's starting directory
+                        &si,      // Pointer to STARTUPINFO structure
+                        &pi)      // Pointer to PROCESS_INFORMATION structure
+    ) {
+        fprintf(stderr, "CreateProcess failed: %s\n",
+                android::base::SystemErrorCodeToString(GetLastError()).c_str());
         return -1;
     }
-    if (initial_dir.empty()) {
-        make_ext4fs_sparse_fd_align(fd, partSize, NULL, NULL, eraseBlkSize, logicalBlkSize);
-    } else {
-        make_ext4fs_sparse_fd_directory_align(fd, partSize, NULL, NULL, initial_dir.c_str(),
-                                              eraseBlkSize, logicalBlkSize);
+
+    WaitForSingleObject(pi.hProcess, INFINITE);
+
+    GetExitCodeProcess(pi.hProcess, &exit_code);
+
+    CloseHandle(pi.hProcess);
+    CloseHandle(pi.hThread);
+
+    if (exit_code != 0) {
+        fprintf(stderr, "%s failed: %lu\n", path, exit_code);
+        return -1;
     }
     return 0;
 }
+#else
+static int exec_cmd(const char* path, const char** argv, const char** envp) {
+    int status;
+    pid_t child;
+    if ((child = fork()) == 0) {
+        execve(path, const_cast<char**>(argv), const_cast<char**>(envp));
+        _exit(EXIT_FAILURE);
+    }
+    if (child < 0) {
+        fprintf(stderr, "%s failed with fork %s\n", path, strerror(errno));
+        return -1;
+    }
+    if (TEMP_FAILURE_RETRY(waitpid(child, &status, 0)) == -1) {
+        fprintf(stderr, "%s failed with waitpid %s\n", path, strerror(errno));
+        return -1;
+    }
+    int ret = -1;
+    if (WIFEXITED(status)) {
+        ret = WEXITSTATUS(status);
+    }
 
-#ifdef USE_F2FS
+    if (ret != 0) {
+        fprintf(stderr, "%s failed with status %d\n", path, ret);
+        return -1;
+    }
+    return 0;
+}
+#endif
+
+static int generate_ext4_image(const char* fileName, long long partSize,
+                               const std::string& initial_dir, unsigned eraseBlkSize,
+                               unsigned logicalBlkSize) {
+    static constexpr int block_size = 4096;
+    const std::string exec_dir = android::base::GetExecutableDirectory();
+
+    const std::string mke2fs_path = exec_dir + "/mke2fs";
+    std::vector<const char*> mke2fs_args = {mke2fs_path.c_str(), "-t", "ext4", "-b"};
+
+    std::string block_size_str = std::to_string(block_size);
+    mke2fs_args.push_back(block_size_str.c_str());
+
+    std::string ext_attr = "android_sparse";
+    if (eraseBlkSize != 0 && logicalBlkSize != 0) {
+        int raid_stride = logicalBlkSize / block_size;
+        int raid_stripe_width = eraseBlkSize / block_size;
+        // stride should be the max of 8kb and logical block size
+        if (logicalBlkSize != 0 && logicalBlkSize < 8192) raid_stride = 8192 / block_size;
+        // stripe width should be >= stride
+        if (raid_stripe_width < raid_stride) raid_stripe_width = raid_stride;
+        ext_attr += StringPrintf(",stride=%d,stripe-width=%d", raid_stride, raid_stripe_width);
+    }
+    mke2fs_args.push_back("-E");
+    mke2fs_args.push_back(ext_attr.c_str());
+    mke2fs_args.push_back("-O");
+    mke2fs_args.push_back("uninit_bg");
+    mke2fs_args.push_back(fileName);
+
+    std::string size_str = std::to_string(partSize / block_size);
+    mke2fs_args.push_back(size_str.c_str());
+    mke2fs_args.push_back(nullptr);
+
+    const std::string mke2fs_env = "MKE2FS_CONFIG=" + GetExecutableDirectory() + "/mke2fs.conf";
+    std::vector<const char*> mke2fs_envp = {mke2fs_env.c_str(), nullptr};
+
+    int ret = exec_cmd(mke2fs_args[0], mke2fs_args.data(), mke2fs_envp.data());
+    if (ret != 0) {
+        return -1;
+    }
+
+    if (initial_dir.empty()) {
+        return 0;
+    }
+
+    const std::string e2fsdroid_path = exec_dir + "/e2fsdroid";
+    std::vector<const char*> e2fsdroid_args = {e2fsdroid_path.c_str(), "-f", initial_dir.c_str(),
+                                               fileName, nullptr};
+
+    return exec_cmd(e2fsdroid_args[0], e2fsdroid_args.data(), nullptr);
+}
+
 static int generate_f2fs_image(const char* fileName, long long partSize, const std::string& initial_dir,
                                unsigned /* unused */, unsigned /* unused */)
 {
-    if (!initial_dir.empty()) {
-        fprintf(stderr, "Unable to set initial directory on F2FS filesystem: %s\n", strerror(errno));
+    const std::string exec_dir = android::base::GetExecutableDirectory();
+    const std::string mkf2fs_path = exec_dir + "/make_f2fs";
+    std::vector<const char*> mkf2fs_args = {mkf2fs_path.c_str()};
+
+    mkf2fs_args.push_back("-S");
+    std::string size_str = std::to_string(partSize);
+    mkf2fs_args.push_back(size_str.c_str());
+    mkf2fs_args.push_back("-f");
+    mkf2fs_args.push_back("-O");
+    mkf2fs_args.push_back("encrypt");
+    mkf2fs_args.push_back("-O");
+    mkf2fs_args.push_back("quota");
+    mkf2fs_args.push_back(fileName);
+    mkf2fs_args.push_back(nullptr);
+
+    int ret = exec_cmd(mkf2fs_args[0], mkf2fs_args.data(), nullptr);
+    if (ret != 0) {
         return -1;
     }
-    unique_fd fd(open(fileName, O_CREAT | O_RDWR | O_TRUNC, S_IRUSR | S_IWUSR));
-    if (fd == -1) {
-        fprintf(stderr, "Unable to open output file for F2FS filesystem: %s\n", strerror(errno));
-        return -1;
+
+    if (initial_dir.empty()) {
+        return 0;
     }
-    return make_f2fs_sparse_fd(fd, partSize, NULL, NULL);
+
+    const std::string sload_path = exec_dir + "/sload_f2fs";
+    std::vector<const char*> sload_args = {sload_path.c_str(), "-S",
+                                       "-f", initial_dir.c_str(), fileName, nullptr};
+
+    return exec_cmd(sload_args[0], sload_args.data(), nullptr);
 }
-#endif
 
 static const struct fs_generator {
     const char* fs_type;  //must match what fastboot reports for partition type
@@ -61,9 +205,7 @@
 
 } generators[] = {
     { "ext4", generate_ext4_image},
-#ifdef USE_F2FS
     { "f2fs", generate_f2fs_image},
-#endif
 };
 
 const struct fs_generator* fs_get_generator(const std::string& fs_type) {
diff --git a/fastboot/fs.h b/fastboot/fs.h
index c6baa7f..331100d 100644
--- a/fastboot/fs.h
+++ b/fastboot/fs.h
@@ -1,5 +1,4 @@
-#ifndef _FS_H_
-#define _FS_H_
+#pragma once
 
 #include <string>
 #include <stdint.h>
@@ -9,5 +8,3 @@
 const struct fs_generator* fs_get_generator(const std::string& fs_type);
 int fs_generator_generate(const struct fs_generator* gen, const char* fileName, long long partSize,
     const std::string& initial_dir, unsigned eraseBlkSize = 0, unsigned logicalBlkSize = 0);
-
-#endif
diff --git a/fastboot/main.cpp b/fastboot/main.cpp
new file mode 100644
index 0000000..c3683f7
--- /dev/null
+++ b/fastboot/main.cpp
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "engine.h"
+
+int main(int argc, char* argv[]) {
+    FastBootTool fb;
+    return fb.Main(argc, argv);
+}
diff --git a/fastboot/protocol.cpp b/fastboot/protocol.cpp
deleted file mode 100644
index dcdf8f0..0000000
--- a/fastboot/protocol.cpp
+++ /dev/null
@@ -1,358 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *  * Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- *  * Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in
- *    the documentation and/or other materials provided with the
- *    distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
- * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
- * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
- * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
- * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
- * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#define round_down(a, b) \
-    ({ typeof(a) _a = (a); typeof(b) _b = (b); _a - (_a % _b); })
-
-#include <fcntl.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <errno.h>
-
-#include <algorithm>
-#include <vector>
-
-#include <android-base/file.h>
-#include <android-base/stringprintf.h>
-#include <android-base/unique_fd.h>
-#include <sparse/sparse.h>
-#include <utils/FileMap.h>
-
-#include "fastboot.h"
-#include "transport.h"
-
-static std::string g_error;
-
-using android::base::unique_fd;
-using android::base::WriteStringToFile;
-
-const std::string fb_get_error() {
-    return g_error;
-}
-
-static int64_t check_response(Transport* transport, uint32_t size, char* response) {
-    char status[65];
-
-    while (true) {
-        int r = transport->Read(status, 64);
-        if (r < 0) {
-            g_error = android::base::StringPrintf("status read failed (%s)", strerror(errno));
-            transport->Close();
-            return -1;
-        }
-        status[r] = 0;
-
-        if (r < 4) {
-            g_error = android::base::StringPrintf("status malformed (%d bytes)", r);
-            transport->Close();
-            return -1;
-        }
-
-        if (!memcmp(status, "INFO", 4)) {
-            fprintf(stderr,"(bootloader) %s\n", status + 4);
-            continue;
-        }
-
-        if (!memcmp(status, "OKAY", 4)) {
-            if (response) {
-                strcpy(response, (char*) status + 4);
-            }
-            return 0;
-        }
-
-        if (!memcmp(status, "FAIL", 4)) {
-            if (r > 4) {
-                g_error = android::base::StringPrintf("remote: %s", status + 4);
-            } else {
-                g_error = "remote failure";
-            }
-            return -1;
-        }
-
-        if (!memcmp(status, "DATA", 4) && size > 0){
-            uint32_t dsize = strtol(status + 4, 0, 16);
-            if (dsize > size) {
-                g_error = android::base::StringPrintf("data size too large (%d)", dsize);
-                transport->Close();
-                return -1;
-            }
-            return dsize;
-        }
-
-        g_error = "unknown status code";
-        transport->Close();
-        break;
-    }
-
-    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);
-        return -1;
-    }
-
-    if (response) {
-        response[0] = 0;
-    }
-
-    if (transport->Write(cmd, cmdsize) != static_cast<int>(cmdsize)) {
-        g_error = android::base::StringPrintf("command write failed (%s)", strerror(errno));
-        transport->Close();
-        return -1;
-    }
-
-    return check_response(transport, size, response);
-}
-
-static int64_t _command_write_data(Transport* transport, const void* data, uint32_t size) {
-    int64_t r = transport->Write(data, size);
-    if (r < 0) {
-        g_error = android::base::StringPrintf("data write failure (%s)", strerror(errno));
-        transport->Close();
-        return -1;
-    }
-    if (r != static_cast<int64_t>(size)) {
-        g_error = "data write failure (short transfer)";
-        transport->Close();
-        return -1;
-    }
-    return r;
-}
-
-static int64_t _command_read_data(Transport* transport, void* data, uint32_t size) {
-    int64_t r = transport->Read(data, size);
-    if (r < 0) {
-        g_error = android::base::StringPrintf("data read failure (%s)", strerror(errno));
-        transport->Close();
-        return -1;
-    }
-    if (r != (static_cast<int64_t>(size))) {
-        g_error = "data read failure (short transfer)";
-        transport->Close();
-        return -1;
-    }
-    return r;
-}
-
-static int64_t _command_end(Transport* transport) {
-    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) {
-    if (size == 0) {
-        return -1;
-    }
-
-    int64_t r = _command_start(transport, cmd, size, response);
-    if (r < 0) {
-        return -1;
-    }
-    r = _command_write_data(transport, data, size);
-    if (r < 0) {
-        return -1;
-    }
-
-    r = _command_end(transport);
-    if (r < 0) {
-        return -1;
-    }
-
-    return size;
-}
-
-static int64_t _command_send_fd(Transport* transport, const char* cmd, int fd, uint32_t size,
-                                char* response) {
-    static constexpr uint32_t MAX_MAP_SIZE = 512 * 1024 * 1024;
-    off64_t offset = 0;
-    uint32_t remaining = size;
-
-    if (_command_start(transport, cmd, size, response) < 0) {
-        return -1;
-    }
-
-    while (remaining) {
-        android::FileMap filemap;
-        size_t len = std::min(remaining, MAX_MAP_SIZE);
-
-        if (!filemap.create(NULL, fd, offset, len, true)) {
-            return -1;
-        }
-
-        if (_command_write_data(transport, filemap.getDataPtr(), len) < 0) {
-            return -1;
-        }
-
-        remaining -= len;
-        offset += len;
-    }
-
-    if (_command_end(transport) < 0) {
-        return -1;
-    }
-
-    return size;
-}
-
-static int _command_send_no_data(Transport* transport, const char* cmd, char* response) {
-    return _command_start(transport, cmd, 0, response);
-}
-
-int fb_command(Transport* transport, const char* cmd) {
-    return _command_send_no_data(transport, cmd, 0);
-}
-
-int fb_command_response(Transport* transport, const char* cmd, char* response) {
-    return _command_send_no_data(transport, cmd, response);
-}
-
-int64_t fb_download_data(Transport* transport, const void* data, uint32_t size) {
-    std::string cmd(android::base::StringPrintf("download:%08x", size));
-    return _command_send(transport, cmd.c_str(), data, size, 0) < 0 ? -1 : 0;
-}
-
-int64_t fb_download_data_fd(Transport* transport, int fd, uint32_t size) {
-    std::string cmd(android::base::StringPrintf("download:%08x", size));
-    return _command_send_fd(transport, cmd.c_str(), fd, size, 0) < 0 ? -1 : 0;
-}
-
-int64_t fb_upload_data(Transport* transport, const char* outfile) {
-    // positive return value is the upload size sent by the device
-    int64_t r = _command_start(transport, "upload", std::numeric_limits<int32_t>::max(), nullptr);
-    if (r <= 0) {
-        g_error = android::base::StringPrintf("command start failed (%s)", strerror(errno));
-        return r;
-    }
-
-    std::string data;
-    data.resize(r);
-    if ((r = _command_read_data(transport, &data[0], data.size())) == -1) {
-        return r;
-    }
-
-    if (!WriteStringToFile(data, outfile, true)) {
-        g_error = android::base::StringPrintf("write to '%s' failed", outfile);
-        return -1;
-    }
-
-    return _command_end(transport);
-}
-
-#define TRANSPORT_BUF_SIZE 1024
-static char transport_buf[TRANSPORT_BUF_SIZE];
-static int transport_buf_len;
-
-static int fb_download_data_sparse_write(void *priv, const void *data, int len)
-{
-    int r;
-    Transport* transport = reinterpret_cast<Transport*>(priv);
-    int to_write;
-    const char* ptr = reinterpret_cast<const char*>(data);
-
-    if (transport_buf_len) {
-        to_write = std::min(TRANSPORT_BUF_SIZE - transport_buf_len, len);
-
-        memcpy(transport_buf + transport_buf_len, ptr, to_write);
-        transport_buf_len += to_write;
-        ptr += to_write;
-        len -= to_write;
-    }
-
-    if (transport_buf_len == TRANSPORT_BUF_SIZE) {
-        r = _command_write_data(transport, transport_buf, TRANSPORT_BUF_SIZE);
-        if (r != TRANSPORT_BUF_SIZE) {
-            return -1;
-        }
-        transport_buf_len = 0;
-    }
-
-    if (len > TRANSPORT_BUF_SIZE) {
-        if (transport_buf_len > 0) {
-            g_error = "internal error: transport_buf not empty";
-            return -1;
-        }
-        to_write = round_down(len, TRANSPORT_BUF_SIZE);
-        r = _command_write_data(transport, ptr, to_write);
-        if (r != to_write) {
-            return -1;
-        }
-        ptr += to_write;
-        len -= to_write;
-    }
-
-    if (len > 0) {
-        if (len > TRANSPORT_BUF_SIZE) {
-            g_error = "internal error: too much left for transport_buf";
-            return -1;
-        }
-        memcpy(transport_buf, ptr, len);
-        transport_buf_len = len;
-    }
-
-    return 0;
-}
-
-static int fb_download_data_sparse_flush(Transport* transport) {
-    if (transport_buf_len > 0) {
-        int64_t r = _command_write_data(transport, transport_buf, transport_buf_len);
-        if (r != static_cast<int64_t>(transport_buf_len)) {
-            return -1;
-        }
-        transport_buf_len = 0;
-    }
-    return 0;
-}
-
-int fb_download_data_sparse(Transport* transport, struct sparse_file* s) {
-    int size = sparse_file_len(s, true, false);
-    if (size <= 0) {
-        return -1;
-    }
-
-    std::string cmd(android::base::StringPrintf("download:%08x", size));
-    int r = _command_start(transport, cmd.c_str(), size, 0);
-    if (r < 0) {
-        return -1;
-    }
-
-    r = sparse_file_callback(s, true, false, fb_download_data_sparse_write, transport);
-    if (r < 0) {
-        return -1;
-    }
-
-    r = fb_download_data_sparse_flush(transport);
-    if (r < 0) {
-        return -1;
-    }
-
-    return _command_end(transport);
-}
diff --git a/fastboot/socket.h b/fastboot/socket.h
index 7eaa0ab..e791f2c 100644
--- a/fastboot/socket.h
+++ b/fastboot/socket.h
@@ -30,8 +30,7 @@
 // engine should not be using this interface directly, but instead should use a higher-level
 // interface that enforces the fastboot protocol.
 
-#ifndef SOCKET_H_
-#define SOCKET_H_
+#pragma once
 
 #include <functional>
 #include <memory>
@@ -125,5 +124,3 @@
 
     DISALLOW_COPY_AND_ASSIGN(Socket);
 };
-
-#endif  // SOCKET_H_
diff --git a/fastboot/socket_mock.h b/fastboot/socket_mock.h
index eacd6bb..6e95b16 100644
--- a/fastboot/socket_mock.h
+++ b/fastboot/socket_mock.h
@@ -26,8 +26,7 @@
  * SUCH DAMAGE.
  */
 
-#ifndef SOCKET_MOCK_H_
-#define SOCKET_MOCK_H_
+#pragma once
 
 #include <memory>
 #include <queue>
@@ -97,5 +96,3 @@
 
     DISALLOW_COPY_AND_ASSIGN(SocketMock);
 };
-
-#endif  // SOCKET_MOCK_H_
diff --git a/fastboot/tcp.h b/fastboot/tcp.h
index aa3ef13..8b638a4 100644
--- a/fastboot/tcp.h
+++ b/fastboot/tcp.h
@@ -26,8 +26,7 @@
  * SUCH DAMAGE.
  */
 
-#ifndef TCP_H_
-#define TCP_H_
+#pragma once
 
 #include <memory>
 #include <string>
@@ -55,5 +54,3 @@
 }  // namespace internal
 
 }  // namespace tcp
-
-#endif  // TCP_H_
diff --git a/fastboot/transport.h b/fastboot/transport.h
index 67d01f9..96b90d2 100644
--- a/fastboot/transport.h
+++ b/fastboot/transport.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef TRANSPORT_H_
-#define TRANSPORT_H_
+#pragma once
 
 #include <android-base/macros.h>
 
@@ -44,5 +43,3 @@
   private:
     DISALLOW_COPY_AND_ASSIGN(Transport);
 };
-
-#endif  // TRANSPORT_H_
diff --git a/fastboot/udp.h b/fastboot/udp.h
index 14f5b35..8d37b84 100644
--- a/fastboot/udp.h
+++ b/fastboot/udp.h
@@ -26,8 +26,7 @@
  * SUCH DAMAGE.
  */
 
-#ifndef UDP_H_
-#define UDP_H_
+#pragma once
 
 #include <memory>
 #include <string>
@@ -77,5 +76,3 @@
 }  // namespace internal
 
 }  // namespace udp
-
-#endif  // UDP_H_
diff --git a/fastboot/usb.h b/fastboot/usb.h
index 4acf12d..7ca44c4 100644
--- a/fastboot/usb.h
+++ b/fastboot/usb.h
@@ -26,8 +26,7 @@
  * SUCH DAMAGE.
  */
 
-#ifndef _USB_H_
-#define _USB_H_
+#pragma once
 
 #include "transport.h"
 
@@ -53,8 +52,14 @@
     char device_path[256];
 };
 
+class UsbTransport : public Transport {
+    // Resets the underlying transport.  Returns 0 on success.
+    // This effectively simulates unplugging and replugging
+  public:
+    virtual int Reset() = 0;
+};
+
 typedef int (*ifc_match_func)(usb_ifc_info *ifc);
 
-Transport* usb_open(ifc_match_func callback);
-
-#endif
+// 0 is non blocking
+UsbTransport* usb_open(ifc_match_func callback, uint32_t timeout_ms = 0);
diff --git a/fastboot/usb_linux.cpp b/fastboot/usb_linux.cpp
index cdab4f1..9b779dd 100644
--- a/fastboot/usb_linux.cpp
+++ b/fastboot/usb_linux.cpp
@@ -47,12 +47,12 @@
 #include <memory>
 #include <thread>
 
-#include "fastboot.h"
 #include "usb.h"
+#include "util.h"
 
 using namespace std::chrono_literals;
 
-#define MAX_RETRIES 5
+#define MAX_RETRIES 2
 
 /* Timeout in seconds for usb_wait_for_disconnect.
  * It doesn't usually take long for a device to disconnect (almost always
@@ -91,18 +91,21 @@
     unsigned char ep_out;
 };
 
-class LinuxUsbTransport : public Transport {
+class LinuxUsbTransport : public UsbTransport {
   public:
-    explicit LinuxUsbTransport(std::unique_ptr<usb_handle> handle) : handle_(std::move(handle)) {}
+    explicit LinuxUsbTransport(std::unique_ptr<usb_handle> handle, uint32_t ms_timeout = 0)
+        : handle_(std::move(handle)), ms_timeout_(ms_timeout) {}
     ~LinuxUsbTransport() override = default;
 
     ssize_t Read(void* data, size_t len) override;
     ssize_t Write(const void* data, size_t len) override;
     int Close() override;
+    int Reset() override;
     int WaitForDisconnect() override;
 
   private:
     std::unique_ptr<usb_handle> handle_;
+    const uint32_t ms_timeout_;
 
     DISALLOW_COPY_AND_ASSIGN(LinuxUsbTransport);
 };
@@ -402,7 +405,7 @@
         bulk.ep = handle_->ep_out;
         bulk.len = xfer;
         bulk.data = data;
-        bulk.timeout = 0;
+        bulk.timeout = ms_timeout_;
 
         n = ioctl(handle_->desc, USBDEVFS_BULK, &bulk);
         if(n != xfer) {
@@ -436,7 +439,7 @@
         bulk.ep = handle_->ep_in;
         bulk.len = xfer;
         bulk.data = data;
-        bulk.timeout = 0;
+        bulk.timeout = ms_timeout_;
         retry = 0;
 
         do {
@@ -447,7 +450,7 @@
             if (n < 0) {
                 DBG1("ERROR: n = %d, errno = %d (%s)\n",n, errno, strerror(errno));
                 if (++retry > MAX_RETRIES) return -1;
-                std::this_thread::sleep_for(1s);
+                std::this_thread::sleep_for(100ms);
             }
         } while (n < 0);
 
@@ -477,10 +480,19 @@
     return 0;
 }
 
-Transport* usb_open(ifc_match_func callback)
-{
+int LinuxUsbTransport::Reset() {
+    int ret = 0;
+    // We reset the USB connection
+    if ((ret = ioctl(handle_->desc, USBDEVFS_RESET, 0))) {
+        return ret;
+    }
+
+    return 0;
+}
+
+UsbTransport* usb_open(ifc_match_func callback, uint32_t timeout_ms) {
     std::unique_ptr<usb_handle> handle = find_usb_device("/sys/bus/usb/devices", callback);
-    return handle ? new LinuxUsbTransport(std::move(handle)) : nullptr;
+    return handle ? new LinuxUsbTransport(std::move(handle), timeout_ms) : nullptr;
 }
 
 /* Wait for the system to notice the device is gone, so that a subsequent
diff --git a/fastboot/usb_osx.cpp b/fastboot/usb_osx.cpp
index 9069baa..4d48f6e 100644
--- a/fastboot/usb_osx.cpp
+++ b/fastboot/usb_osx.cpp
@@ -65,17 +65,21 @@
     unsigned int zero_mask;
 };
 
-class OsxUsbTransport : public Transport {
+class OsxUsbTransport : public UsbTransport {
   public:
-    OsxUsbTransport(std::unique_ptr<usb_handle> handle) : handle_(std::move(handle)) {}
+    // A timeout of 0 is blocking
+    OsxUsbTransport(std::unique_ptr<usb_handle> handle, uint32_t ms_timeout = 0)
+        : handle_(std::move(handle)), ms_timeout_(ms_timeout) {}
     ~OsxUsbTransport() override = default;
 
     ssize_t Read(void* data, size_t len) override;
     ssize_t Write(const void* data, size_t len) override;
     int Close() override;
+    int Reset() override;
 
   private:
     std::unique_ptr<usb_handle> handle_;
+    const uint32_t ms_timeout_;
 
     DISALLOW_COPY_AND_ASSIGN(OsxUsbTransport);
 };
@@ -93,12 +97,9 @@
     SInt32 score;
     UInt8 interfaceNumEndpoints;
 
-    // Placing the constant KIOUSBFindInterfaceDontCare into the following
-    // fields of the IOUSBFindInterfaceRequest structure will allow us to
-    // find all of the interfaces
-    request.bInterfaceClass = kIOUSBFindInterfaceDontCare;
-    request.bInterfaceSubClass = kIOUSBFindInterfaceDontCare;
-    request.bInterfaceProtocol = kIOUSBFindInterfaceDontCare;
+    request.bInterfaceClass = 0xff;
+    request.bInterfaceSubClass = 0x42;
+    request.bInterfaceProtocol = 0x03;
     request.bAlternateSetting = kIOUSBFindInterfaceDontCare;
 
     // Get an iterator for the interfaces on the device
@@ -282,7 +283,6 @@
             &plugin, &score);
 
     if ((kr != 0) || (plugin == NULL)) {
-        ERR("Unable to create a plug-in (%08x)\n", kr);
         goto error;
     }
 
@@ -436,8 +436,7 @@
 
         if (try_device(device, &h) != 0) {
             IOObjectRelease(device);
-            ret = -1;
-            break;
+            continue;
         }
 
         if (h.success) {
@@ -461,7 +460,7 @@
  * Definitions of this file's public functions.
  */
 
-Transport* usb_open(ifc_match_func callback) {
+UsbTransport* usb_open(ifc_match_func callback, uint32_t timeout_ms) {
     std::unique_ptr<usb_handle> handle;
 
     if (init_usb(callback, &handle) < 0) {
@@ -469,7 +468,7 @@
         return nullptr;
     }
 
-    return new OsxUsbTransport(std::move(handle));
+    return new OsxUsbTransport(std::move(handle), timeout_ms);
 }
 
 int OsxUsbTransport::Close() {
@@ -477,6 +476,19 @@
     return 0;
 }
 
+/*
+  TODO: this SHOULD be easy to do with ResetDevice() from IOUSBDeviceInterface.
+  However to perform operations that manipulate the state of the device, you must
+  claim ownership of the device with USBDeviceOpenSeize(). However, this operation
+  always fails with kIOReturnExclusiveAccess.
+  It seems that the kext com.apple.driver.usb.AppleUSBHostCompositeDevice
+  always loads and claims ownership of the device and refuses to give it up.
+*/
+int OsxUsbTransport::Reset() {
+    ERR("USB reset is currently unsupported on osx\n");
+    return -1;
+}
+
 ssize_t OsxUsbTransport::Read(void* data, size_t len) {
     IOReturn result;
     UInt32 numBytes = len;
@@ -499,7 +511,14 @@
         return -1;
     }
 
-    result = (*handle_->interface)->ReadPipe(handle_->interface, handle_->bulkIn, data, &numBytes);
+    if (!ms_timeout_) {
+        result = (*handle_->interface)
+                         ->ReadPipe(handle_->interface, handle_->bulkIn, data, &numBytes);
+    } else {
+        result = (*handle_->interface)
+                         ->ReadPipeTO(handle_->interface, handle_->bulkIn, data, &numBytes,
+                                      ms_timeout_, ms_timeout_);
+    }
 
     if (result == 0) {
         return (int) numBytes;
@@ -546,8 +565,16 @@
         int lenToSend = lenRemaining > maxLenToSend
             ? maxLenToSend : lenRemaining;
 
-        result = (*handle_->interface)->WritePipe(
-                handle_->interface, handle_->bulkOut, (void *)data, lenToSend);
+        if (!ms_timeout_) {  // blocking
+            result = (*handle_->interface)
+                             ->WritePipe(handle_->interface, handle_->bulkOut, (void*)data,
+                                         lenToSend);
+        } else {
+            result = (*handle_->interface)
+                             ->WritePipeTO(handle_->interface, handle_->bulkOut, (void*)data,
+                                           lenToSend, ms_timeout_, ms_timeout_);
+        }
+
         if (result != 0) break;
 
         lenRemaining -= lenToSend;
diff --git a/fastboot/usb_windows.cpp b/fastboot/usb_windows.cpp
index 3dab5ac..8c60a71 100644
--- a/fastboot/usb_windows.cpp
+++ b/fastboot/usb_windows.cpp
@@ -66,7 +66,7 @@
     std::string interface_name;
 };
 
-class WindowsUsbTransport : public Transport {
+class WindowsUsbTransport : public UsbTransport {
   public:
     WindowsUsbTransport(std::unique_ptr<usb_handle> handle) : handle_(std::move(handle)) {}
     ~WindowsUsbTransport() override = default;
@@ -74,6 +74,7 @@
     ssize_t Read(void* data, size_t len) override;
     ssize_t Write(const void* data, size_t len) override;
     int Close() override;
+    int Reset() override;
 
   private:
     std::unique_ptr<usb_handle> handle_;
@@ -106,6 +107,7 @@
 
     if (nullptr == ret->adb_interface) {
         errno = GetLastError();
+        DBG("failed to open interface %S\n", interface_name);
         return nullptr;
     }
 
@@ -157,7 +159,7 @@
     unsigned count = 0;
     int ret;
 
-    DBG("usb_write %d\n", len);
+    DBG("usb_write %zu\n", len);
     if (nullptr != handle_) {
         // Perform write
         while(len > 0) {
@@ -195,7 +197,7 @@
     unsigned long read = 0;
     int ret;
 
-    DBG("usb_read %d\n", len);
+    DBG("usb_read %zu\n", len);
     if (nullptr != handle_) {
         while (1) {
             int xfer = (len > MAX_USBFS_BULK_SIZE) ? MAX_USBFS_BULK_SIZE : len;
@@ -260,6 +262,12 @@
     return 0;
 }
 
+int WindowsUsbTransport::Reset() {
+    DBG("usb_reset currently unsupported\n\n");
+    // TODO, this is a bit complicated since it is using ADB
+    return -1;
+}
+
 int recognized_device(usb_handle* handle, ifc_match_func callback) {
     struct usb_ifc_info info;
     USB_DEVICE_DESCRIPTOR device_desc;
@@ -269,19 +277,22 @@
         return 0;
 
     // Check vendor and product id first
-    if (!AdbGetUsbDeviceDescriptor(handle->adb_interface,
-                                 &device_desc)) {
+    if (!AdbGetUsbDeviceDescriptor(handle->adb_interface, &device_desc)) {
+        DBG("skipping device %x:%x\n", device_desc.idVendor, device_desc.idProduct);
         return 0;
     }
 
     // Then check interface properties
-    if (!AdbGetUsbInterfaceDescriptor(handle->adb_interface,
-                                    &interf_desc)) {
+    if (!AdbGetUsbInterfaceDescriptor(handle->adb_interface, &interf_desc)) {
+        DBG("skipping device %x:%x, failed to find interface\n", device_desc.idVendor,
+            device_desc.idProduct);
         return 0;
     }
 
     // Must have two endpoints
     if (2 != interf_desc.bNumEndpoints) {
+        DBG("skipping device %x:%x, incorrect number of endpoints\n", device_desc.idVendor,
+            device_desc.idProduct);
         return 0;
     }
 
@@ -305,9 +316,13 @@
     info.device_path[0] = 0;
 
     if (callback(&info) == 0) {
+        DBG("skipping device %x:%x, not selected by callback\n", device_desc.idVendor,
+            device_desc.idProduct);
         return 1;
     }
 
+    DBG("found device %x:%x (%s)\n", device_desc.idVendor, device_desc.idProduct,
+        info.serial_number);
     return 0;
 }
 
@@ -338,6 +353,7 @@
         }
         *copy_name = '\0';
 
+        DBG("attempting to open interface %S\n", next_interface->device_name);
         handle = do_usb_open(next_interface->device_name);
         if (NULL != handle) {
             // Lets see if this interface (device) belongs to us
@@ -357,8 +373,7 @@
     return handle;
 }
 
-Transport* usb_open(ifc_match_func callback)
-{
+UsbTransport* usb_open(ifc_match_func callback, uint32_t) {
     std::unique_ptr<usb_handle> handle = find_usb_device(callback);
     return handle ? new WindowsUsbTransport(std::move(handle)) : nullptr;
 }
diff --git a/fastboot/usbtest.cpp b/fastboot/usbtest.cpp
deleted file mode 100644
index 9423c6d..0000000
--- a/fastboot/usbtest.cpp
+++ /dev/null
@@ -1,212 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *  * Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- *  * Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in
- *    the documentation and/or other materials provided with the
- *    distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
- * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
- * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
- * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
- * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
- * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <errno.h>
-
-#include <sys/time.h>
-
-#include "usb.h"
-
-static unsigned arg_size = 4096;
-static unsigned arg_count = 4096;
-
-long long NOW(void)
-{
-    struct timeval tv;
-    gettimeofday(&tv, 0);
-
-    return (((long long) tv.tv_sec) * ((long long) 1000000)) +
-        (((long long) tv.tv_usec));
-}
-
-int printifc(usb_ifc_info *info)
-{
-    printf("dev: csp=%02x/%02x/%02x v=%04x p=%04x  ",
-           info->dev_class, info->dev_subclass, info->dev_protocol,
-           info->dev_vendor, info->dev_product);
-    printf("ifc: csp=%02x/%02x/%02x%s%s\n",
-           info->ifc_class, info->ifc_subclass, info->ifc_protocol,
-           info->has_bulk_in ? " in" : "",
-           info->has_bulk_out ? " out" : "");
-    return -1;
-}
-
-int match_null(usb_ifc_info *info)
-{
-    if(info->dev_vendor != 0x18d1) return -1;
-    if(info->ifc_class != 0xff) return -1;
-    if(info->ifc_subclass != 0xfe) return -1;
-    if(info->ifc_protocol != 0x01) return -1;
-    return 0;
-}
-
-int match_zero(usb_ifc_info *info)
-{
-    if(info->dev_vendor != 0x18d1) return -1;
-    if(info->ifc_class != 0xff) return -1;
-    if(info->ifc_subclass != 0xfe) return -1;
-    if(info->ifc_protocol != 0x02) return -1;
-    return 0;
-}
-
-int match_loop(usb_ifc_info *info)
-{
-    if(info->dev_vendor != 0x18d1) return -1;
-    if(info->ifc_class != 0xff) return -1;
-    if(info->ifc_subclass != 0xfe) return -1;
-    if(info->ifc_protocol != 0x03) return -1;
-    return 0;
-}
-
-int test_null(Transport* usb)
-{
-    unsigned i;
-    unsigned char buf[4096];
-    memset(buf, 0xee, 4096);
-    long long t0, t1;
-
-    t0 = NOW();
-    for (i = 0; i < arg_count; i++) {
-        if (usb->Write(buf, arg_size) != static_cast<int>(arg_size)) {
-            fprintf(stderr,"write failed (%s)\n", strerror(errno));
-            return -1;
-        }
-    }
-    t1 = NOW();
-    fprintf(stderr,"%d bytes in %lld uS\n", arg_count * arg_size, (t1 - t0));
-    return 0;
-}
-
-int test_zero(Transport* usb)
-{
-    unsigned i;
-    unsigned char buf[4096];
-    long long t0, t1;
-
-    t0 = NOW();
-    for (i = 0; i < arg_count; i++) {
-        if (usb->Read(buf, arg_size) != static_cast<int>(arg_size)) {
-            fprintf(stderr,"read failed (%s)\n", strerror(errno));
-            return -1;
-        }
-    }
-    t1 = NOW();
-    fprintf(stderr,"%d bytes in %lld uS\n", arg_count * arg_size, (t1 - t0));
-    return 0;
-}
-
-struct
-{
-    const char *cmd;
-    ifc_match_func match;
-    int (*test)(Transport* usb);
-    const char *help;
-} tests[] = {
-    { "list", printifc,   NULL,      "list interfaces" },
-    { "send", match_null, test_null, "send to null interface" },
-    { "recv", match_zero, test_zero, "recv from zero interface" },
-    { "loop", match_loop, NULL,      "exercise loopback interface" },
-    { NULL, NULL, NULL, NULL },
-};
-
-int usage(void)
-{
-    int i;
-
-    fprintf(stderr,"usage: usbtest <testname>\n\navailable tests:\n");
-    for(i = 0; tests[i].cmd; i++) {
-        fprintf(stderr," %-8s %s\n", tests[i].cmd, tests[i].help);
-    }
-    return -1;
-}
-
-int process_args(int argc, char **argv)
-{
-    while(argc-- > 0) {
-        char *arg = *argv++;
-        if(!strncmp(arg,"count=",6)) {
-            arg_count = atoi(arg + 6);
-        } else if(!strncmp(arg,"size=",5)) {
-            arg_size = atoi(arg + 5);
-        } else {
-            fprintf(stderr,"unknown argument: %s\n", arg);
-            return -1;
-        }
-    }
-
-    if(arg_count == 0) {
-        fprintf(stderr,"count may not be zero\n");
-        return -1;
-    }
-
-    if(arg_size > 4096) {
-        fprintf(stderr,"size may not be greater than 4096\n");
-        return -1;
-    }
-
-    return 0;
-}
-
-int main(int argc, char **argv)
-{
-    Transport* usb;
-    int i;
-
-    if(argc < 2)
-        return usage();
-
-    if(argc > 2) {
-        if(process_args(argc - 2, argv + 2))
-            return -1;
-    }
-
-    for(i = 0; tests[i].cmd; i++) {
-        if(!strcmp(argv[1], tests[i].cmd)) {
-            usb = usb_open(tests[i].match);
-            if(tests[i].test) {
-                if(usb == 0) {
-                    fprintf(stderr,"usbtest: %s: could not find interface\n",
-                            tests[i].cmd);
-                    return -1;
-                }
-                if(tests[i].test(usb)) {
-                    fprintf(stderr,"usbtest: %s: FAIL\n", tests[i].cmd);
-                    return -1;
-                } else {
-                    fprintf(stderr,"usbtest: %s: OKAY\n", tests[i].cmd);
-                }
-            }
-            return 0;
-        }
-    }
-
-    return usage();
-}
diff --git a/fastboot/util.cpp b/fastboot/util.cpp
index f2bbd34..8f6e52a 100644
--- a/fastboot/util.cpp
+++ b/fastboot/util.cpp
@@ -33,37 +33,45 @@
 
 #include <sys/time.h>
 
-#include "fastboot.h"
+#include "util.h"
 
-double now()
-{
+static bool g_verbose = false;
+
+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;
+void die(const char* fmt, ...) {
     va_list ap;
-
     va_start(ap, fmt);
-    vsprintf(buf, fmt, ap);
+    fprintf(stderr, "fastboot: error: ");
+    vfprintf(stderr, fmt, ap);
+    fprintf(stderr, "\n");
     va_end(ap);
-
-    s = strdup(buf);
-    if (s == 0) die("out of memory");
-    return s;
+    exit(EXIT_FAILURE);
 }
 
-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);
+void set_verbose() {
+    g_verbose = true;
+}
+
+void verbose(const char* fmt, ...) {
+    if (!g_verbose) return;
+
+    if (*fmt != '\n') {
+        va_list ap;
+        va_start(ap, fmt);
+        fprintf(stderr, "fastboot: verbose: ");
+        vfprintf(stderr, fmt, ap);
+        va_end(ap);
+    }
+    fprintf(stderr, "\n");
+}
+
+char* xstrdup(const char* s) {
+    char* result = strdup(s);
+    if (!result) die("out of memory");
+    return result;
 }
diff --git a/fastboot/util.h b/fastboot/util.h
new file mode 100644
index 0000000..9033f93
--- /dev/null
+++ b/fastboot/util.h
@@ -0,0 +1,19 @@
+#pragma once
+
+#include <inttypes.h>
+#include <stdlib.h>
+
+#include <string>
+
+#include <bootimg.h>
+
+/* util stuff */
+double now();
+char* xstrdup(const char*);
+void set_verbose();
+
+// These printf-like functions are implemented in terms of vsnprintf, so they
+// use the same attribute for compile-time format string checking.
+void die(const char* fmt, ...) __attribute__((__noreturn__))
+__attribute__((__format__(__printf__, 1, 2)));
+void verbose(const char* fmt, ...) __attribute__((__format__(__printf__, 1, 2)));
diff --git a/fingerprintd/Android.mk b/fingerprintd/Android.mk
deleted file mode 100644
index 48b9525..0000000
--- a/fingerprintd/Android.mk
+++ /dev/null
@@ -1,33 +0,0 @@
-#
-# Copyright (C) 2015 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.
-#
-
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-LOCAL_CFLAGS := -Wall -Wextra -Werror -Wunused
-LOCAL_SRC_FILES := \
-	FingerprintDaemonProxy.cpp \
-	IFingerprintDaemon.cpp \
-	IFingerprintDaemonCallback.cpp \
-	fingerprintd.cpp
-LOCAL_MODULE := fingerprintd
-LOCAL_SHARED_LIBRARIES := \
-	libbinder \
-	liblog \
-	libhardware \
-	libutils \
-	libkeystore_binder
-include $(BUILD_EXECUTABLE)
diff --git a/fingerprintd/FingerprintDaemonProxy.cpp b/fingerprintd/FingerprintDaemonProxy.cpp
deleted file mode 100644
index b3c0cd7..0000000
--- a/fingerprintd/FingerprintDaemonProxy.cpp
+++ /dev/null
@@ -1,268 +0,0 @@
-/*
- * Copyright (C) 2015 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.
- */
-
-#define LOG_TAG "fingerprintd"
-
-#include <binder/IServiceManager.h>
-#include <hardware/hardware.h>
-#include <hardware/fingerprint.h>
-#include <hardware/hw_auth_token.h>
-#include <keystore/IKeystoreService.h>
-#include <keystore/keystore.h> // for error codes
-#include <utils/Log.h>
-
-#include "FingerprintDaemonProxy.h"
-
-namespace android {
-
-FingerprintDaemonProxy* FingerprintDaemonProxy::sInstance = NULL;
-
-// Supported fingerprint HAL version
-static const uint16_t kVersion = HARDWARE_MODULE_API_VERSION(2, 1);
-
-FingerprintDaemonProxy::FingerprintDaemonProxy() : mModule(NULL), mDevice(NULL), mCallback(NULL) {
-
-}
-
-FingerprintDaemonProxy::~FingerprintDaemonProxy() {
-    closeHal();
-}
-
-void FingerprintDaemonProxy::hal_notify_callback(const fingerprint_msg_t *msg) {
-    FingerprintDaemonProxy* instance = FingerprintDaemonProxy::getInstance();
-    const sp<IFingerprintDaemonCallback> callback = instance->mCallback;
-    if (callback == NULL) {
-        ALOGE("Invalid callback object");
-        return;
-    }
-    const int64_t device = (int64_t) instance->mDevice;
-    switch (msg->type) {
-        case FINGERPRINT_ERROR:
-            ALOGD("onError(%d)", msg->data.error);
-            callback->onError(device, msg->data.error);
-            break;
-        case FINGERPRINT_ACQUIRED:
-            ALOGD("onAcquired(%d)", msg->data.acquired.acquired_info);
-            callback->onAcquired(device, msg->data.acquired.acquired_info);
-            break;
-        case FINGERPRINT_AUTHENTICATED:
-            ALOGD("onAuthenticated(fid=%d, gid=%d)",
-                    msg->data.authenticated.finger.fid,
-                    msg->data.authenticated.finger.gid);
-            if (msg->data.authenticated.finger.fid != 0) {
-                const uint8_t* hat = reinterpret_cast<const uint8_t *>(&msg->data.authenticated.hat);
-                instance->notifyKeystore(hat, sizeof(msg->data.authenticated.hat));
-            }
-            callback->onAuthenticated(device,
-                    msg->data.authenticated.finger.fid,
-                    msg->data.authenticated.finger.gid);
-            break;
-        case FINGERPRINT_TEMPLATE_ENROLLING:
-            ALOGD("onEnrollResult(fid=%d, gid=%d, rem=%d)",
-                    msg->data.enroll.finger.fid,
-                    msg->data.enroll.finger.gid,
-                    msg->data.enroll.samples_remaining);
-            callback->onEnrollResult(device,
-                    msg->data.enroll.finger.fid,
-                    msg->data.enroll.finger.gid,
-                    msg->data.enroll.samples_remaining);
-            break;
-        case FINGERPRINT_TEMPLATE_REMOVED:
-            ALOGD("onRemove(fid=%d, gid=%d)",
-                    msg->data.removed.finger.fid,
-                    msg->data.removed.finger.gid);
-            callback->onRemoved(device,
-                    msg->data.removed.finger.fid,
-                    msg->data.removed.finger.gid);
-            break;
-        case FINGERPRINT_TEMPLATE_ENUMERATING:
-            ALOGD("onEnumerate(fid=%d, gid=%d, rem=%d)",
-                    msg->data.enumerated.finger.fid,
-                    msg->data.enumerated.finger.gid,
-                    msg->data.enumerated.remaining_templates);
-            callback->onEnumerate(device,
-                    msg->data.enumerated.finger.fid,
-                    msg->data.enumerated.finger.gid,
-                    msg->data.enumerated.remaining_templates);
-            break;
-        default:
-            ALOGE("invalid msg type: %d", msg->type);
-            return;
-    }
-}
-
-void FingerprintDaemonProxy::notifyKeystore(const uint8_t *auth_token, const size_t auth_token_length) {
-    if (auth_token != NULL && auth_token_length > 0) {
-        // TODO: cache service?
-        sp < IServiceManager > sm = defaultServiceManager();
-        sp < IBinder > binder = sm->getService(String16("android.security.keystore"));
-        sp < IKeystoreService > service = interface_cast < IKeystoreService > (binder);
-        if (service != NULL) {
-            status_t ret = service->addAuthToken(auth_token, auth_token_length);
-            if (ret != ResponseCode::NO_ERROR) {
-                ALOGE("Falure sending auth token to KeyStore: %d", ret);
-            }
-        } else {
-            ALOGE("Unable to communicate with KeyStore");
-        }
-    }
-}
-
-void FingerprintDaemonProxy::init(const sp<IFingerprintDaemonCallback>& callback) {
-    if (mCallback != NULL && IInterface::asBinder(callback) != IInterface::asBinder(mCallback)) {
-        IInterface::asBinder(mCallback)->unlinkToDeath(this);
-    }
-    IInterface::asBinder(callback)->linkToDeath(this);
-    mCallback = callback;
-}
-
-int32_t FingerprintDaemonProxy::enroll(const uint8_t* token, ssize_t tokenSize, int32_t groupId,
-        int32_t timeout) {
-    ALOG(LOG_VERBOSE, LOG_TAG, "enroll(gid=%d, timeout=%d)\n", groupId, timeout);
-    if (tokenSize != sizeof(hw_auth_token_t) ) {
-        ALOG(LOG_VERBOSE, LOG_TAG, "enroll() : invalid token size %zu\n", tokenSize);
-        return -1;
-    }
-    const hw_auth_token_t* authToken = reinterpret_cast<const hw_auth_token_t*>(token);
-    return mDevice->enroll(mDevice, authToken, groupId, timeout);
-}
-
-uint64_t FingerprintDaemonProxy::preEnroll() {
-    return mDevice->pre_enroll(mDevice);
-}
-
-int32_t FingerprintDaemonProxy::postEnroll() {
-    return mDevice->post_enroll(mDevice);
-}
-
-int32_t FingerprintDaemonProxy::stopEnrollment() {
-    ALOG(LOG_VERBOSE, LOG_TAG, "stopEnrollment()\n");
-    return mDevice->cancel(mDevice);
-}
-
-int32_t FingerprintDaemonProxy::authenticate(uint64_t sessionId, uint32_t groupId) {
-    ALOG(LOG_VERBOSE, LOG_TAG, "authenticate(sid=%" PRId64 ", gid=%d)\n", sessionId, groupId);
-    return mDevice->authenticate(mDevice, sessionId, groupId);
-}
-
-int32_t FingerprintDaemonProxy::stopAuthentication() {
-    ALOG(LOG_VERBOSE, LOG_TAG, "stopAuthentication()\n");
-    return mDevice->cancel(mDevice);
-}
-
-int32_t FingerprintDaemonProxy::remove(int32_t fingerId, int32_t groupId) {
-    ALOG(LOG_VERBOSE, LOG_TAG, "remove(fid=%d, gid=%d)\n", fingerId, groupId);
-    return mDevice->remove(mDevice, groupId, fingerId);
-}
-
-int32_t FingerprintDaemonProxy::enumerate() {
-    ALOG(LOG_VERBOSE, LOG_TAG, "enumerate()\n");
-    return mDevice->enumerate(mDevice);
-}
-
-uint64_t FingerprintDaemonProxy::getAuthenticatorId() {
-    return mDevice->get_authenticator_id(mDevice);
-}
-
-int32_t FingerprintDaemonProxy::setActiveGroup(int32_t groupId, const uint8_t* path,
-        ssize_t pathlen) {
-    if (pathlen >= PATH_MAX || pathlen <= 0) {
-        ALOGE("Bad path length: %zd", pathlen);
-        return -1;
-    }
-    // Convert to null-terminated string
-    char path_name[PATH_MAX];
-    memcpy(path_name, path, pathlen);
-    path_name[pathlen] = '\0';
-    ALOG(LOG_VERBOSE, LOG_TAG, "setActiveGroup(%d, %s, %zu)", groupId, path_name, pathlen);
-    return mDevice->set_active_group(mDevice, groupId, path_name);
-}
-
-int64_t FingerprintDaemonProxy::openHal() {
-    ALOG(LOG_VERBOSE, LOG_TAG, "nativeOpenHal()\n");
-    int err;
-    const hw_module_t *hw_module = NULL;
-    if (0 != (err = hw_get_module(FINGERPRINT_HARDWARE_MODULE_ID, &hw_module))) {
-        ALOGE("Can't open fingerprint HW Module, error: %d", err);
-        return 0;
-    }
-    if (NULL == hw_module) {
-        ALOGE("No valid fingerprint module");
-        return 0;
-    }
-
-    mModule = reinterpret_cast<const fingerprint_module_t*>(hw_module);
-
-    if (mModule->common.methods->open == NULL) {
-        ALOGE("No valid open method");
-        return 0;
-    }
-
-    hw_device_t *device = NULL;
-
-    if (0 != (err = mModule->common.methods->open(hw_module, NULL, &device))) {
-        ALOGE("Can't open fingerprint methods, error: %d", err);
-        return 0;
-    }
-
-    if (kVersion != device->version) {
-        ALOGE("Wrong fp version. Expected %d, got %d", kVersion, device->version);
-        // return 0; // FIXME
-    }
-
-    mDevice = reinterpret_cast<fingerprint_device_t*>(device);
-    err = mDevice->set_notify(mDevice, hal_notify_callback);
-    if (err < 0) {
-        ALOGE("Failed in call to set_notify(), err=%d", err);
-        return 0;
-    }
-
-    // Sanity check - remove
-    if (mDevice->notify != hal_notify_callback) {
-        ALOGE("NOTIFY not set properly: %p != %p", mDevice->notify, hal_notify_callback);
-    }
-
-    ALOG(LOG_VERBOSE, LOG_TAG, "fingerprint HAL successfully initialized");
-    return reinterpret_cast<int64_t>(mDevice); // This is just a handle
-}
-
-int32_t FingerprintDaemonProxy::closeHal() {
-    ALOG(LOG_VERBOSE, LOG_TAG, "nativeCloseHal()\n");
-    if (mDevice == NULL) {
-        ALOGE("No valid device");
-        return -ENOSYS;
-    }
-    int err;
-    if (0 != (err = mDevice->common.close(reinterpret_cast<hw_device_t*>(mDevice)))) {
-        ALOGE("Can't close fingerprint module, error: %d", err);
-        return err;
-    }
-    mDevice = NULL;
-    return 0;
-}
-
-void FingerprintDaemonProxy::binderDied(const wp<IBinder>& who) {
-    ALOGD("binder died");
-    int err;
-    if (0 != (err = closeHal())) {
-        ALOGE("Can't close fingerprint device, error: %d", err);
-    }
-    if (IInterface::asBinder(mCallback) == who) {
-        mCallback = NULL;
-    }
-}
-
-}
diff --git a/fingerprintd/FingerprintDaemonProxy.h b/fingerprintd/FingerprintDaemonProxy.h
deleted file mode 100644
index 145b4c9..0000000
--- a/fingerprintd/FingerprintDaemonProxy.h
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Copyright (C) 2015 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 FINGERPRINT_DAEMON_PROXY_H_
-#define FINGERPRINT_DAEMON_PROXY_H_
-
-#include "IFingerprintDaemon.h"
-#include "IFingerprintDaemonCallback.h"
-
-namespace android {
-
-class FingerprintDaemonProxy : public BnFingerprintDaemon {
-    public:
-        static FingerprintDaemonProxy* getInstance() {
-            if (sInstance == NULL) {
-                sInstance = new FingerprintDaemonProxy();
-            }
-            return sInstance;
-        }
-
-        // These reflect binder methods.
-        virtual void init(const sp<IFingerprintDaemonCallback>& callback);
-        virtual int32_t enroll(const uint8_t* token, ssize_t tokenLength, int32_t groupId, int32_t timeout);
-        virtual uint64_t preEnroll();
-        virtual int32_t postEnroll();
-        virtual int32_t stopEnrollment();
-        virtual int32_t authenticate(uint64_t sessionId, uint32_t groupId);
-        virtual int32_t stopAuthentication();
-        virtual int32_t remove(int32_t fingerId, int32_t groupId);
-        virtual int32_t enumerate();
-        virtual uint64_t getAuthenticatorId();
-        virtual int32_t setActiveGroup(int32_t groupId, const uint8_t* path, ssize_t pathLen);
-        virtual int64_t openHal();
-        virtual int32_t closeHal();
-
-    private:
-        FingerprintDaemonProxy();
-        virtual ~FingerprintDaemonProxy();
-        void binderDied(const wp<IBinder>& who);
-        void notifyKeystore(const uint8_t *auth_token, const size_t auth_token_length);
-        static void hal_notify_callback(const fingerprint_msg_t *msg);
-
-        static FingerprintDaemonProxy* sInstance;
-        fingerprint_module_t const* mModule;
-        fingerprint_device_t* mDevice;
-        sp<IFingerprintDaemonCallback> mCallback;
-};
-
-} // namespace android
-
-#endif // FINGERPRINT_DAEMON_PROXY_H_
diff --git a/fingerprintd/IFingerprintDaemon.cpp b/fingerprintd/IFingerprintDaemon.cpp
deleted file mode 100644
index bc4af56..0000000
--- a/fingerprintd/IFingerprintDaemon.cpp
+++ /dev/null
@@ -1,205 +0,0 @@
-/*
- * Copyright 2015, 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 <inttypes.h>
-
-#include <binder/IPCThreadState.h>
-#include <binder/IServiceManager.h>
-#include <binder/PermissionCache.h>
-#include <utils/String16.h>
-#include <utils/Looper.h>
-#include <keystore/IKeystoreService.h>
-#include <keystore/keystore.h> // for error code
-#include <hardware/hardware.h>
-#include <hardware/fingerprint.h>
-#include <hardware/hw_auth_token.h>
-#include "IFingerprintDaemon.h"
-#include "IFingerprintDaemonCallback.h"
-
-namespace android {
-
-static const String16 USE_FINGERPRINT_PERMISSION("android.permission.USE_FINGERPRINT");
-static const String16 MANAGE_FINGERPRINT_PERMISSION("android.permission.MANAGE_FINGERPRINT");
-static const String16 HAL_FINGERPRINT_PERMISSION("android.permission.MANAGE_FINGERPRINT"); // TODO
-static const String16 DUMP_PERMISSION("android.permission.DUMP");
-
-const android::String16
-IFingerprintDaemon::descriptor("android.hardware.fingerprint.IFingerprintDaemon");
-
-const android::String16&
-IFingerprintDaemon::getInterfaceDescriptor() const {
-    return IFingerprintDaemon::descriptor;
-}
-
-status_t BnFingerprintDaemon::onTransact(uint32_t code, const Parcel& data, Parcel* reply,
-        uint32_t flags) {
-    switch(code) {
-        case AUTHENTICATE: {
-            CHECK_INTERFACE(IFingerprintDaemon, data, reply);
-            if (!checkPermission(HAL_FINGERPRINT_PERMISSION)) {
-                return PERMISSION_DENIED;
-            }
-            const uint64_t sessionId = data.readInt64();
-            const uint32_t groupId = data.readInt32();
-            const int32_t ret = authenticate(sessionId, groupId);
-            reply->writeNoException();
-            reply->writeInt32(ret);
-            return NO_ERROR;
-        };
-        case CANCEL_AUTHENTICATION: {
-            CHECK_INTERFACE(IFingerprintDaemon, data, reply);
-            if (!checkPermission(HAL_FINGERPRINT_PERMISSION)) {
-                return PERMISSION_DENIED;
-            }
-            const int32_t ret = stopAuthentication();
-            reply->writeNoException();
-            reply->writeInt32(ret);
-            return NO_ERROR;
-        }
-        case ENROLL: {
-            CHECK_INTERFACE(IFingerprintDaemon, data, reply);
-            if (!checkPermission(HAL_FINGERPRINT_PERMISSION)) {
-                return PERMISSION_DENIED;
-            }
-            const ssize_t tokenSize = data.readInt32();
-            const uint8_t* token = static_cast<const uint8_t *>(data.readInplace(tokenSize));
-            const int32_t groupId = data.readInt32();
-            const int32_t timeout = data.readInt32();
-            const int32_t ret = enroll(token, tokenSize, groupId, timeout);
-            reply->writeNoException();
-            reply->writeInt32(ret);
-            return NO_ERROR;
-        }
-        case CANCEL_ENROLLMENT: {
-            CHECK_INTERFACE(IFingerprintDaemon, data, reply);
-            if (!checkPermission(HAL_FINGERPRINT_PERMISSION)) {
-                return PERMISSION_DENIED;
-            }
-            const int32_t ret = stopEnrollment();
-            reply->writeNoException();
-            reply->writeInt32(ret);
-            return NO_ERROR;
-        }
-        case PRE_ENROLL: {
-            CHECK_INTERFACE(IFingerprintDaemon, data, reply);
-            if (!checkPermission(HAL_FINGERPRINT_PERMISSION)) {
-                return PERMISSION_DENIED;
-            }
-            const uint64_t ret = preEnroll();
-            reply->writeNoException();
-            reply->writeInt64(ret);
-            return NO_ERROR;
-        }
-        case POST_ENROLL: {
-            CHECK_INTERFACE(IFingerprintDaemon, data, reply);
-            if (!checkPermission(HAL_FINGERPRINT_PERMISSION)) {
-                return PERMISSION_DENIED;
-            }
-            const int32_t ret = postEnroll();
-            reply->writeNoException();
-            reply->writeInt32(ret);
-            return NO_ERROR;
-        }
-        case REMOVE: {
-            CHECK_INTERFACE(IFingerprintDaemon, data, reply);
-            if (!checkPermission(HAL_FINGERPRINT_PERMISSION)) {
-                return PERMISSION_DENIED;
-            }
-            const int32_t fingerId = data.readInt32();
-            const int32_t groupId = data.readInt32();
-            const int32_t ret = remove(fingerId, groupId);
-            reply->writeNoException();
-            reply->writeInt32(ret);
-            return NO_ERROR;
-        }
-        case ENUMERATE: {
-            CHECK_INTERFACE(IFingerprintDaemon, data, reply);
-            if (!checkPermission(HAL_FINGERPRINT_PERMISSION)) {
-                return PERMISSION_DENIED;
-            }
-            const int32_t ret = enumerate();
-            reply->writeNoException();
-            reply->writeInt32(ret);
-            return NO_ERROR;
-        }
-        case GET_AUTHENTICATOR_ID: {
-            CHECK_INTERFACE(IFingerprintDaemon, data, reply);
-            if (!checkPermission(HAL_FINGERPRINT_PERMISSION)) {
-                return PERMISSION_DENIED;
-            }
-            const uint64_t ret = getAuthenticatorId();
-            reply->writeNoException();
-            reply->writeInt64(ret);
-            return NO_ERROR;
-        }
-        case SET_ACTIVE_GROUP: {
-            CHECK_INTERFACE(IFingerprintDaemon, data, reply);
-            if (!checkPermission(HAL_FINGERPRINT_PERMISSION)) {
-                return PERMISSION_DENIED;
-            }
-            const int32_t group = data.readInt32();
-            const ssize_t pathSize = data.readInt32();
-            const uint8_t* path = static_cast<const uint8_t *>(data.readInplace(pathSize));
-            const int32_t ret = setActiveGroup(group, path, pathSize);
-            reply->writeNoException();
-            reply->writeInt32(ret);
-            return NO_ERROR;
-        }
-        case OPEN_HAL: {
-            CHECK_INTERFACE(IFingerprintDaemon, data, reply);
-            if (!checkPermission(HAL_FINGERPRINT_PERMISSION)) {
-                return PERMISSION_DENIED;
-            }
-            const int64_t ret = openHal();
-            reply->writeNoException();
-            reply->writeInt64(ret);
-            return NO_ERROR;
-        }
-        case CLOSE_HAL: {
-            CHECK_INTERFACE(IFingerprintDaemon, data, reply);
-            if (!checkPermission(HAL_FINGERPRINT_PERMISSION)) {
-                return PERMISSION_DENIED;
-            }
-            const int32_t ret = closeHal();
-            reply->writeNoException();
-            reply->writeInt32(ret);
-            return NO_ERROR;
-        }
-        case INIT: {
-            CHECK_INTERFACE(IFingerprintDaemon, data, reply);
-            if (!checkPermission(HAL_FINGERPRINT_PERMISSION)) {
-                return PERMISSION_DENIED;
-            }
-            sp<IFingerprintDaemonCallback> callback =
-                    interface_cast<IFingerprintDaemonCallback>(data.readStrongBinder());
-            init(callback);
-            reply->writeNoException();
-            return NO_ERROR;
-        }
-        default:
-            return BBinder::onTransact(code, data, reply, flags);
-    }
-};
-
-bool BnFingerprintDaemon::checkPermission(const String16& permission) {
-    const IPCThreadState* ipc = IPCThreadState::self();
-    const int calling_pid = ipc->getCallingPid();
-    const int calling_uid = ipc->getCallingUid();
-    return PermissionCache::checkPermission(permission, calling_pid, calling_uid);
-}
-
-
-}; // namespace android
diff --git a/fingerprintd/IFingerprintDaemon.h b/fingerprintd/IFingerprintDaemon.h
deleted file mode 100644
index 23c36ff..0000000
--- a/fingerprintd/IFingerprintDaemon.h
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * Copyright (C) 2015 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 IFINGERPRINT_DAEMON_H_
-#define IFINGERPRINT_DAEMON_H_
-
-#include <binder/IInterface.h>
-#include <binder/Parcel.h>
-
-namespace android {
-
-class IFingerprintDaemonCallback;
-
-/*
-* Abstract base class for native implementation of FingerprintService.
-*
-* Note: This must be kept manually in sync with IFingerprintDaemon.aidl
-*/
-class IFingerprintDaemon : public IInterface, public IBinder::DeathRecipient {
-    public:
-        enum {
-           AUTHENTICATE = IBinder::FIRST_CALL_TRANSACTION + 0,
-           CANCEL_AUTHENTICATION = IBinder::FIRST_CALL_TRANSACTION + 1,
-           ENROLL = IBinder::FIRST_CALL_TRANSACTION + 2,
-           CANCEL_ENROLLMENT = IBinder::FIRST_CALL_TRANSACTION + 3,
-           PRE_ENROLL = IBinder::FIRST_CALL_TRANSACTION + 4,
-           REMOVE = IBinder::FIRST_CALL_TRANSACTION + 5,
-           GET_AUTHENTICATOR_ID = IBinder::FIRST_CALL_TRANSACTION + 6,
-           SET_ACTIVE_GROUP = IBinder::FIRST_CALL_TRANSACTION + 7,
-           OPEN_HAL = IBinder::FIRST_CALL_TRANSACTION + 8,
-           CLOSE_HAL = IBinder::FIRST_CALL_TRANSACTION + 9,
-           INIT = IBinder::FIRST_CALL_TRANSACTION + 10,
-           POST_ENROLL = IBinder::FIRST_CALL_TRANSACTION + 11,
-           ENUMERATE = IBinder::FIRST_CALL_TRANSACTION + 12,
-        };
-
-        IFingerprintDaemon() { }
-        virtual ~IFingerprintDaemon() { }
-        virtual const android::String16& getInterfaceDescriptor() const;
-
-        // Binder interface methods
-        virtual void init(const sp<IFingerprintDaemonCallback>& callback) = 0;
-        virtual int32_t enroll(const uint8_t* token, ssize_t tokenLength, int32_t groupId,
-                int32_t timeout) = 0;
-        virtual uint64_t preEnroll() = 0;
-        virtual int32_t postEnroll() = 0;
-        virtual int32_t stopEnrollment() = 0;
-        virtual int32_t authenticate(uint64_t sessionId, uint32_t groupId) = 0;
-        virtual int32_t stopAuthentication() = 0;
-        virtual int32_t remove(int32_t fingerId, int32_t groupId) = 0;
-        virtual int32_t enumerate() = 0;
-        virtual uint64_t getAuthenticatorId() = 0;
-        virtual int32_t setActiveGroup(int32_t groupId, const uint8_t* path, ssize_t pathLen) = 0;
-        virtual int64_t openHal() = 0;
-        virtual int32_t closeHal() = 0;
-
-        // DECLARE_META_INTERFACE - C++ client interface not needed
-        static const android::String16 descriptor;
-        static void hal_notify_callback(const fingerprint_msg_t *msg);
-};
-
-// ----------------------------------------------------------------------------
-
-class BnFingerprintDaemon: public BnInterface<IFingerprintDaemon> {
-    public:
-       virtual status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply,
-               uint32_t flags = 0);
-    private:
-       bool checkPermission(const String16& permission);
-};
-
-} // namespace android
-
-#endif // IFINGERPRINT_DAEMON_H_
-
diff --git a/fingerprintd/IFingerprintDaemonCallback.cpp b/fingerprintd/IFingerprintDaemonCallback.cpp
deleted file mode 100644
index 1d75aa7..0000000
--- a/fingerprintd/IFingerprintDaemonCallback.cpp
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * Copyright (C) 2015 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.
- */
-
-#define LOG_TAG "IFingerprintDaemonCallback"
-#include <stdint.h>
-#include <sys/types.h>
-#include <utils/Log.h>
-#include <binder/Parcel.h>
-
-#include "IFingerprintDaemonCallback.h"
-
-namespace android {
-
-class BpFingerprintDaemonCallback : public BpInterface<IFingerprintDaemonCallback>
-{
-public:
-    explicit BpFingerprintDaemonCallback(const sp<IBinder>& impl) :
-            BpInterface<IFingerprintDaemonCallback>(impl) {
-    }
-    virtual status_t onEnrollResult(int64_t devId, int32_t fpId, int32_t gpId, int32_t rem) {
-        Parcel data, reply;
-        data.writeInterfaceToken(IFingerprintDaemonCallback::getInterfaceDescriptor());
-        data.writeInt64(devId);
-        data.writeInt32(fpId);
-        data.writeInt32(gpId);
-        data.writeInt32(rem);
-        return remote()->transact(ON_ENROLL_RESULT, data, &reply, IBinder::FLAG_ONEWAY);
-    }
-
-    virtual status_t onAcquired(int64_t devId, int32_t acquiredInfo) {
-        Parcel data, reply;
-        data.writeInterfaceToken(IFingerprintDaemonCallback::getInterfaceDescriptor());
-        data.writeInt64(devId);
-        data.writeInt32(acquiredInfo);
-        return remote()->transact(ON_ACQUIRED, data, &reply, IBinder::FLAG_ONEWAY);
-    }
-
-    virtual status_t onAuthenticated(int64_t devId, int32_t fpId, int32_t gpId) {
-        Parcel data, reply;
-        data.writeInterfaceToken(IFingerprintDaemonCallback::getInterfaceDescriptor());
-        data.writeInt64(devId);
-        data.writeInt32(fpId);
-        data.writeInt32(gpId);
-        return remote()->transact(ON_AUTHENTICATED, data, &reply, IBinder::FLAG_ONEWAY);
-    }
-
-    virtual status_t onError(int64_t devId, int32_t error) {
-        Parcel data, reply;
-        data.writeInterfaceToken(IFingerprintDaemonCallback::getInterfaceDescriptor());
-        data.writeInt64(devId);
-        data.writeInt32(error);
-        return remote()->transact(ON_ERROR, data, &reply, IBinder::FLAG_ONEWAY);
-    }
-
-    virtual status_t onRemoved(int64_t devId, int32_t fpId, int32_t gpId) {
-        Parcel data, reply;
-        data.writeInterfaceToken(IFingerprintDaemonCallback::getInterfaceDescriptor());
-        data.writeInt64(devId);
-        data.writeInt32(fpId);
-        data.writeInt32(gpId);
-        return remote()->transact(ON_REMOVED, data, &reply, IBinder::FLAG_ONEWAY);
-    }
-
-    virtual status_t onEnumerate(int64_t devId, int32_t fpId, int32_t gpId, int32_t rem) {
-        Parcel data, reply;
-        data.writeInterfaceToken(IFingerprintDaemonCallback::getInterfaceDescriptor());
-        data.writeInt64(devId);
-        data.writeInt32(fpId);
-        data.writeInt32(gpId);
-        data.writeInt32(rem);
-        return remote()->transact(ON_ENUMERATE, data, &reply, IBinder::FLAG_ONEWAY);
-    }
-};
-
-IMPLEMENT_META_INTERFACE(FingerprintDaemonCallback,
-        "android.hardware.fingerprint.IFingerprintDaemonCallback");
-
-}; // namespace android
diff --git a/fingerprintd/IFingerprintDaemonCallback.h b/fingerprintd/IFingerprintDaemonCallback.h
deleted file mode 100644
index e343cb4..0000000
--- a/fingerprintd/IFingerprintDaemonCallback.h
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright (C) 2015 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 IFINGERPRINT_DAEMON_CALLBACK_H_
-#define IFINGERPRINT_DAEMON_CALLBACK_H_
-
-#include <inttypes.h>
-#include <utils/Errors.h>
-#include <binder/IInterface.h>
-#include <binder/Parcel.h>
-
-namespace android {
-
-/*
-* Communication channel back to FingerprintService.java
-*/
-class IFingerprintDaemonCallback : public IInterface {
-    public:
-        // must be kept in sync with IFingerprintService.aidl
-        enum {
-            ON_ENROLL_RESULT = IBinder::FIRST_CALL_TRANSACTION + 0,
-            ON_ACQUIRED = IBinder::FIRST_CALL_TRANSACTION + 1,
-            ON_AUTHENTICATED = IBinder::FIRST_CALL_TRANSACTION + 2,
-            ON_ERROR = IBinder::FIRST_CALL_TRANSACTION + 3,
-            ON_REMOVED = IBinder::FIRST_CALL_TRANSACTION + 4,
-            ON_ENUMERATE = IBinder::FIRST_CALL_TRANSACTION + 5,
-        };
-
-        virtual status_t onEnrollResult(int64_t devId, int32_t fpId, int32_t gpId, int32_t rem) = 0;
-        virtual status_t onAcquired(int64_t devId, int32_t acquiredInfo) = 0;
-        virtual status_t onAuthenticated(int64_t devId, int32_t fingerId, int32_t groupId) = 0;
-        virtual status_t onError(int64_t devId, int32_t error) = 0;
-        virtual status_t onRemoved(int64_t devId, int32_t fingerId, int32_t groupId) = 0;
-        virtual status_t onEnumerate(int64_t devId, int32_t fingerId, int32_t groupId, int32_t rem) = 0;
-
-        DECLARE_META_INTERFACE(FingerprintDaemonCallback);
-};
-
-}; // namespace android
-
-#endif // IFINGERPRINT_DAEMON_CALLBACK_H_
diff --git a/fingerprintd/fingerprintd.cpp b/fingerprintd/fingerprintd.cpp
deleted file mode 100644
index 2fc2d0a..0000000
--- a/fingerprintd/fingerprintd.cpp
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright (C) 2015 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.
- */
-
-#define LOG_TAG "fingerprintd"
-
-#include <binder/IPCThreadState.h>
-#include <binder/IServiceManager.h>
-#include <binder/PermissionCache.h>
-#include <hardware/hardware.h>
-#include <hardware/fingerprint.h>
-#include <hardware/hw_auth_token.h>
-#include <keystore/IKeystoreService.h>
-#include <keystore/keystore.h> // for error codes
-#include <log/log.h>
-#include <utils/Log.h>
-#include <utils/String16.h>
-
-#include "FingerprintDaemonProxy.h"
-
-int main() {
-    ALOGI("Starting " LOG_TAG);
-    android::sp<android::IServiceManager> serviceManager = android::defaultServiceManager();
-    android::sp<android::FingerprintDaemonProxy> proxy =
-            android::FingerprintDaemonProxy::getInstance();
-    android::status_t ret = serviceManager->addService(
-            android::FingerprintDaemonProxy::descriptor, proxy);
-    if (ret != android::OK) {
-        ALOGE("Couldn't register " LOG_TAG " binder service!");
-        return -1;
-    }
-
-    /*
-     * We're the only thread in existence, so we're just going to process
-     * Binder transaction as a single-threaded program.
-     */
-    android::IPCThreadState::self()->joinThreadPool();
-    ALOGI("Done");
-    return 0;
-}
diff --git a/fs_mgr/Android.bp b/fs_mgr/Android.bp
index 608917a..196321c 100644
--- a/fs_mgr/Android.bp
+++ b/fs_mgr/Android.bp
@@ -20,47 +20,70 @@
         misc_undefined: ["integer"],
     },
     local_include_dirs: ["include/"],
-    cppflags: ["-Werror"],
+    cflags: [
+        "-Wall",
+        "-Werror",
+        "-Wno-unused-variable",
+    ],
+    cppflags: [
+        "-std=gnu++1z",
+    ],
 }
 
-cc_library_static {
+cc_library {
     name: "libfs_mgr",
     defaults: ["fs_mgr_defaults"],
+    recovery_available: true,
     export_include_dirs: ["include"],
     include_dirs: ["system/vold"],
     srcs: [
         "fs_mgr.cpp",
-        "fs_mgr_dm_ioctl.cpp",
         "fs_mgr_format.cpp",
         "fs_mgr_verity.cpp",
         "fs_mgr_avb.cpp",
         "fs_mgr_avb_ops.cpp",
+        "fs_mgr_dm_linear.cpp",
     ],
-    static_libs: [
-        "liblogwrap",
+    shared_libs: [
         "libfec",
         "libfec_rs",
         "libbase",
         "libcrypto_utils",
         "libcrypto",
+        "libcutils",
         "libext4_utils",
+        "libkeyutils",
+        "liblog",
         "libsquashfs_utils",
         "libselinux",
+        "liblp",
+    ],
+    static_libs: [
         "libavb",
         "libfstab",
+        "libdm",
     ],
     export_static_lib_headers: [
         "libfstab",
+        "libdm",
+    ],
+    export_shared_lib_headers: [
+        "liblp",
     ],
     whole_static_libs: [
+        "liblogwrap",
+        "libdm",
         "libfstab",
     ],
+    cppflags: [
+        "-DALLOW_ADBD_DISABLE_VERITY=0",
+    ],
     product_variables: {
         debuggable: {
-            cppflags: ["-DALLOW_ADBD_DISABLE_VERITY=1"],
-        },
-        eng: {
-            cppflags: ["-DALLOW_SKIP_SECURE_CHECK=1"],
+            cppflags: [
+                "-UALLOW_ADBD_DISABLE_VERITY",
+                "-DALLOW_ADBD_DISABLE_VERITY=1",
+            ],
         },
     },
 }
@@ -68,6 +91,7 @@
 cc_library_static {
     name: "libfstab",
     vendor_available: true,
+    recovery_available: true,
     defaults: ["fs_mgr_defaults"],
     srcs: [
         "fs_mgr_fstab.cpp",
diff --git a/fs_mgr/Android.mk b/fs_mgr/Android.mk
deleted file mode 100644
index f3ca724..0000000
--- a/fs_mgr/Android.mk
+++ /dev/null
@@ -1,37 +0,0 @@
-# Copyright 2011 The Android Open Source Project
-
-LOCAL_PATH:= $(call my-dir)
-
-common_static_libraries := \
-    liblogwrap \
-    libfec \
-    libfec_rs \
-    libbase \
-    libcrypto_utils \
-    libcrypto \
-    libext4_utils \
-    libsquashfs_utils \
-    libselinux \
-    libavb
-
-include $(CLEAR_VARS)
-LOCAL_CLANG := true
-LOCAL_SANITIZE := integer
-LOCAL_SRC_FILES:= fs_mgr_main.cpp
-LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
-LOCAL_MODULE:= fs_mgr
-LOCAL_MODULE_TAGS := optional
-LOCAL_FORCE_STATIC_EXECUTABLE := true
-LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT)/sbin
-LOCAL_UNSTRIPPED_PATH := $(TARGET_ROOT_OUT_UNSTRIPPED)
-LOCAL_STATIC_LIBRARIES := libfs_mgr \
-    $(common_static_libraries) \
-    libcutils \
-    liblog \
-    libc \
-    libsparse \
-    libz \
-    libselinux
-LOCAL_CXX_STL := libc++_static
-LOCAL_CFLAGS := -Werror
-include $(BUILD_EXECUTABLE)
diff --git a/fs_mgr/OWNERS b/fs_mgr/OWNERS
new file mode 100644
index 0000000..817a0b8
--- /dev/null
+++ b/fs_mgr/OWNERS
@@ -0,0 +1,2 @@
+bowgotsai@google.com
+tomcherry@google.com
diff --git a/fs_mgr/fs_mgr.cpp b/fs_mgr/fs_mgr.cpp
index 292541d..5f57182 100644
--- a/fs_mgr/fs_mgr.cpp
+++ b/fs_mgr/fs_mgr.cpp
@@ -31,13 +31,18 @@
 #include <time.h>
 #include <unistd.h>
 
+#include <functional>
 #include <memory>
+#include <string>
 #include <thread>
+#include <vector>
 
 #include <android-base/file.h>
 #include <android-base/properties.h>
 #include <android-base/stringprintf.h>
+#include <android-base/strings.h>
 #include <android-base/unique_fd.h>
+#include <cutils/android_filesystem_config.h>
 #include <cutils/android_reboot.h>
 #include <cutils/partition_utils.h>
 #include <cutils/properties.h>
@@ -46,6 +51,7 @@
 #include <ext4_utils/ext4_sb.h>
 #include <ext4_utils/ext4_utils.h>
 #include <ext4_utils/wipe.h>
+#include <libdm/dm.h>
 #include <linux/fs.h>
 #include <linux/loop.h>
 #include <linux/magic.h>
@@ -55,7 +61,6 @@
 #include "fs_mgr.h"
 #include "fs_mgr_avb.h"
 #include "fs_mgr_priv.h"
-#include "fs_mgr_priv_dm_ioctl.h"
 
 #define KEY_LOC_PROP   "ro.crypto.keyfile.userdata"
 #define KEY_IN_FOOTER  "footer"
@@ -72,6 +77,8 @@
 
 #define ARRAY_SIZE(a) (sizeof(a) / sizeof(*(a)))
 
+using DeviceMapper = android::dm::DeviceMapper;
+
 // record fs stat
 enum FsStatFlags {
     FS_STAT_IS_EXT4 = 0x0001,
@@ -79,13 +86,15 @@
     FS_STAT_E2FSCK_F_ALWAYS = 0x0004,
     FS_STAT_UNCLEAN_SHUTDOWN = 0x0008,
     FS_STAT_QUOTA_ENABLED = 0x0010,
-    FS_STAT_TUNE2FS_FAILED = 0x0020,
     FS_STAT_RO_MOUNT_FAILED = 0x0040,
     FS_STAT_RO_UNMOUNT_FAILED = 0x0080,
     FS_STAT_FULL_MOUNT_FAILED = 0x0100,
     FS_STAT_E2FSCK_FAILED = 0x0200,
     FS_STAT_E2FSCK_FS_FIXED = 0x0400,
     FS_STAT_EXT4_INVALID_MAGIC = 0x0800,
+    FS_STAT_TOGGLE_QUOTAS_FAILED = 0x10000,
+    FS_STAT_SET_RESERVED_BLOCKS_FAILED = 0x20000,
+    FS_STAT_ENABLE_ENCRYPTION_FAILED = 0x40000,
 };
 
 // TODO: switch to inotify()
@@ -117,10 +126,16 @@
     }
 }
 
+static bool is_extfs(const std::string& fs_type) {
+    return fs_type == "ext4" || fs_type == "ext3" || fs_type == "ext2";
+}
+
 static bool should_force_check(int fs_stat) {
-    return fs_stat & (FS_STAT_E2FSCK_F_ALWAYS | FS_STAT_UNCLEAN_SHUTDOWN | FS_STAT_QUOTA_ENABLED |
-                      FS_STAT_TUNE2FS_FAILED | FS_STAT_RO_MOUNT_FAILED | FS_STAT_RO_UNMOUNT_FAILED |
-                      FS_STAT_FULL_MOUNT_FAILED | FS_STAT_E2FSCK_FAILED);
+    return fs_stat &
+           (FS_STAT_E2FSCK_F_ALWAYS | FS_STAT_UNCLEAN_SHUTDOWN | FS_STAT_QUOTA_ENABLED |
+            FS_STAT_RO_MOUNT_FAILED | FS_STAT_RO_UNMOUNT_FAILED | FS_STAT_FULL_MOUNT_FAILED |
+            FS_STAT_E2FSCK_FAILED | FS_STAT_TOGGLE_QUOTAS_FAILED |
+            FS_STAT_SET_RESERVED_BLOCKS_FAILED | FS_STAT_ENABLE_ENCRYPTION_FAILED);
 }
 
 static void check_fs(const char *blk_device, char *fs_type, char *target, int *fs_stat)
@@ -133,7 +148,7 @@
     const char* e2fsck_forced_argv[] = {E2FSCK_BIN, "-f", "-y", blk_device};
 
     /* Check for the types of filesystems we know how to check */
-    if (!strcmp(fs_type, "ext2") || !strcmp(fs_type, "ext3") || !strcmp(fs_type, "ext4")) {
+    if (is_extfs(fs_type)) {
         if (*fs_stat & FS_STAT_EXT4_INVALID_MAGIC) {  // will fail, so do not try
             return;
         }
@@ -231,186 +246,215 @@
     return;
 }
 
-/* Function to read the primary superblock */
-static int read_super_block(int fd, struct ext4_super_block *sb)
-{
-    off64_t ret;
-
-    ret = lseek64(fd, 1024, SEEK_SET);
-    if (ret < 0)
-        return ret;
-
-    ret = read(fd, sb, sizeof(*sb));
-    if (ret < 0)
-        return ret;
-    if (ret != sizeof(*sb))
-        return ret;
-
-    return 0;
-}
-
-static ext4_fsblk_t ext4_blocks_count(struct ext4_super_block *es)
-{
+static ext4_fsblk_t ext4_blocks_count(const struct ext4_super_block* es) {
     return ((ext4_fsblk_t)le32_to_cpu(es->s_blocks_count_hi) << 32) |
-            le32_to_cpu(es->s_blocks_count_lo);
+           le32_to_cpu(es->s_blocks_count_lo);
 }
 
-static ext4_fsblk_t ext4_r_blocks_count(struct ext4_super_block *es)
-{
+static ext4_fsblk_t ext4_r_blocks_count(const struct ext4_super_block* es) {
     return ((ext4_fsblk_t)le32_to_cpu(es->s_r_blocks_count_hi) << 32) |
-            le32_to_cpu(es->s_r_blocks_count_lo);
+           le32_to_cpu(es->s_r_blocks_count_lo);
 }
 
-static int do_quota_with_shutdown_check(char *blk_device, char *fs_type,
-                                        struct fstab_rec *rec, int *fs_stat)
-{
-    int force_check = 0;
-    if (!strcmp(fs_type, "ext4")) {
-        /*
-         * Some system images do not have tune2fs for licensing reasons
-         * Detect these and skip reserve blocks.
-         */
-        if (access(TUNE2FS_BIN, X_OK)) {
-            LERROR << "Not running " << TUNE2FS_BIN << " on "
-                   << blk_device << " (executable not in system image)";
-        } else {
-            const char* arg1 = nullptr;
-            const char* arg2 = nullptr;
-            int status = 0;
-            int ret = 0;
-            android::base::unique_fd fd(
-                TEMP_FAILURE_RETRY(open(blk_device, O_RDONLY | O_CLOEXEC)));
-            if (fd >= 0) {
-                struct ext4_super_block sb;
-                ret = read_super_block(fd, &sb);
-                if (ret < 0) {
-                    PERROR << "Can't read '" << blk_device << "' super block";
-                    return force_check;
-                }
-                if (sb.s_magic != EXT4_SUPER_MAGIC) {
-                    LINFO << "Invalid ext4 magic:0x" << std::hex << sb.s_magic << "," << blk_device;
-                    *fs_stat |= FS_STAT_EXT4_INVALID_MAGIC;
-                    return 0;  // not a valid fs, tune2fs, fsck, and mount  will all fail.
-                }
-                *fs_stat |= FS_STAT_IS_EXT4;
-                LINFO << "superblock s_max_mnt_count:" << sb.s_max_mnt_count << "," << blk_device;
-                if (sb.s_max_mnt_count == 0xffff) {  // -1 (int16) in ext2, but uint16 in ext4
-                    *fs_stat |= FS_STAT_NEW_IMAGE_VERSION;
-                }
-                if ((sb.s_feature_incompat & EXT4_FEATURE_INCOMPAT_RECOVER) != 0 ||
-                    (sb.s_state & EXT4_VALID_FS) == 0) {
-                    LINFO << __FUNCTION__ << "(): was not clealy shutdown, state flag:"
-                          << std::hex << sb.s_state
-                          << "incompat flag:" << std::hex << sb.s_feature_incompat;
-                    force_check = 1;
-                    *fs_stat |= FS_STAT_UNCLEAN_SHUTDOWN;
-                }
-                int has_quota = (sb.s_feature_ro_compat
-                        & cpu_to_le32(EXT4_FEATURE_RO_COMPAT_QUOTA)) != 0;
-                int want_quota = fs_mgr_is_quota(rec) != 0;
-
-                if (has_quota == want_quota) {
-                    LINFO << "Requested quota status is match on " << blk_device;
-                    return force_check;
-                } else if (want_quota) {
-                    LINFO << "Enabling quota on " << blk_device;
-                    arg1 = "-Oquota";
-                    arg2 = "-Qusrquota,grpquota";
-                    force_check = 1;
-                    *fs_stat |= FS_STAT_QUOTA_ENABLED;
-                } else {
-                    LINFO << "Disabling quota on " << blk_device;
-                    arg1 = "-Q^usrquota,^grpquota";
-                    arg2 = "-O^quota";
-                }
-            } else {
-                PERROR << "Failed to open '" << blk_device << "'";
-                return force_check;
-            }
-
-            const char *tune2fs_argv[] = {
-                TUNE2FS_BIN,
-                arg1,
-                arg2,
-                blk_device,
-            };
-            ret = android_fork_execvp_ext(ARRAY_SIZE(tune2fs_argv),
-                                          const_cast<char **>(tune2fs_argv),
-                                          &status, true, LOG_KLOG | LOG_FILE,
-                                          true, NULL, NULL, 0);
-            if (ret < 0) {
-                /* No need to check for error in fork, we can't really handle it now */
-                LERROR << "Failed trying to run " << TUNE2FS_BIN;
-                *fs_stat |= FS_STAT_TUNE2FS_FAILED;
-            }
-        }
-    }
-    return force_check;
+static bool is_ext4_superblock_valid(const struct ext4_super_block* es) {
+    if (es->s_magic != EXT4_SUPER_MAGIC) return false;
+    if (es->s_rev_level != EXT4_DYNAMIC_REV && es->s_rev_level != EXT4_GOOD_OLD_REV) return false;
+    if (EXT4_INODES_PER_GROUP(es) == 0) return false;
+    return true;
 }
 
-static void do_reserved_size(char *blk_device, char *fs_type, struct fstab_rec *rec, int *fs_stat)
-{
-    /* Check for the types of filesystems we know how to check */
-    if (!strcmp(fs_type, "ext2") || !strcmp(fs_type, "ext3") || !strcmp(fs_type, "ext4")) {
-        /*
-         * Some system images do not have tune2fs for licensing reasons
-         * Detect these and skip reserve blocks.
-         */
-        if (access(TUNE2FS_BIN, X_OK)) {
-            LERROR << "Not running " << TUNE2FS_BIN << " on "
-                   << blk_device << " (executable not in system image)";
+// Read the primary superblock from an ext4 filesystem.  On failure return
+// false.  If it's not an ext4 filesystem, also set FS_STAT_EXT4_INVALID_MAGIC.
+static bool read_ext4_superblock(const char* blk_device, struct ext4_super_block* sb, int* fs_stat) {
+    android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(blk_device, O_RDONLY | O_CLOEXEC)));
+
+    if (fd < 0) {
+        PERROR << "Failed to open '" << blk_device << "'";
+        return false;
+    }
+
+    if (pread(fd, sb, sizeof(*sb), 1024) != sizeof(*sb)) {
+        PERROR << "Can't read '" << blk_device << "' superblock";
+        return false;
+    }
+
+    if (!is_ext4_superblock_valid(sb)) {
+        LINFO << "Invalid ext4 superblock on '" << blk_device << "'";
+        // not a valid fs, tune2fs, fsck, and mount  will all fail.
+        *fs_stat |= FS_STAT_EXT4_INVALID_MAGIC;
+        return false;
+    }
+    *fs_stat |= FS_STAT_IS_EXT4;
+    LINFO << "superblock s_max_mnt_count:" << sb->s_max_mnt_count << "," << blk_device;
+    if (sb->s_max_mnt_count == 0xffff) {  // -1 (int16) in ext2, but uint16 in ext4
+        *fs_stat |= FS_STAT_NEW_IMAGE_VERSION;
+    }
+    return true;
+}
+
+// Some system images do not have tune2fs for licensing reasons.
+// Detect these and skip running it.
+static bool tune2fs_available(void) {
+    return access(TUNE2FS_BIN, X_OK) == 0;
+}
+
+static bool run_tune2fs(const char* argv[], int argc) {
+    int ret;
+
+    ret = android_fork_execvp_ext(argc, const_cast<char**>(argv), nullptr, true,
+                                  LOG_KLOG | LOG_FILE, true, nullptr, nullptr, 0);
+    return ret == 0;
+}
+
+// Enable/disable quota support on the filesystem if needed.
+static void tune_quota(const char* blk_device, const struct fstab_rec* rec,
+                       const struct ext4_super_block* sb, int* fs_stat) {
+    bool has_quota = (sb->s_feature_ro_compat & cpu_to_le32(EXT4_FEATURE_RO_COMPAT_QUOTA)) != 0;
+    bool want_quota = fs_mgr_is_quota(rec) != 0;
+
+    if (has_quota == want_quota) {
+        return;
+    }
+
+    if (!tune2fs_available()) {
+        LERROR << "Unable to " << (want_quota ? "enable" : "disable") << " quotas on " << blk_device
+               << " because " TUNE2FS_BIN " is missing";
+        return;
+    }
+
+    const char* argv[] = {TUNE2FS_BIN, nullptr, nullptr, blk_device};
+
+    if (want_quota) {
+        LINFO << "Enabling quotas on " << blk_device;
+        argv[1] = "-Oquota";
+        argv[2] = "-Qusrquota,grpquota";
+        *fs_stat |= FS_STAT_QUOTA_ENABLED;
+    } else {
+        LINFO << "Disabling quotas on " << blk_device;
+        argv[1] = "-O^quota";
+        argv[2] = "-Q^usrquota,^grpquota";
+    }
+
+    if (!run_tune2fs(argv, ARRAY_SIZE(argv))) {
+        LERROR << "Failed to run " TUNE2FS_BIN " to " << (want_quota ? "enable" : "disable")
+               << " quotas on " << blk_device;
+        *fs_stat |= FS_STAT_TOGGLE_QUOTAS_FAILED;
+    }
+}
+
+// Set the number of reserved filesystem blocks if needed.
+static void tune_reserved_size(const char* blk_device, const struct fstab_rec* rec,
+                               const struct ext4_super_block* sb, int* fs_stat) {
+    if (!(rec->fs_mgr_flags & MF_RESERVEDSIZE)) {
+        return;
+    }
+
+    // The size to reserve is given in the fstab, but we won't reserve more
+    // than 2% of the filesystem.
+    const uint64_t max_reserved_blocks = ext4_blocks_count(sb) * 0.02;
+    uint64_t reserved_blocks = rec->reserved_size / EXT4_BLOCK_SIZE(sb);
+
+    if (reserved_blocks > max_reserved_blocks) {
+        LWARNING << "Reserved blocks " << reserved_blocks << " is too large; "
+                 << "capping to " << max_reserved_blocks;
+        reserved_blocks = max_reserved_blocks;
+    }
+
+    if ((ext4_r_blocks_count(sb) == reserved_blocks) && (sb->s_def_resgid == AID_RESERVED_DISK)) {
+        return;
+    }
+
+    if (!tune2fs_available()) {
+        LERROR << "Unable to set the number of reserved blocks on " << blk_device
+               << " because " TUNE2FS_BIN " is missing";
+        return;
+    }
+
+    LINFO << "Setting reserved block count on " << blk_device << " to " << reserved_blocks;
+
+    auto reserved_blocks_str = std::to_string(reserved_blocks);
+    auto reserved_gid_str = std::to_string(AID_RESERVED_DISK);
+    const char* argv[] = {
+        TUNE2FS_BIN, "-r", reserved_blocks_str.c_str(), "-g", reserved_gid_str.c_str(), blk_device};
+    if (!run_tune2fs(argv, ARRAY_SIZE(argv))) {
+        LERROR << "Failed to run " TUNE2FS_BIN " to set the number of reserved blocks on "
+               << blk_device;
+        *fs_stat |= FS_STAT_SET_RESERVED_BLOCKS_FAILED;
+    }
+}
+
+// Enable file-based encryption if needed.
+static void tune_encrypt(const char* blk_device, const struct fstab_rec* rec,
+                         const struct ext4_super_block* sb, int* fs_stat) {
+    bool has_encrypt = (sb->s_feature_incompat & cpu_to_le32(EXT4_FEATURE_INCOMPAT_ENCRYPT)) != 0;
+    bool want_encrypt = fs_mgr_is_file_encrypted(rec) != 0;
+
+    if (has_encrypt || !want_encrypt) {
+        return;
+    }
+
+    if (!tune2fs_available()) {
+        LERROR << "Unable to enable ext4 encryption on " << blk_device
+               << " because " TUNE2FS_BIN " is missing";
+        return;
+    }
+
+    const char* argv[] = {TUNE2FS_BIN, "-Oencrypt", blk_device};
+
+    LINFO << "Enabling ext4 encryption on " << blk_device;
+    if (!run_tune2fs(argv, ARRAY_SIZE(argv))) {
+        LERROR << "Failed to run " TUNE2FS_BIN " to enable "
+               << "ext4 encryption on " << blk_device;
+        *fs_stat |= FS_STAT_ENABLE_ENCRYPTION_FAILED;
+    }
+}
+
+//
+// Prepare the filesystem on the given block device to be mounted.
+//
+// If the "check" option was given in the fstab record, or it seems that the
+// filesystem was uncleanly shut down, we'll run fsck on the filesystem.
+//
+// If needed, we'll also enable (or disable) filesystem features as specified by
+// the fstab record.
+//
+static int prepare_fs_for_mount(const char* blk_device, const struct fstab_rec* rec) {
+    int fs_stat = 0;
+
+    if (is_extfs(rec->fs_type)) {
+        struct ext4_super_block sb;
+
+        if (read_ext4_superblock(blk_device, &sb, &fs_stat)) {
+            if ((sb.s_feature_incompat & EXT4_FEATURE_INCOMPAT_RECOVER) != 0 ||
+                (sb.s_state & EXT4_VALID_FS) == 0) {
+                LINFO << "Filesystem on " << blk_device << " was not cleanly shutdown; "
+                      << "state flags: 0x" << std::hex << sb.s_state << ", "
+                      << "incompat feature flags: 0x" << std::hex << sb.s_feature_incompat;
+                fs_stat |= FS_STAT_UNCLEAN_SHUTDOWN;
+            }
+
+            // Note: quotas should be enabled before running fsck.
+            tune_quota(blk_device, rec, &sb, &fs_stat);
         } else {
-            LINFO << "Running " << TUNE2FS_BIN << " on " << blk_device;
-
-            int status = 0;
-            int ret = 0;
-            unsigned long reserved_blocks = 0;
-            android::base::unique_fd fd(
-                TEMP_FAILURE_RETRY(open(blk_device, O_RDONLY | O_CLOEXEC)));
-            if (fd >= 0) {
-                struct ext4_super_block sb;
-                ret = read_super_block(fd, &sb);
-                if (ret < 0) {
-                    PERROR << "Can't read '" << blk_device << "' super block";
-                    return;
-                }
-                reserved_blocks = rec->reserved_size / EXT4_BLOCK_SIZE(&sb);
-                unsigned long reserved_threshold = ext4_blocks_count(&sb) * 0.02;
-                if (reserved_threshold < reserved_blocks) {
-                    LWARNING << "Reserved blocks " << reserved_blocks
-                             << " is too large";
-                    reserved_blocks = reserved_threshold;
-                }
-
-                if (ext4_r_blocks_count(&sb) == reserved_blocks) {
-                    LINFO << "Have reserved same blocks";
-                    return;
-                }
-            } else {
-                PERROR << "Failed to open '" << blk_device << "'";
-                return;
-            }
-
-            char buf[16] = {0};
-            snprintf(buf, sizeof (buf), "-r %lu", reserved_blocks);
-            const char *tune2fs_argv[] = {
-                TUNE2FS_BIN,
-                buf,
-                blk_device,
-            };
-
-            ret = android_fork_execvp_ext(ARRAY_SIZE(tune2fs_argv),
-                                          const_cast<char **>(tune2fs_argv),
-                                          &status, true, LOG_KLOG | LOG_FILE,
-                                          true, NULL, NULL, 0);
-
-            if (ret < 0) {
-                /* No need to check for error in fork, we can't really handle it now */
-                LERROR << "Failed trying to run " << TUNE2FS_BIN;
-                *fs_stat |= FS_STAT_TUNE2FS_FAILED;
-            }
+            return fs_stat;
         }
     }
+
+    if ((rec->fs_mgr_flags & MF_CHECK) ||
+        (fs_stat & (FS_STAT_UNCLEAN_SHUTDOWN | FS_STAT_QUOTA_ENABLED))) {
+        check_fs(blk_device, rec->fs_type, rec->mount_point, &fs_stat);
+    }
+
+    if (is_extfs(rec->fs_type) && (rec->fs_mgr_flags & (MF_RESERVEDSIZE | MF_FILEENCRYPTION))) {
+        struct ext4_super_block sb;
+
+        if (read_ext4_superblock(blk_device, &sb, &fs_stat)) {
+            tune_reserved_size(blk_device, rec, &sb, &fs_stat);
+            tune_encrypt(blk_device, rec, &sb, &fs_stat);
+        }
+    }
+
+    return fs_stat;
 }
 
 static void remove_trailing_slashes(char *n)
@@ -507,15 +551,6 @@
     return ret;
 }
 
-static int device_is_force_encrypted() {
-    int ret = -1;
-    char value[PROP_VALUE_MAX];
-    ret = __system_property_get("ro.vold.forceencryption", value);
-    if (ret < 0)
-        return 0;
-    return strcmp(value, "1") ? 0 : 1;
-}
-
 /*
  * Tries to mount any of the consecutive fstab entries that match
  * the mountpoint of the one given by fstab->recs[start_idx].
@@ -559,10 +594,7 @@
                 continue;
             }
 
-            int fs_stat = 0;
-            int force_check = do_quota_with_shutdown_check(fstab->recs[i].blk_device,
-                                                           fstab->recs[i].fs_type,
-                                                           &fstab->recs[i], &fs_stat);
+            int fs_stat = prepare_fs_for_mount(fstab->recs[i].blk_device, &fstab->recs[i]);
             if (fs_stat & FS_STAT_EXT4_INVALID_MAGIC) {
                 LERROR << __FUNCTION__ << "(): skipping mount, invalid ext4, mountpoint="
                        << fstab->recs[i].mount_point << " rec[" << i
@@ -570,15 +602,6 @@
                 mount_errno = EINVAL;  // continue bootup for FDE
                 continue;
             }
-            if ((fstab->recs[i].fs_mgr_flags & MF_CHECK) || force_check) {
-                check_fs(fstab->recs[i].blk_device, fstab->recs[i].fs_type,
-                         fstab->recs[i].mount_point, &fs_stat);
-            }
-
-            if (fstab->recs[i].fs_mgr_flags & MF_RESERVEDSIZE) {
-                do_reserved_size(fstab->recs[i].blk_device, fstab->recs[i].fs_type,
-                                 &fstab->recs[i], &fs_stat);
-            }
 
             int retry_count = 2;
             while (retry_count-- > 0) {
@@ -702,7 +725,9 @@
 
 static bool needs_block_encryption(const struct fstab_rec* rec)
 {
-    if (device_is_force_encrypted() && fs_mgr_is_encryptable(rec)) return true;
+    if (android::base::GetBoolProperty("ro.vold.forceencryption", false) &&
+        fs_mgr_is_encryptable(rec))
+        return true;
     if (rec->fs_mgr_flags & MF_FORCECRYPT) return true;
     if (rec->fs_mgr_flags & MF_CRYPT) {
         /* Check for existence of convert_fde breadcrumb file */
@@ -721,6 +746,12 @@
     return false;
 }
 
+static bool should_use_metadata_encryption(const struct fstab_rec* rec) {
+    if (!(rec->fs_mgr_flags & (MF_FILEENCRYPTION | MF_FORCEFDEORFBE))) return false;
+    if (!(rec->fs_mgr_flags & MF_KEYDIRECTORY)) return false;
+    return true;
+}
+
 // Check to see if a mountable volume has encryption requirements
 static int handle_encryptable(const struct fstab_rec* rec)
 {
@@ -733,8 +764,14 @@
                      << " - allow continue unencrypted";
             return FS_MGR_MNTALL_DEV_NOT_ENCRYPTED;
         }
+    } else if (should_use_metadata_encryption(rec)) {
+        if (umount(rec->mount_point) == 0) {
+            return FS_MGR_MNTALL_DEV_NEEDS_METADATA_ENCRYPTION;
+        } else {
+            PERROR << "Could not umount " << rec->mount_point << " - fail since can't encrypt";
+            return FS_MGR_MNTALL_FAIL;
+        }
     } else if (rec->fs_mgr_flags & (MF_FILEENCRYPTION | MF_FORCEFDEORFBE)) {
-        // Deal with file level encryption
         LINFO << rec->mount_point << " is file encrypted";
         return FS_MGR_MNTALL_DEV_FILE_ENCRYPTED;
     } else if (fs_mgr_is_encryptable(rec)) {
@@ -744,21 +781,38 @@
     }
 }
 
-bool is_device_secure() {
-    int ret = -1;
-    char value[PROP_VALUE_MAX];
-    ret = __system_property_get("ro.secure", value);
-    if (ret == 0) {
-#ifdef ALLOW_SKIP_SECURE_CHECK
-        // Allow eng builds to skip this check if the property
-        // is not readable (happens during early mount)
-        return false;
-#else
-        // If error and not an 'eng' build, we want to fail secure.
-        return true;
-#endif
+static bool call_vdc(const std::vector<std::string>& args) {
+    std::vector<char const*> argv;
+    argv.emplace_back("/system/bin/vdc");
+    for (auto& arg : args) {
+        argv.emplace_back(arg.c_str());
     }
-    return strcmp(value, "0") ? true : false;
+    LOG(INFO) << "Calling: " << android::base::Join(argv, ' ');
+    int ret = android_fork_execvp(4, const_cast<char**>(argv.data()), nullptr, false, true);
+    if (ret != 0) {
+        LOG(ERROR) << "vdc returned error code: " << ret;
+        return false;
+    }
+    LOG(DEBUG) << "vdc finished successfully";
+    return true;
+}
+
+bool fs_mgr_update_logical_partition(struct fstab_rec* rec) {
+    // Logical partitions are specified with a named partition rather than a
+    // block device, so if the block device is a path, then it has already
+    // been updated.
+    if (rec->blk_device[0] == '/') {
+        return true;
+    }
+
+    DeviceMapper& dm = DeviceMapper::Instance();
+    std::string device_name;
+    if (!dm.GetDmDevicePathByName(rec->blk_device, &device_name)) {
+        return false;
+    }
+    free(rec->blk_device);
+    rec->blk_device = strdup(device_name.c_str());
+    return true;
 }
 
 /* When multiple fstab records share the same mount_point, it will
@@ -804,9 +858,7 @@
         }
 
         /* Translate LABEL= file system labels into block devices */
-        if (!strcmp(fstab->recs[i].fs_type, "ext2") ||
-            !strcmp(fstab->recs[i].fs_type, "ext3") ||
-            !strcmp(fstab->recs[i].fs_type, "ext4")) {
+        if (is_extfs(fstab->recs[i].fs_type)) {
             int tret = translate_ext_labels(&fstab->recs[i]);
             if (tret < 0) {
                 LERROR << "Could not translate label to block device";
@@ -814,6 +866,13 @@
             }
         }
 
+        if ((fstab->recs[i].fs_mgr_flags & MF_LOGICAL)) {
+            if (!fs_mgr_update_logical_partition(&fstab->recs[i])) {
+                LERROR << "Could not set up logical partition, skipping!";
+                continue;
+            }
+        }
+
         if (fstab->recs[i].fs_mgr_flags & MF_WAIT &&
             !fs_mgr_wait_for_file(fstab->recs[i].blk_device, 20s)) {
             LERROR << "Skipping '" << fstab->recs[i].blk_device << "' during mount_all";
@@ -835,7 +894,7 @@
                 /* Skips mounting the device. */
                 continue;
             }
-        } else if ((fstab->recs[i].fs_mgr_flags & MF_VERIFY) && is_device_secure()) {
+        } else if ((fstab->recs[i].fs_mgr_flags & MF_VERIFY)) {
             int rc = fs_mgr_setup_verity(&fstab->recs[i], true);
             if (__android_log_is_debuggable() &&
                     (rc == FS_MGR_SETUP_VERITY_DISABLED ||
@@ -869,13 +928,19 @@
                     LERROR << "Only one encryptable/encrypted partition supported";
                 }
                 encryptable = status;
+                if (status == FS_MGR_MNTALL_DEV_NEEDS_METADATA_ENCRYPTION) {
+                    if (!call_vdc(
+                            {"cryptfs", "encryptFstab", fstab->recs[attempted_idx].mount_point})) {
+                        LERROR << "Encryption failed";
+                        return FS_MGR_MNTALL_FAIL;
+                    }
+                }
             }
 
             /* Success!  Go get the next one */
             continue;
         }
 
-        /* mount(2) returned an error, handle the encryptable/formattable case */
         bool wiped = partition_wiped(fstab->recs[top_idx].blk_device);
         bool crypt_footer = false;
         if (mret && mount_errno != EBUSY && mount_errno != EACCES &&
@@ -915,6 +980,8 @@
                 continue;
             }
         }
+
+        /* mount(2) returned an error, handle the encryptable/formattable case */
         if (mret && mount_errno != EBUSY && mount_errno != EACCES &&
             fs_mgr_is_encryptable(&fstab->recs[attempted_idx])) {
             if (wiped) {
@@ -940,6 +1007,13 @@
                 }
             }
             encryptable = FS_MGR_MNTALL_DEV_MIGHT_BE_ENCRYPTED;
+        } else if (mret && mount_errno != EBUSY && mount_errno != EACCES &&
+                   should_use_metadata_encryption(&fstab->recs[attempted_idx])) {
+            if (!call_vdc({"cryptfs", "mountFstab", fstab->recs[attempted_idx].mount_point})) {
+                ++error_count;
+            }
+            encryptable = FS_MGR_MNTALL_DEV_IS_METADATA_ENCRYPTED;
+            continue;
         } else {
             // fs_options might be null so we cannot use PERROR << directly.
             // Use StringPrintf to output "(null)" instead.
@@ -978,6 +1052,9 @@
         return FS_MGR_DOMNT_FAILED;
     }
 
+    // Run fsck if needed
+    prepare_fs_for_mount(rec->blk_device, rec);
+
     int ret = __mount(rec->blk_device, rec->mount_point, rec);
     if (ret) {
       ret = (errno == EBUSY) ? FS_MGR_DOMNT_BUSY : FS_MGR_DOMNT_FAILED;
@@ -1019,24 +1096,20 @@
             return FS_MGR_DOMNT_FAILED;
         }
 
+        if ((fstab->recs[i].fs_mgr_flags & MF_LOGICAL)) {
+            if (!fs_mgr_update_logical_partition(&fstab->recs[i])) {
+                LERROR << "Could not set up logical partition, skipping!";
+                continue;
+            }
+        }
+
         /* First check the filesystem if requested */
         if (fstab->recs[i].fs_mgr_flags & MF_WAIT && !fs_mgr_wait_for_file(n_blk_device, 20s)) {
             LERROR << "Skipping mounting '" << n_blk_device << "'";
             continue;
         }
 
-        int fs_stat = 0;
-        int force_check = do_quota_with_shutdown_check(n_blk_device, fstab->recs[i].fs_type,
-                                                       &fstab->recs[i], &fs_stat);
-
-        if ((fstab->recs[i].fs_mgr_flags & MF_CHECK) || force_check) {
-            check_fs(n_blk_device, fstab->recs[i].fs_type,
-                     fstab->recs[i].mount_point, &fs_stat);
-        }
-
-        if (fstab->recs[i].fs_mgr_flags & MF_RESERVEDSIZE) {
-            do_reserved_size(n_blk_device, fstab->recs[i].fs_type, &fstab->recs[i], &fs_stat);
-        }
+        int fs_stat = prepare_fs_for_mount(n_blk_device, &fstab->recs[i]);
 
         if (fstab->recs[i].fs_mgr_flags & MF_AVB) {
             if (!avb_handle) {
@@ -1053,7 +1126,7 @@
                 /* Skips mounting the device. */
                 continue;
             }
-        } else if ((fstab->recs[i].fs_mgr_flags & MF_VERIFY) && is_device_secure()) {
+        } else if ((fstab->recs[i].fs_mgr_flags & MF_VERIFY)) {
             int rc = fs_mgr_setup_verity(&fstab->recs[i], true);
             if (__android_log_is_debuggable() &&
                     (rc == FS_MGR_SETUP_VERITY_DISABLED ||
@@ -1118,27 +1191,6 @@
     return 0;
 }
 
-int fs_mgr_unmount_all(struct fstab *fstab)
-{
-    int i = 0;
-    int ret = 0;
-
-    if (!fstab) {
-        return -1;
-    }
-
-    while (fstab->recs[i].blk_device) {
-        if (umount(fstab->recs[i].mount_point)) {
-            LERROR << "Cannot unmount filesystem at "
-                   << fstab->recs[i].mount_point;
-            ret = -1;
-        }
-        i++;
-    }
-
-    return ret;
-}
-
 /* This must be called after mount_all, because the mkswap command needs to be
  * available.
  */
@@ -1233,48 +1285,46 @@
     return ret;
 }
 
-/*
- * key_loc must be at least PROPERTY_VALUE_MAX bytes long
- *
- * real_blk_device must be at least PROPERTY_VALUE_MAX bytes long
- */
-int fs_mgr_get_crypt_info(struct fstab *fstab, char *key_loc, char *real_blk_device, int size)
-{
-    int i = 0;
+struct fstab_rec const* fs_mgr_get_crypt_entry(struct fstab const* fstab) {
+    int i;
 
     if (!fstab) {
-        return -1;
-    }
-    /* Initialize return values to null strings */
-    if (key_loc) {
-        *key_loc = '\0';
-    }
-    if (real_blk_device) {
-        *real_blk_device = '\0';
+        return NULL;
     }
 
     /* Look for the encryptable partition to find the data */
     for (i = 0; i < fstab->num_entries; i++) {
         /* Don't deal with vold managed enryptable partitions here */
-        if (fstab->recs[i].fs_mgr_flags & MF_VOLDMANAGED) {
-            continue;
+        if (!(fstab->recs[i].fs_mgr_flags & MF_VOLDMANAGED) &&
+            (fstab->recs[i].fs_mgr_flags &
+             (MF_CRYPT | MF_FORCECRYPT | MF_FORCEFDEORFBE | MF_FILEENCRYPTION))) {
+            return &fstab->recs[i];
         }
-        if (!(fstab->recs[i].fs_mgr_flags
-              & (MF_CRYPT | MF_FORCECRYPT | MF_FORCEFDEORFBE))) {
-            continue;
-        }
-
-        /* We found a match */
-        if (key_loc) {
-            strlcpy(key_loc, fstab->recs[i].key_loc, size);
-        }
-        if (real_blk_device) {
-            strlcpy(real_blk_device, fstab->recs[i].blk_device, size);
-        }
-        break;
     }
+    return NULL;
+}
 
-    return 0;
+/*
+ * key_loc must be at least PROPERTY_VALUE_MAX bytes long
+ *
+ * real_blk_device must be at least PROPERTY_VALUE_MAX bytes long
+ */
+void fs_mgr_get_crypt_info(struct fstab* fstab, char* key_loc, char* real_blk_device, size_t size) {
+    struct fstab_rec const* rec = fs_mgr_get_crypt_entry(fstab);
+    if (key_loc) {
+        if (rec) {
+            strlcpy(key_loc, rec->key_loc, size);
+        } else {
+            *key_loc = '\0';
+        }
+    }
+    if (real_blk_device) {
+        if (rec) {
+            strlcpy(real_blk_device, rec->blk_device, size);
+        } else {
+            *real_blk_device = '\0';
+        }
+    }
 }
 
 bool fs_mgr_load_verity_state(int* mode) {
@@ -1310,7 +1360,7 @@
     return true;
 }
 
-bool fs_mgr_update_verity_state(fs_mgr_verity_state_callback callback) {
+bool fs_mgr_update_verity_state(std::function<fs_mgr_verity_state_callback> callback) {
     if (!callback) {
         return false;
     }
@@ -1320,12 +1370,6 @@
         return false;
     }
 
-    android::base::unique_fd fd(TEMP_FAILURE_RETRY(open("/dev/device-mapper", O_RDWR | O_CLOEXEC)));
-    if (fd == -1) {
-        PERROR << "Error opening device mapper";
-        return false;
-    }
-
     std::unique_ptr<fstab, decltype(&fs_mgr_free_fstab)> fstab(fs_mgr_read_fstab_default(),
                                                                fs_mgr_free_fstab);
     if (!fstab) {
@@ -1333,8 +1377,8 @@
         return false;
     }
 
-    alignas(dm_ioctl) char buffer[DM_BUF_SIZE];
-    struct dm_ioctl* io = (struct dm_ioctl*)buffer;
+    DeviceMapper& dm = DeviceMapper::Instance();
+
     bool system_root = android::base::GetProperty("ro.build.system_root_image", "") == "true";
 
     for (int i = 0; i < fstab->num_entries; i++) {
@@ -1350,20 +1394,20 @@
             mount_point = basename(fstab->recs[i].mount_point);
         }
 
-        fs_mgr_verity_ioctl_init(io, mount_point, 0);
+        const char* status = nullptr;
 
-        const char* status;
-        if (ioctl(fd, DM_TABLE_STATUS, io)) {
+        std::vector<DeviceMapper::TargetInfo> table;
+        if (!dm.GetTableStatus(mount_point, &table) || table.empty() || table[0].data.empty()) {
             if (fstab->recs[i].fs_mgr_flags & MF_VERIFYATBOOT) {
                 status = "V";
             } else {
                 PERROR << "Failed to query DM_TABLE_STATUS for " << mount_point.c_str();
                 continue;
             }
+        } else {
+            status = table[0].data.c_str();
         }
 
-        status = &buffer[io->data_start + sizeof(struct dm_target_spec)];
-
         // To be consistent in vboot 1.0 and vboot 2.0 (AVB), change the mount_point
         // back to 'system' for the callback. So it has property [partition.system.verified]
         // instead of [partition.vroot.verified].
diff --git a/fs_mgr/fs_mgr_avb.cpp b/fs_mgr/fs_mgr_avb.cpp
index 7824cfa..7c6093e 100644
--- a/fs_mgr/fs_mgr_avb.cpp
+++ b/fs_mgr/fs_mgr_avb.cpp
@@ -33,11 +33,11 @@
 #include <android-base/strings.h>
 #include <android-base/unique_fd.h>
 #include <libavb/libavb.h>
+#include <libdm/dm.h>
 
 #include "fs_mgr.h"
 #include "fs_mgr_priv.h"
 #include "fs_mgr_priv_avb_ops.h"
-#include "fs_mgr_priv_dm_ioctl.h"
 #include "fs_mgr_priv_sha.h"
 
 static inline bool nibble_value(const char& c, uint8_t* value) {
@@ -139,38 +139,23 @@
 };
 
 std::unique_ptr<FsManagerAvbVerifier> FsManagerAvbVerifier::Create() {
-    std::string cmdline;
-    if (!android::base::ReadFileToString("/proc/cmdline", &cmdline)) {
-        PERROR << "Failed to read /proc/cmdline";
-        return nullptr;
-    }
-
     std::unique_ptr<FsManagerAvbVerifier> avb_verifier(new FsManagerAvbVerifier());
     if (!avb_verifier) {
         LERROR << "Failed to create unique_ptr<FsManagerAvbVerifier>";
         return nullptr;
     }
 
-    std::string digest;
-    std::string hash_alg;
-    for (const auto& entry : android::base::Split(android::base::Trim(cmdline), " ")) {
-        std::vector<std::string> pieces = android::base::Split(entry, "=");
-        const std::string& key = pieces[0];
-        const std::string& value = pieces[1];
-
-        if (key == "androidboot.vbmeta.hash_alg") {
-            hash_alg = value;
-        } else if (key == "androidboot.vbmeta.size") {
-            if (!android::base::ParseUint(value.c_str(), &avb_verifier->vbmeta_size_)) {
-                return nullptr;
-            }
-        } else if (key == "androidboot.vbmeta.digest") {
-            digest = value;
-        }
+    std::string value;
+    if (!fs_mgr_get_boot_config("vbmeta.size", &value) ||
+        !android::base::ParseUint(value.c_str(), &avb_verifier->vbmeta_size_)) {
+        LERROR << "Invalid hash size: " << value.c_str();
+        return nullptr;
     }
 
     // Reads hash algorithm.
     size_t expected_digest_size = 0;
+    std::string hash_alg;
+    fs_mgr_get_boot_config("vbmeta.hash_alg", &hash_alg);
     if (hash_alg == "sha256") {
         expected_digest_size = SHA256_DIGEST_LENGTH * 2;
         avb_verifier->hash_alg_ = kSHA256;
@@ -183,6 +168,8 @@
     }
 
     // Reads digest.
+    std::string digest;
+    fs_mgr_get_boot_config("vbmeta.digest", &digest);
     if (digest.size() != expected_digest_size) {
         LERROR << "Unexpected digest size: " << digest.size()
                << " (expected: " << expected_digest_size << ")";
@@ -231,9 +218,9 @@
 // Constructs dm-verity arguments for sending DM_TABLE_LOAD ioctl to kernel.
 // See the following link for more details:
 // https://gitlab.com/cryptsetup/cryptsetup/wikis/DMVerity
-static std::string construct_verity_table(const AvbHashtreeDescriptor& hashtree_desc,
-                                          const std::string& salt, const std::string& root_digest,
-                                          const std::string& blk_device) {
+static bool construct_verity_table(const AvbHashtreeDescriptor& hashtree_desc,
+                                   const std::string& salt, const std::string& root_digest,
+                                   const std::string& blk_device, android::dm::DmTable* table) {
     // Loads androidboot.veritymode from kernel cmdline.
     std::string verity_mode;
     if (!fs_mgr_get_boot_config("veritymode", &verity_mode)) {
@@ -248,144 +235,56 @@
         dm_verity_mode = "ignore_corruption";
     } else if (verity_mode != "eio") {  // Default dm_verity_mode is eio.
         LERROR << "Unknown androidboot.veritymode: " << verity_mode;
-        return "";
+        return false;
     }
 
-    // dm-verity construction parameters:
-    //   <version> <dev> <hash_dev>
-    //   <data_block_size> <hash_block_size>
-    //   <num_data_blocks> <hash_start_block>
-    //   <algorithm> <digest> <salt>
-    //   [<#opt_params> <opt_params>]
-    std::ostringstream verity_table;
-    verity_table << hashtree_desc.dm_verity_version << " " << blk_device << " " << blk_device << " "
-                 << hashtree_desc.data_block_size << " " << hashtree_desc.hash_block_size << " "
-                 << hashtree_desc.image_size / hashtree_desc.data_block_size << " "
-                 << hashtree_desc.tree_offset / hashtree_desc.hash_block_size << " "
-                 << hashtree_desc.hash_algorithm << " " << root_digest << " " << salt;
+    std::ostringstream hash_algorithm;
+    hash_algorithm << hashtree_desc.hash_algorithm;
 
-    // Continued from the above optional parameters:
-    //   [<#opt_params> <opt_params>]
-    int optional_argc = 0;
-    std::ostringstream optional_args;
-
-    // dm-verity optional parameters for FEC (forward error correction):
-    //   use_fec_from_device <fec_dev>
-    //   fec_roots <num>
-    //   fec_blocks <num>
-    //   fec_start <offset>
+    android::dm::DmTargetVerity target(0, hashtree_desc.image_size / 512,
+                                       hashtree_desc.dm_verity_version, blk_device, blk_device,
+                                       hashtree_desc.data_block_size, hashtree_desc.hash_block_size,
+                                       hashtree_desc.image_size / hashtree_desc.data_block_size,
+                                       hashtree_desc.tree_offset / hashtree_desc.hash_block_size,
+                                       hash_algorithm.str(), root_digest, salt);
     if (hashtree_desc.fec_size > 0) {
-        // Note that fec_blocks is the size that FEC covers, *NOT* the
-        // size of the FEC data. Since we use FEC for everything up until
-        // the FEC data, it's the same as the offset (fec_start).
-        optional_argc += 8;
-        // clang-format off
-        optional_args << "use_fec_from_device " << blk_device
-                      << " fec_roots " << hashtree_desc.fec_num_roots
-                      << " fec_blocks " << hashtree_desc.fec_offset / hashtree_desc.data_block_size
-                      << " fec_start " << hashtree_desc.fec_offset / hashtree_desc.data_block_size
-                      << " ";
-        // clang-format on
+        target.UseFec(blk_device, hashtree_desc.fec_num_roots,
+                      hashtree_desc.fec_offset / hashtree_desc.data_block_size,
+                      hashtree_desc.fec_offset / hashtree_desc.data_block_size);
     }
-
     if (!dm_verity_mode.empty()) {
-        optional_argc += 1;
-        optional_args << dm_verity_mode << " ";
+        target.SetVerityMode(dm_verity_mode);
     }
-
     // Always use ignore_zero_blocks.
-    optional_argc += 1;
-    optional_args << "ignore_zero_blocks";
+    target.IgnoreZeroBlocks();
 
-    verity_table << " " << optional_argc << " " << optional_args.str();
-    return verity_table.str();
-}
+    LINFO << "Built verity table: '" << target.GetParameterString() << "'";
 
-static bool load_verity_table(struct dm_ioctl* io, const std::string& dm_device_name, int fd,
-                              uint64_t image_size, const std::string& verity_table) {
-    fs_mgr_verity_ioctl_init(io, dm_device_name, DM_STATUS_TABLE_FLAG);
-
-    // The buffer consists of [dm_ioctl][dm_target_spec][verity_params].
-    char* buffer = (char*)io;
-
-    // Builds the dm_target_spec arguments.
-    struct dm_target_spec* dm_target = (struct dm_target_spec*)&buffer[sizeof(struct dm_ioctl)];
-    io->target_count = 1;
-    dm_target->status = 0;
-    dm_target->sector_start = 0;
-    dm_target->length = image_size / 512;
-    strcpy(dm_target->target_type, "verity");
-
-    // Builds the verity params.
-    char* verity_params = buffer + sizeof(struct dm_ioctl) + sizeof(struct dm_target_spec);
-    size_t bufsize = DM_BUF_SIZE - (verity_params - buffer);
-
-    LINFO << "Loading verity table: '" << verity_table << "'";
-
-    // Copies verity_table to verity_params (including the terminating null byte).
-    if (verity_table.size() > bufsize - 1) {
-        LERROR << "Verity table size too large: " << verity_table.size()
-               << " (max allowable size: " << bufsize - 1 << ")";
-        return false;
-    }
-    memcpy(verity_params, verity_table.c_str(), verity_table.size() + 1);
-
-    // Sets ext target boundary.
-    verity_params += verity_table.size() + 1;
-    verity_params = (char*)(((unsigned long)verity_params + 7) & ~7);
-    dm_target->next = verity_params - buffer;
-
-    // Sends the ioctl to load the verity table.
-    if (ioctl(fd, DM_TABLE_LOAD, io)) {
-        PERROR << "Error loading verity table";
-        return false;
-    }
-
-    return true;
+    return table->AddTarget(std::make_unique<android::dm::DmTargetVerity>(target));
 }
 
 static bool hashtree_dm_verity_setup(struct fstab_rec* fstab_entry,
                                      const AvbHashtreeDescriptor& hashtree_desc,
                                      const std::string& salt, const std::string& root_digest,
                                      bool wait_for_verity_dev) {
-    // Gets the device mapper fd.
-    android::base::unique_fd fd(open("/dev/device-mapper", O_RDWR));
-    if (fd < 0) {
-        PERROR << "Error opening device mapper";
+    android::dm::DmTable table;
+    if (!construct_verity_table(hashtree_desc, salt, root_digest, fstab_entry->blk_device, &table) ||
+        !table.valid()) {
+        LERROR << "Failed to construct verity table.";
         return false;
     }
+    table.set_readonly(true);
 
-    // Creates the device.
-    alignas(dm_ioctl) char buffer[DM_BUF_SIZE];
-    struct dm_ioctl* io = (struct dm_ioctl*)buffer;
     const std::string mount_point(basename(fstab_entry->mount_point));
-    if (!fs_mgr_create_verity_device(io, mount_point, fd)) {
+    android::dm::DeviceMapper& dm = android::dm::DeviceMapper::Instance();
+    if (!dm.CreateDevice(mount_point, table)) {
         LERROR << "Couldn't create verity device!";
         return false;
     }
 
-    // Gets the name of the device file.
-    std::string verity_blk_name;
-    if (!fs_mgr_get_verity_device_name(io, mount_point, fd, &verity_blk_name)) {
-        LERROR << "Couldn't get verity device number!";
-        return false;
-    }
-
-    std::string verity_table =
-        construct_verity_table(hashtree_desc, salt, root_digest, fstab_entry->blk_device);
-    if (verity_table.empty()) {
-        LERROR << "Failed to construct verity table.";
-        return false;
-    }
-
-    // Loads the verity mapping table.
-    if (!load_verity_table(io, mount_point, fd, hashtree_desc.image_size, verity_table)) {
-        LERROR << "Couldn't load verity table!";
-        return false;
-    }
-
-    // Activates the device.
-    if (!fs_mgr_resume_verity_table(io, mount_point, fd)) {
+    std::string dev_path;
+    if (!dm.GetDmDevicePathByName(mount_point, &dev_path)) {
+        LERROR << "Couldn't get verity device path!";
         return false;
     }
 
@@ -394,10 +293,10 @@
 
     // Updates fstab_rec->blk_device to verity device name.
     free(fstab_entry->blk_device);
-    fstab_entry->blk_device = strdup(verity_blk_name.c_str());
+    fstab_entry->blk_device = strdup(dev_path.c_str());
 
     // Makes sure we've set everything up properly.
-    if (wait_for_verity_dev && !fs_mgr_wait_for_file(verity_blk_name, 1s)) {
+    if (wait_for_verity_dev && !fs_mgr_wait_for_file(dev_path, 1s)) {
         return false;
     }
 
@@ -584,7 +483,13 @@
 
     // Derives partition_name from blk_device to query the corresponding AVB HASHTREE descriptor
     // to setup dm-verity. The partition_names in AVB descriptors are without A/B suffix.
-    std::string partition_name(basename(fstab_entry->blk_device));
+    std::string partition_name;
+    if (fstab_entry->fs_mgr_flags & MF_LOGICAL) {
+        partition_name = fstab_entry->logical_partition_name;
+    } else {
+        partition_name = basename(fstab_entry->blk_device);
+    }
+
     if (fstab_entry->fs_mgr_flags & MF_SLOTSELECT) {
         auto ab_suffix = partition_name.rfind(fs_mgr_get_slot_suffix());
         if (ab_suffix != std::string::npos) {
diff --git a/fs_mgr/fs_mgr_boot_config.cpp b/fs_mgr/fs_mgr_boot_config.cpp
index ab5beed..733ad55 100644
--- a/fs_mgr/fs_mgr_boot_config.cpp
+++ b/fs_mgr/fs_mgr_boot_config.cpp
@@ -14,6 +14,11 @@
  * limitations under the License.
  */
 
+#include <algorithm>
+#include <iterator>
+#include <string>
+#include <vector>
+
 #include <android-base/file.h>
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
@@ -21,40 +26,90 @@
 
 #include "fs_mgr_priv.h"
 
-// Tries to get the boot config value in properties, kernel cmdline and
-// device tree (in that order).  returns 'true' if successfully found, 'false'
-// otherwise
+std::vector<std::pair<std::string, std::string>> fs_mgr_parse_boot_config(const std::string& cmdline) {
+    static constexpr char quote = '"';
+
+    std::vector<std::pair<std::string, std::string>> result;
+    size_t base = 0;
+    while (true) {
+        // skip quoted spans
+        auto found = base;
+        while (((found = cmdline.find_first_of(" \"", found)) != cmdline.npos) &&
+               (cmdline[found] == quote)) {
+            // unbalanced quote is ok
+            if ((found = cmdline.find(quote, found + 1)) == cmdline.npos) break;
+            ++found;
+        }
+        std::string piece;
+        auto source = cmdline.substr(base, found - base);
+        std::remove_copy(source.begin(), source.end(),
+                         std::back_insert_iterator<std::string>(piece), quote);
+        auto equal_sign = piece.find('=');
+        if (equal_sign == piece.npos) {
+            if (!piece.empty()) {
+                // no difference between <key> and <key>=
+                result.emplace_back(std::move(piece), "");
+            }
+        } else {
+            result.emplace_back(piece.substr(0, equal_sign), piece.substr(equal_sign + 1));
+        }
+        if (found == cmdline.npos) break;
+        base = found + 1;
+    }
+
+    return result;
+}
+
+bool fs_mgr_get_boot_config_from_kernel(const std::string& cmdline, const std::string& android_key,
+                                        std::string* out_val) {
+    FS_MGR_CHECK(out_val != nullptr);
+
+    const std::string cmdline_key("androidboot." + android_key);
+    for (const auto& [key, value] : fs_mgr_parse_boot_config(cmdline)) {
+        if (key == cmdline_key) {
+            *out_val = value;
+            return true;
+        }
+    }
+
+    *out_val = "";
+    return false;
+}
+
+// Tries to get the given boot config value from kernel cmdline.
+// Returns true if successfully found, false otherwise.
+bool fs_mgr_get_boot_config_from_kernel_cmdline(const std::string& key, std::string* out_val) {
+    std::string cmdline;
+    if (!android::base::ReadFileToString("/proc/cmdline", &cmdline)) return false;
+    return fs_mgr_get_boot_config_from_kernel(cmdline, key, out_val);
+}
+
+// Tries to get the boot config value in device tree, properties and
+// kernel cmdline (in that order).  Returns 'true' if successfully
+// found, 'false' otherwise.
 bool fs_mgr_get_boot_config(const std::string& key, std::string* out_val) {
     FS_MGR_CHECK(out_val != nullptr);
 
-    // first check if we have "ro.boot" property already
+    // firstly, check the device tree
+    if (is_dt_compatible()) {
+        std::string file_name = get_android_dt_dir() + "/" + key;
+        if (android::base::ReadFileToString(file_name, out_val)) {
+            if (!out_val->empty()) {
+                out_val->pop_back();  // Trims the trailing '\0' out.
+                return true;
+            }
+        }
+    }
+
+    // next, check if we have "ro.boot" property already
     *out_val = android::base::GetProperty("ro.boot." + key, "");
     if (!out_val->empty()) {
         return true;
     }
 
-    // fallback to kernel cmdline, properties may not be ready yet
-    std::string cmdline;
-    std::string cmdline_key("androidboot." + key);
-    if (android::base::ReadFileToString("/proc/cmdline", &cmdline)) {
-        for (const auto& entry : android::base::Split(android::base::Trim(cmdline), " ")) {
-            std::vector<std::string> pieces = android::base::Split(entry, "=");
-            if (pieces.size() == 2) {
-                if (pieces[0] == cmdline_key) {
-                    *out_val = pieces[1];
-                    return true;
-                }
-            }
-        }
-    }
-
-    // lastly, check the device tree
-    if (is_dt_compatible()) {
-        std::string file_name = kAndroidDtDir + "/" + key;
-        // DT entries terminate with '\0' but so do the properties
-        if (android::base::ReadFileToString(file_name, out_val)) {
-            return true;
-        }
+    // finally, fallback to kernel cmdline, properties may not be ready yet
+    if (fs_mgr_get_boot_config_from_kernel_cmdline(key, out_val)) {
+        return true;
     }
 
     return false;
diff --git a/fs_mgr/fs_mgr_dm_ioctl.cpp b/fs_mgr/fs_mgr_dm_ioctl.cpp
deleted file mode 100644
index 4cbd5a8..0000000
--- a/fs_mgr/fs_mgr_dm_ioctl.cpp
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * Copyright (C) 2016 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 <errno.h>
-#include <string.h>
-
-#include <android-base/logging.h>
-#include <sys/ioctl.h>
-
-#include "fs_mgr_priv.h"
-#include "fs_mgr_priv_dm_ioctl.h"
-
-void fs_mgr_verity_ioctl_init(struct dm_ioctl* io, const std::string& name, unsigned flags) {
-    memset(io, 0, DM_BUF_SIZE);
-    io->data_size = DM_BUF_SIZE;
-    io->data_start = sizeof(struct dm_ioctl);
-    io->version[0] = 4;
-    io->version[1] = 0;
-    io->version[2] = 0;
-    io->flags = flags | DM_READONLY_FLAG;
-    if (!name.empty()) {
-        strlcpy(io->name, name.c_str(), sizeof(io->name));
-    }
-}
-
-bool fs_mgr_create_verity_device(struct dm_ioctl* io, const std::string& name, int fd) {
-    fs_mgr_verity_ioctl_init(io, name, 1);
-    if (ioctl(fd, DM_DEV_CREATE, io)) {
-        PERROR << "Error creating device mapping";
-        return false;
-    }
-    return true;
-}
-
-bool fs_mgr_destroy_verity_device(struct dm_ioctl* io, const std::string& name, int fd) {
-    fs_mgr_verity_ioctl_init(io, name, 0);
-    if (ioctl(fd, DM_DEV_REMOVE, io)) {
-        PERROR << "Error removing device mapping";
-        return false;
-    }
-    return true;
-}
-
-bool fs_mgr_get_verity_device_name(struct dm_ioctl* io, const std::string& name, int fd,
-                                   std::string* out_dev_name) {
-    FS_MGR_CHECK(out_dev_name != nullptr);
-
-    fs_mgr_verity_ioctl_init(io, name, 0);
-    if (ioctl(fd, DM_DEV_STATUS, io)) {
-        PERROR << "Error fetching verity device number";
-        return false;
-    }
-
-    int dev_num = (io->dev & 0xff) | ((io->dev >> 12) & 0xfff00);
-    *out_dev_name = "/dev/block/dm-" + std::to_string(dev_num);
-
-    return true;
-}
-
-bool fs_mgr_resume_verity_table(struct dm_ioctl* io, const std::string& name, int fd) {
-    fs_mgr_verity_ioctl_init(io, name, 0);
-    if (ioctl(fd, DM_DEV_SUSPEND, io)) {
-        PERROR << "Error activating verity device";
-        return false;
-    }
-    return true;
-}
diff --git a/fs_mgr/fs_mgr_dm_linear.cpp b/fs_mgr/fs_mgr_dm_linear.cpp
new file mode 100644
index 0000000..05e03e1
--- /dev/null
+++ b/fs_mgr/fs_mgr_dm_linear.cpp
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "fs_mgr_dm_linear.h"
+
+#include <inttypes.h>
+#include <linux/dm-ioctl.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <sstream>
+
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+#include <android-base/unique_fd.h>
+#include <liblp/reader.h>
+
+#include "fs_mgr_priv.h"
+
+namespace android {
+namespace fs_mgr {
+
+using DeviceMapper = android::dm::DeviceMapper;
+using DmTable = android::dm::DmTable;
+using DmTarget = android::dm::DmTarget;
+using DmTargetZero = android::dm::DmTargetZero;
+using DmTargetLinear = android::dm::DmTargetLinear;
+
+static bool CreateDmTable(const std::string& block_device, const LpMetadata& metadata,
+                          const LpMetadataPartition& partition, DmTable* table) {
+    uint64_t sector = 0;
+    for (size_t i = 0; i < partition.num_extents; i++) {
+        const auto& extent = metadata.extents[partition.first_extent_index + i];
+        std::unique_ptr<DmTarget> target;
+        switch (extent.target_type) {
+            case LP_TARGET_TYPE_ZERO:
+                target = std::make_unique<DmTargetZero>(sector, extent.num_sectors);
+                break;
+            case LP_TARGET_TYPE_LINEAR:
+                target = std::make_unique<DmTargetLinear>(sector, extent.num_sectors, block_device,
+                                                          extent.target_data);
+                break;
+            default:
+                LOG(ERROR) << "Unknown target type in metadata: " << extent.target_type;
+                return false;
+        }
+        if (!table->AddTarget(std::move(target))) {
+            return false;
+        }
+        sector += extent.num_sectors;
+    }
+    if (partition.attributes & LP_PARTITION_ATTR_READONLY) {
+        table->set_readonly(true);
+    }
+    return true;
+}
+
+static bool CreateLogicalPartition(const std::string& block_device, const LpMetadata& metadata,
+                                   const LpMetadataPartition& partition, std::string* path) {
+    DeviceMapper& dm = DeviceMapper::Instance();
+
+    DmTable table;
+    if (!CreateDmTable(block_device, metadata, partition, &table)) {
+        return false;
+    }
+    std::string name = GetPartitionName(partition);
+    if (!dm.CreateDevice(name, table)) {
+        return false;
+    }
+    if (!dm.GetDmDevicePathByName(name, path)) {
+        return false;
+    }
+    LINFO << "Created logical partition " << name << " on device " << *path;
+    return true;
+}
+
+bool CreateLogicalPartitions(const std::string& block_device) {
+    uint32_t slot = SlotNumberForSlotSuffix(fs_mgr_get_slot_suffix());
+    auto metadata = ReadMetadata(block_device.c_str(), slot);
+    if (!metadata) {
+        LOG(ERROR) << "Could not read partition table.";
+        return true;
+    }
+    for (const auto& partition : metadata->partitions) {
+        std::string path;
+        if (!CreateLogicalPartition(block_device, *metadata.get(), partition, &path)) {
+            LERROR << "Could not create logical partition: " << GetPartitionName(partition);
+            return false;
+        }
+    }
+    return true;
+}
+
+bool CreateLogicalPartition(const std::string& block_device, uint32_t metadata_slot,
+                            const std::string& partition_name, std::string* path) {
+    auto metadata = ReadMetadata(block_device.c_str(), metadata_slot);
+    if (!metadata) {
+        LOG(ERROR) << "Could not read partition table.";
+        return true;
+    }
+    for (const auto& partition : metadata->partitions) {
+        if (GetPartitionName(partition) == partition_name) {
+            return CreateLogicalPartition(block_device, *metadata.get(), partition, path);
+        }
+    }
+    LERROR << "Could not find any partition with name: " << partition_name;
+    return false;
+}
+
+bool DestroyLogicalPartition(const std::string& name) {
+    DeviceMapper& dm = DeviceMapper::Instance();
+    return dm.DeleteDevice(name);
+}
+
+}  // namespace fs_mgr
+}  // namespace android
diff --git a/fs_mgr/fs_mgr_format.cpp b/fs_mgr/fs_mgr_format.cpp
index 5705f93..63a6839 100644
--- a/fs_mgr/fs_mgr_format.cpp
+++ b/fs_mgr/fs_mgr_format.cpp
@@ -24,104 +24,112 @@
 #include <cutils/partition_utils.h>
 #include <sys/mount.h>
 
-#include <ext4_utils/ext4_utils.h>
 #include <ext4_utils/ext4.h>
-#include <ext4_utils/make_ext4fs.h>
-#include <selinux/selinux.h>
-#include <selinux/label.h>
+#include <ext4_utils/ext4_utils.h>
+#include <logwrap/logwrap.h>
 #include <selinux/android.h>
+#include <selinux/label.h>
+#include <selinux/selinux.h>
 
 #include "fs_mgr_priv.h"
 #include "cryptfs.h"
 
-extern "C" {
-extern struct fs_info info;     /* magic global from ext4_utils */
-extern void reset_ext4fs_info();
-}
-
-static int format_ext4(char *fs_blkdev, char *fs_mnt_point, bool crypt_footer)
+static int get_dev_sz(char *fs_blkdev, uint64_t *dev_sz)
 {
-    uint64_t dev_sz;
-    int fd, rc = 0;
+    int fd;
 
-    if ((fd = open(fs_blkdev, O_WRONLY)) < 0) {
+    if ((fd = open(fs_blkdev, O_RDONLY)) < 0) {
         PERROR << "Cannot open block device";
         return -1;
     }
 
-    if ((ioctl(fd, BLKGETSIZE64, &dev_sz)) == -1) {
+    if ((ioctl(fd, BLKGETSIZE64, dev_sz)) == -1) {
         PERROR << "Cannot get block device size";
         close(fd);
         return -1;
     }
 
-    struct selabel_handle *sehandle = selinux_android_file_context_handle();
-    if (!sehandle) {
-        /* libselinux logs specific error */
-        LERROR << "Cannot initialize android file_contexts";
-        close(fd);
-        return -1;
+    close(fd);
+    return 0;
+}
+
+static int format_ext4(char *fs_blkdev, char *fs_mnt_point, bool crypt_footer)
+{
+    uint64_t dev_sz;
+    int rc = 0;
+    int status;
+
+    rc = get_dev_sz(fs_blkdev, &dev_sz);
+    if (rc) {
+        return rc;
     }
 
     /* Format the partition using the calculated length */
-    reset_ext4fs_info();
-    info.len = (off64_t)dev_sz;
     if (crypt_footer) {
-        info.len -= CRYPT_FOOTER_OFFSET;
+        dev_sz -= CRYPT_FOOTER_OFFSET;
     }
 
-    /* Use make_ext4fs_internal to avoid wiping an already-wiped partition. */
-    rc = make_ext4fs_internal(fd, NULL, NULL, fs_mnt_point, 0, 0, 0, 0, 0, 0, sehandle, 0, 0, NULL, NULL, NULL);
+    std::string size_str = std::to_string(dev_sz / 4096);
+    const char* const mke2fs_args[] = {
+        "/system/bin/mke2fs", "-t", "ext4", "-b", "4096", fs_blkdev, size_str.c_str(), nullptr};
+
+    rc = android_fork_execvp_ext(arraysize(mke2fs_args), const_cast<char**>(mke2fs_args), NULL,
+                                 true, LOG_KLOG, true, nullptr, nullptr, 0);
     if (rc) {
-        LERROR << "make_ext4fs returned " << rc;
+        LERROR << "mke2fs returned " << rc;
+        return rc;
     }
-    close(fd);
 
-    if (sehandle) {
-        selabel_close(sehandle);
+    const char* const e2fsdroid_args[] = {
+        "/system/bin/e2fsdroid",
+        "-e",
+        "-a",
+        fs_mnt_point,
+        fs_blkdev,
+        nullptr};
+
+    rc = android_fork_execvp_ext(arraysize(e2fsdroid_args), const_cast<char**>(e2fsdroid_args),
+                                 NULL, true, LOG_KLOG, true, nullptr, nullptr, 0);
+    if (rc) {
+        LERROR << "e2fsdroid returned " << rc;
     }
 
     return rc;
 }
 
-static int format_f2fs(char *fs_blkdev)
+static int format_f2fs(char *fs_blkdev, uint64_t dev_sz, bool crypt_footer)
 {
-    char * args[3];
-    int pid;
-    int rc = 0;
+    int status;
 
-    args[0] = (char *)"/sbin/mkfs.f2fs";
-    args[1] = fs_blkdev;
-    args[2] = (char *)0;
-
-    pid = fork();
-    if (pid < 0) {
-       return pid;
-    }
-    if (!pid) {
-        /* This doesn't return */
-        execv("/sbin/mkfs.f2fs", args);
-        exit(1);
-    }
-    for(;;) {
-        pid_t p = waitpid(pid, &rc, 0);
-        if (p != pid) {
-            LERROR << "Error waiting for child process - " << p;
-            rc = -1;
-            break;
+    if (!dev_sz) {
+        int rc = get_dev_sz(fs_blkdev, &dev_sz);
+        if (rc) {
+            return rc;
         }
-        if (WIFEXITED(rc)) {
-            rc = WEXITSTATUS(rc);
-            LINFO << args[0] << " done, status " << rc;
-            if (rc) {
-                rc = -1;
-            }
-            break;
-        }
-        LERROR << "Still waiting for " << args[0] << "...";
     }
 
-    return rc;
+    /* Format the partition using the calculated length */
+    if (crypt_footer) {
+        dev_sz -= CRYPT_FOOTER_OFFSET;
+    }
+
+    std::string size_str = std::to_string(dev_sz / 4096);
+    // clang-format off
+    const char* const args[] = {
+        "/system/bin/make_f2fs",
+        "-d1",
+        "-f",
+        "-O", "encrypt",
+        "-O", "quota",
+        "-w", "4096",
+        fs_blkdev,
+        size_str.c_str(),
+        nullptr
+    };
+    // clang-format on
+
+    return android_fork_execvp_ext(arraysize(args), const_cast<char**>(args), NULL, true,
+                                   LOG_KLOG, true, nullptr, nullptr, 0);
 }
 
 int fs_mgr_do_format(struct fstab_rec *fstab, bool crypt_footer)
@@ -132,7 +140,7 @@
            << " as '" << fstab->fs_type << "'";
 
     if (!strncmp(fstab->fs_type, "f2fs", 4)) {
-        rc = format_f2fs(fstab->blk_device);
+        rc = format_f2fs(fstab->blk_device, fstab->length, crypt_footer);
     } else if (!strncmp(fstab->fs_type, "ext4", 4)) {
         rc = format_ext4(fstab->blk_device, fstab->mount_point, crypt_footer);
     } else {
diff --git a/fs_mgr/fs_mgr_fstab.cpp b/fs_mgr/fs_mgr_fstab.cpp
index 7a41b14..8b46d64 100644
--- a/fs_mgr/fs_mgr_fstab.cpp
+++ b/fs_mgr/fs_mgr_fstab.cpp
@@ -23,15 +23,25 @@
 #include <sys/mount.h>
 #include <unistd.h>
 
+#include <algorithm>
+#include <utility>
+#include <vector>
+
 #include <android-base/file.h>
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
 
 #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;
@@ -39,7 +49,8 @@
     int max_comp_streams;
     unsigned int zram_size;
     uint64_t reserved_size;
-    unsigned int file_encryption_mode;
+    unsigned int file_contents_mode;
+    unsigned int file_names_mode;
     unsigned int erase_blk_size;
     unsigned int logical_blk_size;
 };
@@ -69,45 +80,88 @@
 };
 
 static struct flag_list fs_mgr_flags[] = {
-    { "wait",               MF_WAIT },
-    { "check",              MF_CHECK },
-    { "encryptable=",       MF_CRYPT },
-    { "forceencrypt=",      MF_FORCECRYPT },
-    { "fileencryption=",    MF_FILEENCRYPTION },
-    { "forcefdeorfbe=",     MF_FORCEFDEORFBE },
-    { "nonremovable",       MF_NONREMOVABLE },
-    { "voldmanaged=",       MF_VOLDMANAGED},
-    { "length=",            MF_LENGTH },
-    { "recoveryonly",       MF_RECOVERYONLY },
-    { "swapprio=",          MF_SWAPPRIO },
-    { "zramsize=",          MF_ZRAMSIZE },
-    { "max_comp_streams=",  MF_MAX_COMP_STREAMS },
-    { "verifyatboot",       MF_VERIFYATBOOT },
-    { "verify",             MF_VERIFY },
-    { "avb",                MF_AVB },
-    { "noemulatedsd",       MF_NOEMULATEDSD },
-    { "notrim",             MF_NOTRIM },
-    { "formattable",        MF_FORMATTABLE },
-    { "slotselect",         MF_SLOTSELECT },
-    { "nofail",             MF_NOFAIL },
-    { "latemount",          MF_LATEMOUNT },
-    { "reservedsize=",      MF_RESERVEDSIZE },
-    { "quota",              MF_QUOTA },
-    { "eraseblk=",          MF_ERASEBLKSIZE },
-    { "logicalblk=",        MF_LOGICALBLKSIZE },
-    { "defaults",           0 },
-    { 0,                    0 },
+    {"wait", MF_WAIT},
+    {"check", MF_CHECK},
+    {"encryptable=", MF_CRYPT},
+    {"forceencrypt=", MF_FORCECRYPT},
+    {"fileencryption=", MF_FILEENCRYPTION},
+    {"forcefdeorfbe=", MF_FORCEFDEORFBE},
+    {"keydirectory=", MF_KEYDIRECTORY},
+    {"nonremovable", MF_NONREMOVABLE},
+    {"voldmanaged=", MF_VOLDMANAGED},
+    {"length=", MF_LENGTH},
+    {"recoveryonly", MF_RECOVERYONLY},
+    {"swapprio=", MF_SWAPPRIO},
+    {"zramsize=", MF_ZRAMSIZE},
+    {"max_comp_streams=", MF_MAX_COMP_STREAMS},
+    {"verifyatboot", MF_VERIFYATBOOT},
+    {"verify", MF_VERIFY},
+    {"avb", MF_AVB},
+    {"noemulatedsd", MF_NOEMULATEDSD},
+    {"notrim", MF_NOTRIM},
+    {"formattable", MF_FORMATTABLE},
+    {"slotselect", MF_SLOTSELECT},
+    {"nofail", MF_NOFAIL},
+    {"latemount", MF_LATEMOUNT},
+    {"reservedsize=", MF_RESERVEDSIZE},
+    {"quota", MF_QUOTA},
+    {"eraseblk=", MF_ERASEBLKSIZE},
+    {"logicalblk=", MF_LOGICALBLKSIZE},
+    {"sysfs_path=", MF_SYSFS},
+    {"defaults", 0},
+    {"logical", MF_LOGICAL},
+    {0, 0},
 };
 
-#define EM_SOFTWARE 1
-#define EM_ICE      2
+#define EM_AES_256_XTS  1
+#define EM_ICE          2
+#define EM_AES_256_CTS  3
+#define EM_AES_256_HEH  4
+#define EM_SPECK_128_256_XTS 5
+#define EM_SPECK_128_256_CTS 6
 
-static struct flag_list encryption_modes[] = {
-    {"software", EM_SOFTWARE},
-    {"ice", EM_ICE},
-    {0, 0}
+static const struct flag_list file_contents_encryption_modes[] = {
+    {"aes-256-xts", EM_AES_256_XTS},
+    {"speck128/256-xts", EM_SPECK_128_256_XTS},
+    {"software", EM_AES_256_XTS}, /* alias for backwards compatibility */
+    {"ice", EM_ICE},              /* hardware-specific inline cryptographic engine */
+    {0, 0},
 };
 
+static const struct flag_list file_names_encryption_modes[] = {
+    {"aes-256-cts", EM_AES_256_CTS},
+    {"aes-256-heh", EM_AES_256_HEH},
+    {"speck128/256-cts", EM_SPECK_128_256_CTS},
+    {0, 0},
+};
+
+static unsigned int encryption_mode_to_flag(const struct flag_list *list,
+                                            const char *mode, const char *type)
+{
+    const struct flag_list *j;
+
+    for (j = list; j->name; ++j) {
+        if (!strcmp(mode, j->name)) {
+            return j->flag;
+        }
+    }
+    LERROR << "Unknown " << type << " encryption mode: " << mode;
+    return 0;
+}
+
+static const char *flag_to_encryption_mode(const struct flag_list *list,
+                                           unsigned int flag)
+{
+    const struct flag_list *j;
+
+    for (j = list; j->name; ++j) {
+        if (flag == j->flag) {
+            return j->name;
+        }
+    }
+    return nullptr;
+}
+
 static uint64_t calculate_zram_size(unsigned int percentage)
 {
     uint64_t total;
@@ -181,68 +235,83 @@
          * If not found, the loop exits with fl[i].name being null.
          */
         for (i = 0; fl[i].name; i++) {
-            if (!strncmp(p, fl[i].name, strlen(fl[i].name))) {
+            auto name = fl[i].name;
+            auto len = strlen(name);
+            auto end = len;
+            if (name[end - 1] == '=') --end;
+            if (!strncmp(p, name, len) && (p[end] == name[end])) {
                 f |= fl[i].flag;
-                if ((fl[i].flag == MF_CRYPT) && flag_vals) {
+                if (!flag_vals) break;
+                if (p[end] != '=') break;
+                char* arg = p + end + 1;
+                auto flag = fl[i].flag;
+                if (flag == MF_CRYPT) {
                     /* The encryptable flag is followed by an = and the
                      * location of the keys.  Get it and return it.
                      */
-                    flag_vals->key_loc = strdup(strchr(p, '=') + 1);
-                } else if ((fl[i].flag == MF_VERIFY) && flag_vals) {
+                    flag_vals->key_loc = strdup(arg);
+                } else if (flag == MF_VERIFY) {
                     /* If the verify flag is followed by an = and the
                      * location for the verity state,  get it and return it.
                      */
-                    char *start = strchr(p, '=');
-                    if (start) {
-                        flag_vals->verity_loc = strdup(start + 1);
-                    }
-                } else if ((fl[i].flag == MF_FORCECRYPT) && flag_vals) {
+                    flag_vals->verity_loc = strdup(arg);
+                } else if (flag == MF_FORCECRYPT) {
                     /* The forceencrypt flag is followed by an = and the
                      * location of the keys.  Get it and return it.
                      */
-                    flag_vals->key_loc = strdup(strchr(p, '=') + 1);
-                } else if ((fl[i].flag == MF_FORCEFDEORFBE) && flag_vals) {
+                    flag_vals->key_loc = strdup(arg);
+                } else if (flag == MF_FORCEFDEORFBE) {
                     /* The forcefdeorfbe flag is followed by an = and the
                      * location of the keys.  Get it and return it.
                      */
-                    flag_vals->key_loc = strdup(strchr(p, '=') + 1);
-                    flag_vals->file_encryption_mode = EM_SOFTWARE;
-                } else if ((fl[i].flag == MF_FILEENCRYPTION) && flag_vals) {
-                    /* The fileencryption flag is followed by an = and the
-                     * type of the encryption.  Get it and return it.
+                    flag_vals->key_loc = strdup(arg);
+                    flag_vals->file_contents_mode = EM_AES_256_XTS;
+                    flag_vals->file_names_mode = EM_AES_256_CTS;
+                } else if (flag == MF_FILEENCRYPTION) {
+                    /* The fileencryption flag is followed by an = and
+                     * the mode of contents encryption, then optionally a
+                     * : and the mode of filenames encryption (defaults
+                     * to aes-256-cts).  Get it and return it.
                      */
-                    const struct flag_list *j;
-                    const char *mode = strchr(p, '=') + 1;
-                    for (j = encryption_modes; j->name; ++j) {
-                        if (!strcmp(mode, j->name)) {
-                            flag_vals->file_encryption_mode = j->flag;
-                        }
+                    auto mode = arg;
+                    auto colon = strchr(mode, ':');
+                    if (colon) {
+                        *colon = '\0';
                     }
-                    if (flag_vals->file_encryption_mode == 0) {
-                        LERROR << "Unknown file encryption mode: " << mode;
+                    flag_vals->file_contents_mode =
+                        encryption_mode_to_flag(file_contents_encryption_modes,
+                                                mode, "file contents");
+                    if (colon) {
+                        flag_vals->file_names_mode =
+                            encryption_mode_to_flag(file_names_encryption_modes,
+                                                    colon + 1, "file names");
+                    } else {
+                        flag_vals->file_names_mode = EM_AES_256_CTS;
                     }
-                } else if ((fl[i].flag == MF_LENGTH) && flag_vals) {
+                } else if (flag == MF_KEYDIRECTORY) {
+                    /* The metadata flag is followed by an = and the
+                     * directory for the keys.  Get it and return it.
+                     */
+                    flag_vals->key_dir = strdup(arg);
+                } else if (flag == MF_LENGTH) {
                     /* The length flag is followed by an = and the
                      * size of the partition.  Get it and return it.
                      */
-                    flag_vals->part_length = strtoll(strchr(p, '=') + 1, NULL, 0);
-                } else if ((fl[i].flag == MF_VOLDMANAGED) && flag_vals) {
+                    flag_vals->part_length = strtoll(arg, NULL, 0);
+                } else if (flag == MF_VOLDMANAGED) {
                     /* The voldmanaged flag is followed by an = and the
                      * label, a colon and the partition number or the
                      * word "auto", e.g.
                      *   voldmanaged=sdcard:3
                      * Get and return them.
                      */
-                    char *label_start;
-                    char *label_end;
-                    char *part_start;
+                    auto label_start = arg;
+                    auto label_end = strchr(label_start, ':');
 
-                    label_start = strchr(p, '=') + 1;
-                    label_end = strchr(p, ':');
                     if (label_end) {
                         flag_vals->label = strndup(label_start,
                                                    (int) (label_end - label_start));
-                        part_start = strchr(p, ':') + 1;
+                        auto part_start = label_end + 1;
                         if (!strcmp(part_start, "auto")) {
                             flag_vals->partnum = -1;
                         } else {
@@ -251,38 +320,41 @@
                     } else {
                         LERROR << "Warning: voldmanaged= flag malformed";
                     }
-                } else if ((fl[i].flag == MF_SWAPPRIO) && flag_vals) {
-                    flag_vals->swap_prio = strtoll(strchr(p, '=') + 1, NULL, 0);
-                } else if ((fl[i].flag == MF_MAX_COMP_STREAMS) && flag_vals) {
-                    flag_vals->max_comp_streams = strtoll(strchr(p, '=') + 1, NULL, 0);
-                } else if ((fl[i].flag == MF_ZRAMSIZE) && flag_vals) {
-                    int is_percent = !!strrchr(p, '%');
-                    unsigned int val = strtoll(strchr(p, '=') + 1, NULL, 0);
+                } else if (flag == MF_SWAPPRIO) {
+                    flag_vals->swap_prio = strtoll(arg, NULL, 0);
+                } else if (flag == MF_MAX_COMP_STREAMS) {
+                    flag_vals->max_comp_streams = strtoll(arg, NULL, 0);
+                } else if (flag == MF_ZRAMSIZE) {
+                    auto is_percent = !!strrchr(arg, '%');
+                    auto val = strtoll(arg, NULL, 0);
                     if (is_percent)
                         flag_vals->zram_size = calculate_zram_size(val);
                     else
                         flag_vals->zram_size = val;
-                } else if ((fl[i].flag == MF_RESERVEDSIZE) && flag_vals) {
+                } else if (flag == MF_RESERVEDSIZE) {
                     /* The reserved flag is followed by an = and the
                      * reserved size of the partition.  Get it and return it.
                      */
-                    flag_vals->reserved_size = parse_size(strchr(p, '=') + 1);
-                } else if ((fl[i].flag == MF_ERASEBLKSIZE) && flag_vals) {
+                    flag_vals->reserved_size = parse_size(arg);
+                } else if (flag == MF_ERASEBLKSIZE) {
                     /* The erase block size flag is followed by an = and the flash
                      * erase block size. Get it, check that it is a power of 2 and
                      * at least 4096, and return it.
                      */
-                    unsigned int val = strtoul(strchr(p, '=') + 1, NULL, 0);
+                    auto val = strtoul(arg, NULL, 0);
                     if (val >= 4096 && (val & (val - 1)) == 0)
                         flag_vals->erase_blk_size = val;
-                } else if ((fl[i].flag == MF_LOGICALBLKSIZE) && flag_vals) {
+                } else if (flag == MF_LOGICALBLKSIZE) {
                     /* The logical block size flag is followed by an = and the flash
                      * logical block size. Get it, check that it is a power of 2 and
                      * at least 4096, and return it.
                      */
-                    unsigned int val = strtoul(strchr(p, '=') + 1, NULL, 0);
+                    auto val = strtoul(arg, NULL, 0);
                     if (val >= 4096 && (val & (val - 1)) == 0)
                         flag_vals->logical_blk_size = val;
+                } else if (flag == MF_SYSFS) {
+                    /* The path to trigger device gc by idle-maint of vold. */
+                    flag_vals->sysfs_path = strdup(arg);
                 }
                 break;
             }
@@ -313,9 +385,26 @@
     return f;
 }
 
+static std::string init_android_dt_dir() {
+    std::string android_dt_dir;
+    // The platform may specify a custom Android DT path in kernel cmdline
+    if (!fs_mgr_get_boot_config_from_kernel_cmdline("android_dt_dir", &android_dt_dir)) {
+        // Fall back to the standard procfs-based path
+        android_dt_dir = kDefaultAndroidDtDir;
+    }
+    return android_dt_dir;
+}
+
+// FIXME: The same logic is duplicated in system/core/init/
+const std::string& get_android_dt_dir() {
+    // Set once and saves time for subsequent calls to this function
+    static const std::string kAndroidDtDir = init_android_dt_dir();
+    return kAndroidDtDir;
+}
+
 static bool is_dt_fstab_compatible() {
     std::string dt_value;
-    std::string file_name = kAndroidDtDir + "/fstab/compatible";
+    std::string file_name = get_android_dt_dir() + "/fstab/compatible";
     if (read_dt_file(file_name, &dt_value)) {
         if (dt_value == "android,fstab") {
             return true;
@@ -326,16 +415,17 @@
 }
 
 static std::string read_fstab_from_dt() {
-    std::string fstab;
     if (!is_dt_compatible() || !is_dt_fstab_compatible()) {
-        return fstab;
+        return {};
     }
 
-    std::string fstabdir_name = kAndroidDtDir + "/fstab";
+    std::string fstabdir_name = get_android_dt_dir() + "/fstab";
     std::unique_ptr<DIR, int (*)(DIR*)> fstabdir(opendir(fstabdir_name.c_str()), closedir);
-    if (!fstabdir) return fstab;
+    if (!fstabdir) return {};
 
     dirent* dp;
+    // Each element in fstab_dt_entries is <mount point, the line format in fstab file>.
+    std::vector<std::pair<std::string, std::string>> fstab_dt_entries;
     while ((dp = readdir(fstabdir.get())) != NULL) {
         // skip over name, compatible and .
         if (dp->d_type != DT_DIR || dp->d_name[0] == '.') continue;
@@ -356,45 +446,58 @@
         file_name = android::base::StringPrintf("%s/%s/dev", fstabdir_name.c_str(), dp->d_name);
         if (!read_dt_file(file_name, &value)) {
             LERROR << "dt_fstab: Failed to find device for partition " << dp->d_name;
-            fstab.clear();
-            break;
+            return {};
         }
         fstab_entry.push_back(value);
-        fstab_entry.push_back(android::base::StringPrintf("/%s", dp->d_name));
+
+        std::string mount_point;
+        file_name =
+            android::base::StringPrintf("%s/%s/mnt_point", fstabdir_name.c_str(), dp->d_name);
+        if (read_dt_file(file_name, &value)) {
+            LINFO << "dt_fstab: Using a specified mount point " << value << " for " << dp->d_name;
+            mount_point = value;
+        } else {
+            mount_point = android::base::StringPrintf("/%s", dp->d_name);
+        }
+        fstab_entry.push_back(mount_point);
 
         file_name = android::base::StringPrintf("%s/%s/type", fstabdir_name.c_str(), dp->d_name);
         if (!read_dt_file(file_name, &value)) {
             LERROR << "dt_fstab: Failed to find type for partition " << dp->d_name;
-            fstab.clear();
-            break;
+            return {};
         }
         fstab_entry.push_back(value);
 
         file_name = android::base::StringPrintf("%s/%s/mnt_flags", fstabdir_name.c_str(), dp->d_name);
         if (!read_dt_file(file_name, &value)) {
             LERROR << "dt_fstab: Failed to find type for partition " << dp->d_name;
-            fstab.clear();
-            break;
+            return {};
         }
         fstab_entry.push_back(value);
 
         file_name = android::base::StringPrintf("%s/%s/fsmgr_flags", fstabdir_name.c_str(), dp->d_name);
         if (!read_dt_file(file_name, &value)) {
             LERROR << "dt_fstab: Failed to find type for partition " << dp->d_name;
-            fstab.clear();
-            break;
+            return {};
         }
         fstab_entry.push_back(value);
-
-        fstab += android::base::Join(fstab_entry, " ");
-        fstab += '\n';
+        // Adds a fstab_entry to fstab_dt_entries, to be sorted by mount_point later.
+        fstab_dt_entries.emplace_back(mount_point, android::base::Join(fstab_entry, " "));
     }
 
-    return fstab;
+    // Sort fstab_dt entries, to ensure /vendor is mounted before /vendor/abc is attempted.
+    std::sort(fstab_dt_entries.begin(), fstab_dt_entries.end(),
+              [](const auto& a, const auto& b) { return a.first < b.first; });
+
+    std::string fstab_result;
+    for (const auto& [_, dt_entry] : fstab_dt_entries) {
+        fstab_result += dt_entry + "\n";
+    }
+    return fstab_result;
 }
 
 bool is_dt_compatible() {
-    std::string file_name = kAndroidDtDir + "/compatible";
+    std::string file_name = get_android_dt_dir() + "/compatible";
     std::string dt_value;
     if (read_dt_file(file_name, &dt_value)) {
         if (dt_value == "android,firmware") {
@@ -405,8 +508,7 @@
     return false;
 }
 
-static struct fstab *fs_mgr_read_fstab_file(FILE *fstab_file)
-{
+static struct fstab* fs_mgr_read_fstab_file(FILE* fstab_file, bool proc_mounts) {
     int cnt, entries;
     ssize_t len;
     size_t alloc_len = 0;
@@ -506,13 +608,17 @@
             fstab->recs[cnt].fs_options = NULL;
         }
 
-        if (!(p = strtok_r(NULL, delim, &save_ptr))) {
+        // For /proc/mounts, ignore everything after mnt_freq and mnt_passno
+        if (proc_mounts) {
+            p += strlen(p);
+        } else if (!(p = strtok_r(NULL, delim, &save_ptr))) {
             LERROR << "Error parsing fs_mgr_options";
             goto err;
         }
         fstab->recs[cnt].fs_mgr_flags = parse_flags(p, fs_mgr_flags,
                                                     &flag_vals, NULL, 0);
         fstab->recs[cnt].key_loc = flag_vals.key_loc;
+        fstab->recs[cnt].key_dir = flag_vals.key_dir;
         fstab->recs[cnt].verity_loc = flag_vals.verity_loc;
         fstab->recs[cnt].length = flag_vals.part_length;
         fstab->recs[cnt].label = flag_vals.label;
@@ -521,9 +627,15 @@
         fstab->recs[cnt].max_comp_streams = flag_vals.max_comp_streams;
         fstab->recs[cnt].zram_size = flag_vals.zram_size;
         fstab->recs[cnt].reserved_size = flag_vals.reserved_size;
-        fstab->recs[cnt].file_encryption_mode = flag_vals.file_encryption_mode;
+        fstab->recs[cnt].file_contents_mode = flag_vals.file_contents_mode;
+        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;
+        if (fstab->recs[cnt].fs_mgr_flags & MF_LOGICAL) {
+            fstab->recs[cnt].logical_partition_name = strdup(fstab->recs[cnt].blk_device);
+        }
+
         cnt++;
     }
     /* If an A/B partition, modify block device to be the real block device */
@@ -547,6 +659,7 @@
  * frees up memory of the return value without touching a and b. */
 static struct fstab *in_place_merge(struct fstab *a, struct fstab *b)
 {
+    if (!a && !b) return nullptr;
     if (!a) return b;
     if (!b) return a;
 
@@ -563,19 +676,62 @@
     }
 
     for (int i = a->num_entries, j = 0; i < total_entries; i++, j++) {
-        // copy the pointer directly *without* malloc and memcpy
+        // Copy the structs by assignment.
         a->recs[i] = b->recs[j];
     }
 
-    // Frees up b, but don't free b->recs[X] to make sure they are
-    // accessible through a->recs[X].
-    free(b->fstab_filename);
+    // We can't call fs_mgr_free_fstab because a->recs still references the
+    // memory allocated by strdup.
+    free(b->recs);
     free(b);
 
     a->num_entries = total_entries;
     return a;
 }
 
+/* Extracts <device>s from the by-name symlinks specified in a fstab:
+ *   /dev/block/<type>/<device>/by-name/<partition>
+ *
+ * <type> can be: platform, pci or vbd.
+ *
+ * For example, given the following entries in the input fstab:
+ *   /dev/block/platform/soc/1da4000.ufshc/by-name/system
+ *   /dev/block/pci/soc.0/f9824900.sdhci/by-name/vendor
+ * it returns a set { "soc/1da4000.ufshc", "soc.0/f9824900.sdhci" }.
+ */
+static std::set<std::string> extract_boot_devices(const fstab& fstab) {
+    std::set<std::string> boot_devices;
+
+    for (int i = 0; i < fstab.num_entries; i++) {
+        std::string blk_device(fstab.recs[i].blk_device);
+        // Skips blk_device that doesn't conform to the format.
+        if (!android::base::StartsWith(blk_device, "/dev/block") ||
+            android::base::StartsWith(blk_device, "/dev/block/by-name") ||
+            android::base::StartsWith(blk_device, "/dev/block/bootdevice/by-name")) {
+            continue;
+        }
+        // Skips non-by_name blk_device.
+        // /dev/block/<type>/<device>/by-name/<partition>
+        //                           ^ slash_by_name
+        auto slash_by_name = blk_device.find("/by-name");
+        if (slash_by_name == std::string::npos) continue;
+        blk_device.erase(slash_by_name);  // erases /by-name/<partition>
+
+        // Erases /dev/block/, now we have <type>/<device>
+        blk_device.erase(0, std::string("/dev/block/").size());
+
+        // <type>/<device>
+        //       ^ first_slash
+        auto first_slash = blk_device.find('/');
+        if (first_slash == std::string::npos) continue;
+
+        auto boot_device = blk_device.substr(first_slash + 1);
+        if (!boot_device.empty()) boot_devices.insert(std::move(boot_device));
+    }
+
+    return boot_devices;
+}
+
 struct fstab *fs_mgr_read_fstab(const char *fstab_path)
 {
     FILE *fstab_file;
@@ -587,10 +743,8 @@
         return nullptr;
     }
 
-    fstab = fs_mgr_read_fstab_file(fstab_file);
-    if (fstab) {
-        fstab->fstab_filename = strdup(fstab_path);
-    } else {
+    fstab = fs_mgr_read_fstab_file(fstab_file, !strcmp("/proc/mounts", fstab_path));
+    if (!fstab) {
         LERROR << __FUNCTION__ << "(): failed to load fstab from : '" << fstab_path << "'";
     }
 
@@ -617,7 +771,7 @@
         return nullptr;
     }
 
-    struct fstab *fstab = fs_mgr_read_fstab_file(fstab_file.get());
+    struct fstab* fstab = fs_mgr_read_fstab_file(fstab_file.get(), false);
     if (!fstab) {
         LERROR << __FUNCTION__ << "(): failed to load fstab from kernel:"
                << std::endl << fstab_buf;
@@ -627,32 +781,53 @@
 }
 
 /*
- * tries to load default fstab.<hardware> file from /odm/etc, /vendor/etc
- * or /. loads the first one found and also combines fstab entries passed
- * in from device tree.
+ * Identify path to fstab file. Lookup is based on pattern
+ * fstab.<hardware>, fstab.<hardware.platform> in folders
+   /odm/etc, vendor/etc, or /.
+ */
+static std::string get_fstab_path()
+{
+    for (const char* prop : {"hardware", "hardware.platform"}) {
+        std::string hw;
+
+        if (!fs_mgr_get_boot_config(prop, &hw)) continue;
+
+        for (const char* prefix : {"/odm/etc/fstab.", "/vendor/etc/fstab.", "/fstab."}) {
+            std::string fstab_path = prefix + hw;
+            if (access(fstab_path.c_str(), F_OK) == 0) {
+                return fstab_path;
+            }
+        }
+    }
+
+    return std::string();
+}
+
+/*
+ * loads the fstab file and combines with fstab entries passed in from device tree.
  */
 struct fstab *fs_mgr_read_fstab_default()
 {
-    std::string hw;
     std::string default_fstab;
 
     // Use different fstab paths for normal boot and recovery boot, respectively
-    if (access("/sbin/recovery", F_OK) == 0) {
+    if (access("/system/bin/recovery", F_OK) == 0) {
         default_fstab = "/etc/recovery.fstab";
-    } else if (fs_mgr_get_boot_config("hardware", &hw)) {  // normal boot
-        for (const char *prefix : {"/odm/etc/fstab.","/vendor/etc/fstab.", "/fstab."}) {
-            default_fstab = prefix + hw;
-            if (access(default_fstab.c_str(), F_OK) == 0) break;
-        }
-    } else {
-        LWARNING << __FUNCTION__ << "(): failed to find device hardware name";
+    } else {  // normal boot
+        default_fstab = get_fstab_path();
     }
 
+    struct fstab* fstab = nullptr;
+    if (!default_fstab.empty()) {
+        fstab = fs_mgr_read_fstab(default_fstab.c_str());
+    } else {
+        LINFO << __FUNCTION__ << "(): failed to find device default fstab";
+    }
+
+    struct fstab* fstab_dt = fs_mgr_read_fstab_dt();
+
     // combines fstab entries passed in from device tree with
     // the ones found from default_fstab file
-    struct fstab *fstab_dt = fs_mgr_read_fstab_dt();
-    struct fstab *fstab = fs_mgr_read_fstab(default_fstab.c_str());
-
     return in_place_merge(fstab_dt, fstab);
 }
 
@@ -667,19 +842,19 @@
     for (i = 0; i < fstab->num_entries; i++) {
         /* Free the pointers return by strdup(3) */
         free(fstab->recs[i].blk_device);
+        free(fstab->recs[i].logical_partition_name);
         free(fstab->recs[i].mount_point);
         free(fstab->recs[i].fs_type);
         free(fstab->recs[i].fs_options);
         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) */
     free(fstab->recs);
 
-    /* Free the fstab filename */
-    free(fstab->fstab_filename);
-
     /* Free fstab */
     free(fstab);
 }
@@ -714,44 +889,36 @@
 }
 
 /*
- * Returns the 1st matching fstab_rec that follows the start_rec.
- * start_rec is the result of a previous search or NULL.
+ * Returns the fstab_rec* whose mount_point is path.
+ * Returns nullptr if not found.
  */
-struct fstab_rec *fs_mgr_get_entry_for_mount_point_after(struct fstab_rec *start_rec, struct fstab *fstab, const char *path)
-{
-    int i;
+struct fstab_rec* fs_mgr_get_entry_for_mount_point(struct fstab* fstab, const std::string& path) {
     if (!fstab) {
-        return NULL;
+        return nullptr;
     }
-
-    if (start_rec) {
-        for (i = 0; i < fstab->num_entries; i++) {
-            if (&fstab->recs[i] == start_rec) {
-                i++;
-                break;
-            }
-        }
-    } else {
-        i = 0;
-    }
-    for (; i < fstab->num_entries; i++) {
-        int len = strlen(fstab->recs[i].mount_point);
-        if (strncmp(path, fstab->recs[i].mount_point, len) == 0 &&
-            (path[len] == '\0' || path[len] == '/')) {
+    for (int i = 0; i < fstab->num_entries; i++) {
+        if (fstab->recs[i].mount_point && path == fstab->recs[i].mount_point) {
             return &fstab->recs[i];
         }
     }
-    return NULL;
+    return nullptr;
 }
 
-/*
- * Returns the 1st matching mount point.
- * There might be more. To look for others, use fs_mgr_get_entry_for_mount_point_after()
- * and give the fstab_rec from the previous search.
- */
-struct fstab_rec *fs_mgr_get_entry_for_mount_point(struct fstab *fstab, const char *path)
-{
-    return fs_mgr_get_entry_for_mount_point_after(NULL, fstab, path);
+std::set<std::string> fs_mgr_get_boot_devices() {
+    // boot_devices can be specified in device tree.
+    std::string dt_value;
+    std::string file_name = get_android_dt_dir() + "/boot_devices";
+    if (read_dt_file(file_name, &dt_value)) {
+        auto boot_devices = android::base::Split(dt_value, ",");
+        return std::set<std::string>(boot_devices.begin(), boot_devices.end());
+    }
+
+    // Fallback to extract boot devices from fstab.
+    std::unique_ptr<fstab, decltype(&fs_mgr_free_fstab)> fstab(fs_mgr_read_fstab_default(),
+                                                               fs_mgr_free_fstab);
+    if (fstab) return extract_boot_devices(*fstab);
+
+    return {};
 }
 
 int fs_mgr_is_voldmanaged(const struct fstab_rec *fstab)
@@ -789,15 +956,14 @@
     return fstab->fs_mgr_flags & MF_FILEENCRYPTION;
 }
 
-const char* fs_mgr_get_file_encryption_mode(const struct fstab_rec *fstab)
+void fs_mgr_get_file_encryption_modes(const struct fstab_rec *fstab,
+                                      const char **contents_mode_ret,
+                                      const char **filenames_mode_ret)
 {
-    const struct flag_list *j;
-    for (j = encryption_modes; j->name; ++j) {
-        if (fstab->file_encryption_mode == j->flag) {
-            return j->name;
-        }
-    }
-    return NULL;
+    *contents_mode_ret = flag_to_encryption_mode(file_contents_encryption_modes,
+                                                 fstab->file_contents_mode);
+    *filenames_mode_ret = flag_to_encryption_mode(file_names_encryption_modes,
+                                                  fstab->file_names_mode);
 }
 
 int fs_mgr_is_convertible_to_fbe(const struct fstab_rec *fstab)
@@ -810,32 +976,35 @@
     return fstab->fs_mgr_flags & MF_NOEMULATEDSD;
 }
 
-int fs_mgr_is_notrim(struct fstab_rec *fstab)
-{
+int fs_mgr_is_notrim(const struct fstab_rec* fstab) {
     return fstab->fs_mgr_flags & MF_NOTRIM;
 }
 
-int fs_mgr_is_formattable(struct fstab_rec *fstab)
-{
+int fs_mgr_is_formattable(const struct fstab_rec* fstab) {
     return fstab->fs_mgr_flags & (MF_FORMATTABLE);
 }
 
-int fs_mgr_is_slotselect(struct fstab_rec *fstab)
-{
+int fs_mgr_is_slotselect(const struct fstab_rec* fstab) {
     return fstab->fs_mgr_flags & MF_SLOTSELECT;
 }
 
-int fs_mgr_is_nofail(struct fstab_rec *fstab)
-{
+int fs_mgr_is_nofail(const struct fstab_rec* fstab) {
     return fstab->fs_mgr_flags & MF_NOFAIL;
 }
 
-int fs_mgr_is_latemount(struct fstab_rec *fstab)
-{
+int fs_mgr_is_latemount(const struct fstab_rec* fstab) {
     return fstab->fs_mgr_flags & MF_LATEMOUNT;
 }
 
-int fs_mgr_is_quota(struct fstab_rec *fstab)
-{
+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;
+}
+
+int fs_mgr_is_logical(const struct fstab_rec* fstab) {
+    return fstab->fs_mgr_flags & MF_LOGICAL;
+}
diff --git a/fs_mgr/fs_mgr_main.cpp b/fs_mgr/fs_mgr_main.cpp
deleted file mode 100644
index f3919d9..0000000
--- a/fs_mgr/fs_mgr_main.cpp
+++ /dev/null
@@ -1,118 +0,0 @@
-/*
- * Copyright (C) 2012 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 <stdio.h>
-#include <string.h>
-#include <stdlib.h>
-#include "fs_mgr_priv.h"
-
-#ifdef _LIBGEN_H
-#warning "libgen.h must not be included"
-#endif
-
-char *me = nullptr;
-
-static void usage(void)
-{
-    LERROR << me << ": usage: " << me
-           << " <-a | -n mnt_point blk_dev | -u> <fstab_file>";
-    exit(1);
-}
-
-/* Parse the command line.  If an error is encountered, print an error message
- * and exit the program, do not return to the caller.
- * Return the number of argv[] entries consumed.
- */
-static void parse_options(int argc, char * const argv[], int *a_flag, int *u_flag, int *n_flag,
-                     const char **n_name, const char **n_blk_dev)
-{
-    me = basename(argv[0]);
-
-    if (argc <= 1) {
-        usage();
-    }
-
-    if (!strcmp(argv[1], "-a")) {
-        if (argc != 3) {
-            usage();
-        }
-        *a_flag = 1;
-    }
-    if (!strcmp(argv[1], "-n")) {
-        if (argc != 5) {
-            usage();
-        }
-        *n_flag = 1;
-        *n_name = argv[2];
-        *n_blk_dev = argv[3];
-    }
-    if (!strcmp(argv[1], "-u")) {
-        if (argc != 3) {
-            usage();
-        }
-        *u_flag = 1;
-    }
-
-    /* If no flag is specified, it's an error */
-    if (!(*a_flag | *n_flag | *u_flag)) {
-        usage();
-    }
-
-    /* If more than one flag is specified, it's an error */
-    if ((*a_flag + *n_flag + *u_flag) > 1) {
-        usage();
-    }
-
-    return;
-}
-
-int main(int argc, char * const argv[])
-{
-    int a_flag=0;
-    int u_flag=0;
-    int n_flag=0;
-    const char *n_name=NULL;
-    const char *n_blk_dev=NULL;
-    const char *fstab_file=NULL;
-    struct fstab *fstab=NULL;
-
-    setenv("ANDROID_LOG_TAGS", "*:i", 1); // Set log level to INFO
-    android::base::InitLogging(
-        const_cast<char **>(argv), &android::base::KernelLogger);
-
-    parse_options(argc, argv, &a_flag, &u_flag, &n_flag, &n_name, &n_blk_dev);
-
-    /* The name of the fstab file is last, after the option */
-    fstab_file = argv[argc - 1];
-
-    fstab = fs_mgr_read_fstab(fstab_file);
-
-    if (a_flag) {
-        return fs_mgr_mount_all(fstab, MOUNT_MODE_DEFAULT);
-    } else if (n_flag) {
-        return fs_mgr_do_mount(fstab, n_name, (char *)n_blk_dev, 0);
-    } else if (u_flag) {
-        return fs_mgr_unmount_all(fstab);
-    } else {
-        LERROR << me << ": Internal error, unknown option";
-        exit(1);
-    }
-
-    fs_mgr_free_fstab(fstab);
-
-    /* Should not get here */
-    exit(1);
-}
diff --git a/fs_mgr/fs_mgr_priv.h b/fs_mgr/fs_mgr_priv.h
index 5ea6e98..afd7227 100644
--- a/fs_mgr/fs_mgr_priv.h
+++ b/fs_mgr/fs_mgr_priv.h
@@ -82,6 +82,7 @@
  *
  */
 
+// clang-format off
 #define MF_WAIT                  0x1
 #define MF_CHECK                 0x2
 #define MF_CRYPT                 0x4
@@ -109,6 +110,10 @@
 #define MF_ERASEBLKSIZE     0x800000
 #define MF_LOGICALBLKSIZE  0X1000000
 #define MF_AVB             0X2000000
+#define MF_KEYDIRECTORY    0X4000000
+#define MF_SYSFS           0X8000000
+#define MF_LOGICAL        0x10000000
+// clang-format on
 
 #define DM_BUF_SIZE 4096
 
@@ -119,8 +124,8 @@
                           const std::chrono::milliseconds relative_timeout);
 bool fs_mgr_update_for_slotselect(struct fstab *fstab);
 bool fs_mgr_is_device_unlocked();
+const std::string& get_android_dt_dir();
 bool is_dt_compatible();
-bool is_device_secure();
 int load_verity_state(struct fstab_rec* fstab, int* mode);
 
 #endif /* __CORE_FS_MGR_PRIV_H */
diff --git a/fs_mgr/fs_mgr_priv_boot_config.h b/fs_mgr/fs_mgr_priv_boot_config.h
index 8773d33..417fb38 100644
--- a/fs_mgr/fs_mgr_priv_boot_config.h
+++ b/fs_mgr/fs_mgr_priv_boot_config.h
@@ -19,9 +19,14 @@
 
 #include <sys/cdefs.h>
 #include <string>
+#include <utility>
+#include <vector>
 
-const std::string kAndroidDtDir("/proc/device-tree/firmware/android");
+std::vector<std::pair<std::string, std::string>> fs_mgr_parse_boot_config(const std::string& cmdline);
 
+bool fs_mgr_get_boot_config_from_kernel(const std::string& cmdline, const std::string& key,
+                                        std::string* out_val);
+bool fs_mgr_get_boot_config_from_kernel_cmdline(const std::string& key, std::string* out_val);
 bool fs_mgr_get_boot_config(const std::string& key, std::string* out_val);
 
 #endif /* __CORE_FS_MGR_PRIV_BOOTCONFIG_H */
diff --git a/fs_mgr/fs_mgr_priv_dm_ioctl.h b/fs_mgr/fs_mgr_priv_dm_ioctl.h
deleted file mode 100644
index a00a9c1..0000000
--- a/fs_mgr/fs_mgr_priv_dm_ioctl.h
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright (C) 2016 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 __CORE_FS_MGR_PRIV_DM_IOCTL_H
-#define __CORE_FS_MGR_PRIV_DM_IOCTL_H
-
-#include <linux/dm-ioctl.h>
-#include <string>
-
-void fs_mgr_verity_ioctl_init(struct dm_ioctl* io, const std::string& name, unsigned flags);
-
-bool fs_mgr_create_verity_device(struct dm_ioctl* io, const std::string& name, int fd);
-
-bool fs_mgr_destroy_verity_device(struct dm_ioctl* io, const std::string& name, int fd);
-
-bool fs_mgr_get_verity_device_name(struct dm_ioctl* io, const std::string& name, int fd,
-                                   std::string* out_dev_name);
-
-bool fs_mgr_resume_verity_table(struct dm_ioctl* io, const std::string& name, int fd);
-
-#endif /* __CORE_FS_MGR_PRIV_DM_IOCTL_H */
diff --git a/fs_mgr/fs_mgr_slotselect.cpp b/fs_mgr/fs_mgr_slotselect.cpp
index 9ca15e2..3b01d0e 100644
--- a/fs_mgr/fs_mgr_slotselect.cpp
+++ b/fs_mgr/fs_mgr_slotselect.cpp
@@ -21,19 +21,12 @@
 #include "fs_mgr.h"
 #include "fs_mgr_priv.h"
 
-// Returns "_a" or "_b" based on two possible values in kernel cmdline:
-//   - androidboot.slot = a or b OR
-//   - androidboot.slot_suffix = _a or _b
-// TODO: remove slot_suffix once it's deprecated.
+// Returns "_a" or "_b" based on androidboot.slot_suffix in kernel cmdline, or an empty string
+// if that parameter does not exist.
 std::string fs_mgr_get_slot_suffix() {
-    std::string slot;
     std::string ab_suffix;
 
-    if (fs_mgr_get_boot_config("slot", &slot)) {
-        ab_suffix = "_" + slot;
-    } else if (!fs_mgr_get_boot_config("slot_suffix", &ab_suffix)) {
-        ab_suffix = "";
-    }
+    fs_mgr_get_boot_config("slot_suffix", &ab_suffix);
     return ab_suffix;
 }
 
@@ -43,19 +36,30 @@
     std::string ab_suffix;
 
     for (n = 0; n < fstab->num_entries; n++) {
-        if (fstab->recs[n].fs_mgr_flags & MF_SLOTSELECT) {
-            char *tmp;
+        fstab_rec& record = fstab->recs[n];
+        if (record.fs_mgr_flags & MF_SLOTSELECT) {
             if (ab_suffix.empty()) {
                 ab_suffix = fs_mgr_get_slot_suffix();
-                // Returns false as non A/B devices should not have MF_SLOTSELECT.
+                // Return false if failed to get ab_suffix when MF_SLOTSELECT is specified.
                 if (ab_suffix.empty()) return false;
             }
-            if (asprintf(&tmp, "%s%s", fstab->recs[n].blk_device, ab_suffix.c_str()) > 0) {
-                free(fstab->recs[n].blk_device);
-                fstab->recs[n].blk_device = tmp;
-            } else {
+
+            char* new_blk_device;
+            if (asprintf(&new_blk_device, "%s%s", record.blk_device, ab_suffix.c_str()) <= 0) {
                 return false;
             }
+            free(record.blk_device);
+            record.blk_device = new_blk_device;
+
+            char* new_partition_name;
+            if (record.logical_partition_name) {
+                if (asprintf(&new_partition_name, "%s%s", record.logical_partition_name,
+                             ab_suffix.c_str()) <= 0) {
+                    return false;
+                }
+                free(record.logical_partition_name);
+                record.logical_partition_name = new_partition_name;
+            }
         }
     }
     return true;
diff --git a/fs_mgr/fs_mgr_verity.cpp b/fs_mgr/fs_mgr_verity.cpp
index 5de0903..5fb4ebb 100644
--- a/fs_mgr/fs_mgr_verity.cpp
+++ b/fs_mgr/fs_mgr_verity.cpp
@@ -35,6 +35,7 @@
 #include <android-base/unique_fd.h>
 #include <crypto_utils/android_pubkey.h>
 #include <cutils/properties.h>
+#include <libdm/dm.h>
 #include <logwrap/logwrap.h>
 #include <openssl/obj_mac.h>
 #include <openssl/rsa.h>
@@ -44,7 +45,6 @@
 
 #include "fs_mgr.h"
 #include "fs_mgr_priv.h"
-#include "fs_mgr_priv_dm_ioctl.h"
 
 #define VERITY_TABLE_RSA_KEY "/verity_key"
 #define VERITY_TABLE_HASH_IDX 8
@@ -250,47 +250,27 @@
     return true;
 }
 
-static int load_verity_table(struct dm_ioctl *io, const std::string &name,
-                             uint64_t device_size, int fd,
-        const struct verity_table_params *params, format_verity_table_func format)
-{
-    char *verity_params;
-    char *buffer = (char*) io;
-    size_t bufsize;
+static int load_verity_table(android::dm::DeviceMapper& dm, const std::string& name,
+                             uint64_t device_size, const struct verity_table_params* params,
+                             format_verity_table_func format) {
+    android::dm::DmTable table;
+    table.set_readonly(true);
 
-    fs_mgr_verity_ioctl_init(io, name, DM_STATUS_TABLE_FLAG);
-
-    struct dm_target_spec *tgt = (struct dm_target_spec *) &buffer[sizeof(struct dm_ioctl)];
-
-    // set tgt arguments
-    io->target_count = 1;
-    tgt->status = 0;
-    tgt->sector_start = 0;
-    tgt->length = device_size / 512;
-    strcpy(tgt->target_type, "verity");
-
-    // build the verity params
-    verity_params = buffer + sizeof(struct dm_ioctl) + sizeof(struct dm_target_spec);
-    bufsize = DM_BUF_SIZE - (verity_params - buffer);
-
-    if (!format(verity_params, bufsize, params)) {
+    char buffer[DM_BUF_SIZE];
+    if (!format(buffer, sizeof(buffer), params)) {
         LERROR << "Failed to format verity parameters";
         return -1;
     }
 
-    LINFO << "loading verity table: '" << verity_params << "'";
-
-    // set next target boundary
-    verity_params += strlen(verity_params) + 1;
-    verity_params = (char*)(((uintptr_t)verity_params + 7) & ~7);
-    tgt->next = verity_params - buffer;
-
-    // send the ioctl to load the verity table
-    if (ioctl(fd, DM_TABLE_LOAD, io)) {
-        PERROR << "Error loading verity table";
+    android::dm::DmTargetVerityString target(0, device_size / 512, buffer);
+    if (!table.AddTarget(std::make_unique<decltype(target)>(target))) {
+        LERROR << "Failed to add verity target";
         return -1;
     }
-
+    if (!dm.CreateDevice(name, table)) {
+        LERROR << "Failed to create verity device \"" << name << "\"";
+        return -1;
+    }
     return 0;
 }
 
@@ -760,17 +740,10 @@
     struct fec_verity_metadata verity;
     struct verity_table_params params = { .table = NULL };
 
-    alignas(dm_ioctl) char buffer[DM_BUF_SIZE];
-    struct dm_ioctl *io = (struct dm_ioctl *) buffer;
     const std::string mount_point(basename(fstab->mount_point));
     bool verified_at_boot = false;
 
-    // This is a public API and so deserves its own check to see if verity
-    // setup is needed at all.
-    if (!is_device_secure()) {
-        LINFO << "Verity setup skipped for " << mount_point;
-        return FS_MGR_SETUP_VERITY_SKIPPED;
-    }
+    android::dm::DeviceMapper& dm = android::dm::DeviceMapper::Instance();
 
     if (fec_open(&f, fstab->blk_device, O_RDONLY, FEC_VERITY_DISABLE,
             FEC_DEFAULT_ROOTS) < 0) {
@@ -792,7 +765,7 @@
 #ifdef ALLOW_ADBD_DISABLE_VERITY
     if (verity.disabled) {
         retval = FS_MGR_SETUP_VERITY_DISABLED;
-        LINFO << "Attempt to cleanly disable verity - only works in USERDEBUG";
+        LINFO << "Attempt to cleanly disable verity - only works in USERDEBUG/ENG";
         goto out;
     }
 #endif
@@ -804,24 +777,6 @@
 
     params.ecc_dev = fstab->blk_device;
 
-    // get the device mapper fd
-    if ((fd = open("/dev/device-mapper", O_RDWR)) < 0) {
-        PERROR << "Error opening device mapper";
-        goto out;
-    }
-
-    // create the device
-    if (!fs_mgr_create_verity_device(io, mount_point, fd)) {
-        LERROR << "Couldn't create verity device!";
-        goto out;
-    }
-
-    // get the name of the device file
-    if (!fs_mgr_get_verity_device_name(io, mount_point, fd, &verity_blk_name)) {
-        LERROR << "Couldn't get verity device number!";
-        goto out;
-    }
-
     if (load_verity_state(fstab, &params.mode) < 0) {
         /* if accessing or updating the state failed, switch to the default
          * safe mode. This makes sure the device won't end up in an endless
@@ -841,9 +796,15 @@
 
     // verify the signature on the table
     if (verify_verity_signature(verity) < 0) {
+        // Allow signature verification error when the device is unlocked
+        if (fs_mgr_is_device_unlocked()) {
+            retval = FS_MGR_SETUP_VERITY_SKIPPED;
+            LWARNING << "Allow signature verification error when the device is unlocked";
+            goto out;
+        }
         if (params.mode == VERITY_MODE_LOGGING) {
             // the user has been warned, allow mounting without dm-verity
-            retval = FS_MGR_SETUP_VERITY_SUCCESS;
+            retval = FS_MGR_SETUP_VERITY_SKIPPED;
             goto out;
         }
 
@@ -861,8 +822,7 @@
                                    fstab->fs_mgr_flags & MF_SLOTSELECT);
 
     // load the verity mapping table
-    if (load_verity_table(io, mount_point, verity.data_size, fd, &params,
-            format_verity_table) == 0) {
+    if (load_verity_table(dm, mount_point, verity.data_size, &params, format_verity_table) == 0) {
         goto loaded;
     }
 
@@ -871,15 +831,14 @@
         LINFO << "Disabling error correction for " << mount_point.c_str();
         params.ecc.valid = false;
 
-        if (load_verity_table(io, mount_point, verity.data_size, fd, &params,
-                format_verity_table) == 0) {
+        if (load_verity_table(dm, mount_point, verity.data_size, &params, format_verity_table) == 0) {
             goto loaded;
         }
     }
 
     // try the legacy format for backwards compatibility
-    if (load_verity_table(io, mount_point, verity.data_size, fd, &params,
-            format_legacy_verity_table) == 0) {
+    if (load_verity_table(dm, mount_point, verity.data_size, &params, format_legacy_verity_table) ==
+        0) {
         goto loaded;
     }
 
@@ -888,8 +847,8 @@
         LINFO << "Falling back to EIO mode for " << mount_point.c_str();
         params.mode = VERITY_MODE_EIO;
 
-        if (load_verity_table(io, mount_point, verity.data_size, fd, &params,
-                format_legacy_verity_table) == 0) {
+        if (load_verity_table(dm, mount_point, verity.data_size, &params,
+                              format_legacy_verity_table) == 0) {
             goto loaded;
         }
     }
@@ -898,9 +857,8 @@
     goto out;
 
 loaded:
-
-    // activate the device
-    if (!fs_mgr_resume_verity_table(io, mount_point, fd)) {
+    if (!dm.GetDmDevicePathByName(mount_point, &verity_blk_name)) {
+        LERROR << "Couldn't get verity device number!";
         goto out;
     }
 
@@ -923,7 +881,7 @@
     if (!verified_at_boot) {
         free(fstab->blk_device);
         fstab->blk_device = strdup(verity_blk_name.c_str());
-    } else if (!fs_mgr_destroy_verity_device(io, mount_point, fd)) {
+    } else if (!dm.DeleteDevice(mount_point)) {
         LERROR << "Failed to remove verity device " << mount_point.c_str();
         goto out;
     }
diff --git a/fs_mgr/include/fs_mgr.h b/fs_mgr/include/fs_mgr.h
index 3d3faf3..1049fb6 100644
--- a/fs_mgr/include/fs_mgr.h
+++ b/fs_mgr/include/fs_mgr.h
@@ -22,6 +22,8 @@
 #include <stdbool.h>
 #include <linux/dm-ioctl.h>
 
+#include <functional>
+
 #include <fstab/fstab.h>
 
 // Magic number at start of verity metadata
@@ -31,8 +33,6 @@
 // turn verity off in userdebug builds.
 #define VERITY_METADATA_MAGIC_DISABLE 0x46464f56 // "VOFF"
 
-__BEGIN_DECLS
-
 // Verity modes
 enum verity_mode {
     VERITY_MODE_EIO = 0,
@@ -50,9 +50,11 @@
 };
 
 // Callback function for verity status
-typedef void (*fs_mgr_verity_state_callback)(struct fstab_rec *fstab,
-        const char *mount_point, int mode, int status);
+typedef void fs_mgr_verity_state_callback(struct fstab_rec* fstab, const char* mount_point,
+                                          int mode, int status);
 
+#define FS_MGR_MNTALL_DEV_IS_METADATA_ENCRYPTED 7
+#define FS_MGR_MNTALL_DEV_NEEDS_METADATA_ENCRYPTION 6
 #define FS_MGR_MNTALL_DEV_FILE_ENCRYPTED 5
 #define FS_MGR_MNTALL_DEV_NEEDS_RECOVERY 4
 #define FS_MGR_MNTALL_DEV_NEEDS_ENCRYPTION 3
@@ -70,12 +72,12 @@
                     char *tmp_mount_point);
 int fs_mgr_do_mount_one(struct fstab_rec *rec);
 int fs_mgr_do_tmpfs_mount(const char *n_name);
-int fs_mgr_unmount_all(struct fstab *fstab);
-int fs_mgr_get_crypt_info(struct fstab *fstab, char *key_loc,
-                          char *real_blk_device, int size);
+struct fstab_rec const* fs_mgr_get_crypt_entry(struct fstab const* fstab);
+void fs_mgr_get_crypt_info(struct fstab* fstab, char* key_loc, char* real_blk_device, size_t size);
 bool fs_mgr_load_verity_state(int* mode);
-bool fs_mgr_update_verity_state(fs_mgr_verity_state_callback callback);
+bool fs_mgr_update_verity_state(std::function<fs_mgr_verity_state_callback> callback);
 int fs_mgr_swapon_all(struct fstab *fstab);
+bool fs_mgr_update_logical_partition(struct fstab_rec* rec);
 
 int fs_mgr_do_format(struct fstab_rec *fstab, bool reserve_footer);
 
@@ -85,6 +87,4 @@
 #define FS_MGR_SETUP_VERITY_SUCCESS 0
 int fs_mgr_setup_verity(struct fstab_rec *fstab, bool wait_for_verity_dev);
 
-__END_DECLS
-
 #endif /* __CORE_FS_MGR_H */
diff --git a/fs_mgr/include/fs_mgr_dm_linear.h b/fs_mgr/include/fs_mgr_dm_linear.h
new file mode 100644
index 0000000..cac475c
--- /dev/null
+++ b/fs_mgr/include/fs_mgr_dm_linear.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef __CORE_FS_MGR_DM_LINEAR_H
+#define __CORE_FS_MGR_DM_LINEAR_H
+
+#include <stdint.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <libdm/dm.h>
+#include <liblp/metadata_format.h>
+
+namespace android {
+namespace fs_mgr {
+
+bool CreateLogicalPartitions(const std::string& block_device);
+
+// Create a block device for a single logical partition, given metadata and
+// the partition name. On success, a path to the partition's block device is
+// returned.
+bool CreateLogicalPartition(const std::string& block_device, uint32_t metadata_slot,
+                            const std::string& partition_name, std::string* path);
+
+// Destroy the block device for a logical partition, by name.
+bool DestroyLogicalPartition(const std::string& name);
+
+}  // namespace fs_mgr
+}  // namespace android
+
+#endif  // __CORE_FS_MGR_DM_LINEAR_H
diff --git a/fs_mgr/include_fstab/fstab/fstab.h b/fs_mgr/include_fstab/fstab/fstab.h
index 3ea4e03..b1ee328 100644
--- a/fs_mgr/include_fstab/fstab/fstab.h
+++ b/fs_mgr/include_fstab/fstab/fstab.h
@@ -22,13 +22,8 @@
 #include <stdint.h>
 #include <stdio.h>
 
-// C++ only headers
-// TODO: move this into separate header files under include/fs_mgr/*.h
-#ifdef __cplusplus
+#include <set>
 #include <string>
-#endif
-
-__BEGIN_DECLS
 
 /*
  * The entries must be kept in the same order as they were seen in the fstab.
@@ -38,17 +33,18 @@
 struct fstab {
     int num_entries;
     struct fstab_rec* recs;
-    char* fstab_filename;
 };
 
 struct fstab_rec {
     char* blk_device;
+    char* logical_partition_name;
     char* mount_point;
     char* fs_type;
     unsigned long flags;
     char* fs_options;
     int fs_mgr_flags;
     char* key_loc;
+    char* key_dir;
     char* verity_loc;
     long long length;
     char* label;
@@ -57,9 +53,11 @@
     int max_comp_streams;
     unsigned int zram_size;
     uint64_t reserved_size;
-    unsigned int file_encryption_mode;
+    unsigned int file_contents_mode;
+    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();
@@ -69,7 +67,7 @@
 
 int fs_mgr_add_entry(struct fstab* fstab, const char* mount_point, const char* fs_type,
                      const char* blk_device);
-struct fstab_rec* fs_mgr_get_entry_for_mount_point(struct fstab* fstab, const char* path);
+struct fstab_rec* fs_mgr_get_entry_for_mount_point(struct fstab* fstab, const std::string& path);
 int fs_mgr_is_voldmanaged(const struct fstab_rec* fstab);
 int fs_mgr_is_nonremovable(const struct fstab_rec* fstab);
 int fs_mgr_is_verified(const struct fstab_rec* fstab);
@@ -77,22 +75,20 @@
 int fs_mgr_is_avb(const struct fstab_rec* fstab);
 int fs_mgr_is_encryptable(const struct fstab_rec* fstab);
 int fs_mgr_is_file_encrypted(const struct fstab_rec* fstab);
-const char* fs_mgr_get_file_encryption_mode(const struct fstab_rec* fstab);
+void fs_mgr_get_file_encryption_modes(const struct fstab_rec* fstab, const char** contents_mode_ret,
+                                      const char** filenames_mode_ret);
 int fs_mgr_is_convertible_to_fbe(const struct fstab_rec* fstab);
 int fs_mgr_is_noemulatedsd(const struct fstab_rec* fstab);
-int fs_mgr_is_notrim(struct fstab_rec* fstab);
-int fs_mgr_is_formattable(struct fstab_rec* fstab);
-int fs_mgr_is_slotselect(struct fstab_rec* fstab);
-int fs_mgr_is_nofail(struct fstab_rec* fstab);
-int fs_mgr_is_latemount(struct fstab_rec* fstab);
-int fs_mgr_is_quota(struct fstab_rec* fstab);
+int fs_mgr_is_notrim(const struct fstab_rec* fstab);
+int fs_mgr_is_formattable(const struct fstab_rec* fstab);
+int fs_mgr_is_slotselect(const struct fstab_rec* fstab);
+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_is_logical(const struct fstab_rec* fstab);
+int fs_mgr_has_sysfs_path(const struct fstab_rec* fstab);
 
-__END_DECLS
-
-// C++ only functions
-// TODO: move this into separate header files under include/fs_mgr/*.h
-#ifdef __cplusplus
 std::string fs_mgr_get_slot_suffix();
-#endif
+std::set<std::string> fs_mgr_get_boot_devices();
 
 #endif /* __CORE_FS_TAB_H */
diff --git a/fs_mgr/libdm/Android.bp b/fs_mgr/libdm/Android.bp
new file mode 100644
index 0000000..22af123
--- /dev/null
+++ b/fs_mgr/libdm/Android.bp
@@ -0,0 +1,54 @@
+//
+// 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_library_static {
+    name: "libdm",
+    defaults: ["fs_mgr_defaults"],
+    recovery_available: true,
+
+    export_include_dirs: ["include"],
+    cflags: [
+        // TODO(b/110035986): Allows us to create a skeleton of required classes
+        "-Wno-unused-private-field",
+    ],
+
+    srcs: [
+        "dm_table.cpp",
+        "dm_target.cpp",
+        "dm.cpp",
+        "loop_control.cpp",
+    ],
+
+    header_libs: [
+        "libbase_headers",
+        "liblog_headers",
+    ],
+}
+
+cc_test {
+    name: "libdm_test",
+    defaults: ["fs_mgr_defaults"],
+    static_libs: [
+        "libdm",
+        "libbase",
+        "liblog",
+    ],
+    srcs: [
+        "dm_test.cpp",
+        "loop_control_test.cpp",
+        "test_util.cpp",
+    ]
+}
diff --git a/fs_mgr/libdm/dm.cpp b/fs_mgr/libdm/dm.cpp
new file mode 100644
index 0000000..ad3b6f4
--- /dev/null
+++ b/fs_mgr/libdm/dm.cpp
@@ -0,0 +1,331 @@
+/*
+ * 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 "libdm/dm.h"
+
+#include <sys/ioctl.h>
+#include <sys/sysmacros.h>
+#include <sys/types.h>
+
+#include <android-base/macros.h>
+
+namespace android {
+namespace dm {
+
+DeviceMapper& DeviceMapper::Instance() {
+    static DeviceMapper instance;
+    return instance;
+}
+// Creates a new device mapper device
+bool DeviceMapper::CreateDevice(const std::string& name) {
+    if (name.empty()) {
+        LOG(ERROR) << "Unnamed device mapper device creation is not supported";
+        return false;
+    }
+
+    if (name.size() >= DM_NAME_LEN) {
+        LOG(ERROR) << "[" << name << "] is too long to be device mapper name";
+        return false;
+    }
+
+    struct dm_ioctl io;
+    InitIo(&io, name);
+
+    if (ioctl(fd_, DM_DEV_CREATE, &io)) {
+        PLOG(ERROR) << "DM_DEV_CREATE failed for [" << name << "]";
+        return false;
+    }
+
+    // Check to make sure the newly created device doesn't already have targets
+    // added or opened by someone
+    CHECK(io.target_count == 0) << "Unexpected targets for newly created [" << name << "] device";
+    CHECK(io.open_count == 0) << "Unexpected opens for newly created [" << name << "] device";
+
+    // Creates a new device mapper device with the name passed in
+    return true;
+}
+
+bool DeviceMapper::DeleteDevice(const std::string& name) {
+    if (name.empty()) {
+        LOG(ERROR) << "Unnamed device mapper device creation is not supported";
+        return false;
+    }
+
+    if (name.size() >= DM_NAME_LEN) {
+        LOG(ERROR) << "[" << name << "] is too long to be device mapper name";
+        return false;
+    }
+
+    struct dm_ioctl io;
+    InitIo(&io, name);
+
+    if (ioctl(fd_, DM_DEV_REMOVE, &io)) {
+        PLOG(ERROR) << "DM_DEV_REMOVE failed for [" << name << "]";
+        return false;
+    }
+
+    // Check to make sure appropriate uevent is generated so ueventd will
+    // do the right thing and remove the corresponding device node and symlinks.
+    CHECK(io.flags & DM_UEVENT_GENERATED_FLAG)
+            << "Didn't generate uevent for [" << name << "] removal";
+
+    return true;
+}
+
+const std::unique_ptr<DmTable> DeviceMapper::table(const std::string& /* name */) const {
+    // TODO(b/110035986): Return the table, as read from the kernel instead
+    return nullptr;
+}
+
+DmDeviceState DeviceMapper::state(const std::string& /* name */) const {
+    // TODO(b/110035986): Return the state, as read from the kernel instead
+    return DmDeviceState::INVALID;
+}
+
+bool DeviceMapper::CreateDevice(const std::string& name, const DmTable& table) {
+    if (!CreateDevice(name)) {
+        return false;
+    }
+    if (!LoadTableAndActivate(name, table)) {
+        DeleteDevice(name);
+        return false;
+    }
+    return true;
+}
+
+bool DeviceMapper::LoadTableAndActivate(const std::string& name, const DmTable& table) {
+    std::string ioctl_buffer(sizeof(struct dm_ioctl), 0);
+    ioctl_buffer += table.Serialize();
+
+    struct dm_ioctl* io = reinterpret_cast<struct dm_ioctl*>(&ioctl_buffer[0]);
+    InitIo(io, name);
+    io->data_size = ioctl_buffer.size();
+    io->data_start = sizeof(struct dm_ioctl);
+    io->target_count = static_cast<uint32_t>(table.num_targets());
+    if (table.readonly()) {
+        io->flags |= DM_READONLY_FLAG;
+    }
+    if (ioctl(fd_, DM_TABLE_LOAD, io)) {
+        PLOG(ERROR) << "DM_TABLE_LOAD failed";
+        return false;
+    }
+
+    InitIo(io, name);
+    if (ioctl(fd_, DM_DEV_SUSPEND, io)) {
+        PLOG(ERROR) << "DM_TABLE_SUSPEND resume failed";
+        return false;
+    }
+    return true;
+}
+
+// Reads all the available device mapper targets and their corresponding
+// versions from the kernel and returns in a vector
+bool DeviceMapper::GetAvailableTargets(std::vector<DmTargetTypeInfo>* targets) {
+    targets->clear();
+
+    // calculate the space needed to read a maximum of kMaxPossibleDmTargets
+    uint32_t payload_size = sizeof(struct dm_target_versions);
+    payload_size += DM_MAX_TYPE_NAME;
+    // device mapper wants every target spec to be aligned at 8-byte boundary
+    payload_size = DM_ALIGN(payload_size);
+    payload_size *= kMaxPossibleDmTargets;
+
+    uint32_t data_size = sizeof(struct dm_ioctl) + payload_size;
+    auto buffer = std::unique_ptr<void, void (*)(void*)>(calloc(1, data_size), free);
+    if (buffer == nullptr) {
+        LOG(ERROR) << "failed to allocate memory";
+        return false;
+    }
+
+    // Sets appropriate data size and data_start to make sure we tell kernel
+    // about the total size of the buffer we are passing and where to start
+    // writing the list of targets.
+    struct dm_ioctl* io = reinterpret_cast<struct dm_ioctl*>(buffer.get());
+    InitIo(io);
+    io->data_size = data_size;
+    io->data_start = sizeof(*io);
+
+    if (ioctl(fd_, DM_LIST_VERSIONS, io)) {
+        PLOG(ERROR) << "DM_LIST_VERSIONS failed";
+        return false;
+    }
+
+    // If the provided buffer wasn't enough to list all targets, note that
+    // any data beyond sizeof(*io) must not be read in this case
+    if (io->flags & DM_BUFFER_FULL_FLAG) {
+        LOG(INFO) << data_size << " is not enough memory to list all dm targets";
+        return false;
+    }
+
+    // if there are no targets registered, return success with empty vector
+    if (io->data_size == sizeof(*io)) {
+        return true;
+    }
+
+    // Parse each target and list the name and version
+    // TODO(b/110035986): Templatize this
+    uint32_t next = sizeof(*io);
+    data_size = io->data_size - next;
+    struct dm_target_versions* vers =
+            reinterpret_cast<struct dm_target_versions*>(static_cast<char*>(buffer.get()) + next);
+    while (next && data_size) {
+        targets->emplace_back(vers);
+        if (vers->next == 0) {
+            break;
+        }
+        next += vers->next;
+        data_size -= vers->next;
+        vers = reinterpret_cast<struct dm_target_versions*>(static_cast<char*>(buffer.get()) + next);
+    }
+
+    return true;
+}
+
+bool DeviceMapper::GetAvailableDevices(std::vector<DmBlockDevice>* devices) {
+    devices->clear();
+
+    // calculate the space needed to read a maximum of 256 targets, each with
+    // name with maximum length of 16 bytes
+    uint32_t payload_size = sizeof(struct dm_name_list);
+    // 128-bytes for the name
+    payload_size += DM_NAME_LEN;
+    // dm wants every device spec to be aligned at 8-byte boundary
+    payload_size = DM_ALIGN(payload_size);
+    payload_size *= kMaxPossibleDmDevices;
+    uint32_t data_size = sizeof(struct dm_ioctl) + payload_size;
+    auto buffer = std::unique_ptr<void, void (*)(void*)>(calloc(1, data_size), free);
+    if (buffer == nullptr) {
+        LOG(ERROR) << "failed to allocate memory";
+        return false;
+    }
+
+    // Sets appropriate data size and data_start to make sure we tell kernel
+    // about the total size of the buffer we are passing and where to start
+    // writing the list of targets.
+    struct dm_ioctl* io = reinterpret_cast<struct dm_ioctl*>(buffer.get());
+    InitIo(io);
+    io->data_size = data_size;
+    io->data_start = sizeof(*io);
+
+    if (ioctl(fd_, DM_LIST_DEVICES, io)) {
+        PLOG(ERROR) << "DM_LIST_DEVICES failed";
+        return false;
+    }
+
+    // If the provided buffer wasn't enough to list all devices any data
+    // beyond sizeof(*io) must not be read.
+    if (io->flags & DM_BUFFER_FULL_FLAG) {
+        LOG(INFO) << data_size << " is not enough memory to list all dm devices";
+        return false;
+    }
+
+    // if there are no devices created yet, return success with empty vector
+    if (io->data_size == sizeof(*io)) {
+        return true;
+    }
+
+    // Parse each device and add a new DmBlockDevice to the vector
+    // created from the kernel data.
+    uint32_t next = sizeof(*io);
+    data_size = io->data_size - next;
+    struct dm_name_list* dm_dev =
+            reinterpret_cast<struct dm_name_list*>(static_cast<char*>(buffer.get()) + next);
+
+    while (next && data_size) {
+        devices->emplace_back((dm_dev));
+        if (dm_dev->next == 0) {
+            break;
+        }
+        next += dm_dev->next;
+        data_size -= dm_dev->next;
+        dm_dev = reinterpret_cast<struct dm_name_list*>(static_cast<char*>(buffer.get()) + next);
+    }
+
+    return true;
+}
+
+// Accepts a device mapper device name (like system_a, vendor_b etc) and
+// returns the path to it's device node (or symlink to the device node)
+bool DeviceMapper::GetDmDevicePathByName(const std::string& name, std::string* path) {
+    struct dm_ioctl io;
+    InitIo(&io, name);
+    if (ioctl(fd_, DM_DEV_STATUS, &io) < 0) {
+        PLOG(ERROR) << "DM_DEV_STATUS failed for " << name;
+        return false;
+    }
+
+    uint32_t dev_num = minor(io.dev);
+    *path = "/dev/block/dm-" + std::to_string(dev_num);
+    return true;
+}
+
+bool DeviceMapper::GetTableStatus(const std::string& name, std::vector<TargetInfo>* table) {
+    char buffer[4096];
+    struct dm_ioctl* io = reinterpret_cast<struct dm_ioctl*>(buffer);
+
+    InitIo(io, name);
+    io->data_size = sizeof(buffer);
+    io->data_start = sizeof(*io);
+    if (ioctl(fd_, DM_TABLE_STATUS, io) < 0) {
+        PLOG(ERROR) << "DM_TABLE_STATUS failed for " << name;
+        return false;
+    }
+    if (io->flags & DM_BUFFER_FULL_FLAG) {
+        PLOG(ERROR) << "DM_TABLE_STATUS result for " << name << " was too large";
+        return false;
+    }
+
+    uint32_t cursor = io->data_start;
+    uint32_t data_end = std::min(io->data_size, uint32_t(sizeof(buffer)));
+    for (uint32_t i = 0; i < io->target_count; i++) {
+        if (cursor + sizeof(struct dm_target_spec) > data_end) {
+            break;
+        }
+        // After each dm_target_spec is a status string. spec->next is an
+        // offset from |io->data_start|, and we clamp it to the size of our
+        // buffer.
+        struct dm_target_spec* spec = reinterpret_cast<struct dm_target_spec*>(buffer + cursor);
+        uint32_t data_offset = cursor + sizeof(dm_target_spec);
+        uint32_t next_cursor = std::min(io->data_start + spec->next, data_end);
+
+        std::string data;
+        if (next_cursor > data_offset) {
+            // Note: we use c_str() to eliminate any extra trailing 0s.
+            data = std::string(buffer + data_offset, next_cursor - data_offset).c_str();
+        }
+        table->emplace_back(*spec, data);
+        cursor = next_cursor;
+    }
+    return true;
+}
+
+// private methods of DeviceMapper
+void DeviceMapper::InitIo(struct dm_ioctl* io, const std::string& name) const {
+    CHECK(io != nullptr) << "nullptr passed to dm_ioctl initialization";
+    memset(io, 0, sizeof(*io));
+
+    io->version[0] = DM_VERSION0;
+    io->version[1] = DM_VERSION1;
+    io->version[2] = DM_VERSION2;
+    io->data_size = sizeof(*io);
+    io->data_start = 0;
+    if (!name.empty()) {
+        strlcpy(io->name, name.c_str(), sizeof(io->name));
+    }
+}
+
+}  // namespace dm
+}  // namespace android
diff --git a/fs_mgr/libdm/dm_table.cpp b/fs_mgr/libdm/dm_table.cpp
new file mode 100644
index 0000000..15c7ce1
--- /dev/null
+++ b/fs_mgr/libdm/dm_table.cpp
@@ -0,0 +1,72 @@
+/*
+ * 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 "libdm/dm_table.h"
+
+#include <android-base/logging.h>
+#include <android-base/macros.h>
+
+namespace android {
+namespace dm {
+
+bool DmTable::AddTarget(std::unique_ptr<DmTarget>&& target) {
+    if (!target->Valid()) {
+        return false;
+    }
+    targets_.push_back(std::move(target));
+    return true;
+}
+
+bool DmTable::RemoveTarget(std::unique_ptr<DmTarget>&& /* target */) {
+    return true;
+}
+
+bool DmTable::valid() const {
+    if (targets_.empty()) {
+        LOG(ERROR) << "Device-mapper table must have at least one target.";
+        return "";
+    }
+    if (targets_[0]->start() != 0) {
+        LOG(ERROR) << "Device-mapper table must start at logical sector 0.";
+        return "";
+    }
+    return true;
+}
+
+uint64_t DmTable::num_sectors() const {
+    return valid() ? num_sectors_ : 0;
+}
+
+// Returns a string representation of the table that is ready to be passed
+// down to the kernel for loading.
+//
+// Implementation must verify there are no gaps in the table, table starts
+// with sector == 0, and iterate over each target to get its table
+// serialized.
+std::string DmTable::Serialize() const {
+    if (!valid()) {
+        return "";
+    }
+
+    std::string table;
+    for (const auto& target : targets_) {
+        table += target->Serialize();
+    }
+    return table;
+}
+
+}  // namespace dm
+}  // namespace android
diff --git a/fs_mgr/libdm/dm_target.cpp b/fs_mgr/libdm/dm_target.cpp
new file mode 100644
index 0000000..7c18267
--- /dev/null
+++ b/fs_mgr/libdm/dm_target.cpp
@@ -0,0 +1,119 @@
+/*
+ * 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 "libdm/dm_target.h"
+
+#include <android-base/logging.h>
+#include <android-base/macros.h>
+#include <android-base/strings.h>
+
+#include <libdm/dm.h>
+
+namespace android {
+namespace dm {
+
+std::string DmTarget::Serialize() const {
+    // Create a string containing a dm_target_spec, parameter data, and an
+    // explicit null terminator.
+    std::string data(sizeof(dm_target_spec), '\0');
+    data += GetParameterString();
+    data.push_back('\0');
+
+    // The kernel expects each target to be 8-byte aligned.
+    size_t padding = DM_ALIGN(data.size()) - data.size();
+    for (size_t i = 0; i < padding; i++) {
+        data.push_back('\0');
+    }
+
+    // Finally fill in the dm_target_spec.
+    struct dm_target_spec* spec = reinterpret_cast<struct dm_target_spec*>(&data[0]);
+    spec->sector_start = start();
+    spec->length = size();
+    strlcpy(spec->target_type, name().c_str(), sizeof(spec->target_type));
+    spec->next = (uint32_t)data.size();
+    return data;
+}
+
+std::string DmTargetZero::GetParameterString() const {
+    // The zero target type has no additional parameters.
+    return "";
+}
+
+std::string DmTargetLinear::GetParameterString() const {
+    return block_device_ + " " + std::to_string(physical_sector_);
+}
+
+DmTargetVerity::DmTargetVerity(uint64_t start, uint64_t length, uint32_t version,
+                               const std::string& block_device, const std::string& hash_device,
+                               uint32_t data_block_size, uint32_t hash_block_size,
+                               uint32_t num_data_blocks, uint32_t hash_start_block,
+                               const std::string& hash_algorithm, const std::string& root_digest,
+                               const std::string& salt)
+    : DmTarget(start, length), valid_(true) {
+    base_args_ = {
+            std::to_string(version),
+            block_device,
+            hash_device,
+            std::to_string(data_block_size),
+            std::to_string(hash_block_size),
+            std::to_string(num_data_blocks),
+            std::to_string(hash_start_block),
+            hash_algorithm,
+            root_digest,
+            salt,
+    };
+}
+
+void DmTargetVerity::UseFec(const std::string& device, uint32_t num_roots, uint32_t num_blocks,
+                            uint32_t start) {
+    optional_args_.emplace_back("use_fec_from_device");
+    optional_args_.emplace_back(device);
+    optional_args_.emplace_back("fec_roots");
+    optional_args_.emplace_back(std::to_string(num_roots));
+    optional_args_.emplace_back("fec_blocks");
+    optional_args_.emplace_back(std::to_string(num_blocks));
+    optional_args_.emplace_back("fec_start");
+    optional_args_.emplace_back(std::to_string(start));
+}
+
+void DmTargetVerity::SetVerityMode(const std::string& mode) {
+    if (mode != "restart_on_corruption" && mode != "ignore_corruption") {
+        LOG(ERROR) << "Unknown verity mode: " << mode;
+        valid_ = false;
+        return;
+    }
+    optional_args_.emplace_back(mode);
+}
+
+void DmTargetVerity::IgnoreZeroBlocks() {
+    optional_args_.emplace_back("ignore_zero_blocks");
+}
+
+std::string DmTargetVerity::GetParameterString() const {
+    std::string base = android::base::Join(base_args_, " ");
+    if (optional_args_.empty()) {
+        return base;
+    }
+    std::string optional = android::base::Join(optional_args_, " ");
+    return base + " " + std::to_string(optional_args_.size()) + " " + optional;
+}
+
+std::string DmTargetAndroidVerity::GetParameterString() const {
+    return keyid_ + " " + block_device_;
+}
+
+}  // namespace dm
+}  // namespace android
diff --git a/fs_mgr/libdm/dm_test.cpp b/fs_mgr/libdm/dm_test.cpp
new file mode 100644
index 0000000..cc61917
--- /dev/null
+++ b/fs_mgr/libdm/dm_test.cpp
@@ -0,0 +1,202 @@
+/*
+ * 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 <fcntl.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <chrono>
+#include <ctime>
+#include <map>
+#include <thread>
+
+#include <android-base/file.h>
+#include <android-base/unique_fd.h>
+#include <gtest/gtest.h>
+#include <libdm/dm.h>
+#include <libdm/loop_control.h>
+#include "test_util.h"
+
+using namespace std;
+using namespace android::dm;
+using unique_fd = android::base::unique_fd;
+
+TEST(libdm, HasMinimumTargets) {
+    DeviceMapper& dm = DeviceMapper::Instance();
+    vector<DmTargetTypeInfo> targets;
+    ASSERT_TRUE(dm.GetAvailableTargets(&targets));
+
+    map<string, DmTargetTypeInfo> by_name;
+    for (const auto& target : targets) {
+        by_name[target.name()] = target;
+    }
+
+    auto iter = by_name.find("linear");
+    EXPECT_NE(iter, by_name.end());
+}
+
+// Helper to ensure that device mapper devices are released.
+class TempDevice {
+  public:
+    TempDevice(const std::string& name, const DmTable& table)
+        : dm_(DeviceMapper::Instance()), name_(name), valid_(false) {
+        valid_ = dm_.CreateDevice(name, table);
+    }
+    TempDevice(TempDevice&& other) : dm_(other.dm_), name_(other.name_), valid_(other.valid_) {
+        other.valid_ = false;
+    }
+    ~TempDevice() {
+        if (valid_) {
+            dm_.DeleteDevice(name_);
+        }
+    }
+    bool Destroy() {
+        if (!valid_) {
+            return false;
+        }
+        valid_ = false;
+        return dm_.DeleteDevice(name_);
+    }
+    bool WaitForUdev() const {
+        auto start_time = std::chrono::steady_clock::now();
+        while (true) {
+            if (!access(path().c_str(), F_OK)) {
+                return true;
+            }
+            if (errno != ENOENT) {
+                return false;
+            }
+            std::this_thread::sleep_for(50ms);
+            std::chrono::duration elapsed = std::chrono::steady_clock::now() - start_time;
+            if (elapsed >= 5s) {
+                return false;
+            }
+        }
+    }
+    std::string path() const {
+        std::string device_path;
+        if (!dm_.GetDmDevicePathByName(name_, &device_path)) {
+            return "";
+        }
+        return device_path;
+    }
+    const std::string& name() const { return name_; }
+    bool valid() const { return valid_; }
+
+    TempDevice(const TempDevice&) = delete;
+    TempDevice& operator=(const TempDevice&) = delete;
+
+    TempDevice& operator=(TempDevice&& other) {
+        name_ = other.name_;
+        valid_ = other.valid_;
+        other.valid_ = false;
+        return *this;
+    }
+
+  private:
+    DeviceMapper& dm_;
+    std::string name_;
+    bool valid_;
+};
+
+TEST(libdm, DmLinear) {
+    unique_fd tmp1(CreateTempFile("file_1", 4096));
+    ASSERT_GE(tmp1, 0);
+    unique_fd tmp2(CreateTempFile("file_2", 4096));
+    ASSERT_GE(tmp2, 0);
+
+    // Create two different files. These will back two separate loop devices.
+    const char message1[] = "Hello! This is sector 1.";
+    const char message2[] = "Goodbye. This is sector 2.";
+    ASSERT_TRUE(android::base::WriteFully(tmp1, message1, sizeof(message1)));
+    ASSERT_TRUE(android::base::WriteFully(tmp2, message2, sizeof(message2)));
+
+    LoopDevice loop_a(tmp1);
+    ASSERT_TRUE(loop_a.valid());
+    LoopDevice loop_b(tmp2);
+    ASSERT_TRUE(loop_b.valid());
+
+    // Define a 2-sector device, with each sector mapping to the first sector
+    // of one of our loop devices.
+    DmTable table;
+    ASSERT_TRUE(table.AddTarget(make_unique<DmTargetLinear>(0, 1, loop_a.device(), 0)));
+    ASSERT_TRUE(table.AddTarget(make_unique<DmTargetLinear>(1, 1, loop_b.device(), 0)));
+    ASSERT_TRUE(table.valid());
+
+    TempDevice dev("libdm-test-dm-linear", table);
+    ASSERT_TRUE(dev.valid());
+    ASSERT_FALSE(dev.path().empty());
+    ASSERT_TRUE(dev.WaitForUdev());
+
+    // Note: a scope is needed to ensure that there are no open descriptors
+    // when we go to close the device.
+    {
+        unique_fd dev_fd(open(dev.path().c_str(), O_RDWR));
+        ASSERT_GE(dev_fd, 0);
+
+        // Test that each sector of our device is correctly mapped to each loop
+        // device.
+        char sector[512];
+        ASSERT_TRUE(android::base::ReadFully(dev_fd, sector, sizeof(sector)));
+        ASSERT_EQ(strncmp(sector, message1, sizeof(message1)), 0);
+        ASSERT_TRUE(android::base::ReadFully(dev_fd, sector, sizeof(sector)));
+        ASSERT_EQ(strncmp(sector, message2, sizeof(message2)), 0);
+    }
+
+    // Test GetTableStatus.
+    DeviceMapper& dm = DeviceMapper::Instance();
+    vector<DeviceMapper::TargetInfo> targets;
+    ASSERT_TRUE(dm.GetTableStatus(dev.name(), &targets));
+    ASSERT_EQ(targets.size(), 2);
+    EXPECT_EQ(strcmp(targets[0].spec.target_type, "linear"), 0);
+    EXPECT_TRUE(targets[0].data.empty());
+    EXPECT_EQ(targets[0].spec.sector_start, 0);
+    EXPECT_EQ(targets[0].spec.length, 1);
+    EXPECT_EQ(strcmp(targets[1].spec.target_type, "linear"), 0);
+    EXPECT_TRUE(targets[1].data.empty());
+    EXPECT_EQ(targets[1].spec.sector_start, 1);
+    EXPECT_EQ(targets[1].spec.length, 1);
+
+    // Normally the TestDevice destructor would delete this, but at least one
+    // test should ensure that device deletion works.
+    ASSERT_TRUE(dev.Destroy());
+}
+
+TEST(libdm, DmVerityArgsAvb2) {
+    std::string device = "/dev/block/platform/soc/1da4000.ufshc/by-name/vendor_a";
+    std::string algorithm = "sha1";
+    std::string digest = "4be7e823b8c40f7bd5c8ccd5123f0722c5baca21";
+    std::string salt = "cc99f81ecb9484220a003b0719ee59dcf9be7e5d";
+
+    DmTargetVerity target(0, 10000, 1, device, device, 4096, 4096, 125961, 125961, algorithm,
+                          digest, salt);
+    target.UseFec(device, 2, 126955, 126955);
+    target.SetVerityMode("restart_on_corruption");
+    target.IgnoreZeroBlocks();
+
+    // Verity table from a walleye build.
+    std::string expected =
+            "1 /dev/block/platform/soc/1da4000.ufshc/by-name/vendor_a "
+            "/dev/block/platform/soc/1da4000.ufshc/by-name/vendor_a 4096 4096 125961 125961 sha1 "
+            "4be7e823b8c40f7bd5c8ccd5123f0722c5baca21 cc99f81ecb9484220a003b0719ee59dcf9be7e5d 10 "
+            "use_fec_from_device /dev/block/platform/soc/1da4000.ufshc/by-name/vendor_a fec_roots "
+            "2 fec_blocks 126955 fec_start 126955 restart_on_corruption ignore_zero_blocks";
+    EXPECT_EQ(target.GetParameterString(), expected);
+}
diff --git a/fs_mgr/libdm/include/libdm/dm.h b/fs_mgr/libdm/include/libdm/dm.h
new file mode 100644
index 0000000..e2bc729
--- /dev/null
+++ b/fs_mgr/libdm/include/libdm/dm.h
@@ -0,0 +1,171 @@
+/*
+ *  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 _LIBDM_DM_H_
+#define _LIBDM_DM_H_
+
+#include <fcntl.h>
+#include <linux/dm-ioctl.h>
+#include <linux/kdev_t.h>
+#include <stdint.h>
+#include <sys/sysmacros.h>
+#include <unistd.h>
+
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include <android-base/logging.h>
+
+#include "dm_table.h"
+
+// The minimum expected device mapper major.minor version
+#define DM_VERSION0 (4)
+#define DM_VERSION1 (0)
+#define DM_VERSION2 (0)
+
+#define DM_ALIGN_MASK (7)
+#define DM_ALIGN(x) ((x + DM_ALIGN_MASK) & ~DM_ALIGN_MASK)
+
+namespace android {
+namespace dm {
+
+enum class DmDeviceState { INVALID, SUSPENDED, ACTIVE };
+
+class DeviceMapper final {
+  public:
+    class DmBlockDevice final {
+      public:
+        // only allow creating this with dm_name_list
+        DmBlockDevice() = delete;
+
+        explicit DmBlockDevice(struct dm_name_list* d) : name_(d->name), dev_(d->dev){};
+
+        // Returs device mapper name associated with the block device
+        const std::string& name() const { return name_; }
+
+        // Return major number for the block device
+        uint32_t Major() const { return major(dev_); }
+
+        // Return minor number for the block device
+        uint32_t Minor() const { return minor(dev_); }
+        ~DmBlockDevice() = default;
+
+      private:
+        std::string name_;
+        uint64_t dev_;
+    };
+
+    // Removes a device mapper device with the given name.
+    // Returns 'true' on success, false otherwise.
+    bool DeleteDevice(const std::string& name);
+
+    // Reads the device mapper table from the device with given anme and
+    // returns it in a DmTable object.
+    const std::unique_ptr<DmTable> table(const std::string& name) const;
+
+    // Returns the current state of the underlying device mapper device
+    // with given name.
+    // One of INVALID, SUSPENDED or ACTIVE.
+    DmDeviceState state(const std::string& name) const;
+
+    // Creates a device, loads the given table, and activates it. If the device
+    // is not able to be activated, it is destroyed, and false is returned.
+    bool CreateDevice(const std::string& name, const DmTable& table);
+
+    // Loads the device mapper table from parameter into the underlying device
+    // mapper device with given name and activate / resumes the device in the
+    // process. A device with the given name must already exist.
+    //
+    // Returns 'true' on success, false otherwise.
+    bool LoadTableAndActivate(const std::string& name, const DmTable& table);
+
+    // Returns true if a list of available device mapper targets registered in the kernel was
+    // successfully read and stored in 'targets'. Returns 'false' otherwise.
+    bool GetAvailableTargets(std::vector<DmTargetTypeInfo>* targets);
+
+    // Return 'true' if it can successfully read the list of device mapper block devices
+    // currently created. 'devices' will be empty if the kernel interactions
+    // were successful and there are no block devices at the moment. Returns
+    // 'false' in case of any failure along the way.
+    bool GetAvailableDevices(std::vector<DmBlockDevice>* devices);
+
+    // Returns the path to the device mapper device node in '/dev' corresponding to
+    // 'name'. If the device does not exist, false is returned, and the path
+    // parameter is not set.
+    bool GetDmDevicePathByName(const std::string& name, std::string* path);
+
+    // The only way to create a DeviceMapper object.
+    static DeviceMapper& Instance();
+
+    ~DeviceMapper() {
+        if (fd_ != -1) {
+            ::close(fd_);
+        }
+    }
+
+    // Query the status of a table, given a device name. The output vector will
+    // contain one TargetInfo for each target in the table. If the device does
+    // not exist, or there were too many targets, the call will fail and return
+    // false.
+    struct TargetInfo {
+        struct dm_target_spec spec;
+        std::string data;
+        TargetInfo(const struct dm_target_spec& spec, const std::string& data)
+            : spec(spec), data(data) {}
+    };
+    bool GetTableStatus(const std::string& name, std::vector<TargetInfo>* table);
+
+  private:
+    // Maximum possible device mapper targets registered in the kernel.
+    // This is only used to read the list of targets from kernel so we allocate
+    // a finite amount of memory. This limit is in no way enforced by the kernel.
+    static constexpr uint32_t kMaxPossibleDmTargets = 256;
+
+    // Maximum possible device mapper created block devices. Note that this is restricted by
+    // the minor numbers (that used to be 8 bits) that can be range from 0 to 2^20-1 in newer
+    // kernels. In Android systems however, we never expect these to grow beyond the artificial
+    // limit we are imposing here of 256.
+    static constexpr uint32_t kMaxPossibleDmDevices = 256;
+
+    void InitIo(struct dm_ioctl* io, const std::string& name = std::string()) const;
+
+    DeviceMapper() : fd_(-1) {
+        fd_ = TEMP_FAILURE_RETRY(open("/dev/device-mapper", O_RDWR | O_CLOEXEC));
+        if (fd_ < 0) {
+            PLOG(ERROR) << "Failed to open device-mapper";
+        }
+    }
+
+    // Creates a device mapper device with given name.
+    // Return 'true' on success and 'false' on failure to
+    // create OR if a device mapper device with the same name already
+    // exists.
+    bool CreateDevice(const std::string& name);
+
+    int fd_;
+    // Non-copyable & Non-movable
+    DeviceMapper(const DeviceMapper&) = delete;
+    DeviceMapper& operator=(const DeviceMapper&) = delete;
+    DeviceMapper& operator=(DeviceMapper&&) = delete;
+    DeviceMapper(DeviceMapper&&) = delete;
+};
+
+}  // namespace dm
+}  // namespace android
+
+#endif /* _LIBDM_DM_H_ */
diff --git a/fs_mgr/libdm/include/libdm/dm_table.h b/fs_mgr/libdm/include/libdm/dm_table.h
new file mode 100644
index 0000000..5c639be
--- /dev/null
+++ b/fs_mgr/libdm/include/libdm/dm_table.h
@@ -0,0 +1,84 @@
+/*
+ *  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 _LIBDM_DMTABLE_H_
+#define _LIBDM_DMTABLE_H_
+
+#include <stdint.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "dm_target.h"
+
+namespace android {
+namespace dm {
+
+class DmTable {
+  public:
+    DmTable() : num_sectors_(0), readonly_(false) {}
+
+    // Adds a target to the device mapper table for a range specified in the target object.
+    // The function will return 'true' if the target was successfully added and doesn't overlap with
+    // any of the existing targets in the table. Gaps are allowed. The final check, including
+    // overlaps and gaps are done before loading the table. Returns 'false' on failure.
+    bool AddTarget(std::unique_ptr<DmTarget>&& target);
+
+    // Removes a target from the table for the range specified in the target object. Returns 'false'
+    // if the target name doesn't match with the one in the table. Returns 'true' if target is
+    // successfully removed.
+    bool RemoveTarget(std::unique_ptr<DmTarget>&& target);
+
+    // Checks the table to make sure it is valid. i.e. Checks for range overlaps, range gaps
+    // and returns 'true' if the table is ready to be loaded into kernel. Returns 'false' if the
+    // table is malformed.
+    bool valid() const;
+
+    // Returns the toatl number of targets.
+    size_t num_targets() const { return targets_.size(); }
+
+    // Returns the total size represented by the table in terms of number of 512-byte sectors.
+    // NOTE: This function will overlook if there are any gaps in the targets added in the table.
+    uint64_t num_sectors() const;
+
+    // Returns the string represntation of the table that is ready to be passed into the kernel
+    // as part of the DM_TABLE_LOAD ioctl.
+    std::string Serialize() const;
+
+    void set_readonly(bool readonly) { readonly_ = readonly; }
+    bool readonly() const { return readonly_; }
+
+    ~DmTable() = default;
+
+  private:
+    // list of targets defined in this table sorted by
+    // their start and end sectors.
+    // Note: Overlapping targets MUST never be added in this list.
+    std::vector<std::unique_ptr<DmTarget>> targets_;
+
+    // Total size in terms of # of sectors, as calculated by looking at the last and the first
+    // target in 'target_'.
+    uint64_t num_sectors_;
+
+    // True if the device should be read-only; false otherwise.
+    bool readonly_;
+};
+
+}  // namespace dm
+}  // namespace android
+
+#endif /* _LIBDM_DMTABLE_H_ */
diff --git a/fs_mgr/libdm/include/libdm/dm_target.h b/fs_mgr/libdm/include/libdm/dm_target.h
new file mode 100644
index 0000000..31863c8
--- /dev/null
+++ b/fs_mgr/libdm/include/libdm/dm_target.h
@@ -0,0 +1,164 @@
+/*
+ *  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 _LIBDM_DMTARGET_H_
+#define _LIBDM_DMTARGET_H_
+
+#include <linux/dm-ioctl.h>
+#include <stdint.h>
+
+#include <string>
+#include <vector>
+
+#include <android-base/logging.h>
+
+namespace android {
+namespace dm {
+
+class DmTargetTypeInfo {
+  public:
+    DmTargetTypeInfo() : major_(0), minor_(0), patch_(0) {}
+    DmTargetTypeInfo(const struct dm_target_versions* info)
+        : name_(info->name),
+          major_(info->version[0]),
+          minor_(info->version[1]),
+          patch_(info->version[2]) {}
+
+    const std::string& name() const { return name_; }
+    std::string version() const {
+        return std::to_string(major_) + "." + std::to_string(minor_) + "." + std::to_string(patch_);
+    }
+
+  private:
+    std::string name_;
+    uint32_t major_;
+    uint32_t minor_;
+    uint32_t patch_;
+};
+
+class DmTarget {
+  public:
+    DmTarget(uint64_t start, uint64_t length) : start_(start), length_(length) {}
+
+    virtual ~DmTarget() = default;
+
+    // Returns name of the target.
+    virtual std::string name() const = 0;
+
+    // Return the first logical sector represented by this target.
+    uint64_t start() const { return start_; }
+
+    // Returns size in number of sectors when this target is part of
+    // a DmTable, return 0 otherwise.
+    uint64_t size() const { return length_; }
+
+    // Function that converts this object to a string of arguments that can
+    // be passed to the kernel for adding this target in a table. Each target (e.g. verity, linear)
+    // must implement this, for it to be used on a device.
+    std::string Serialize() const;
+
+    virtual bool Valid() const { return true; }
+
+  protected:
+    // Get the parameter string that is passed to the end of the dm_target_spec
+    // for this target type.
+    virtual std::string GetParameterString() const = 0;
+
+  private:
+    // logical sector number start and total length (in terms of 512-byte sectors) represented
+    // by this target within a DmTable.
+    uint64_t start_, length_;
+};
+
+class DmTargetZero final : public DmTarget {
+  public:
+    DmTargetZero(uint64_t start, uint64_t length) : DmTarget(start, length) {}
+
+    std::string name() const override { return "zero"; }
+    std::string GetParameterString() const override;
+};
+
+class DmTargetLinear final : public DmTarget {
+  public:
+    DmTargetLinear(uint64_t start, uint64_t length, const std::string& block_device,
+                   uint64_t physical_sector)
+        : DmTarget(start, length), block_device_(block_device), physical_sector_(physical_sector) {}
+
+    std::string name() const override { return "linear"; }
+    std::string GetParameterString() const override;
+    const std::string& block_device() const { return block_device_; }
+
+  private:
+    std::string block_device_;
+    uint64_t physical_sector_;
+};
+
+class DmTargetVerity final : public DmTarget {
+  public:
+    DmTargetVerity(uint64_t start, uint64_t length, uint32_t version,
+                   const std::string& block_device, const std::string& hash_device,
+                   uint32_t data_block_size, uint32_t hash_block_size, uint32_t num_data_blocks,
+                   uint32_t hash_start_block, const std::string& hash_algorithm,
+                   const std::string& root_digest, const std::string& salt);
+
+    void UseFec(const std::string& device, uint32_t num_roots, uint32_t num_blocks, uint32_t start);
+    void SetVerityMode(const std::string& mode);
+    void IgnoreZeroBlocks();
+
+    std::string name() const override { return "verity"; }
+    std::string GetParameterString() const override;
+    bool Valid() const override { return valid_; }
+
+  private:
+    std::vector<std::string> base_args_;
+    std::vector<std::string> optional_args_;
+    bool valid_;
+};
+
+class DmTargetAndroidVerity final : public DmTarget {
+  public:
+    DmTargetAndroidVerity(uint64_t start, uint64_t length, const std::string& block_device,
+                          const std::string& keyid)
+        : DmTarget(start, length), keyid_(keyid), block_device_(block_device) {}
+
+    std::string name() const override { return "android-verity"; }
+    std::string GetParameterString() const override;
+
+  private:
+    std::string keyid_;
+    std::string block_device_;
+};
+
+// This is the same as DmTargetVerity, but the table may be specified as a raw
+// string. This code exists only for fs_mgr_verity and should be avoided. Use
+// DmTargetVerity for new code instead.
+class DmTargetVerityString final : public DmTarget {
+  public:
+    DmTargetVerityString(uint64_t start, uint64_t length, const std::string& target_string)
+        : DmTarget(start, length), target_string_(target_string) {}
+
+    std::string name() const override { return "verity"; }
+    std::string GetParameterString() const override { return target_string_; }
+    bool Valid() const override { return true; }
+
+  private:
+    std::string target_string_;
+};
+
+}  // namespace dm
+}  // namespace android
+
+#endif /* _LIBDM_DMTARGET_H_ */
diff --git a/fs_mgr/libdm/include/libdm/loop_control.h b/fs_mgr/libdm/include/libdm/loop_control.h
new file mode 100644
index 0000000..e6e83f4
--- /dev/null
+++ b/fs_mgr/libdm/include/libdm/loop_control.h
@@ -0,0 +1,82 @@
+/*
+ *  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 _LIBDM_LOOP_CONTROL_H_
+#define _LIBDM_LOOP_CONTROL_H_
+
+#include <string>
+
+#include <android-base/unique_fd.h>
+
+namespace android {
+namespace dm {
+
+class LoopControl final {
+  public:
+    LoopControl();
+
+    // Attaches the file specified by 'file_fd' to the loop device specified
+    // by 'loopdev'
+    bool Attach(int file_fd, std::string* loopdev) const;
+
+    // Detach the loop device given by 'loopdev' from the attached backing file.
+    bool Detach(const std::string& loopdev) const;
+
+    LoopControl(const LoopControl&) = delete;
+    LoopControl& operator=(const LoopControl&) = delete;
+    LoopControl& operator=(LoopControl&&) = default;
+    LoopControl(LoopControl&&) = default;
+
+  private:
+    bool FindFreeLoopDevice(std::string* loopdev) const;
+
+    static constexpr const char* kLoopControlDevice = "/dev/loop-control";
+
+    android::base::unique_fd control_fd_;
+};
+
+// Create a temporary loop device around a file descriptor or path.
+class LoopDevice {
+  public:
+    // Create a loop device for the given file descriptor. It is closed when
+    // LoopDevice is destroyed only if auto_close is true.
+    LoopDevice(int fd, bool auto_close = false);
+    // Create a loop device for the given file path. It will be opened for
+    // reading and writing and closed when the loop device is detached.
+    explicit LoopDevice(const std::string& path);
+    ~LoopDevice();
+
+    bool valid() const { return fd_ != -1 && !device_.empty(); }
+    const std::string& device() const { return device_; }
+
+    LoopDevice(const LoopDevice&) = delete;
+    LoopDevice& operator=(const LoopDevice&) = delete;
+    LoopDevice& operator=(LoopDevice&&) = default;
+    LoopDevice(LoopDevice&&) = default;
+
+  private:
+    void Init();
+
+    android::base::unique_fd fd_;
+    bool owns_fd_;
+    std::string device_;
+    LoopControl control_;
+};
+
+}  // namespace dm
+}  // namespace android
+
+#endif /* _LIBDM_LOOP_CONTROL_H_ */
diff --git a/fs_mgr/libdm/loop_control.cpp b/fs_mgr/libdm/loop_control.cpp
new file mode 100644
index 0000000..0beb1a6
--- /dev/null
+++ b/fs_mgr/libdm/loop_control.cpp
@@ -0,0 +1,121 @@
+/*
+ * 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 "libdm/loop_control.h"
+
+#include <fcntl.h>
+#include <linux/loop.h>
+#include <stdint.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <android-base/unique_fd.h>
+
+namespace android {
+namespace dm {
+
+LoopControl::LoopControl() : control_fd_(-1) {
+    control_fd_.reset(TEMP_FAILURE_RETRY(open(kLoopControlDevice, O_RDWR | O_CLOEXEC)));
+    if (control_fd_ < 0) {
+        PLOG(ERROR) << "Failed to open loop-control";
+    }
+}
+
+bool LoopControl::Attach(int file_fd, std::string* loopdev) const {
+    if (!FindFreeLoopDevice(loopdev)) {
+        LOG(ERROR) << "Failed to attach, no free loop devices";
+        return false;
+    }
+
+    android::base::unique_fd loop_fd(TEMP_FAILURE_RETRY(open(loopdev->c_str(), O_RDWR | O_CLOEXEC)));
+    if (loop_fd < 0) {
+        PLOG(ERROR) << "Failed to open: " << *loopdev;
+        return false;
+    }
+
+    int rc = ioctl(loop_fd, LOOP_SET_FD, file_fd);
+    if (rc < 0) {
+        PLOG(ERROR) << "Failed LOOP_SET_FD";
+        return false;
+    }
+    return true;
+}
+
+bool LoopControl::Detach(const std::string& loopdev) const {
+    if (loopdev.empty()) {
+        LOG(ERROR) << "Must provide a loop device";
+        return false;
+    }
+
+    android::base::unique_fd loop_fd(TEMP_FAILURE_RETRY(open(loopdev.c_str(), O_RDWR | O_CLOEXEC)));
+    if (loop_fd < 0) {
+        PLOG(ERROR) << "Failed to open: " << loopdev;
+        return false;
+    }
+
+    int rc = ioctl(loop_fd, LOOP_CLR_FD, 0);
+    if (rc) {
+        PLOG(ERROR) << "Failed LOOP_CLR_FD for '" << loopdev << "'";
+        return false;
+    }
+    return true;
+}
+
+bool LoopControl::FindFreeLoopDevice(std::string* loopdev) const {
+    int rc = ioctl(control_fd_, LOOP_CTL_GET_FREE);
+    if (rc < 0) {
+        PLOG(ERROR) << "Failed to get free loop device";
+        return false;
+    }
+
+    // Ueventd on android creates all loop devices as /dev/block/loopX
+    // The total number of available devices is determined by 'loop.max_part'
+    // kernel command line argument.
+    *loopdev = ::android::base::StringPrintf("/dev/block/loop%d", rc);
+    return true;
+}
+
+LoopDevice::LoopDevice(int fd, bool auto_close) : fd_(fd), owns_fd_(auto_close) {
+    Init();
+}
+
+LoopDevice::LoopDevice(const std::string& path) : fd_(-1), owns_fd_(true) {
+    fd_.reset(open(path.c_str(), O_RDWR | O_CLOEXEC));
+    if (fd_ < -1) {
+        PLOG(ERROR) << "open failed for " << path;
+        return;
+    }
+    Init();
+}
+
+LoopDevice::~LoopDevice() {
+    if (valid()) {
+        control_.Detach(device_);
+    }
+    if (!owns_fd_) {
+        (void)fd_.release();
+    }
+}
+
+void LoopDevice::Init() {
+    control_.Attach(fd_, &device_);
+}
+
+}  // namespace dm
+}  // namespace android
diff --git a/fs_mgr/libdm/loop_control_test.cpp b/fs_mgr/libdm/loop_control_test.cpp
new file mode 100644
index 0000000..08bdc00
--- /dev/null
+++ b/fs_mgr/libdm/loop_control_test.cpp
@@ -0,0 +1,64 @@
+/*
+ * 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 "libdm/loop_control.h"
+
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <android-base/file.h>
+#include <android-base/unique_fd.h>
+#include <gtest/gtest.h>
+#include "test_util.h"
+
+using namespace std;
+using namespace android::dm;
+using unique_fd = android::base::unique_fd;
+
+static unique_fd TempFile() {
+    // A loop device needs to be at least one sector to actually work, so fill
+    // up the file with a message.
+    unique_fd fd(CreateTempFile("temp", 0));
+    if (fd < 0) {
+        return {};
+    }
+    char buffer[] = "Hello";
+    for (size_t i = 0; i < 1000; i++) {
+        if (!android::base::WriteFully(fd, buffer, sizeof(buffer))) {
+            perror("write");
+            return {};
+        }
+    }
+    return fd;
+}
+
+TEST(libdm, LoopControl) {
+    unique_fd fd = TempFile();
+    ASSERT_GE(fd, 0);
+
+    LoopDevice loop(fd);
+    ASSERT_TRUE(loop.valid());
+
+    char buffer[6];
+    unique_fd loop_fd(open(loop.device().c_str(), O_RDWR));
+    ASSERT_GE(loop_fd, 0);
+    ASSERT_TRUE(android::base::ReadFully(loop_fd, buffer, sizeof(buffer)));
+    ASSERT_EQ(memcmp(buffer, "Hello", 6), 0);
+}
diff --git a/fs_mgr/libdm/test_util.cpp b/fs_mgr/libdm/test_util.cpp
new file mode 100644
index 0000000..307251c
--- /dev/null
+++ b/fs_mgr/libdm/test_util.cpp
@@ -0,0 +1,52 @@
+/*
+ * 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 <fcntl.h>
+#include <linux/memfd.h>
+#include <stdio.h>
+#include <sys/syscall.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "test_util.h"
+
+namespace android {
+namespace dm {
+
+using unique_fd = android::base::unique_fd;
+
+// Create a temporary in-memory file. If size is non-zero, the file will be
+// created with a fixed size.
+unique_fd CreateTempFile(const std::string& name, size_t size) {
+    unique_fd fd(syscall(__NR_memfd_create, name.c_str(), MFD_ALLOW_SEALING));
+    if (fd < 0) {
+        return {};
+    }
+    if (size) {
+        if (ftruncate(fd, size) < 0) {
+            perror("ftruncate");
+            return {};
+        }
+        if (fcntl(fd, F_ADD_SEALS, F_SEAL_GROW | F_SEAL_SHRINK) < 0) {
+            perror("fcntl");
+            return {};
+        }
+    }
+    return fd;
+}
+
+}  // namespace dm
+}  // namespace android
diff --git a/fs_mgr/libdm/test_util.h b/fs_mgr/libdm/test_util.h
new file mode 100644
index 0000000..96b051c
--- /dev/null
+++ b/fs_mgr/libdm/test_util.h
@@ -0,0 +1,35 @@
+/*
+ * 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 _LIBDM_TEST_UTILS_H_
+#define _LIBDM_TEST_UTILS_H_
+
+#include <android-base/unique_fd.h>
+#include <stddef.h>
+
+#include <string>
+
+namespace android {
+namespace dm {
+
+// Create a temporary in-memory file. If size is non-zero, the file will be
+// created with a fixed size.
+android::base::unique_fd CreateTempFile(const std::string& name, size_t size);
+
+}  // namespace dm
+}  // namespace android
+
+#endif  // _LIBDM_TEST_UTILS_H_
diff --git a/fs_mgr/liblp/Android.bp b/fs_mgr/liblp/Android.bp
new file mode 100644
index 0000000..89282db
--- /dev/null
+++ b/fs_mgr/liblp/Android.bp
@@ -0,0 +1,61 @@
+//
+// 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_library {
+    name: "liblp",
+    host_supported: true,
+    recovery_available: true,
+    defaults: ["fs_mgr_defaults"],
+    cppflags: [
+        "-D_FILE_OFFSET_BITS=64",
+    ],
+    srcs: [
+        "builder.cpp",
+        "images.cpp",
+        "reader.cpp",
+        "utility.cpp",
+        "writer.cpp",
+    ],
+    shared_libs: [
+        "libbase",
+        "liblog",
+        "libcrypto",
+        "libcrypto_utils",
+        "libsparse",
+        "libext2_uuid",
+        "libext4_utils",
+        "libz",
+    ],
+    export_include_dirs: ["include"],
+}
+
+cc_test {
+    name: "liblp_test",
+    defaults: ["fs_mgr_defaults"],
+    cppflags: [
+        "-Wno-unused-parameter",
+    ],
+    shared_libs: [
+        "liblp",
+        "libbase",
+        "libfs_mgr",
+    ],
+    srcs: [
+        "builder_test.cpp",
+        "io_test.cpp",
+        "utility_test.cpp",
+    ],
+}
diff --git a/fs_mgr/liblp/builder.cpp b/fs_mgr/liblp/builder.cpp
new file mode 100644
index 0000000..9d710f9
--- /dev/null
+++ b/fs_mgr/liblp/builder.cpp
@@ -0,0 +1,469 @@
+/*
+ * 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 "liblp/builder.h"
+
+#if defined(__linux__)
+#include <linux/fs.h>
+#endif
+#include <string.h>
+#include <sys/ioctl.h>
+
+#include <algorithm>
+
+#include <android-base/unique_fd.h>
+#include <uuid/uuid.h>
+
+#include "liblp/liblp.h"
+#include "reader.h"
+#include "utility.h"
+
+namespace android {
+namespace fs_mgr {
+
+bool GetBlockDeviceInfo(const std::string& block_device, BlockDeviceInfo* device_info) {
+#if defined(__linux__)
+    android::base::unique_fd fd(open(block_device.c_str(), O_RDONLY));
+    if (fd < 0) {
+        PERROR << __PRETTY_FUNCTION__ << "open '" << block_device << "' failed";
+        return false;
+    }
+    if (!GetDescriptorSize(fd, &device_info->size)) {
+        return false;
+    }
+    if (ioctl(fd, BLKIOMIN, &device_info->alignment) < 0) {
+        PERROR << __PRETTY_FUNCTION__ << "BLKIOMIN failed";
+        return false;
+    }
+    if (ioctl(fd, BLKALIGNOFF, &device_info->alignment_offset) < 0) {
+        PERROR << __PRETTY_FUNCTION__ << "BLKIOMIN failed";
+        return false;
+    }
+    return true;
+#else
+    (void)block_device;
+    (void)device_info;
+    LERROR << __PRETTY_FUNCTION__ << ": Not supported on this operating system.";
+    return false;
+#endif
+}
+
+void LinearExtent::AddTo(LpMetadata* out) const {
+    out->extents.push_back(LpMetadataExtent{num_sectors_, LP_TARGET_TYPE_LINEAR, physical_sector_});
+}
+
+void ZeroExtent::AddTo(LpMetadata* out) const {
+    out->extents.push_back(LpMetadataExtent{num_sectors_, LP_TARGET_TYPE_ZERO, 0});
+}
+
+Partition::Partition(const std::string& name, const std::string& guid, uint32_t attributes)
+    : name_(name), guid_(guid), attributes_(attributes), size_(0) {}
+
+void Partition::AddExtent(std::unique_ptr<Extent>&& extent) {
+    size_ += extent->num_sectors() * LP_SECTOR_SIZE;
+    extents_.push_back(std::move(extent));
+}
+
+void Partition::RemoveExtents() {
+    size_ = 0;
+    extents_.clear();
+}
+
+void Partition::ShrinkTo(uint64_t requested_size) {
+    uint64_t aligned_size = AlignTo(requested_size, LP_SECTOR_SIZE);
+    if (size_ <= aligned_size) {
+        return;
+    }
+    if (aligned_size == 0) {
+        RemoveExtents();
+        return;
+    }
+
+    // Remove or shrink extents of any kind until the total partition size is
+    // equal to the requested size.
+    uint64_t sectors_to_remove = (size_ - aligned_size) / LP_SECTOR_SIZE;
+    while (sectors_to_remove) {
+        Extent* extent = extents_.back().get();
+        if (extent->num_sectors() > sectors_to_remove) {
+            size_ -= sectors_to_remove * LP_SECTOR_SIZE;
+            extent->set_num_sectors(extent->num_sectors() - sectors_to_remove);
+            break;
+        }
+        size_ -= (extent->num_sectors() * LP_SECTOR_SIZE);
+        sectors_to_remove -= extent->num_sectors();
+        extents_.pop_back();
+    }
+    DCHECK(size_ == requested_size);
+}
+
+std::unique_ptr<MetadataBuilder> MetadataBuilder::New(const std::string& block_device,
+                                                      uint32_t slot_number) {
+    std::unique_ptr<LpMetadata> metadata = ReadMetadata(block_device.c_str(), slot_number);
+    if (!metadata) {
+        return nullptr;
+    }
+    std::unique_ptr<MetadataBuilder> builder = New(*metadata.get());
+    if (!builder) {
+        return nullptr;
+    }
+    BlockDeviceInfo device_info;
+    if (fs_mgr::GetBlockDeviceInfo(block_device, &device_info)) {
+        builder->set_block_device_info(device_info);
+    }
+    return builder;
+}
+
+std::unique_ptr<MetadataBuilder> MetadataBuilder::New(const BlockDeviceInfo& device_info,
+                                                      uint32_t metadata_max_size,
+                                                      uint32_t metadata_slot_count) {
+    std::unique_ptr<MetadataBuilder> builder(new MetadataBuilder());
+    if (!builder->Init(device_info, metadata_max_size, metadata_slot_count)) {
+        return nullptr;
+    }
+    return builder;
+}
+
+std::unique_ptr<MetadataBuilder> MetadataBuilder::New(const LpMetadata& metadata) {
+    std::unique_ptr<MetadataBuilder> builder(new MetadataBuilder());
+    if (!builder->Init(metadata)) {
+        return nullptr;
+    }
+    return builder;
+}
+
+MetadataBuilder::MetadataBuilder() {
+    memset(&geometry_, 0, sizeof(geometry_));
+    geometry_.magic = LP_METADATA_GEOMETRY_MAGIC;
+    geometry_.struct_size = sizeof(geometry_);
+
+    memset(&header_, 0, sizeof(header_));
+    header_.magic = LP_METADATA_HEADER_MAGIC;
+    header_.major_version = LP_METADATA_MAJOR_VERSION;
+    header_.minor_version = LP_METADATA_MINOR_VERSION;
+    header_.header_size = sizeof(header_);
+    header_.partitions.entry_size = sizeof(LpMetadataPartition);
+    header_.extents.entry_size = sizeof(LpMetadataExtent);
+}
+
+bool MetadataBuilder::Init(const LpMetadata& metadata) {
+    geometry_ = metadata.geometry;
+
+    for (const auto& partition : metadata.partitions) {
+        Partition* builder = AddPartition(GetPartitionName(partition), GetPartitionGuid(partition),
+                                          partition.attributes);
+        if (!builder) {
+            return false;
+        }
+
+        for (size_t i = 0; i < partition.num_extents; i++) {
+            const LpMetadataExtent& extent = metadata.extents[partition.first_extent_index + i];
+            if (extent.target_type == LP_TARGET_TYPE_LINEAR) {
+                auto copy = std::make_unique<LinearExtent>(extent.num_sectors, extent.target_data);
+                builder->AddExtent(std::move(copy));
+            } else if (extent.target_type == LP_TARGET_TYPE_ZERO) {
+                auto copy = std::make_unique<ZeroExtent>(extent.num_sectors);
+                builder->AddExtent(std::move(copy));
+            }
+        }
+    }
+
+    device_info_.alignment = geometry_.alignment;
+    device_info_.alignment_offset = geometry_.alignment_offset;
+    return true;
+}
+
+bool MetadataBuilder::Init(const BlockDeviceInfo& device_info, uint32_t metadata_max_size,
+                           uint32_t metadata_slot_count) {
+    if (metadata_max_size < sizeof(LpMetadataHeader)) {
+        LERROR << "Invalid metadata maximum size.";
+        return false;
+    }
+    if (metadata_slot_count == 0) {
+        LERROR << "Invalid metadata slot count.";
+        return false;
+    }
+
+    // Align the metadata size up to the nearest sector.
+    metadata_max_size = AlignTo(metadata_max_size, LP_SECTOR_SIZE);
+
+    // Check that device properties are sane.
+    device_info_ = device_info;
+    if (device_info_.size % LP_SECTOR_SIZE != 0) {
+        LERROR << "Block device size must be a multiple of 512.";
+        return false;
+    }
+    if (device_info_.alignment_offset % LP_SECTOR_SIZE != 0) {
+        LERROR << "Alignment offset is not sector-aligned.";
+        return false;
+    }
+    if (device_info_.alignment % LP_SECTOR_SIZE != 0) {
+        LERROR << "Partition alignment is not sector-aligned.";
+        return false;
+    }
+    if (device_info_.alignment_offset > device_info_.alignment) {
+        LERROR << "Partition alignment offset is greater than its alignment.";
+        return false;
+    }
+
+    // We reserve a geometry block (4KB) plus space for each copy of the
+    // maximum size of a metadata blob. Then, we double that space since
+    // we store a backup copy of everything.
+    uint64_t reserved =
+            LP_METADATA_GEOMETRY_SIZE + (uint64_t(metadata_max_size) * metadata_slot_count);
+    uint64_t total_reserved = reserved * 2;
+    if (device_info_.size < total_reserved) {
+        LERROR << "Attempting to create metadata on a block device that is too small.";
+        return false;
+    }
+
+    // Compute the first free sector, factoring in alignment.
+    uint64_t free_area = AlignTo(reserved, device_info_.alignment, device_info_.alignment_offset);
+    uint64_t first_sector = free_area / LP_SECTOR_SIZE;
+
+    // Compute the last free sector, which is inclusive. We subtract 1 to make
+    // sure that logical partitions won't overlap with the same sector as the
+    // backup metadata, which could happen if the block device was not aligned
+    // to LP_SECTOR_SIZE.
+    uint64_t last_sector = ((device_info_.size - reserved) / LP_SECTOR_SIZE) - 1;
+
+    // If this check fails, it means either (1) we did not have free space to
+    // allocate a single sector, or (2) we did, but the alignment was high
+    // enough to bump the first sector out of range. Either way, we cannot
+    // continue.
+    if (first_sector > last_sector) {
+        LERROR << "Not enough space to allocate any partition tables.";
+        return false;
+    }
+
+    geometry_.first_logical_sector = first_sector;
+    geometry_.last_logical_sector = last_sector;
+    geometry_.metadata_max_size = metadata_max_size;
+    geometry_.metadata_slot_count = metadata_slot_count;
+    geometry_.alignment = device_info_.alignment;
+    geometry_.alignment_offset = device_info_.alignment_offset;
+    geometry_.block_device_size = device_info_.size;
+    return true;
+}
+
+Partition* MetadataBuilder::AddPartition(const std::string& name, const std::string& guid,
+                                         uint32_t attributes) {
+    if (name.empty()) {
+        LERROR << "Partition must have a non-empty name.";
+        return nullptr;
+    }
+    if (FindPartition(name)) {
+        LERROR << "Attempting to create duplication partition with name: " << name;
+        return nullptr;
+    }
+    partitions_.push_back(std::make_unique<Partition>(name, guid, attributes));
+    return partitions_.back().get();
+}
+
+Partition* MetadataBuilder::FindPartition(const std::string& name) {
+    for (const auto& partition : partitions_) {
+        if (partition->name() == name) {
+            return partition.get();
+        }
+    }
+    return nullptr;
+}
+
+void MetadataBuilder::RemovePartition(const std::string& name) {
+    for (auto iter = partitions_.begin(); iter != partitions_.end(); iter++) {
+        if ((*iter)->name() == name) {
+            partitions_.erase(iter);
+            return;
+        }
+    }
+}
+
+bool MetadataBuilder::GrowPartition(Partition* partition, uint64_t requested_size) {
+    // Align the space needed up to the nearest sector.
+    uint64_t aligned_size = AlignTo(requested_size, LP_SECTOR_SIZE);
+    if (partition->size() >= aligned_size) {
+        return true;
+    }
+
+    // Figure out how much we need to allocate.
+    uint64_t space_needed = aligned_size - partition->size();
+    uint64_t sectors_needed = space_needed / LP_SECTOR_SIZE;
+    DCHECK(sectors_needed * LP_SECTOR_SIZE == space_needed);
+
+    struct Interval {
+        uint64_t start;
+        uint64_t end;
+
+        Interval(uint64_t start, uint64_t end) : start(start), end(end) {}
+        bool operator<(const Interval& other) const { return start < other.start; }
+    };
+    std::vector<Interval> intervals;
+
+    // Collect all extents in the partition table.
+    for (const auto& partition : partitions_) {
+        for (const auto& extent : partition->extents()) {
+            LinearExtent* linear = extent->AsLinearExtent();
+            if (!linear) {
+                continue;
+            }
+            intervals.emplace_back(linear->physical_sector(),
+                                   linear->physical_sector() + extent->num_sectors());
+        }
+    }
+
+    // Sort extents by starting sector.
+    std::sort(intervals.begin(), intervals.end());
+
+    // Find gaps that we can use for new extents. Note we store new extents in a
+    // temporary vector, and only commit them if we are guaranteed enough free
+    // space.
+    std::vector<std::unique_ptr<LinearExtent>> new_extents;
+    for (size_t i = 1; i < intervals.size(); i++) {
+        const Interval& previous = intervals[i - 1];
+        const Interval& current = intervals[i];
+
+        if (previous.end >= current.start) {
+            // There is no gap between these two extents, try the next one. Note that
+            // extents may never overlap, but just for safety, we ignore them if they
+            // do.
+            DCHECK(previous.end == current.start);
+            continue;
+        }
+
+        uint64_t aligned = AlignSector(previous.end);
+        if (aligned >= current.start) {
+            // After alignment, this extent is not usable.
+            continue;
+        }
+
+        // This gap is enough to hold the remainder of the space requested, so we
+        // can allocate what we need and return.
+        if (current.start - aligned >= sectors_needed) {
+            auto extent = std::make_unique<LinearExtent>(sectors_needed, aligned);
+            sectors_needed -= extent->num_sectors();
+            new_extents.push_back(std::move(extent));
+            break;
+        }
+
+        // This gap is not big enough to fit the remainder of the space requested,
+        // so consume the whole thing and keep looking for more.
+        auto extent = std::make_unique<LinearExtent>(current.start - aligned, aligned);
+        sectors_needed -= extent->num_sectors();
+        new_extents.push_back(std::move(extent));
+    }
+
+    // If we still have more to allocate, take it from the remaining free space
+    // in the allocatable region.
+    if (sectors_needed) {
+        uint64_t first_sector;
+        if (intervals.empty()) {
+            first_sector = geometry_.first_logical_sector;
+        } else {
+            first_sector = intervals.back().end;
+        }
+        DCHECK(first_sector <= geometry_.last_logical_sector);
+
+        // Note: After alignment, |first_sector| may be > the last usable sector.
+        first_sector = AlignSector(first_sector);
+
+        // Note: the last usable sector is inclusive.
+        if (first_sector > geometry_.last_logical_sector ||
+            geometry_.last_logical_sector + 1 - first_sector < sectors_needed) {
+            LERROR << "Not enough free space to expand partition: " << partition->name();
+            return false;
+        }
+        auto extent = std::make_unique<LinearExtent>(sectors_needed, first_sector);
+        new_extents.push_back(std::move(extent));
+    }
+
+    for (auto& extent : new_extents) {
+        partition->AddExtent(std::move(extent));
+    }
+    return true;
+}
+
+void MetadataBuilder::ShrinkPartition(Partition* partition, uint64_t requested_size) {
+    partition->ShrinkTo(requested_size);
+}
+
+std::unique_ptr<LpMetadata> MetadataBuilder::Export() {
+    std::unique_ptr<LpMetadata> metadata = std::make_unique<LpMetadata>();
+    metadata->header = header_;
+    metadata->geometry = geometry_;
+
+    // Flatten the partition and extent structures into an LpMetadata, which
+    // makes it very easy to validate, serialize, or pass on to device-mapper.
+    for (const auto& partition : partitions_) {
+        LpMetadataPartition part;
+        memset(&part, 0, sizeof(part));
+
+        if (partition->name().size() > sizeof(part.name)) {
+            LERROR << "Partition name is too long: " << partition->name();
+            return nullptr;
+        }
+        if (partition->attributes() & ~(LP_PARTITION_ATTRIBUTE_MASK)) {
+            LERROR << "Partition " << partition->name() << " has unsupported attribute.";
+            return nullptr;
+        }
+
+        strncpy(part.name, partition->name().c_str(), sizeof(part.name));
+        if (uuid_parse(partition->guid().c_str(), part.guid) != 0) {
+            LERROR << "Could not parse guid " << partition->guid() << " for partition "
+                   << partition->name();
+            return nullptr;
+        }
+
+        part.first_extent_index = static_cast<uint32_t>(metadata->extents.size());
+        part.num_extents = static_cast<uint32_t>(partition->extents().size());
+        part.attributes = partition->attributes();
+
+        for (const auto& extent : partition->extents()) {
+            extent->AddTo(metadata.get());
+        }
+        metadata->partitions.push_back(part);
+    }
+
+    metadata->header.partitions.num_entries = static_cast<uint32_t>(metadata->partitions.size());
+    metadata->header.extents.num_entries = static_cast<uint32_t>(metadata->extents.size());
+    return metadata;
+}
+
+uint64_t MetadataBuilder::AllocatableSpace() const {
+    return (geometry_.last_logical_sector - geometry_.first_logical_sector + 1) * LP_SECTOR_SIZE;
+}
+
+uint64_t MetadataBuilder::AlignSector(uint64_t sector) {
+    // Note: when reading alignment info from the Kernel, we don't assume it
+    // is aligned to the sector size, so we round up to the nearest sector.
+    uint64_t lba = sector * LP_SECTOR_SIZE;
+    uint64_t aligned = AlignTo(lba, device_info_.alignment, device_info_.alignment_offset);
+    return AlignTo(aligned, LP_SECTOR_SIZE) / LP_SECTOR_SIZE;
+}
+
+void MetadataBuilder::set_block_device_info(const BlockDeviceInfo& device_info) {
+    device_info_.size = device_info.size;
+
+    // The kernel does not guarantee these values are present, so we only
+    // replace existing values if the new values are non-zero.
+    if (device_info.alignment) {
+        device_info_.alignment = device_info.alignment;
+    }
+    if (device_info.alignment_offset) {
+        device_info_.alignment_offset = device_info.alignment_offset;
+    }
+}
+
+}  // namespace fs_mgr
+}  // namespace android
diff --git a/fs_mgr/liblp/builder_test.cpp b/fs_mgr/liblp/builder_test.cpp
new file mode 100644
index 0000000..b610fd4
--- /dev/null
+++ b/fs_mgr/liblp/builder_test.cpp
@@ -0,0 +1,453 @@
+/*
+ * 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 <gtest/gtest.h>
+#include <liblp/builder.h>
+#include "fs_mgr.h"
+#include "utility.h"
+
+using namespace std;
+using namespace android::fs_mgr;
+
+static const char* TEST_GUID = "A799D1D6-669F-41D8-A3F0-EBB7572D8302";
+static const char* TEST_GUID2 = "A799D1D6-669F-41D8-A3F0-EBB7572D8303";
+
+TEST(liblp, BuildBasic) {
+    unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1024, 2);
+
+    Partition* partition = builder->AddPartition("system", TEST_GUID, LP_PARTITION_ATTR_READONLY);
+    ASSERT_NE(partition, nullptr);
+    EXPECT_EQ(partition->name(), "system");
+    EXPECT_EQ(partition->guid(), TEST_GUID);
+    EXPECT_EQ(partition->attributes(), LP_PARTITION_ATTR_READONLY);
+    EXPECT_EQ(partition->size(), 0);
+    EXPECT_EQ(builder->FindPartition("system"), partition);
+
+    builder->RemovePartition("system");
+    EXPECT_EQ(builder->FindPartition("system"), nullptr);
+}
+
+TEST(liblp, ResizePartition) {
+    unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1024, 2);
+
+    Partition* system = builder->AddPartition("system", TEST_GUID, LP_PARTITION_ATTR_READONLY);
+    ASSERT_NE(system, nullptr);
+    EXPECT_EQ(builder->GrowPartition(system, 65536), true);
+    EXPECT_EQ(system->size(), 65536);
+    ASSERT_EQ(system->extents().size(), 1);
+
+    LinearExtent* extent = system->extents()[0]->AsLinearExtent();
+    ASSERT_NE(extent, nullptr);
+    EXPECT_EQ(extent->num_sectors(), 65536 / LP_SECTOR_SIZE);
+    // The first logical sector will be (4096+1024*2)/512 = 12.
+    EXPECT_EQ(extent->physical_sector(), 12);
+
+    // Test growing to the same size.
+    EXPECT_EQ(builder->GrowPartition(system, 65536), true);
+    EXPECT_EQ(system->size(), 65536);
+    EXPECT_EQ(system->extents().size(), 1);
+    EXPECT_EQ(system->extents()[0]->num_sectors(), 65536 / LP_SECTOR_SIZE);
+    // Test growing to a smaller size.
+    EXPECT_EQ(builder->GrowPartition(system, 0), true);
+    EXPECT_EQ(system->size(), 65536);
+    EXPECT_EQ(system->extents().size(), 1);
+    EXPECT_EQ(system->extents()[0]->num_sectors(), 65536 / LP_SECTOR_SIZE);
+    // Test shrinking to a greater size.
+    builder->ShrinkPartition(system, 131072);
+    EXPECT_EQ(system->size(), 65536);
+    EXPECT_EQ(system->extents().size(), 1);
+    EXPECT_EQ(system->extents()[0]->num_sectors(), 65536 / LP_SECTOR_SIZE);
+
+    // Test shrinking within the same extent.
+    builder->ShrinkPartition(system, 32768);
+    EXPECT_EQ(system->size(), 32768);
+    EXPECT_EQ(system->extents().size(), 1);
+    extent = system->extents()[0]->AsLinearExtent();
+    ASSERT_NE(extent, nullptr);
+    EXPECT_EQ(extent->num_sectors(), 32768 / LP_SECTOR_SIZE);
+    EXPECT_EQ(extent->physical_sector(), 12);
+
+    // Test shrinking to 0.
+    builder->ShrinkPartition(system, 0);
+    EXPECT_EQ(system->size(), 0);
+    EXPECT_EQ(system->extents().size(), 0);
+}
+
+TEST(liblp, PartitionAlignment) {
+    unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1024, 2);
+
+    // Test that we align up to one sector.
+    Partition* system = builder->AddPartition("system", TEST_GUID, LP_PARTITION_ATTR_READONLY);
+    ASSERT_NE(system, nullptr);
+    EXPECT_EQ(builder->GrowPartition(system, 10000), true);
+    EXPECT_EQ(system->size(), 10240);
+    EXPECT_EQ(system->extents().size(), 1);
+
+    builder->ShrinkPartition(system, 9000);
+    EXPECT_EQ(system->size(), 9216);
+    EXPECT_EQ(system->extents().size(), 1);
+}
+
+TEST(liblp, DiskAlignment) {
+    static const uint64_t kDiskSize = 1000000;
+    static const uint32_t kMetadataSize = 1024;
+    static const uint32_t kMetadataSlots = 2;
+
+    unique_ptr<MetadataBuilder> builder =
+            MetadataBuilder::New(kDiskSize, kMetadataSize, kMetadataSlots);
+    ASSERT_EQ(builder, nullptr);
+}
+
+TEST(liblp, MetadataAlignment) {
+    // Make sure metadata sizes get aligned up.
+    unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1000, 2);
+    unique_ptr<LpMetadata> exported = builder->Export();
+    ASSERT_NE(exported, nullptr);
+    EXPECT_EQ(exported->geometry.metadata_max_size, 1024);
+}
+
+TEST(liblp, InternalAlignment) {
+    // Test the metadata fitting within alignment.
+    BlockDeviceInfo device_info(1024 * 1024, 768 * 1024, 0);
+    unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, 1024, 2);
+    ASSERT_NE(builder, nullptr);
+    unique_ptr<LpMetadata> exported = builder->Export();
+    ASSERT_NE(exported, nullptr);
+    EXPECT_EQ(exported->geometry.first_logical_sector, 1536);
+    EXPECT_EQ(exported->geometry.last_logical_sector, 2035);
+
+    // Test a large alignment offset thrown in.
+    device_info.alignment_offset = 753664;
+    builder = MetadataBuilder::New(device_info, 1024, 2);
+    ASSERT_NE(builder, nullptr);
+    exported = builder->Export();
+    ASSERT_NE(exported, nullptr);
+    EXPECT_EQ(exported->geometry.first_logical_sector, 1472);
+    EXPECT_EQ(exported->geometry.last_logical_sector, 2035);
+
+    // Alignment offset without alignment doesn't mean anything.
+    device_info.alignment = 0;
+    builder = MetadataBuilder::New(device_info, 1024, 2);
+    ASSERT_EQ(builder, nullptr);
+
+    // Test a small alignment with an alignment offset.
+    device_info.alignment = 12 * 1024;
+    device_info.alignment_offset = 3 * 1024;
+    builder = MetadataBuilder::New(device_info, 16 * 1024, 2);
+    ASSERT_NE(builder, nullptr);
+    exported = builder->Export();
+    ASSERT_NE(exported, nullptr);
+    EXPECT_EQ(exported->geometry.first_logical_sector, 78);
+    EXPECT_EQ(exported->geometry.last_logical_sector, 1975);
+
+    // Test a small alignment with no alignment offset.
+    device_info.alignment = 11 * 1024;
+    builder = MetadataBuilder::New(device_info, 16 * 1024, 2);
+    ASSERT_NE(builder, nullptr);
+    exported = builder->Export();
+    ASSERT_NE(exported, nullptr);
+    EXPECT_EQ(exported->geometry.first_logical_sector, 72);
+    EXPECT_EQ(exported->geometry.last_logical_sector, 1975);
+}
+
+TEST(liblp, InternalPartitionAlignment) {
+    BlockDeviceInfo device_info(512 * 1024 * 1024, 768 * 1024, 753664);
+    unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, 32 * 1024, 2);
+
+    Partition* a = builder->AddPartition("a", TEST_GUID, 0);
+    ASSERT_NE(a, nullptr);
+    Partition* b = builder->AddPartition("b", TEST_GUID2, 0);
+    ASSERT_NE(b, nullptr);
+
+    // Add a bunch of small extents to each, interleaving.
+    for (size_t i = 0; i < 10; i++) {
+        ASSERT_TRUE(builder->GrowPartition(a, a->size() + 4096));
+        ASSERT_TRUE(builder->GrowPartition(b, b->size() + 4096));
+    }
+    EXPECT_EQ(a->size(), 40960);
+    EXPECT_EQ(b->size(), 40960);
+
+    unique_ptr<LpMetadata> exported = builder->Export();
+    ASSERT_NE(exported, nullptr);
+
+    // Check that each starting sector is aligned.
+    for (const auto& extent : exported->extents) {
+        ASSERT_EQ(extent.target_type, LP_TARGET_TYPE_LINEAR);
+        EXPECT_EQ(extent.num_sectors, 8);
+
+        uint64_t lba = extent.target_data * LP_SECTOR_SIZE;
+        uint64_t aligned_lba = AlignTo(lba, device_info.alignment, device_info.alignment_offset);
+        EXPECT_EQ(lba, aligned_lba);
+    }
+
+    // Sanity check one extent.
+    EXPECT_EQ(exported->extents.back().target_data, 30656);
+}
+
+TEST(liblp, UseAllDiskSpace) {
+    unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1024, 2);
+    EXPECT_EQ(builder->AllocatableSpace(), 1036288);
+
+    Partition* system = builder->AddPartition("system", TEST_GUID, LP_PARTITION_ATTR_READONLY);
+    ASSERT_NE(system, nullptr);
+    EXPECT_EQ(builder->GrowPartition(system, 1036288), true);
+    EXPECT_EQ(system->size(), 1036288);
+    EXPECT_EQ(builder->GrowPartition(system, 1036289), false);
+}
+
+TEST(liblp, BuildComplex) {
+    unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1024, 2);
+
+    Partition* system = builder->AddPartition("system", TEST_GUID, LP_PARTITION_ATTR_READONLY);
+    Partition* vendor = builder->AddPartition("vendor", TEST_GUID2, LP_PARTITION_ATTR_READONLY);
+    ASSERT_NE(system, nullptr);
+    ASSERT_NE(vendor, nullptr);
+    EXPECT_EQ(builder->GrowPartition(system, 65536), true);
+    EXPECT_EQ(builder->GrowPartition(vendor, 32768), true);
+    EXPECT_EQ(builder->GrowPartition(system, 98304), true);
+    EXPECT_EQ(system->size(), 98304);
+    EXPECT_EQ(vendor->size(), 32768);
+
+    // We now expect to have 3 extents total: 2 for system, 1 for vendor, since
+    // our allocation strategy is greedy/first-fit.
+    ASSERT_EQ(system->extents().size(), 2);
+    ASSERT_EQ(vendor->extents().size(), 1);
+
+    LinearExtent* system1 = system->extents()[0]->AsLinearExtent();
+    LinearExtent* system2 = system->extents()[1]->AsLinearExtent();
+    LinearExtent* vendor1 = vendor->extents()[0]->AsLinearExtent();
+    ASSERT_NE(system1, nullptr);
+    ASSERT_NE(system2, nullptr);
+    ASSERT_NE(vendor1, nullptr);
+    EXPECT_EQ(system1->num_sectors(), 65536 / LP_SECTOR_SIZE);
+    EXPECT_EQ(system1->physical_sector(), 12);
+    EXPECT_EQ(system2->num_sectors(), 32768 / LP_SECTOR_SIZE);
+    EXPECT_EQ(system2->physical_sector(), 204);
+    EXPECT_EQ(vendor1->num_sectors(), 32768 / LP_SECTOR_SIZE);
+    EXPECT_EQ(vendor1->physical_sector(), 140);
+    EXPECT_EQ(system1->physical_sector() + system1->num_sectors(), vendor1->physical_sector());
+    EXPECT_EQ(vendor1->physical_sector() + vendor1->num_sectors(), system2->physical_sector());
+}
+
+TEST(liblp, AddInvalidPartition) {
+    unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1024, 2);
+
+    Partition* partition = builder->AddPartition("system", TEST_GUID, LP_PARTITION_ATTR_READONLY);
+    ASSERT_NE(partition, nullptr);
+
+    // Duplicate name.
+    partition = builder->AddPartition("system", TEST_GUID, LP_PARTITION_ATTR_READONLY);
+    EXPECT_EQ(partition, nullptr);
+
+    // Empty name.
+    partition = builder->AddPartition("", TEST_GUID, LP_PARTITION_ATTR_READONLY);
+    EXPECT_EQ(partition, nullptr);
+}
+
+TEST(liblp, BuilderExport) {
+    static const uint64_t kDiskSize = 1024 * 1024;
+    static const uint32_t kMetadataSize = 1024;
+    static const uint32_t kMetadataSlots = 2;
+    unique_ptr<MetadataBuilder> builder =
+            MetadataBuilder::New(kDiskSize, kMetadataSize, kMetadataSlots);
+
+    Partition* system = builder->AddPartition("system", TEST_GUID, LP_PARTITION_ATTR_READONLY);
+    Partition* vendor = builder->AddPartition("vendor", TEST_GUID2, LP_PARTITION_ATTR_READONLY);
+    ASSERT_NE(system, nullptr);
+    ASSERT_NE(vendor, nullptr);
+    EXPECT_EQ(builder->GrowPartition(system, 65536), true);
+    EXPECT_EQ(builder->GrowPartition(vendor, 32768), true);
+    EXPECT_EQ(builder->GrowPartition(system, 98304), true);
+
+    unique_ptr<LpMetadata> exported = builder->Export();
+    EXPECT_NE(exported, nullptr);
+
+    // Verify geometry. Some details of this may change if we change the
+    // metadata structures. So in addition to checking the exact values, we
+    // also check that they are internally consistent after.
+    const LpMetadataGeometry& geometry = exported->geometry;
+    EXPECT_EQ(geometry.magic, LP_METADATA_GEOMETRY_MAGIC);
+    EXPECT_EQ(geometry.struct_size, sizeof(geometry));
+    EXPECT_EQ(geometry.metadata_max_size, 1024);
+    EXPECT_EQ(geometry.metadata_slot_count, 2);
+    EXPECT_EQ(geometry.first_logical_sector, 12);
+    EXPECT_EQ(geometry.last_logical_sector, 2035);
+
+    static const size_t kMetadataSpace =
+            (kMetadataSize * kMetadataSlots) + LP_METADATA_GEOMETRY_SIZE;
+    uint64_t space_at_end = kDiskSize - (geometry.last_logical_sector + 1) * LP_SECTOR_SIZE;
+    EXPECT_GE(space_at_end, kMetadataSpace);
+    EXPECT_GE(geometry.first_logical_sector * LP_SECTOR_SIZE, kMetadataSpace);
+
+    // Verify header.
+    const LpMetadataHeader& header = exported->header;
+    EXPECT_EQ(header.magic, LP_METADATA_HEADER_MAGIC);
+    EXPECT_EQ(header.major_version, LP_METADATA_MAJOR_VERSION);
+    EXPECT_EQ(header.minor_version, LP_METADATA_MINOR_VERSION);
+
+    ASSERT_EQ(exported->partitions.size(), 2);
+    ASSERT_EQ(exported->extents.size(), 3);
+
+    for (const auto& partition : exported->partitions) {
+        Partition* original = builder->FindPartition(GetPartitionName(partition));
+        ASSERT_NE(original, nullptr);
+        EXPECT_EQ(original->guid(), GetPartitionGuid(partition));
+        for (size_t i = 0; i < partition.num_extents; i++) {
+            const auto& extent = exported->extents[partition.first_extent_index + i];
+            LinearExtent* original_extent = original->extents()[i]->AsLinearExtent();
+            EXPECT_EQ(extent.num_sectors, original_extent->num_sectors());
+            EXPECT_EQ(extent.target_type, LP_TARGET_TYPE_LINEAR);
+            EXPECT_EQ(extent.target_data, original_extent->physical_sector());
+        }
+        EXPECT_EQ(partition.attributes, original->attributes());
+    }
+}
+
+TEST(liblp, BuilderImport) {
+    unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1024, 2);
+
+    Partition* system = builder->AddPartition("system", TEST_GUID, LP_PARTITION_ATTR_READONLY);
+    Partition* vendor = builder->AddPartition("vendor", TEST_GUID2, LP_PARTITION_ATTR_READONLY);
+    ASSERT_NE(system, nullptr);
+    ASSERT_NE(vendor, nullptr);
+    EXPECT_EQ(builder->GrowPartition(system, 65536), true);
+    EXPECT_EQ(builder->GrowPartition(vendor, 32768), true);
+    EXPECT_EQ(builder->GrowPartition(system, 98304), true);
+
+    unique_ptr<LpMetadata> exported = builder->Export();
+    ASSERT_NE(exported, nullptr);
+
+    builder = MetadataBuilder::New(*exported.get());
+    ASSERT_NE(builder, nullptr);
+    system = builder->FindPartition("system");
+    ASSERT_NE(system, nullptr);
+    vendor = builder->FindPartition("vendor");
+    ASSERT_NE(vendor, nullptr);
+
+    EXPECT_EQ(system->size(), 98304);
+    ASSERT_EQ(system->extents().size(), 2);
+    EXPECT_EQ(system->guid(), TEST_GUID);
+    EXPECT_EQ(system->attributes(), LP_PARTITION_ATTR_READONLY);
+    EXPECT_EQ(vendor->size(), 32768);
+    ASSERT_EQ(vendor->extents().size(), 1);
+    EXPECT_EQ(vendor->guid(), TEST_GUID2);
+    EXPECT_EQ(vendor->attributes(), LP_PARTITION_ATTR_READONLY);
+
+    LinearExtent* system1 = system->extents()[0]->AsLinearExtent();
+    LinearExtent* system2 = system->extents()[1]->AsLinearExtent();
+    LinearExtent* vendor1 = vendor->extents()[0]->AsLinearExtent();
+    EXPECT_EQ(system1->num_sectors(), 65536 / LP_SECTOR_SIZE);
+    EXPECT_EQ(system1->physical_sector(), 12);
+    EXPECT_EQ(system2->num_sectors(), 32768 / LP_SECTOR_SIZE);
+    EXPECT_EQ(system2->physical_sector(), 204);
+    EXPECT_EQ(vendor1->num_sectors(), 32768 / LP_SECTOR_SIZE);
+}
+
+TEST(liblp, ExportNameTooLong) {
+    unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1024, 2);
+
+    std::string name = "abcdefghijklmnopqrstuvwxyz0123456789";
+    Partition* system = builder->AddPartition(name + name, TEST_GUID, LP_PARTITION_ATTR_READONLY);
+    EXPECT_NE(system, nullptr);
+
+    unique_ptr<LpMetadata> exported = builder->Export();
+    EXPECT_EQ(exported, nullptr);
+}
+
+TEST(liblp, ExportInvalidGuid) {
+    unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1024, 2);
+
+    Partition* system = builder->AddPartition("system", "bad", LP_PARTITION_ATTR_READONLY);
+    EXPECT_NE(system, nullptr);
+
+    unique_ptr<LpMetadata> exported = builder->Export();
+    EXPECT_EQ(exported, nullptr);
+}
+
+TEST(liblp, MetadataTooLarge) {
+    static const size_t kDiskSize = 128 * 1024;
+    static const size_t kMetadataSize = 64 * 1024;
+
+    // No space to store metadata + geometry.
+    BlockDeviceInfo device_info(kDiskSize, 0, 0);
+    unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, kMetadataSize, 1);
+    EXPECT_EQ(builder, nullptr);
+
+    // No space to store metadata + geometry + one free sector.
+    device_info.size += LP_METADATA_GEOMETRY_SIZE * 2;
+    builder = MetadataBuilder::New(device_info, kMetadataSize, 1);
+    EXPECT_EQ(builder, nullptr);
+
+    // Space for metadata + geometry + one free sector.
+    device_info.size += LP_SECTOR_SIZE;
+    builder = MetadataBuilder::New(device_info, kMetadataSize, 1);
+    EXPECT_NE(builder, nullptr);
+
+    // Test with alignment.
+    device_info.alignment = 131072;
+    builder = MetadataBuilder::New(device_info, kMetadataSize, 1);
+    EXPECT_EQ(builder, nullptr);
+
+    device_info.alignment = 0;
+    device_info.alignment_offset = 32768 - LP_SECTOR_SIZE;
+    builder = MetadataBuilder::New(device_info, kMetadataSize, 1);
+    EXPECT_EQ(builder, nullptr);
+}
+
+TEST(liblp, block_device_info) {
+    std::unique_ptr<fstab, decltype(&fs_mgr_free_fstab)> fstab(fs_mgr_read_fstab_default(),
+                                                               fs_mgr_free_fstab);
+    ASSERT_NE(fstab, nullptr);
+
+    // This should read from the "super" partition once we have a well-defined
+    // way to access it.
+    struct fstab_rec* rec = fs_mgr_get_entry_for_mount_point(fstab.get(), "/data");
+    ASSERT_NE(rec, nullptr);
+
+    BlockDeviceInfo device_info;
+    ASSERT_TRUE(GetBlockDeviceInfo(rec->blk_device, &device_info));
+
+    // Sanity check that the device doesn't give us some weird inefficient
+    // alignment.
+    ASSERT_EQ(device_info.alignment % LP_SECTOR_SIZE, 0);
+    ASSERT_EQ(device_info.alignment_offset % LP_SECTOR_SIZE, 0);
+    ASSERT_LE(device_info.alignment_offset, INT_MAX);
+
+    // Having an alignment offset > alignment doesn't really make sense.
+    ASSERT_LT(device_info.alignment_offset, device_info.alignment);
+}
+
+TEST(liblp, UpdateBlockDeviceInfo) {
+    BlockDeviceInfo device_info(1024 * 1024, 4096, 1024);
+    unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, 1024, 1);
+    ASSERT_NE(builder, nullptr);
+
+    EXPECT_EQ(builder->block_device_info().size, device_info.size);
+    EXPECT_EQ(builder->block_device_info().alignment, device_info.alignment);
+    EXPECT_EQ(builder->block_device_info().alignment_offset, device_info.alignment_offset);
+
+    device_info.alignment = 0;
+    device_info.alignment_offset = 2048;
+    builder->set_block_device_info(device_info);
+    EXPECT_EQ(builder->block_device_info().alignment, 4096);
+    EXPECT_EQ(builder->block_device_info().alignment_offset, device_info.alignment_offset);
+
+    device_info.alignment = 8192;
+    device_info.alignment_offset = 0;
+    builder->set_block_device_info(device_info);
+    EXPECT_EQ(builder->block_device_info().alignment, 8192);
+    EXPECT_EQ(builder->block_device_info().alignment_offset, 2048);
+}
diff --git a/fs_mgr/liblp/images.cpp b/fs_mgr/liblp/images.cpp
new file mode 100644
index 0000000..a361a5d
--- /dev/null
+++ b/fs_mgr/liblp/images.cpp
@@ -0,0 +1,355 @@
+/*
+ * 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 "images.h"
+
+#include <limits.h>
+
+#include <android-base/file.h>
+#include <android-base/unique_fd.h>
+#include <sparse/sparse.h>
+
+#include "reader.h"
+#include "utility.h"
+#include "writer.h"
+
+namespace android {
+namespace fs_mgr {
+
+std::unique_ptr<LpMetadata> ReadFromImageFile(int fd) {
+    LpMetadataGeometry geometry;
+    if (!ReadLogicalPartitionGeometry(fd, &geometry)) {
+        return nullptr;
+    }
+    if (SeekFile64(fd, LP_METADATA_GEOMETRY_SIZE, SEEK_SET) < 0) {
+        PERROR << __PRETTY_FUNCTION__ << "lseek failed: offset " << LP_METADATA_GEOMETRY_SIZE;
+        return nullptr;
+    }
+    std::unique_ptr<LpMetadata> metadata = ParseMetadata(fd);
+    if (!metadata) {
+        return nullptr;
+    }
+    metadata->geometry = geometry;
+    return metadata;
+}
+
+std::unique_ptr<LpMetadata> ReadFromImageFile(const char* file) {
+    android::base::unique_fd fd(open(file, O_RDONLY));
+    if (fd < 0) {
+        PERROR << __PRETTY_FUNCTION__ << "open failed: " << file;
+        return nullptr;
+    }
+    return ReadFromImageFile(fd);
+}
+
+bool WriteToImageFile(int fd, const LpMetadata& input) {
+    std::string geometry = SerializeGeometry(input.geometry);
+    std::string metadata = SerializeMetadata(input);
+
+    std::string everything = geometry + metadata;
+
+    if (!android::base::WriteFully(fd, everything.data(), everything.size())) {
+        PERROR << __PRETTY_FUNCTION__ << "write " << everything.size() << " bytes failed";
+        return false;
+    }
+    return true;
+}
+
+bool WriteToImageFile(const char* file, const LpMetadata& input) {
+    android::base::unique_fd fd(open(file, O_CREAT | O_RDWR | O_TRUNC, 0644));
+    if (fd < 0) {
+        PERROR << __PRETTY_FUNCTION__ << "open failed: " << file;
+        return false;
+    }
+    return WriteToImageFile(fd, input);
+}
+
+// We use an object to build the sparse file since it requires that data
+// pointers be held alive until the sparse file is destroyed. It's easier
+// to do this when the data pointers are all in one place.
+class SparseBuilder {
+  public:
+    SparseBuilder(const LpMetadata& metadata, uint32_t block_size,
+                  const std::map<std::string, std::string>& images);
+
+    bool Build();
+    bool Export(const char* file);
+    bool IsValid() const { return file_ != nullptr; }
+
+  private:
+    bool AddData(const std::string& blob, uint64_t sector);
+    bool AddPartitionImage(const LpMetadataPartition& partition, const std::string& file);
+    int OpenImageFile(const std::string& file);
+    bool SectorToBlock(uint64_t sector, uint32_t* block);
+
+    const LpMetadata& metadata_;
+    const LpMetadataGeometry& geometry_;
+    uint32_t block_size_;
+    std::unique_ptr<sparse_file, decltype(&sparse_file_destroy)> file_;
+    std::string primary_blob_;
+    std::string backup_blob_;
+    std::map<std::string, std::string> images_;
+    std::vector<android::base::unique_fd> temp_fds_;
+};
+
+SparseBuilder::SparseBuilder(const LpMetadata& metadata, uint32_t block_size,
+                             const std::map<std::string, std::string>& images)
+    : metadata_(metadata),
+      geometry_(metadata.geometry),
+      block_size_(block_size),
+      file_(sparse_file_new(block_size_, geometry_.block_device_size), sparse_file_destroy),
+      images_(images) {}
+
+bool SparseBuilder::Export(const char* file) {
+    android::base::unique_fd fd(open(file, O_CREAT | O_RDWR | O_TRUNC, 0644));
+    if (fd < 0) {
+        PERROR << "open failed: " << file;
+        return false;
+    }
+    // No gzip compression; sparseify; no checksum.
+    int ret = sparse_file_write(file_.get(), fd, false, true, false);
+    if (ret != 0) {
+        LERROR << "sparse_file_write failed (error code " << ret << ")";
+        return false;
+    }
+    return true;
+}
+
+bool SparseBuilder::AddData(const std::string& blob, uint64_t sector) {
+    uint32_t block;
+    if (!SectorToBlock(sector, &block)) {
+        return false;
+    }
+    void* data = const_cast<char*>(blob.data());
+    int ret = sparse_file_add_data(file_.get(), data, blob.size(), block);
+    if (ret != 0) {
+        LERROR << "sparse_file_add_data failed (error code " << ret << ")";
+        return false;
+    }
+    return true;
+}
+
+bool SparseBuilder::SectorToBlock(uint64_t sector, uint32_t* block) {
+    // The caller must ensure that the metadata has an alignment that is a
+    // multiple of the block size. liblp will take care of the rest, ensuring
+    // that all partitions are on an aligned boundary. Therefore all writes
+    // should be block-aligned, and if they are not, the table was misconfigured.
+    // Note that the default alignment is 1MiB, which is a multiple of the
+    // default block size (4096).
+    if ((sector * LP_SECTOR_SIZE) % block_size_ != 0) {
+        LERROR << "sector " << sector << " is not aligned to block size " << block_size_;
+        return false;
+    }
+    *block = (sector * LP_SECTOR_SIZE) / block_size_;
+    return true;
+}
+
+bool SparseBuilder::Build() {
+    std::string geometry_blob = SerializeGeometry(geometry_);
+    std::string metadata_blob = SerializeMetadata(metadata_);
+    metadata_blob.resize(geometry_.metadata_max_size);
+
+    std::string all_metadata;
+    for (size_t i = 0; i < geometry_.metadata_slot_count; i++) {
+        all_metadata += metadata_blob;
+    }
+
+    // Metadata immediately follows geometry, and we write the same metadata
+    // to all slots. Note that we don't bother trying to write skip chunks
+    // here since it's a small amount of data.
+    primary_blob_ = geometry_blob + all_metadata;
+    if (!AddData(primary_blob_, 0)) {
+        return false;
+    }
+
+    for (const auto& partition : metadata_.partitions) {
+        auto iter = images_.find(GetPartitionName(partition));
+        if (iter == images_.end()) {
+            continue;
+        }
+        if (!AddPartitionImage(partition, iter->second)) {
+            return false;
+        }
+        images_.erase(iter);
+    }
+
+    if (!images_.empty()) {
+        LERROR << "Partition image was specified but no partition was found.";
+        return false;
+    }
+
+    // The backup area contains all metadata slots, and then geometry. Similar
+    // to before we write the metadata to every slot.
+    int64_t backup_offset = GetBackupMetadataOffset(geometry_, 0);
+    uint64_t backups_start = geometry_.block_device_size + backup_offset;
+    uint64_t backup_sector = backups_start / LP_SECTOR_SIZE;
+
+    backup_blob_ = all_metadata + geometry_blob;
+    if (!AddData(backup_blob_, backup_sector)) {
+        return false;
+    }
+    return true;
+}
+
+static inline bool HasFillValue(uint32_t* buffer, size_t count) {
+    uint32_t fill_value = buffer[0];
+    for (size_t i = 1; i < count; i++) {
+        if (fill_value != buffer[i]) {
+            return false;
+        }
+    }
+    return true;
+}
+
+bool SparseBuilder::AddPartitionImage(const LpMetadataPartition& partition,
+                                      const std::string& file) {
+    if (partition.num_extents != 1) {
+        LERROR << "Partition for new tables should not have more than one extent: "
+               << GetPartitionName(partition);
+        return false;
+    }
+
+    const LpMetadataExtent& extent = metadata_.extents[partition.first_extent_index];
+    if (extent.target_type != LP_TARGET_TYPE_LINEAR) {
+        LERROR << "Partition should only have linear extents: " << GetPartitionName(partition);
+        return false;
+    }
+
+    int fd = OpenImageFile(file);
+    if (fd < 0) {
+        LERROR << "Could not open image for partition: " << GetPartitionName(partition);
+        return false;
+    }
+
+    // Make sure the image does not exceed the partition size.
+    uint64_t file_length;
+    if (!GetDescriptorSize(fd, &file_length)) {
+        LERROR << "Could not compute image size";
+        return false;
+    }
+    if (file_length > extent.num_sectors * LP_SECTOR_SIZE) {
+        LERROR << "Image for partition '" << GetPartitionName(partition)
+               << "' is greater than its size";
+        return false;
+    }
+    if (SeekFile64(fd, 0, SEEK_SET)) {
+        PERROR << "lseek failed";
+        return false;
+    }
+
+    uint32_t output_block;
+    if (!SectorToBlock(extent.target_data, &output_block)) {
+        return false;
+    }
+
+    uint64_t pos = 0;
+    uint64_t remaining = file_length;
+    while (remaining) {
+        uint32_t buffer[block_size_ / sizeof(uint32_t)];
+        size_t read_size = remaining >= sizeof(buffer) ? sizeof(buffer) : size_t(remaining);
+        if (!android::base::ReadFully(fd, buffer, sizeof(buffer))) {
+            PERROR << "read failed";
+            return false;
+        }
+        if (read_size != sizeof(buffer) || !HasFillValue(buffer, read_size / sizeof(uint32_t))) {
+            int rv = sparse_file_add_fd(file_.get(), fd, pos, read_size, output_block);
+            if (rv) {
+                LERROR << "sparse_file_add_fd failed with code: " << rv;
+                return false;
+            }
+        } else {
+            int rv = sparse_file_add_fill(file_.get(), buffer[0], read_size, output_block);
+            if (rv) {
+                LERROR << "sparse_file_add_fill failed with code: " << rv;
+                return false;
+            }
+        }
+        pos += read_size;
+        remaining -= read_size;
+        output_block++;
+    }
+
+    return true;
+}
+
+int SparseBuilder::OpenImageFile(const std::string& file) {
+    android::base::unique_fd source_fd(open(file.c_str(), O_RDONLY));
+    if (source_fd < 0) {
+        PERROR << "open image file failed: " << file;
+        return -1;
+    }
+
+    std::unique_ptr<sparse_file, decltype(&sparse_file_destroy)> source(
+            sparse_file_import(source_fd, true, true), sparse_file_destroy);
+    if (!source) {
+        int fd = source_fd.get();
+        temp_fds_.push_back(std::move(source_fd));
+        return fd;
+    }
+
+    char temp_file[PATH_MAX];
+    snprintf(temp_file, sizeof(temp_file), "%s/imageXXXXXX", P_tmpdir);
+    android::base::unique_fd temp_fd(mkstemp(temp_file));
+    if (temp_fd < 0) {
+        PERROR << "mkstemp failed";
+        return -1;
+    }
+    if (unlink(temp_file) < 0) {
+        PERROR << "unlink failed";
+        return -1;
+    }
+
+    // We temporarily unsparse the file, rather than try to merge its chunks.
+    int rv = sparse_file_write(source.get(), temp_fd, false, false, false);
+    if (rv) {
+        LERROR << "sparse_file_write failed with code: " << rv;
+        return -1;
+    }
+    temp_fds_.push_back(std::move(temp_fd));
+    return temp_fds_.back().get();
+}
+
+bool WriteToSparseFile(const char* file, const LpMetadata& metadata, uint32_t block_size,
+                       const std::map<std::string, std::string>& images) {
+    if (block_size % LP_SECTOR_SIZE != 0) {
+        LERROR << "Block size must be a multiple of the sector size, " << LP_SECTOR_SIZE;
+        return false;
+    }
+    if (metadata.geometry.block_device_size % block_size != 0) {
+        LERROR << "Device size must be a multiple of the block size, " << block_size;
+        return false;
+    }
+    uint64_t num_blocks = metadata.geometry.block_device_size % block_size;
+    if (num_blocks >= UINT_MAX) {
+        // libsparse counts blocks in unsigned 32-bit integers, so we check to
+        // make sure we're not going to overflow.
+        LERROR << "Block device is too large to encode with libsparse.";
+        return false;
+    }
+
+    SparseBuilder builder(metadata, block_size, images);
+    if (!builder.IsValid()) {
+        LERROR << "Could not allocate sparse file of size " << metadata.geometry.block_device_size;
+        return false;
+    }
+    if (!builder.Build()) {
+        return false;
+    }
+    return builder.Export(file);
+}
+
+}  // namespace fs_mgr
+}  // namespace android
diff --git a/fs_mgr/liblp/images.h b/fs_mgr/liblp/images.h
new file mode 100644
index 0000000..3a999b8
--- /dev/null
+++ b/fs_mgr/liblp/images.h
@@ -0,0 +1,29 @@
+/*
+ * 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 <liblp/liblp.h>
+
+namespace android {
+namespace fs_mgr {
+
+// Helper function to serialize geometry and metadata to a normal file, for
+// flashing or debugging.
+std::unique_ptr<LpMetadata> ReadFromImageFile(int fd);
+bool WriteToImageFile(const char* file, const LpMetadata& metadata);
+bool WriteToImageFile(int fd, const LpMetadata& metadata);
+
+}  // namespace fs_mgr
+}  // namespace android
diff --git a/fs_mgr/liblp/include/liblp/builder.h b/fs_mgr/liblp/include/liblp/builder.h
new file mode 100644
index 0000000..8bde313
--- /dev/null
+++ b/fs_mgr/liblp/include/liblp/builder.h
@@ -0,0 +1,217 @@
+//
+// 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 LIBLP_METADATA_BUILDER_H
+#define LIBLP_METADATA_BUILDER_H
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <map>
+#include <memory>
+
+#include "liblp.h"
+
+namespace android {
+namespace fs_mgr {
+
+class LinearExtent;
+
+// By default, partitions are aligned on a 1MiB boundary.
+static const uint32_t kDefaultPartitionAlignment = 1024 * 1024;
+
+struct BlockDeviceInfo {
+    BlockDeviceInfo() : size(0), alignment(0), alignment_offset(0) {}
+    BlockDeviceInfo(uint64_t size, uint32_t alignment, uint32_t alignment_offset)
+        : size(size), alignment(alignment), alignment_offset(alignment_offset) {}
+    // Size of the block device, in bytes.
+    uint64_t size;
+    // Optimal target alignment, in bytes. Partition extents will be aligned to
+    // this value by default. This value must be 0 or a multiple of 512.
+    uint32_t alignment;
+    // Alignment offset to parent device (if any), in bytes. The sector at
+    // |alignment_offset| on the target device is correctly aligned on its
+    // parent device. This value must be 0 or a multiple of 512.
+    uint32_t alignment_offset;
+};
+
+// Abstraction around dm-targets that can be encoded into logical partition tables.
+class Extent {
+  public:
+    explicit Extent(uint64_t num_sectors) : num_sectors_(num_sectors) {}
+    virtual ~Extent() {}
+
+    virtual void AddTo(LpMetadata* out) const = 0;
+    virtual LinearExtent* AsLinearExtent() { return nullptr; }
+
+    uint64_t num_sectors() const { return num_sectors_; }
+    void set_num_sectors(uint64_t num_sectors) { num_sectors_ = num_sectors; }
+
+  protected:
+    uint64_t num_sectors_;
+};
+
+// This corresponds to a dm-linear target.
+class LinearExtent final : public Extent {
+  public:
+    LinearExtent(uint64_t num_sectors, uint64_t physical_sector)
+        : Extent(num_sectors), physical_sector_(physical_sector) {}
+
+    void AddTo(LpMetadata* metadata) const override;
+    LinearExtent* AsLinearExtent() override { return this; }
+
+    uint64_t physical_sector() const { return physical_sector_; }
+
+  private:
+    uint64_t physical_sector_;
+};
+
+// This corresponds to a dm-zero target.
+class ZeroExtent final : public Extent {
+  public:
+    explicit ZeroExtent(uint64_t num_sectors) : Extent(num_sectors) {}
+
+    void AddTo(LpMetadata* out) const override;
+};
+
+class Partition final {
+  public:
+    Partition(const std::string& name, const std::string& guid, uint32_t attributes);
+
+    // Add a raw extent.
+    void AddExtent(std::unique_ptr<Extent>&& extent);
+
+    // Remove all extents from this partition.
+    void RemoveExtents();
+
+    // Remove and/or shrink extents until the partition is the requested size.
+    // See MetadataBuilder::ShrinkPartition for more information.
+    void ShrinkTo(uint64_t requested_size);
+
+    const std::string& name() const { return name_; }
+    uint32_t attributes() const { return attributes_; }
+    const std::string& guid() const { return guid_; }
+    const std::vector<std::unique_ptr<Extent>>& extents() const { return extents_; }
+    uint64_t size() const { return size_; }
+
+  private:
+    std::string name_;
+    std::string guid_;
+    std::vector<std::unique_ptr<Extent>> extents_;
+    uint32_t attributes_;
+    uint64_t size_;
+};
+
+class MetadataBuilder {
+  public:
+    // Construct an empty logical partition table builder. The block device size
+    // and maximum metadata size must be specified, as this will determine which
+    // areas of the physical partition can be flashed for metadata vs for logical
+    // partitions.
+    //
+    // If the parameters would yield invalid metadata, nullptr is returned. This
+    // could happen if the block device size is too small to store the metadata
+    // and backup copies.
+    static std::unique_ptr<MetadataBuilder> New(const BlockDeviceInfo& device_info,
+                                                uint32_t metadata_max_size,
+                                                uint32_t metadata_slot_count);
+
+    // Import an existing table for modification. This reads metadata off the
+    // given block device and imports it. It also adjusts alignment information
+    // based on run-time values in the operating system.
+    static std::unique_ptr<MetadataBuilder> New(const std::string& block_device,
+                                                uint32_t slot_number);
+
+    // Import an existing table for modification. If the table is not valid, for
+    // example it contains duplicate partition names, then nullptr is returned.
+    // This method is for testing or changing off-line tables.
+    static std::unique_ptr<MetadataBuilder> New(const LpMetadata& metadata);
+
+    // Wrapper around New() with a BlockDeviceInfo that only specifies a device
+    // size. This is a convenience method for tests.
+    static std::unique_ptr<MetadataBuilder> New(uint64_t blockdev_size, uint32_t metadata_max_size,
+                                                uint32_t metadata_slot_count) {
+        BlockDeviceInfo device_info(blockdev_size, 0, 0);
+        return New(device_info, metadata_max_size, metadata_slot_count);
+    }
+
+    // Export metadata so it can be serialized to an image, to disk, or mounted
+    // via device-mapper.
+    std::unique_ptr<LpMetadata> Export();
+
+    // Add a partition, returning a handle so it can be sized as needed. If a
+    // partition with the given name already exists, nullptr is returned.
+    Partition* AddPartition(const std::string& name, const std::string& guid, uint32_t attributes);
+
+    // Delete a partition by name if it exists.
+    void RemovePartition(const std::string& name);
+
+    // Find a partition by name. If no partition is found, nullptr is returned.
+    Partition* FindPartition(const std::string& name);
+
+    // Grow a partition to the requested size. If the partition's size is already
+    // greater or equal to the requested size, this will return true and the
+    // partition table will not be changed. Otherwise, a greedy algorithm is
+    // used to find free gaps in the partition table and allocate them for this
+    // partition. If not enough space can be allocated, false is returned, and
+    // the parition table will not be modified.
+    //
+    // The size will be rounded UP to the nearest sector.
+    //
+    // Note, this is an in-memory operation, and it does not alter the
+    // underlying filesystem or contents of the partition on disk.
+    bool GrowPartition(Partition* partition, uint64_t requested_size);
+
+    // Shrink a partition to the requested size. If the partition is already
+    // smaller than the given size, this will return and the partition table
+    // will not be changed. Otherwise, extents will be removed and/or shrunk
+    // from the end of the partition until it is the requested size.
+    //
+    // The size will be rounded UP to the nearest sector.
+    //
+    // Note, this is an in-memory operation, and it does not alter the
+    // underlying filesystem or contents of the partition on disk.
+    void ShrinkPartition(Partition* partition, uint64_t requested_size);
+
+    // Amount of space that can be allocated to logical partitions.
+    uint64_t AllocatableSpace() const;
+
+    // Merge new block device information into previous values. Alignment values
+    // are only overwritten if the new values are non-zero.
+    void set_block_device_info(const BlockDeviceInfo& device_info);
+    const BlockDeviceInfo& block_device_info() const { return device_info_; }
+
+  private:
+    MetadataBuilder();
+    bool Init(const BlockDeviceInfo& info, uint32_t metadata_max_size, uint32_t metadata_slot_count);
+    bool Init(const LpMetadata& metadata);
+
+    uint64_t AlignSector(uint64_t sector);
+
+    LpMetadataGeometry geometry_;
+    LpMetadataHeader header_;
+    std::vector<std::unique_ptr<Partition>> partitions_;
+    BlockDeviceInfo device_info_;
+};
+
+// Read BlockDeviceInfo for a given block device. This always returns false
+// for non-Linux operating systems.
+bool GetBlockDeviceInfo(const std::string& block_device, BlockDeviceInfo* device_info);
+
+}  // namespace fs_mgr
+}  // namespace android
+
+#endif /* LIBLP_METADATA_BUILDER_H */
diff --git a/fs_mgr/liblp/include/liblp/liblp.h b/fs_mgr/liblp/include/liblp/liblp.h
new file mode 100644
index 0000000..627aa8c
--- /dev/null
+++ b/fs_mgr/liblp/include/liblp/liblp.h
@@ -0,0 +1,78 @@
+//
+// 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 LIBLP_LIBLP_H
+#define LIBLP_LIBLP_H
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <map>
+#include <memory>
+#include <string>
+
+#include "metadata_format.h"
+
+namespace android {
+namespace fs_mgr {
+
+// Helper structure for easily interpreting deserialized metadata, or
+// re-serializing metadata.
+struct LpMetadata {
+    LpMetadataGeometry geometry;
+    LpMetadataHeader header;
+    std::vector<LpMetadataPartition> partitions;
+    std::vector<LpMetadataExtent> extents;
+};
+
+// Place an initial partition table on the device. This will overwrite the
+// existing geometry, and should not be used for normal partition table
+// updates. False can be returned if the geometry is incompatible with the
+// block device or an I/O error occurs.
+bool FlashPartitionTable(const std::string& block_device, const LpMetadata& metadata,
+                         uint32_t slot_number);
+
+// Update the partition table for a given metadata slot number. False is
+// returned if an error occurs, which can include:
+//  - Invalid slot number.
+//  - I/O error.
+//  - Corrupt or missing metadata geometry on disk.
+//  - Incompatible geometry.
+bool UpdatePartitionTable(const std::string& block_device, const LpMetadata& metadata,
+                          uint32_t slot_number);
+
+// Read logical partition metadata from its predetermined location on a block
+// device. If readback fails, we also attempt to load from a backup copy.
+std::unique_ptr<LpMetadata> ReadMetadata(const char* block_device, uint32_t slot_number);
+
+// Read/Write logical partition metadata to an image file, for diagnostics or
+// flashing.
+bool WriteToSparseFile(const char* file, const LpMetadata& metadata, uint32_t block_size,
+                       const std::map<std::string, std::string>& images);
+bool WriteToImageFile(const char* file, const LpMetadata& metadata);
+std::unique_ptr<LpMetadata> ReadFromImageFile(const char* file);
+
+// Helper to extract safe C++ strings from partition info.
+std::string GetPartitionName(const LpMetadataPartition& partition);
+std::string GetPartitionGuid(const LpMetadataPartition& partition);
+
+// Helper to return a slot number for a slot suffix.
+uint32_t SlotNumberForSlotSuffix(const std::string& suffix);
+
+}  // namespace fs_mgr
+}  // namespace android
+
+#endif  // LIBLP_LIBLP_H
diff --git a/fs_mgr/liblp/include/liblp/metadata_format.h b/fs_mgr/liblp/include/liblp/metadata_format.h
new file mode 100644
index 0000000..e1323e1
--- /dev/null
+++ b/fs_mgr/liblp/include/liblp/metadata_format.h
@@ -0,0 +1,272 @@
+/*
+ * 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 LOGICAL_PARTITION_METADATA_FORMAT_H_
+#define LOGICAL_PARTITION_METADATA_FORMAT_H_
+
+#ifdef __cplusplus
+#include <string>
+#include <vector>
+#endif
+
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Magic signature for LpMetadataGeometry. */
+#define LP_METADATA_GEOMETRY_MAGIC 0x616c4467
+
+/* Space reserved for geometry information. */
+#define LP_METADATA_GEOMETRY_SIZE 4096
+
+/* Magic signature for LpMetadataHeader. */
+#define LP_METADATA_HEADER_MAGIC 0x414C5030
+
+/* Current metadata version. */
+#define LP_METADATA_MAJOR_VERSION 1
+#define LP_METADATA_MINOR_VERSION 0
+
+/* Attributes for the LpMetadataPartition::attributes field.
+ *
+ * READONLY - The partition should not be considered writable. When used with
+ * device mapper, the block device will be created as read-only.
+ */
+#define LP_PARTITION_ATTR_NONE 0x0
+#define LP_PARTITION_ATTR_READONLY 0x1
+
+/* Mask that defines all valid attributes. */
+#define LP_PARTITION_ATTRIBUTE_MASK (LP_PARTITION_ATTR_READONLY)
+
+/* Default name of the physical partition that holds logical partition entries.
+ * The layout of this partition will look like:
+ *
+ *     +--------------------+
+ *     | Disk Geometry      |
+ *     +--------------------+
+ *     | Metadata           |
+ *     +--------------------+
+ *     | Logical Partitions |
+ *     +--------------------+
+ *     | Backup Metadata    |
+ *     +--------------------+
+ *     | Geometry Backup    |
+ *     +--------------------+
+ */
+#define LP_METADATA_PARTITION_NAME "super"
+
+/* Size of a sector is always 512 bytes for compatibility with the Linux kernel. */
+#define LP_SECTOR_SIZE 512
+
+/* This structure is stored at sector 0 in the first 4096 bytes of the
+ * partition, and again in the very last 4096 bytes. It is never modified and
+ * describes how logical partition information can be located.
+ */
+typedef struct LpMetadataGeometry {
+    /*  0: Magic signature (LP_METADATA_GEOMETRY_MAGIC). */
+    uint32_t magic;
+
+    /*  4: Size of the LpMetadataGeometry struct. */
+    uint32_t struct_size;
+
+    /*  8: SHA256 checksum of this struct, with this field set to 0. */
+    uint8_t checksum[32];
+
+    /* 40: Maximum amount of space a single copy of the metadata can use. This
+     * must be a multiple of LP_SECTOR_SIZE.
+     */
+    uint32_t metadata_max_size;
+
+    /* 44: Number of copies of the metadata to keep. For A/B devices, this
+     * will be 2. For an A/B/C device, it would be 3, et cetera. For Non-A/B
+     * it will be 1. A backup copy of each slot is kept, so if this is "2",
+     * there will be four copies total.
+     */
+    uint32_t metadata_slot_count;
+
+    /* 48: First usable sector for allocating logical partitions. this will be
+     * the first sector after the initial 4096 geometry block, followed by the
+     * space consumed by metadata_max_size*metadata_slot_count.
+     */
+    uint64_t first_logical_sector;
+
+    /* 56: Last usable sector, inclusive, for allocating logical partitions.
+     * At the end of this sector will follow backup metadata slots and the
+     * backup geometry block at the very end.
+     */
+    uint64_t last_logical_sector;
+
+    /* 64: Alignment for defining partitions or partition extents. For example,
+     * an alignment of 1MiB will require that all partitions have a size evenly
+     * divisible by 1MiB, and that the smallest unit the partition can grow by
+     * is 1MiB.
+     *
+     * Alignment is normally determined at runtime when growing or adding
+     * partitions. If for some reason the alignment cannot be determined, then
+     * this predefined alignment in the geometry is used instead. By default
+     * it is set to 1MiB.
+     */
+    uint32_t alignment;
+
+    /* 68: Alignment offset for "stacked" devices. For example, if the "super"
+     * partition itself is not aligned within the parent block device's
+     * partition table, then we adjust for this in deciding where to place
+     * |first_logical_sector|.
+     *
+     * Similar to |alignment|, this will be derived from the operating system.
+     * If it cannot be determined, it is assumed to be 0.
+     */
+    uint32_t alignment_offset;
+
+    /* 72: Block device size, as specified when the metadata was created. This
+     * can be used to verify the geometry against a target device.
+     */
+    uint64_t block_device_size;
+} __attribute__((packed)) LpMetadataGeometry;
+
+/* The logical partition metadata has a number of tables; they are described
+ * in the header via the following structure.
+ *
+ * The size of the table can be computed by multiplying entry_size by
+ * num_entries, and the result must not overflow a 32-bit signed integer.
+ */
+typedef struct LpMetadataTableDescriptor {
+    /*  0: Location of the table, relative to the metadata header. */
+    uint32_t offset;
+    /*  4: Number of entries in the table. */
+    uint32_t num_entries;
+    /*  8: Size of each entry in the table, in bytes. */
+    uint32_t entry_size;
+} __attribute__((packed)) LpMetadataTableDescriptor;
+
+/* Binary format for the header of the logical partition metadata format.
+ *
+ * The format has three sections. The header must occur first, and the
+ * proceeding tables may be placed in any order after.
+ *
+ *  +-----------------------------------------+
+ *  | Header data - fixed size                |
+ *  +-----------------------------------------+
+ *  | Partition table - variable size         |
+ *  +-----------------------------------------+
+ *  | Partition table extents - variable size |
+ *  +-----------------------------------------+
+ *
+ * The "Header" portion is described by LpMetadataHeader. It will always
+ * precede the other three blocks.
+ *
+ * All fields are stored in little-endian byte order when serialized.
+ *
+ * This struct is versioned; see the |major_version| and |minor_version|
+ * fields.
+ */
+typedef struct LpMetadataHeader {
+    /*  0: Four bytes equal to LP_METADATA_HEADER_MAGIC. */
+    uint32_t magic;
+
+    /*  4: Version number required to read this metadata. If the version is not
+     * equal to the library version, the metadata should be considered
+     * incompatible.
+     */
+    uint16_t major_version;
+
+    /*  6: Minor version. A library supporting newer features should be able to
+     * read metadata with an older minor version. However, an older library
+     * should not support reading metadata if its minor version is higher.
+     */
+    uint16_t minor_version;
+
+    /*  8: The size of this header struct. */
+    uint32_t header_size;
+
+    /* 12: SHA256 checksum of the header, up to |header_size| bytes, computed as
+     * if this field were set to 0.
+     */
+    uint8_t header_checksum[32];
+
+    /* 44: The total size of all tables. This size is contiguous; tables may not
+     * have gaps in between, and they immediately follow the header.
+     */
+    uint32_t tables_size;
+
+    /* 48: SHA256 checksum of all table contents. */
+    uint8_t tables_checksum[32];
+
+    /* 80: Partition table descriptor. */
+    LpMetadataTableDescriptor partitions;
+    /* 92: Extent table descriptor. */
+    LpMetadataTableDescriptor extents;
+} __attribute__((packed)) LpMetadataHeader;
+
+/* This struct defines a logical partition entry, similar to what would be
+ * present in a GUID Partition Table.
+ */
+typedef struct LpMetadataPartition {
+    /*  0: Name of this partition in ASCII characters. Any unused characters in
+     * the buffer must be set to 0. Characters may only be alphanumeric or _.
+     * The name must include at least one ASCII character, and it must be unique
+     * across all partition names. The length (36) is the same as the maximum
+     * length of a GPT partition name.
+     */
+    char name[36];
+
+    /* 36: Globally unique identifier (GUID) of this partition. */
+    uint8_t guid[16];
+
+    /* 52: Attributes for the partition (see LP_PARTITION_ATTR_* flags above). */
+    uint32_t attributes;
+
+    /* 56: Index of the first extent owned by this partition. The extent will
+     * start at logical sector 0. Gaps between extents are not allowed.
+     */
+    uint32_t first_extent_index;
+
+    /* 60: Number of extents in the partition. Every partition must have at
+     * least one extent.
+     */
+    uint32_t num_extents;
+} __attribute__((packed)) LpMetadataPartition;
+
+/* This extent is a dm-linear target, and the index is an index into the
+ * LinearExtent table.
+ */
+#define LP_TARGET_TYPE_LINEAR 0
+
+/* This extent is a dm-zero target. The index is ignored and must be 0. */
+#define LP_TARGET_TYPE_ZERO 1
+
+/* This struct defines an extent entry in the extent table block. */
+typedef struct LpMetadataExtent {
+    /*  0: Length of this extent, in 512-byte sectors. */
+    uint64_t num_sectors;
+
+    /*  8: Target type for device-mapper (see LP_TARGET_TYPE_* values). */
+    uint32_t target_type;
+
+    /* 12: Contents depends on target_type.
+     *
+     * LINEAR: The sector on the physical partition that this extent maps onto.
+     * ZERO: This field must be 0.
+     */
+    uint64_t target_data;
+} __attribute__((packed)) LpMetadataExtent;
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* LOGICAL_PARTITION_METADATA_FORMAT_H_ */
diff --git a/fs_mgr/liblp/io_test.cpp b/fs_mgr/liblp/io_test.cpp
new file mode 100644
index 0000000..bbbedc7
--- /dev/null
+++ b/fs_mgr/liblp/io_test.cpp
@@ -0,0 +1,523 @@
+/*
+ * 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 <fcntl.h>
+#include <linux/memfd.h>
+#include <stdio.h>
+#include <sys/syscall.h>
+
+#include <android-base/file.h>
+#include <android-base/unique_fd.h>
+#include <gtest/gtest.h>
+#include <liblp/builder.h>
+
+#include "images.h"
+#include "reader.h"
+#include "utility.h"
+#include "writer.h"
+
+using namespace std;
+using namespace android::fs_mgr;
+using unique_fd = android::base::unique_fd;
+
+// Our tests assume a 128KiB disk with two 512 byte metadata slots.
+static const size_t kDiskSize = 131072;
+static const size_t kMetadataSize = 512;
+static const size_t kMetadataSlots = 2;
+static const char* TEST_GUID_BASE = "A799D1D6-669F-41D8-A3F0-EBB7572D830";
+static const char* TEST_GUID = "A799D1D6-669F-41D8-A3F0-EBB7572D8302";
+
+// Helper function for creating an in-memory file descriptor. This lets us
+// simulate read/writing logical partition metadata as if we had a block device
+// for a physical partition.
+static unique_fd CreateFakeDisk(off_t size) {
+    unique_fd fd(syscall(__NR_memfd_create, "fake_disk", MFD_ALLOW_SEALING));
+    if (fd < 0) {
+        perror("memfd_create");
+        return {};
+    }
+    if (ftruncate(fd, size) < 0) {
+        perror("ftruncate");
+        return {};
+    }
+    // Prevent anything from accidentally growing/shrinking the file, as it
+    // would not be allowed on an actual partition.
+    if (fcntl(fd, F_ADD_SEALS, F_SEAL_GROW | F_SEAL_SHRINK) < 0) {
+        perror("fcntl");
+        return {};
+    }
+    // Write garbage to the "disk" so we can tell what has been zeroed or not.
+    unique_ptr<uint8_t[]> buffer = make_unique<uint8_t[]>(size);
+    memset(buffer.get(), 0xcc, size);
+    if (!android::base::WriteFully(fd, buffer.get(), size)) {
+        return {};
+    }
+    return fd;
+}
+
+// Create a disk of the default size.
+static unique_fd CreateFakeDisk() {
+    return CreateFakeDisk(kDiskSize);
+}
+
+// Create a MetadataBuilder around some default sizes.
+static unique_ptr<MetadataBuilder> CreateDefaultBuilder() {
+    unique_ptr<MetadataBuilder> builder =
+            MetadataBuilder::New(kDiskSize, kMetadataSize, kMetadataSlots);
+    return builder;
+}
+
+static bool AddDefaultPartitions(MetadataBuilder* builder) {
+    Partition* system = builder->AddPartition("system", TEST_GUID, LP_PARTITION_ATTR_NONE);
+    if (!system) {
+        return false;
+    }
+    return builder->GrowPartition(system, 24 * 1024);
+}
+
+// Create a temporary disk and flash it with the default partition setup.
+static unique_fd CreateFlashedDisk() {
+    unique_ptr<MetadataBuilder> builder = CreateDefaultBuilder();
+    if (!builder || !AddDefaultPartitions(builder.get())) {
+        return {};
+    }
+    unique_fd fd = CreateFakeDisk();
+    if (fd < 0) {
+        return {};
+    }
+    // Export and flash.
+    unique_ptr<LpMetadata> exported = builder->Export();
+    if (!exported) {
+        return {};
+    }
+    if (!FlashPartitionTable(fd, *exported.get(), 0)) {
+        return {};
+    }
+    return fd;
+}
+
+// Test that our CreateFakeDisk() function works.
+TEST(liblp, CreateFakeDisk) {
+    unique_fd fd = CreateFakeDisk();
+    ASSERT_GE(fd, 0);
+
+    uint64_t size;
+    ASSERT_TRUE(GetDescriptorSize(fd, &size));
+    ASSERT_EQ(size, kDiskSize);
+}
+
+// Flashing metadata should not work if the metadata was created for a larger
+// disk than the destination disk.
+TEST(liblp, ExportDiskTooSmall) {
+    unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(kDiskSize + 1024, 512, 2);
+    ASSERT_NE(builder, nullptr);
+    unique_ptr<LpMetadata> exported = builder->Export();
+    ASSERT_NE(exported, nullptr);
+
+    // A larger geometry should fail to flash, since there won't be enough
+    // space to store the logical partition range that was specified.
+    unique_fd fd = CreateFakeDisk();
+    ASSERT_GE(fd, 0);
+
+    EXPECT_FALSE(FlashPartitionTable(fd, *exported.get(), 0));
+}
+
+// Test the basics of flashing a partition and reading it back.
+TEST(liblp, FlashAndReadback) {
+    unique_ptr<MetadataBuilder> builder = CreateDefaultBuilder();
+    ASSERT_NE(builder, nullptr);
+    ASSERT_TRUE(AddDefaultPartitions(builder.get()));
+
+    unique_fd fd = CreateFakeDisk();
+    ASSERT_GE(fd, 0);
+
+    // Export and flash.
+    unique_ptr<LpMetadata> exported = builder->Export();
+    ASSERT_NE(exported, nullptr);
+    ASSERT_TRUE(FlashPartitionTable(fd, *exported.get(), 0));
+
+    // Read back. Note that some fields are only filled in during
+    // serialization, so exported and imported will not be identical. For
+    // example, table sizes and checksums are computed in WritePartitionTable.
+    // Therefore we check on a field-by-field basis.
+    unique_ptr<LpMetadata> imported = ReadMetadata(fd, 0);
+    ASSERT_NE(imported, nullptr);
+
+    // Check geometry and header.
+    EXPECT_EQ(exported->geometry.metadata_max_size, imported->geometry.metadata_max_size);
+    EXPECT_EQ(exported->geometry.metadata_slot_count, imported->geometry.metadata_slot_count);
+    EXPECT_EQ(exported->geometry.first_logical_sector, imported->geometry.first_logical_sector);
+    EXPECT_EQ(exported->geometry.last_logical_sector, imported->geometry.last_logical_sector);
+    EXPECT_EQ(exported->header.major_version, imported->header.major_version);
+    EXPECT_EQ(exported->header.minor_version, imported->header.minor_version);
+    EXPECT_EQ(exported->header.header_size, imported->header.header_size);
+
+    // Check partition tables.
+    ASSERT_EQ(exported->partitions.size(), imported->partitions.size());
+    EXPECT_EQ(GetPartitionName(exported->partitions[0]), GetPartitionName(imported->partitions[0]));
+    EXPECT_EQ(GetPartitionGuid(exported->partitions[0]), GetPartitionGuid(imported->partitions[0]));
+    EXPECT_EQ(exported->partitions[0].attributes, imported->partitions[0].attributes);
+    EXPECT_EQ(exported->partitions[0].first_extent_index,
+              imported->partitions[0].first_extent_index);
+    EXPECT_EQ(exported->partitions[0].num_extents, imported->partitions[0].num_extents);
+
+    // Check extent tables.
+    ASSERT_EQ(exported->extents.size(), imported->extents.size());
+    EXPECT_EQ(exported->extents[0].num_sectors, imported->extents[0].num_sectors);
+    EXPECT_EQ(exported->extents[0].target_type, imported->extents[0].target_type);
+    EXPECT_EQ(exported->extents[0].target_data, imported->extents[0].target_data);
+}
+
+// Test that we can update metadata slots without disturbing others.
+TEST(liblp, UpdateAnyMetadataSlot) {
+    unique_fd fd = CreateFlashedDisk();
+    ASSERT_GE(fd, 0);
+
+    unique_ptr<LpMetadata> imported = ReadMetadata(fd, 0);
+    ASSERT_NE(imported, nullptr);
+    ASSERT_EQ(imported->partitions.size(), 1);
+    EXPECT_EQ(GetPartitionName(imported->partitions[0]), "system");
+
+    // Verify that we can't read unwritten metadata.
+    ASSERT_EQ(ReadMetadata(fd, 1), nullptr);
+
+    // Change the name before writing to the next slot.
+    strncpy(imported->partitions[0].name, "vendor", sizeof(imported->partitions[0].name));
+    ASSERT_TRUE(UpdatePartitionTable(fd, *imported.get(), 1));
+
+    // Read back the original slot, make sure it hasn't changed.
+    imported = ReadMetadata(fd, 0);
+    ASSERT_NE(imported, nullptr);
+    ASSERT_EQ(imported->partitions.size(), 1);
+    EXPECT_EQ(GetPartitionName(imported->partitions[0]), "system");
+
+    // Now read back the new slot, and verify that it has a different name.
+    imported = ReadMetadata(fd, 1);
+    ASSERT_NE(imported, nullptr);
+    ASSERT_EQ(imported->partitions.size(), 1);
+    EXPECT_EQ(GetPartitionName(imported->partitions[0]), "vendor");
+
+    // Verify that we didn't overwrite anything in the logical paritition area.
+    // We expect the disk to be filled with 0xcc on creation so we can read
+    // this back and compare it.
+    char expected[LP_SECTOR_SIZE];
+    memset(expected, 0xcc, sizeof(expected));
+    for (uint64_t i = imported->geometry.first_logical_sector;
+         i <= imported->geometry.last_logical_sector; i++) {
+        char buffer[LP_SECTOR_SIZE];
+        ASSERT_GE(lseek(fd, i * LP_SECTOR_SIZE, SEEK_SET), 0);
+        ASSERT_TRUE(android::base::ReadFully(fd, buffer, sizeof(buffer)));
+        ASSERT_EQ(memcmp(expected, buffer, LP_SECTOR_SIZE), 0);
+    }
+}
+
+TEST(liblp, InvalidMetadataSlot) {
+    unique_fd fd = CreateFlashedDisk();
+    ASSERT_GE(fd, 0);
+
+    // Make sure all slots are filled.
+    unique_ptr<LpMetadata> metadata = ReadMetadata(fd, 0);
+    ASSERT_NE(metadata, nullptr);
+    for (uint32_t i = 1; i < kMetadataSlots; i++) {
+        ASSERT_TRUE(UpdatePartitionTable(fd, *metadata.get(), i));
+    }
+
+    // Verify that we can't read unavailable slots.
+    EXPECT_EQ(ReadMetadata(fd, kMetadataSlots), nullptr);
+}
+
+// Test that updating a metadata slot does not allow it to be computed based
+// on mismatching geometry.
+TEST(liblp, NoChangingGeometry) {
+    unique_fd fd = CreateFlashedDisk();
+    ASSERT_GE(fd, 0);
+
+    unique_ptr<LpMetadata> imported = ReadMetadata(fd, 0);
+    ASSERT_NE(imported, nullptr);
+    ASSERT_TRUE(UpdatePartitionTable(fd, *imported.get(), 1));
+
+    imported->geometry.metadata_max_size += LP_SECTOR_SIZE;
+    ASSERT_FALSE(UpdatePartitionTable(fd, *imported.get(), 1));
+
+    imported = ReadMetadata(fd, 0);
+    ASSERT_NE(imported, nullptr);
+    imported->geometry.metadata_slot_count++;
+    ASSERT_FALSE(UpdatePartitionTable(fd, *imported.get(), 1));
+
+    imported = ReadMetadata(fd, 0);
+    ASSERT_NE(imported, nullptr);
+    imported->geometry.first_logical_sector++;
+    ASSERT_FALSE(UpdatePartitionTable(fd, *imported.get(), 1));
+
+    imported = ReadMetadata(fd, 0);
+    ASSERT_NE(imported, nullptr);
+    imported->geometry.last_logical_sector--;
+    ASSERT_FALSE(UpdatePartitionTable(fd, *imported.get(), 1));
+}
+
+// Test that changing one bit of metadata is enough to break the checksum.
+TEST(liblp, BitFlipGeometry) {
+    unique_fd fd = CreateFlashedDisk();
+    ASSERT_GE(fd, 0);
+
+    LpMetadataGeometry geometry;
+    ASSERT_GE(lseek(fd, 0, SEEK_SET), 0);
+    ASSERT_TRUE(android::base::ReadFully(fd, &geometry, sizeof(geometry)));
+
+    LpMetadataGeometry bad_geometry = geometry;
+    bad_geometry.metadata_slot_count++;
+    ASSERT_TRUE(android::base::WriteFully(fd, &bad_geometry, sizeof(bad_geometry)));
+
+    unique_ptr<LpMetadata> metadata = ReadMetadata(fd, 0);
+    ASSERT_NE(metadata, nullptr);
+    EXPECT_EQ(metadata->geometry.metadata_slot_count, 2);
+}
+
+TEST(liblp, ReadBackupGeometry) {
+    unique_fd fd = CreateFlashedDisk();
+    ASSERT_GE(fd, 0);
+
+    char corruption[LP_METADATA_GEOMETRY_SIZE];
+    memset(corruption, 0xff, sizeof(corruption));
+
+    // Corrupt the first 4096 bytes of the disk.
+    ASSERT_GE(lseek(fd, 0, SEEK_SET), 0);
+    ASSERT_TRUE(android::base::WriteFully(fd, corruption, sizeof(corruption)));
+    EXPECT_NE(ReadMetadata(fd, 0), nullptr);
+
+    // Corrupt the last 4096 bytes too.
+    ASSERT_GE(lseek(fd, -LP_METADATA_GEOMETRY_SIZE, SEEK_END), 0);
+    ASSERT_TRUE(android::base::WriteFully(fd, corruption, sizeof(corruption)));
+    EXPECT_EQ(ReadMetadata(fd, 0), nullptr);
+}
+
+TEST(liblp, ReadBackupMetadata) {
+    unique_fd fd = CreateFlashedDisk();
+    ASSERT_GE(fd, 0);
+
+    unique_ptr<LpMetadata> metadata = ReadMetadata(fd, 0);
+
+    char corruption[kMetadataSize];
+    memset(corruption, 0xff, sizeof(corruption));
+
+    ASSERT_GE(lseek(fd, LP_METADATA_GEOMETRY_SIZE, SEEK_SET), 0);
+    ASSERT_TRUE(android::base::WriteFully(fd, corruption, sizeof(corruption)));
+    EXPECT_NE(ReadMetadata(fd, 0), nullptr);
+
+    off_t offset = LP_METADATA_GEOMETRY_SIZE + kMetadataSize * 2;
+
+    // Corrupt the backup metadata.
+    ASSERT_GE(lseek(fd, -offset, SEEK_END), 0);
+    ASSERT_TRUE(android::base::WriteFully(fd, corruption, sizeof(corruption)));
+    EXPECT_EQ(ReadMetadata(fd, 0), nullptr);
+}
+
+// Test that we don't attempt to write metadata if it would overflow its
+// reserved space.
+TEST(liblp, TooManyPartitions) {
+    unique_ptr<MetadataBuilder> builder = CreateDefaultBuilder();
+    ASSERT_NE(builder, nullptr);
+
+    // Compute the maximum number of partitions we can fit in 1024 bytes of metadata.
+    size_t max_partitions = (kMetadataSize - sizeof(LpMetadataHeader)) / sizeof(LpMetadataPartition);
+    EXPECT_LT(max_partitions, 10);
+
+    // Add this number of partitions.
+    Partition* partition = nullptr;
+    for (size_t i = 0; i < max_partitions; i++) {
+        std::string guid = std::string(TEST_GUID) + to_string(i);
+        partition = builder->AddPartition(to_string(i), TEST_GUID, LP_PARTITION_ATTR_NONE);
+        ASSERT_NE(partition, nullptr);
+    }
+    ASSERT_NE(partition, nullptr);
+    // Add one extent to any partition to fill up more space - we're at 508
+    // bytes after this, out of 512.
+    ASSERT_TRUE(builder->GrowPartition(partition, 1024));
+
+    unique_ptr<LpMetadata> exported = builder->Export();
+    ASSERT_NE(exported, nullptr);
+
+    unique_fd fd = CreateFakeDisk();
+    ASSERT_GE(fd, 0);
+
+    // Check that we are able to write our table.
+    ASSERT_TRUE(FlashPartitionTable(fd, *exported.get(), 0));
+    ASSERT_TRUE(UpdatePartitionTable(fd, *exported.get(), 1));
+
+    // Check that adding one more partition overflows the metadata allotment.
+    partition = builder->AddPartition("final", TEST_GUID, LP_PARTITION_ATTR_NONE);
+    EXPECT_NE(partition, nullptr);
+
+    exported = builder->Export();
+    ASSERT_NE(exported, nullptr);
+
+    // The new table should be too large to be written.
+    ASSERT_FALSE(UpdatePartitionTable(fd, *exported.get(), 1));
+
+    // Check that the first and last logical sectors weren't touched when we
+    // wrote this almost-full metadata.
+    char expected[LP_SECTOR_SIZE];
+    memset(expected, 0xcc, sizeof(expected));
+    char buffer[LP_SECTOR_SIZE];
+    ASSERT_GE(lseek(fd, exported->geometry.first_logical_sector * LP_SECTOR_SIZE, SEEK_SET), 0);
+    ASSERT_TRUE(android::base::ReadFully(fd, buffer, sizeof(buffer)));
+    EXPECT_EQ(memcmp(expected, buffer, LP_SECTOR_SIZE), 0);
+    ASSERT_GE(lseek(fd, exported->geometry.last_logical_sector * LP_SECTOR_SIZE, SEEK_SET), 0);
+    ASSERT_TRUE(android::base::ReadFully(fd, buffer, sizeof(buffer)));
+    EXPECT_EQ(memcmp(expected, buffer, LP_SECTOR_SIZE), 0);
+}
+
+// Test that we can read and write image files.
+TEST(liblp, ImageFiles) {
+    unique_ptr<MetadataBuilder> builder = CreateDefaultBuilder();
+    ASSERT_NE(builder, nullptr);
+    ASSERT_TRUE(AddDefaultPartitions(builder.get()));
+    unique_ptr<LpMetadata> exported = builder->Export();
+
+    unique_fd fd(syscall(__NR_memfd_create, "image_file", 0));
+    ASSERT_GE(fd, 0);
+    ASSERT_TRUE(WriteToImageFile(fd, *exported.get()));
+
+    unique_ptr<LpMetadata> imported = ReadFromImageFile(fd);
+    ASSERT_NE(imported, nullptr);
+}
+
+class BadWriter {
+  public:
+    // When requested, write garbage instead of the requested bytes, then
+    // return false.
+    bool operator()(int fd, const std::string& blob) {
+        write_count_++;
+        if (write_count_ == fail_on_write_) {
+            std::unique_ptr<char[]> new_data = std::make_unique<char[]>(blob.size());
+            memset(new_data.get(), 0xe5, blob.size());
+            EXPECT_TRUE(android::base::WriteFully(fd, new_data.get(), blob.size()));
+            return false;
+        } else {
+            if (!android::base::WriteFully(fd, blob.data(), blob.size())) {
+                return false;
+            }
+            return fail_after_write_ != write_count_;
+        }
+    }
+    void Reset() {
+        fail_on_write_ = 0;
+        fail_after_write_ = 0;
+        write_count_ = 0;
+    }
+    void FailOnWrite(int number) {
+        Reset();
+        fail_on_write_ = number;
+    }
+    void FailAfterWrite(int number) {
+        Reset();
+        fail_after_write_ = number;
+    }
+
+  private:
+    int fail_on_write_ = 0;
+    int fail_after_write_ = 0;
+    int write_count_ = 0;
+};
+
+// Test that an interrupted flash operation on the "primary" copy of metadata
+// is not fatal.
+TEST(liblp, UpdatePrimaryMetadataFailure) {
+    unique_fd fd = CreateFlashedDisk();
+    ASSERT_GE(fd, 0);
+
+    BadWriter writer;
+
+    // Read and write it back.
+    writer.FailOnWrite(1);
+    unique_ptr<LpMetadata> imported = ReadMetadata(fd, 0);
+    ASSERT_NE(imported, nullptr);
+    ASSERT_FALSE(UpdatePartitionTable(fd, *imported.get(), 0, writer));
+
+    // We should still be able to read the backup copy.
+    imported = ReadMetadata(fd, 0);
+    ASSERT_NE(imported, nullptr);
+
+    // Flash again, this time fail the backup copy. We should still be able
+    // to read the primary.
+    writer.FailOnWrite(3);
+    ASSERT_FALSE(UpdatePartitionTable(fd, *imported.get(), 0, writer));
+    imported = ReadMetadata(fd, 0);
+    ASSERT_NE(imported, nullptr);
+}
+
+// Test that an interrupted flash operation on the "backup" copy of metadata
+// is not fatal.
+TEST(liblp, UpdateBackupMetadataFailure) {
+    unique_fd fd = CreateFlashedDisk();
+    ASSERT_GE(fd, 0);
+
+    BadWriter writer;
+
+    // Read and write it back.
+    writer.FailOnWrite(2);
+    unique_ptr<LpMetadata> imported = ReadMetadata(fd, 0);
+    ASSERT_NE(imported, nullptr);
+    ASSERT_FALSE(UpdatePartitionTable(fd, *imported.get(), 0, writer));
+
+    // We should still be able to read the primary copy.
+    imported = ReadMetadata(fd, 0);
+    ASSERT_NE(imported, nullptr);
+
+    // Flash again, this time fail the primary copy. We should still be able
+    // to read the primary.
+    writer.FailOnWrite(2);
+    ASSERT_FALSE(UpdatePartitionTable(fd, *imported.get(), 0, writer));
+    imported = ReadMetadata(fd, 0);
+    ASSERT_NE(imported, nullptr);
+}
+
+// Test that an interrupted write *in between* writing metadata will read
+// the correct metadata copy. The primary is always considered newer than
+// the backup.
+TEST(liblp, UpdateMetadataCleanFailure) {
+    unique_fd fd = CreateFlashedDisk();
+    ASSERT_GE(fd, 0);
+
+    BadWriter writer;
+
+    // Change the name of the existing partition.
+    unique_ptr<LpMetadata> new_table = ReadMetadata(fd, 0);
+    ASSERT_NE(new_table, nullptr);
+    ASSERT_GE(new_table->partitions.size(), 1);
+    new_table->partitions[0].name[0]++;
+
+    // Flash it, but fail to write the backup copy.
+    writer.FailAfterWrite(2);
+    ASSERT_FALSE(UpdatePartitionTable(fd, *new_table.get(), 0, writer));
+
+    // When we read back, we should get the updated primary copy.
+    unique_ptr<LpMetadata> imported = ReadMetadata(fd, 0);
+    ASSERT_NE(imported, nullptr);
+    ASSERT_GE(new_table->partitions.size(), 1);
+    ASSERT_EQ(GetPartitionName(new_table->partitions[0]), GetPartitionName(imported->partitions[0]));
+
+    // Flash again. After, the backup and primary copy should be coherent.
+    // Note that the sync step should have used the primary to sync, not
+    // the backup.
+    writer.Reset();
+    ASSERT_TRUE(UpdatePartitionTable(fd, *new_table.get(), 0, writer));
+
+    imported = ReadMetadata(fd, 0);
+    ASSERT_NE(imported, nullptr);
+    ASSERT_GE(new_table->partitions.size(), 1);
+    ASSERT_EQ(GetPartitionName(new_table->partitions[0]), GetPartitionName(imported->partitions[0]));
+}
diff --git a/fs_mgr/liblp/reader.cpp b/fs_mgr/liblp/reader.cpp
new file mode 100644
index 0000000..117da59
--- /dev/null
+++ b/fs_mgr/liblp/reader.cpp
@@ -0,0 +1,304 @@
+/*
+ * 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 "reader.h"
+
+#include <stddef.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <functional>
+
+#include <android-base/file.h>
+#include <android-base/unique_fd.h>
+
+#include "utility.h"
+
+namespace android {
+namespace fs_mgr {
+
+// Parse an LpMetadataGeometry from a buffer. The buffer must be at least
+// LP_METADATA_GEOMETRY_SIZE bytes in size.
+static bool ParseGeometry(const void* buffer, LpMetadataGeometry* geometry) {
+    static_assert(sizeof(*geometry) <= LP_METADATA_GEOMETRY_SIZE);
+    memcpy(geometry, buffer, sizeof(*geometry));
+
+    // Check the magic signature.
+    if (geometry->magic != LP_METADATA_GEOMETRY_MAGIC) {
+        LERROR << "Logical partition metadata has invalid geometry magic signature.";
+        return false;
+    }
+    // Reject if the struct size is larger than what we compiled. This is so we
+    // can compute a checksum with the |struct_size| field rather than using
+    // sizeof.
+    if (geometry->struct_size > sizeof(LpMetadataGeometry)) {
+        LERROR << "Logical partition metadata has unrecognized fields.";
+        return false;
+    }
+    // Recompute and check the CRC32.
+    {
+        LpMetadataGeometry temp = *geometry;
+        memset(&temp.checksum, 0, sizeof(temp.checksum));
+        SHA256(&temp, temp.struct_size, temp.checksum);
+        if (memcmp(temp.checksum, geometry->checksum, sizeof(temp.checksum)) != 0) {
+            LERROR << "Logical partition metadata has invalid geometry checksum.";
+            return false;
+        }
+    }
+    // Check that the struct size is equal (this will have to change if we ever
+    // change the struct size in a release).
+    if (geometry->struct_size != sizeof(LpMetadataGeometry)) {
+        LERROR << "Logical partition metadata has invalid struct size.";
+        return false;
+    }
+    if (geometry->metadata_slot_count == 0) {
+        LERROR << "Logical partition metadata has invalid slot count.";
+        return false;
+    }
+    if (geometry->metadata_max_size % LP_SECTOR_SIZE != 0) {
+        LERROR << "Metadata max size is not sector-aligned.";
+        return false;
+    }
+
+    // Check that the metadata area and logical partition areas don't overlap.
+    int64_t end_of_metadata =
+            GetPrimaryMetadataOffset(*geometry, geometry->metadata_slot_count - 1) +
+            geometry->metadata_max_size;
+    if (uint64_t(end_of_metadata) > geometry->first_logical_sector * LP_SECTOR_SIZE) {
+        LERROR << "Logical partition metadata overlaps with logical partition contents.";
+        return false;
+    }
+    return true;
+}
+
+// Read and validate geometry information from a block device that holds
+// logical partitions. If the information is corrupted, this will attempt
+// to read it from a secondary backup location.
+bool ReadLogicalPartitionGeometry(int fd, LpMetadataGeometry* geometry) {
+    // Read the first 4096 bytes.
+    std::unique_ptr<uint8_t[]> buffer = std::make_unique<uint8_t[]>(LP_METADATA_GEOMETRY_SIZE);
+    if (SeekFile64(fd, 0, SEEK_SET) < 0) {
+        PERROR << __PRETTY_FUNCTION__ << "lseek failed";
+        return false;
+    }
+    if (!android::base::ReadFully(fd, buffer.get(), LP_METADATA_GEOMETRY_SIZE)) {
+        PERROR << __PRETTY_FUNCTION__ << "read " << LP_METADATA_GEOMETRY_SIZE << " bytes failed";
+        return false;
+    }
+    if (ParseGeometry(buffer.get(), geometry)) {
+        return true;
+    }
+
+    // Try the backup copy in the last 4096 bytes.
+    if (SeekFile64(fd, -LP_METADATA_GEOMETRY_SIZE, SEEK_END) < 0) {
+        PERROR << __PRETTY_FUNCTION__ << "lseek failed, offset " << -LP_METADATA_GEOMETRY_SIZE;
+        return false;
+    }
+    if (!android::base::ReadFully(fd, buffer.get(), LP_METADATA_GEOMETRY_SIZE)) {
+        PERROR << __PRETTY_FUNCTION__ << "backup read " << LP_METADATA_GEOMETRY_SIZE
+               << " bytes failed";
+        return false;
+    }
+    return ParseGeometry(buffer.get(), geometry);
+}
+
+static bool ValidateTableBounds(const LpMetadataHeader& header,
+                                const LpMetadataTableDescriptor& table) {
+    if (table.offset > header.tables_size) {
+        return false;
+    }
+    uint64_t table_size = uint64_t(table.num_entries) * table.entry_size;
+    if (header.tables_size - table.offset < table_size) {
+        return false;
+    }
+    return true;
+}
+
+static bool ValidateMetadataHeader(const LpMetadataHeader& header) {
+    // To compute the header's checksum, we have to temporarily set its checksum
+    // field to 0.
+    {
+        LpMetadataHeader temp = header;
+        memset(&temp.header_checksum, 0, sizeof(temp.header_checksum));
+        SHA256(&temp, sizeof(temp), temp.header_checksum);
+        if (memcmp(temp.header_checksum, header.header_checksum, sizeof(temp.header_checksum)) != 0) {
+            LERROR << "Logical partition metadata has invalid checksum.";
+            return false;
+        }
+    }
+
+    // Do basic validation of key metadata bits.
+    if (header.magic != LP_METADATA_HEADER_MAGIC) {
+        LERROR << "Logical partition metadata has invalid magic value.";
+        return false;
+    }
+    // Check that the version is compatible.
+    if (header.major_version != LP_METADATA_MAJOR_VERSION ||
+        header.minor_version > LP_METADATA_MINOR_VERSION) {
+        LERROR << "Logical partition metadata has incompatible version.";
+        return false;
+    }
+    if (!ValidateTableBounds(header, header.partitions) ||
+        !ValidateTableBounds(header, header.extents)) {
+        LERROR << "Logical partition metadata has invalid table bounds.";
+        return false;
+    }
+    // Check that table entry sizes can accomodate their respective structs. If
+    // table sizes change, these checks will have to be adjusted.
+    if (header.partitions.entry_size != sizeof(LpMetadataPartition)) {
+        LERROR << "Logical partition metadata has invalid partition table entry size.";
+        return false;
+    }
+    if (header.extents.entry_size != sizeof(LpMetadataExtent)) {
+        LERROR << "Logical partition metadata has invalid extent table entry size.";
+        return false;
+    }
+    return true;
+}
+
+// Parse and validate all metadata at the current position in the given file
+// descriptor.
+std::unique_ptr<LpMetadata> ParseMetadata(int fd) {
+    // First read and validate the header.
+    std::unique_ptr<LpMetadata> metadata = std::make_unique<LpMetadata>();
+    if (!android::base::ReadFully(fd, &metadata->header, sizeof(metadata->header))) {
+        PERROR << __PRETTY_FUNCTION__ << "read " << sizeof(metadata->header) << "bytes failed";
+        return nullptr;
+    }
+    if (!ValidateMetadataHeader(metadata->header)) {
+        return nullptr;
+    }
+
+    LpMetadataHeader& header = metadata->header;
+
+    // Read the metadata payload. Allocation is fallible in case the metadata is
+    // corrupt and has some huge value.
+    std::unique_ptr<uint8_t[]> buffer(new (std::nothrow) uint8_t[header.tables_size]);
+    if (!buffer) {
+        LERROR << "Out of memory reading logical partition tables.";
+        return nullptr;
+    }
+    if (!android::base::ReadFully(fd, buffer.get(), header.tables_size)) {
+        PERROR << __PRETTY_FUNCTION__ << "read " << header.tables_size << "bytes failed";
+        return nullptr;
+    }
+
+    uint8_t checksum[32];
+    SHA256(buffer.get(), header.tables_size, checksum);
+    if (memcmp(checksum, header.tables_checksum, sizeof(checksum)) != 0) {
+        LERROR << "Logical partition metadata has invalid table checksum.";
+        return nullptr;
+    }
+
+    // ValidateTableSize ensured that |cursor| is valid for the number of
+    // entries in the table.
+    uint8_t* cursor = buffer.get() + header.partitions.offset;
+    for (size_t i = 0; i < header.partitions.num_entries; i++) {
+        LpMetadataPartition partition;
+        memcpy(&partition, cursor, sizeof(partition));
+        cursor += header.partitions.entry_size;
+
+        if (partition.attributes & ~LP_PARTITION_ATTRIBUTE_MASK) {
+            LERROR << "Logical partition has invalid attribute set.";
+            return nullptr;
+        }
+        if (partition.first_extent_index + partition.num_extents > header.extents.num_entries) {
+            LERROR << "Logical partition has invalid extent list.";
+            return nullptr;
+        }
+
+        metadata->partitions.push_back(partition);
+    }
+
+    cursor = buffer.get() + header.extents.offset;
+    for (size_t i = 0; i < header.extents.num_entries; i++) {
+        LpMetadataExtent extent;
+        memcpy(&extent, cursor, sizeof(extent));
+        cursor += header.extents.entry_size;
+
+        metadata->extents.push_back(extent);
+    }
+
+    return metadata;
+}
+
+std::unique_ptr<LpMetadata> ReadPrimaryMetadata(int fd, const LpMetadataGeometry& geometry,
+                                                uint32_t slot_number) {
+    int64_t offset = GetPrimaryMetadataOffset(geometry, slot_number);
+    if (SeekFile64(fd, offset, SEEK_SET) < 0) {
+        PERROR << __PRETTY_FUNCTION__ << "lseek failed: offset " << offset;
+        return nullptr;
+    }
+    return ParseMetadata(fd);
+}
+
+std::unique_ptr<LpMetadata> ReadBackupMetadata(int fd, const LpMetadataGeometry& geometry,
+                                               uint32_t slot_number) {
+    int64_t offset = GetBackupMetadataOffset(geometry, slot_number);
+    if (SeekFile64(fd, offset, SEEK_END) < 0) {
+        PERROR << __PRETTY_FUNCTION__ << "lseek failed: offset " << offset;
+        return nullptr;
+    }
+    return ParseMetadata(fd);
+}
+
+std::unique_ptr<LpMetadata> ReadMetadata(int fd, uint32_t slot_number) {
+    LpMetadataGeometry geometry;
+    if (!ReadLogicalPartitionGeometry(fd, &geometry)) {
+        return nullptr;
+    }
+
+    if (slot_number >= geometry.metadata_slot_count) {
+        LERROR << __PRETTY_FUNCTION__ << "invalid metadata slot number";
+        return nullptr;
+    }
+
+    // Read the priamry copy, and if that fails, try the backup.
+    std::unique_ptr<LpMetadata> metadata = ReadPrimaryMetadata(fd, geometry, slot_number);
+    if (!metadata) {
+        metadata = ReadBackupMetadata(fd, geometry, slot_number);
+    }
+    if (metadata) {
+        metadata->geometry = geometry;
+    }
+    return metadata;
+}
+
+std::unique_ptr<LpMetadata> ReadMetadata(const char* block_device, uint32_t slot_number) {
+    android::base::unique_fd fd(open(block_device, O_RDONLY));
+    if (fd < 0) {
+        PERROR << __PRETTY_FUNCTION__ << "open failed: " << block_device;
+        return nullptr;
+    }
+    return ReadMetadata(fd, slot_number);
+}
+
+static std::string NameFromFixedArray(const char* name, size_t buffer_size) {
+    // If the end of the buffer has a null character, it's safe to assume the
+    // buffer is null terminated. Otherwise, we cap the string to the input
+    // buffer size.
+    if (name[buffer_size - 1] == '\0') {
+        return std::string(name);
+    }
+    return std::string(name, buffer_size);
+}
+
+std::string GetPartitionName(const LpMetadataPartition& partition) {
+    return NameFromFixedArray(partition.name, sizeof(partition.name));
+}
+
+}  // namespace fs_mgr
+}  // namespace android
diff --git a/fs_mgr/liblp/reader.h b/fs_mgr/liblp/reader.h
new file mode 100644
index 0000000..843b2f2
--- /dev/null
+++ b/fs_mgr/liblp/reader.h
@@ -0,0 +1,44 @@
+/*
+ * 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 LIBLP_READER_H_
+#define LIBLP_READER_H_
+
+#include <stddef.h>
+
+#include <memory>
+
+#include <liblp/liblp.h>
+
+namespace android {
+namespace fs_mgr {
+
+std::unique_ptr<LpMetadata> ReadMetadata(int fd, uint32_t slot_number);
+
+// Helper functions for manually reading geometry and metadata.
+bool ReadLogicalPartitionGeometry(int fd, LpMetadataGeometry* geometry);
+std::unique_ptr<LpMetadata> ParseMetadata(int fd);
+
+// These functions assume a valid geometry and slot number.
+std::unique_ptr<LpMetadata> ReadPrimaryMetadata(int fd, const LpMetadataGeometry& geometry,
+                                                uint32_t slot_number);
+std::unique_ptr<LpMetadata> ReadBackupMetadata(int fd, const LpMetadataGeometry& geometry,
+                                               uint32_t slot_number);
+
+}  // namespace fs_mgr
+}  // namespace android
+
+#endif /* LIBLP_READER_H_ */
diff --git a/fs_mgr/liblp/utility.cpp b/fs_mgr/liblp/utility.cpp
new file mode 100644
index 0000000..a590037
--- /dev/null
+++ b/fs_mgr/liblp/utility.cpp
@@ -0,0 +1,105 @@
+/*
+ * 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 <fcntl.h>
+#include <stdint.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <android-base/file.h>
+#include <ext4_utils/ext4_utils.h>
+#include <openssl/sha.h>
+#include <uuid/uuid.h>
+
+#include "utility.h"
+
+namespace android {
+namespace fs_mgr {
+
+bool GetDescriptorSize(int fd, uint64_t* size) {
+    struct stat s;
+    if (fstat(fd, &s) < 0) {
+        PERROR << __PRETTY_FUNCTION__ << "fstat failed";
+        return false;
+    }
+
+    if (S_ISBLK(s.st_mode)) {
+        *size = get_block_device_size(fd);
+        return *size != 0;
+    }
+
+    int64_t result = SeekFile64(fd, 0, SEEK_END);
+    if (result == -1) {
+        PERROR << __PRETTY_FUNCTION__ << "lseek failed";
+        return false;
+    }
+
+    *size = result;
+    return true;
+}
+
+int64_t SeekFile64(int fd, int64_t offset, int whence) {
+    static_assert(sizeof(off_t) == sizeof(int64_t), "Need 64-bit lseek");
+    return lseek(fd, offset, whence);
+}
+
+int64_t GetPrimaryMetadataOffset(const LpMetadataGeometry& geometry, uint32_t slot_number) {
+    CHECK(slot_number < geometry.metadata_slot_count);
+
+    int64_t offset = LP_METADATA_GEOMETRY_SIZE + geometry.metadata_max_size * slot_number;
+    CHECK(offset + geometry.metadata_max_size <=
+          int64_t(geometry.first_logical_sector * LP_SECTOR_SIZE));
+    return offset;
+}
+
+int64_t GetBackupMetadataOffset(const LpMetadataGeometry& geometry, uint32_t slot_number) {
+    CHECK(slot_number < geometry.metadata_slot_count);
+    int64_t start = int64_t(-LP_METADATA_GEOMETRY_SIZE) -
+                    int64_t(geometry.metadata_max_size) * geometry.metadata_slot_count;
+    return start + int64_t(geometry.metadata_max_size * slot_number);
+}
+
+void SHA256(const void* data, size_t length, uint8_t out[32]) {
+    SHA256_CTX c;
+    SHA256_Init(&c);
+    SHA256_Update(&c, data, length);
+    SHA256_Final(out, &c);
+}
+
+std::string GetPartitionGuid(const LpMetadataPartition& partition) {
+    // 32 hex characters, four hyphens. Unfortunately libext2_uuid provides no
+    // macro to assist with buffer sizing.
+    static const size_t kGuidLen = 36;
+    char buffer[kGuidLen + 1];
+    uuid_unparse_upper(partition.guid, buffer);
+    return buffer;
+}
+
+uint32_t SlotNumberForSlotSuffix(const std::string& suffix) {
+    if (suffix.empty()) {
+        return 0;
+    }
+    if (suffix.size() != 2 || suffix[0] != '_' || suffix[1] < 'a') {
+        LERROR << __PRETTY_FUNCTION__ << "slot '" << suffix
+               << "' does not have a recognized format.";
+        return 0;
+    }
+    return suffix[1] - 'a';
+}
+
+}  // namespace fs_mgr
+}  // namespace android
diff --git a/fs_mgr/liblp/utility.h b/fs_mgr/liblp/utility.h
new file mode 100644
index 0000000..4522275
--- /dev/null
+++ b/fs_mgr/liblp/utility.h
@@ -0,0 +1,80 @@
+/*
+ * 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 LIBLP_UTILITY_H
+#define LIBLP_UTILITY_H
+
+#include <stddef.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <android-base/logging.h>
+
+#include "liblp/metadata_format.h"
+
+#define LP_TAG "[liblp]"
+#define LERROR LOG(ERROR) << LP_TAG
+#define PERROR PLOG(ERROR) << LP_TAG
+
+namespace android {
+namespace fs_mgr {
+
+// Determine the size of a block device (or file). Logs and returns false on
+// error. After calling this, the position of |fd| may have changed.
+bool GetDescriptorSize(int fd, uint64_t* size);
+
+// Return the offset of a primary metadata slot, relative to the start of the
+// device.
+int64_t GetPrimaryMetadataOffset(const LpMetadataGeometry& geometry, uint32_t slot_number);
+
+// Return the offset of a backup metadata slot, relative to the end of the
+// device.
+int64_t GetBackupMetadataOffset(const LpMetadataGeometry& geometry, uint32_t slot_number);
+
+// Cross-platform helper for lseek64().
+int64_t SeekFile64(int fd, int64_t offset, int whence);
+
+// Compute a SHA256 hash.
+void SHA256(const void* data, size_t length, uint8_t out[32]);
+
+// Align |base| such that it is evenly divisible by |alignment|, which does not
+// have to be a power of two.
+constexpr uint64_t AlignTo(uint64_t base, uint32_t alignment) {
+    if (!alignment) {
+        return base;
+    }
+    uint64_t remainder = base % alignment;
+    if (remainder == 0) {
+        return base;
+    }
+    return base + (alignment - remainder);
+}
+
+// Same as the above |AlignTo|, except that |base| is only aligned when added to
+// |alignment_offset|.
+constexpr uint64_t AlignTo(uint64_t base, uint32_t alignment, uint32_t alignment_offset) {
+    uint64_t aligned = AlignTo(base, alignment) + alignment_offset;
+    if (aligned - alignment >= base) {
+        // We overaligned (base < alignment_offset).
+        return aligned - alignment;
+    }
+    return aligned;
+}
+
+}  // namespace fs_mgr
+}  // namespace android
+
+#endif  // LIBLP_UTILITY_H
diff --git a/fs_mgr/liblp/utility_test.cpp b/fs_mgr/liblp/utility_test.cpp
new file mode 100644
index 0000000..092dbf1
--- /dev/null
+++ b/fs_mgr/liblp/utility_test.cpp
@@ -0,0 +1,64 @@
+/*
+ * 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 <gtest/gtest.h>
+#include <liblp/liblp.h>
+
+#include "utility.h"
+
+using namespace android;
+using namespace android::fs_mgr;
+
+TEST(liblp, SlotNumberForSlotSuffix) {
+    EXPECT_EQ(SlotNumberForSlotSuffix(""), 0);
+    EXPECT_EQ(SlotNumberForSlotSuffix("_a"), 0);
+    EXPECT_EQ(SlotNumberForSlotSuffix("_b"), 1);
+    EXPECT_EQ(SlotNumberForSlotSuffix("_c"), 2);
+    EXPECT_EQ(SlotNumberForSlotSuffix("_d"), 3);
+}
+
+TEST(liblp, GetMetadataOffset) {
+    LpMetadataGeometry geometry = {LP_METADATA_GEOMETRY_MAGIC,
+                                   sizeof(geometry),
+                                   {0},
+                                   16384,
+                                   4,
+                                   10000,
+                                   80000,
+                                   0,
+                                   0,
+                                   1024 * 1024};
+    EXPECT_EQ(GetPrimaryMetadataOffset(geometry, 0), 4096);
+    EXPECT_EQ(GetPrimaryMetadataOffset(geometry, 1), 4096 + 16384);
+    EXPECT_EQ(GetPrimaryMetadataOffset(geometry, 2), 4096 + 16384 * 2);
+    EXPECT_EQ(GetPrimaryMetadataOffset(geometry, 3), 4096 + 16384 * 3);
+
+    EXPECT_EQ(GetBackupMetadataOffset(geometry, 3), -4096 - 16384 * 1);
+    EXPECT_EQ(GetBackupMetadataOffset(geometry, 2), -4096 - 16384 * 2);
+    EXPECT_EQ(GetBackupMetadataOffset(geometry, 1), -4096 - 16384 * 3);
+    EXPECT_EQ(GetBackupMetadataOffset(geometry, 0), -4096 - 16384 * 4);
+}
+
+TEST(liblp, AlignTo) {
+    EXPECT_EQ(AlignTo(37, 0), 37);
+    EXPECT_EQ(AlignTo(1024, 1024), 1024);
+    EXPECT_EQ(AlignTo(555, 1024), 1024);
+    EXPECT_EQ(AlignTo(555, 1000), 1000);
+    EXPECT_EQ(AlignTo(0, 1024), 0);
+    EXPECT_EQ(AlignTo(54, 32, 30), 62);
+    EXPECT_EQ(AlignTo(32, 32, 30), 62);
+    EXPECT_EQ(AlignTo(17, 32, 30), 30);
+}
diff --git a/fs_mgr/liblp/writer.cpp b/fs_mgr/liblp/writer.cpp
new file mode 100644
index 0000000..156319b
--- /dev/null
+++ b/fs_mgr/liblp/writer.cpp
@@ -0,0 +1,325 @@
+/*
+ * Copyright (C) 2007 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 "writer.h"
+
+#include <inttypes.h>
+#include <unistd.h>
+
+#include <string>
+
+#include <android-base/file.h>
+#include <android-base/unique_fd.h>
+
+#include "reader.h"
+#include "utility.h"
+
+namespace android {
+namespace fs_mgr {
+
+std::string SerializeGeometry(const LpMetadataGeometry& input) {
+    LpMetadataGeometry geometry = input;
+    memset(geometry.checksum, 0, sizeof(geometry.checksum));
+    SHA256(&geometry, sizeof(geometry), geometry.checksum);
+
+    std::string blob(reinterpret_cast<const char*>(&geometry), sizeof(geometry));
+    blob.resize(LP_METADATA_GEOMETRY_SIZE);
+    return blob;
+}
+
+static bool CompareGeometry(const LpMetadataGeometry& g1, const LpMetadataGeometry& g2) {
+    return g1.metadata_max_size == g2.metadata_max_size &&
+           g1.metadata_slot_count == g2.metadata_slot_count &&
+           g1.first_logical_sector == g2.first_logical_sector &&
+           g1.last_logical_sector == g2.last_logical_sector;
+}
+
+std::string SerializeMetadata(const LpMetadata& input) {
+    LpMetadata metadata = input;
+    LpMetadataHeader& header = metadata.header;
+
+    // Serialize individual tables.
+    std::string partitions(reinterpret_cast<const char*>(metadata.partitions.data()),
+                           metadata.partitions.size() * sizeof(LpMetadataPartition));
+    std::string extents(reinterpret_cast<const char*>(metadata.extents.data()),
+                        metadata.extents.size() * sizeof(LpMetadataExtent));
+
+    // Compute positions of tables.
+    header.partitions.offset = 0;
+    header.extents.offset = header.partitions.offset + partitions.size();
+    header.tables_size = header.extents.offset + extents.size();
+
+    // Compute payload checksum.
+    std::string tables = partitions + extents;
+    SHA256(tables.data(), tables.size(), header.tables_checksum);
+
+    // Compute header checksum.
+    memset(header.header_checksum, 0, sizeof(header.header_checksum));
+    SHA256(&header, sizeof(header), header.header_checksum);
+
+    std::string header_blob =
+            std::string(reinterpret_cast<const char*>(&metadata.header), sizeof(metadata.header));
+    return header_blob + tables;
+}
+
+// Perform sanity checks so we don't accidentally overwrite valid metadata
+// with potentially invalid metadata, or random partition data with metadata.
+static bool ValidateAndSerializeMetadata(int fd, const LpMetadata& metadata, std::string* blob) {
+    uint64_t blockdevice_size;
+    if (!GetDescriptorSize(fd, &blockdevice_size)) {
+        return false;
+    }
+
+    *blob = SerializeMetadata(metadata);
+
+    const LpMetadataHeader& header = metadata.header;
+    const LpMetadataGeometry& geometry = metadata.geometry;
+    // Validate the usable sector range.
+    if (geometry.first_logical_sector > geometry.last_logical_sector) {
+        LERROR << "Logical partition metadata has invalid sector range.";
+        return false;
+    }
+    // Make sure we're writing within the space reserved.
+    if (blob->size() > geometry.metadata_max_size) {
+        LERROR << "Logical partition metadata is too large.";
+        return false;
+    }
+
+    // Make sure the device has enough space to store two backup copies of the
+    // metadata.
+    uint64_t reserved_size = LP_METADATA_GEOMETRY_SIZE +
+                             uint64_t(geometry.metadata_max_size) * geometry.metadata_slot_count;
+    if (reserved_size > blockdevice_size ||
+        reserved_size > geometry.first_logical_sector * LP_SECTOR_SIZE) {
+        LERROR << "Not enough space to store all logical partition metadata slots.";
+        return false;
+    }
+    if (blockdevice_size - reserved_size < (geometry.last_logical_sector + 1) * LP_SECTOR_SIZE) {
+        LERROR << "Not enough space to backup all logical partition metadata slots.";
+        return false;
+    }
+    if (blockdevice_size != metadata.geometry.block_device_size) {
+        LERROR << "Block device size " << blockdevice_size
+               << " does not match metadata requested size " << metadata.geometry.block_device_size;
+        return false;
+    }
+
+    // Make sure all partition entries reference valid extents.
+    for (const auto& partition : metadata.partitions) {
+        if (partition.first_extent_index + partition.num_extents > metadata.extents.size()) {
+            LERROR << "Partition references invalid extent.";
+            return false;
+        }
+    }
+
+    // Make sure all linear extents have a valid range.
+    for (const auto& extent : metadata.extents) {
+        if (extent.target_type == LP_TARGET_TYPE_LINEAR) {
+            uint64_t physical_sector = extent.target_data;
+            if (physical_sector < geometry.first_logical_sector ||
+                physical_sector + extent.num_sectors > geometry.last_logical_sector) {
+                LERROR << "Extent table entry is out of bounds.";
+                return false;
+            }
+        }
+    }
+    return true;
+}
+
+static bool WritePrimaryMetadata(int fd, const LpMetadataGeometry& geometry, uint32_t slot_number,
+                                 const std::string& blob,
+                                 const std::function<bool(int, const std::string&)>& writer) {
+    int64_t primary_offset = GetPrimaryMetadataOffset(geometry, slot_number);
+    if (SeekFile64(fd, primary_offset, SEEK_SET) < 0) {
+        PERROR << __PRETTY_FUNCTION__ << "lseek failed: offset " << primary_offset;
+        return false;
+    }
+    if (!writer(fd, blob)) {
+        PERROR << __PRETTY_FUNCTION__ << "write " << blob.size() << " bytes failed";
+        return false;
+    }
+    return true;
+}
+
+static bool WriteBackupMetadata(int fd, const LpMetadataGeometry& geometry, uint32_t slot_number,
+                                const std::string& blob,
+                                const std::function<bool(int, const std::string&)>& writer) {
+    int64_t backup_offset = GetBackupMetadataOffset(geometry, slot_number);
+    int64_t abs_offset = SeekFile64(fd, backup_offset, SEEK_END);
+    if (abs_offset == (int64_t)-1) {
+        PERROR << __PRETTY_FUNCTION__ << "lseek failed: offset " << backup_offset;
+        return false;
+    }
+    if (abs_offset < int64_t((geometry.last_logical_sector + 1) * LP_SECTOR_SIZE)) {
+        PERROR << __PRETTY_FUNCTION__ << "backup offset " << abs_offset
+               << " is within logical partition bounds, sector " << geometry.last_logical_sector;
+        return false;
+    }
+    if (!writer(fd, blob)) {
+        PERROR << __PRETTY_FUNCTION__ << "backup write " << blob.size() << " bytes failed";
+        return false;
+    }
+    return true;
+}
+
+static bool WriteMetadata(int fd, const LpMetadataGeometry& geometry, uint32_t slot_number,
+                          const std::string& blob,
+                          const std::function<bool(int, const std::string&)>& writer) {
+    // Make sure we're writing to a valid metadata slot.
+    if (slot_number >= geometry.metadata_slot_count) {
+        LERROR << "Invalid logical partition metadata slot number.";
+        return false;
+    }
+    if (!WritePrimaryMetadata(fd, geometry, slot_number, blob, writer)) {
+        return false;
+    }
+    if (!WriteBackupMetadata(fd, geometry, slot_number, blob, writer)) {
+        return false;
+    }
+    return true;
+}
+
+static bool DefaultWriter(int fd, const std::string& blob) {
+    return android::base::WriteFully(fd, blob.data(), blob.size());
+}
+
+bool FlashPartitionTable(int fd, const LpMetadata& metadata, uint32_t slot_number) {
+    // Before writing geometry and/or logical partition tables, perform some
+    // basic checks that the geometry and tables are coherent, and will fit
+    // on the given block device.
+    std::string metadata_blob;
+    if (!ValidateAndSerializeMetadata(fd, metadata, &metadata_blob)) {
+        return false;
+    }
+
+    // Write geometry to the first and last 4096 bytes of the device.
+    std::string blob = SerializeGeometry(metadata.geometry);
+    if (SeekFile64(fd, 0, SEEK_SET) < 0) {
+        PERROR << __PRETTY_FUNCTION__ << "lseek failed: offset 0";
+        return false;
+    }
+    if (!android::base::WriteFully(fd, blob.data(), blob.size())) {
+        PERROR << __PRETTY_FUNCTION__ << "write " << blob.size() << " bytes failed";
+        return false;
+    }
+    if (SeekFile64(fd, -LP_METADATA_GEOMETRY_SIZE, SEEK_END) < 0) {
+        PERROR << __PRETTY_FUNCTION__ << "lseek failed: offset " << -LP_METADATA_GEOMETRY_SIZE;
+        return false;
+    }
+    if (!android::base::WriteFully(fd, blob.data(), blob.size())) {
+        PERROR << __PRETTY_FUNCTION__ << "backup write " << blob.size() << " bytes failed";
+        return false;
+    }
+
+    // Write metadata to the correct slot, now that geometry is in place.
+    return WriteMetadata(fd, metadata.geometry, slot_number, metadata_blob, DefaultWriter);
+}
+
+static bool CompareMetadata(const LpMetadata& a, const LpMetadata& b) {
+    return !memcmp(a.header.header_checksum, b.header.header_checksum,
+                   sizeof(a.header.header_checksum));
+}
+
+bool UpdatePartitionTable(int fd, const LpMetadata& metadata, uint32_t slot_number,
+                          const std::function<bool(int, const std::string&)>& writer) {
+    // Before writing geometry and/or logical partition tables, perform some
+    // basic checks that the geometry and tables are coherent, and will fit
+    // on the given block device.
+    std::string blob;
+    if (!ValidateAndSerializeMetadata(fd, metadata, &blob)) {
+        return false;
+    }
+
+    // Verify that the old geometry is identical. If it's not, then we might be
+    // writing a table that was built for a different device, so we must reject
+    // it.
+    const LpMetadataGeometry& geometry = metadata.geometry;
+    LpMetadataGeometry old_geometry;
+    if (!ReadLogicalPartitionGeometry(fd, &old_geometry)) {
+        return false;
+    }
+    if (!CompareGeometry(geometry, old_geometry)) {
+        LERROR << "Incompatible geometry in new logical partition metadata";
+        return false;
+    }
+
+    // Validate the slot number now, before we call Read*Metadata.
+    if (slot_number >= geometry.metadata_slot_count) {
+        LERROR << "Invalid logical partition metadata slot number.";
+        return false;
+    }
+
+    // Try to read both existing copies of the metadata, if any.
+    std::unique_ptr<LpMetadata> primary = ReadPrimaryMetadata(fd, geometry, slot_number);
+    std::unique_ptr<LpMetadata> backup = ReadBackupMetadata(fd, geometry, slot_number);
+
+    if (primary && (!backup || !CompareMetadata(*primary.get(), *backup.get()))) {
+        // If the backup copy does not match the primary copy, we first
+        // synchronize the backup copy. This guarantees that a partial write
+        // still leaves one copy intact.
+        std::string old_blob;
+        if (!ValidateAndSerializeMetadata(fd, *primary.get(), &old_blob)) {
+            LERROR << "Error serializing primary metadata to repair corrupted backup";
+            return false;
+        }
+        if (!WriteBackupMetadata(fd, geometry, slot_number, old_blob, writer)) {
+            LERROR << "Error writing primary metadata to repair corrupted backup";
+            return false;
+        }
+    } else if (backup && !primary) {
+        // The backup copy is coherent, and the primary is not. Sync it for
+        // safety.
+        std::string old_blob;
+        if (!ValidateAndSerializeMetadata(fd, *backup.get(), &old_blob)) {
+            LERROR << "Error serializing primary metadata to repair corrupted backup";
+            return false;
+        }
+        if (!WritePrimaryMetadata(fd, geometry, slot_number, old_blob, writer)) {
+            LERROR << "Error writing primary metadata to repair corrupted backup";
+            return false;
+        }
+    }
+
+    // Both copies should now be in sync, so we can continue the update.
+    return WriteMetadata(fd, geometry, slot_number, blob, writer);
+}
+
+bool FlashPartitionTable(const std::string& block_device, const LpMetadata& metadata,
+                         uint32_t slot_number) {
+    android::base::unique_fd fd(open(block_device.c_str(), O_RDWR | O_SYNC));
+    if (fd < 0) {
+        PERROR << __PRETTY_FUNCTION__ << "open failed: " << block_device;
+        return false;
+    }
+    return FlashPartitionTable(fd, metadata, slot_number);
+}
+
+bool UpdatePartitionTable(const std::string& block_device, const LpMetadata& metadata,
+                          uint32_t slot_number) {
+    android::base::unique_fd fd(open(block_device.c_str(), O_RDWR | O_SYNC));
+    if (fd < 0) {
+        PERROR << __PRETTY_FUNCTION__ << "open failed: " << block_device;
+        return false;
+    }
+    return UpdatePartitionTable(fd, metadata, slot_number);
+}
+
+bool UpdatePartitionTable(int fd, const LpMetadata& metadata, uint32_t slot_number) {
+    return UpdatePartitionTable(fd, metadata, slot_number, DefaultWriter);
+}
+
+}  // namespace fs_mgr
+}  // namespace android
diff --git a/fs_mgr/liblp/writer.h b/fs_mgr/liblp/writer.h
new file mode 100644
index 0000000..adbbebf
--- /dev/null
+++ b/fs_mgr/liblp/writer.h
@@ -0,0 +1,42 @@
+/*
+ * 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 LIBLP_WRITER_H
+#define LIBLP_WRITER_H
+
+#include <functional>
+#include <string>
+
+#include <liblp/liblp.h>
+
+namespace android {
+namespace fs_mgr {
+
+std::string SerializeGeometry(const LpMetadataGeometry& input);
+std::string SerializeMetadata(const LpMetadata& input);
+
+// These variants are for testing only. The path-based functions should be used
+// for actual operation, so that open() is called with the correct flags.
+bool FlashPartitionTable(int fd, const LpMetadata& metadata, uint32_t slot_number);
+bool UpdatePartitionTable(int fd, const LpMetadata& metadata, uint32_t slot_number);
+
+bool UpdatePartitionTable(int fd, const LpMetadata& metadata, uint32_t slot_number,
+                          const std::function<bool(int, const std::string&)>& writer);
+
+}  // namespace fs_mgr
+}  // namespace android
+
+#endif /* LIBLP_WRITER_H */
diff --git a/fs_mgr/tests/Android.bp b/fs_mgr/tests/Android.bp
new file mode 100644
index 0000000..5497223
--- /dev/null
+++ b/fs_mgr/tests/Android.bp
@@ -0,0 +1,36 @@
+// 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: "fs_mgr_unit_test",
+
+    shared_libs: [
+        "libbase",
+        "liblog",
+    ],
+    static_libs: [
+        "libfs_mgr",
+        "libfstab",
+    ],
+
+    srcs: [
+        "fs_mgr_test.cpp",
+    ],
+
+    cflags: [
+        "-Wall",
+        "-Wextra",
+        "-Werror",
+    ],
+}
diff --git a/fs_mgr/tests/fs_mgr_test.cpp b/fs_mgr/tests/fs_mgr_test.cpp
new file mode 100644
index 0000000..8b1c55a
--- /dev/null
+++ b/fs_mgr/tests/fs_mgr_test.cpp
@@ -0,0 +1,203 @@
+/*
+ * 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 <linux/fs.h>
+#include <mntent.h>
+
+#include <algorithm>
+#include <iterator>
+#include <set>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include <android-base/strings.h>
+#include <fstab/fstab.h>
+#include <gtest/gtest.h>
+
+#include "../fs_mgr_priv_boot_config.h"
+
+namespace {
+
+const std::string cmdline =
+        "rcupdate.rcu_expedited=1 rootwait ro "
+        "init=/init androidboot.bootdevice=1d84000.ufshc "
+        "androidboot.baseband=sdy androidboot.keymaster=1  skip_initramfs "
+        "androidboot.serialno=BLAHBLAHBLAH androidboot.slot_suffix=_a "
+        "androidboot.hardware.platform=sdw813 androidboot.hardware=foo "
+        "androidboot.revision=EVT1.0 androidboot.bootloader=burp-0.1-7521 "
+        "androidboot.hardware.sku=mary androidboot.hardware.radio.subtype=0 "
+        "androidboot.dtbo_idx=2 androidboot.mode=normal "
+        "androidboot.hardware.ddr=1GB,combuchi,LPDDR4X "
+        "androidboot.ddr_info=combuchiandroidboot.ddr_size=2GB "
+        "androidboot.hardware.ufs=2GB,combushi "
+        "androidboot.boottime=0BLE:58,1BLL:22,1BLE:571,2BLL:105,ODT:0,AVB:123 "
+        "androidboot.ramdump=disabled "
+        "dm=\"1 vroot none ro 1,0 10416 verity 1 624684 fec_start 624684\" "
+        "root=/dev/dm-0 "
+        "androidboot.vbmeta.device=PARTUUID=aa08f1a4-c7c9-402e-9a66-9707cafa9ceb "
+        "androidboot.vbmeta.avb_version=\"1.1\" "
+        "androidboot.vbmeta.device_state=unlocked "
+        "androidboot.vbmeta.hash_alg=sha256 androidboot.vbmeta.size=5248 "
+        "androidboot.vbmeta.digest="
+        "ac13147e959861c20f2a6da97d25fe79e60e902c022a371c5c039d31e7c68860 "
+        "androidboot.vbmeta.invalidate_on_error=yes "
+        "androidboot.veritymode=enforcing androidboot.verifiedbootstate=orange "
+        "androidboot.space=\"sha256 5248 androidboot.nospace=nope\" "
+        "printk.devkmsg=on msm_rtb.filter=0x237 ehci-hcd.park=3 "
+        "\"string =\"\"string '\" "
+        "service_locator.enable=1 firmware_class.path=/vendor/firmware "
+        "cgroup.memory=nokmem lpm_levels.sleep_disabled=1 "
+        "buildvariant=userdebug  console=null "
+        "terminator=\"truncated";
+
+const std::vector<std::pair<std::string, std::string>> result_space = {
+        {"rcupdate.rcu_expedited", "1"},
+        {"rootwait", ""},
+        {"ro", ""},
+        {"init", "/init"},
+        {"androidboot.bootdevice", "1d84000.ufshc"},
+        {"androidboot.baseband", "sdy"},
+        {"androidboot.keymaster", "1"},
+        {"skip_initramfs", ""},
+        {"androidboot.serialno", "BLAHBLAHBLAH"},
+        {"androidboot.slot_suffix", "_a"},
+        {"androidboot.hardware.platform", "sdw813"},
+        {"androidboot.hardware", "foo"},
+        {"androidboot.revision", "EVT1.0"},
+        {"androidboot.bootloader", "burp-0.1-7521"},
+        {"androidboot.hardware.sku", "mary"},
+        {"androidboot.hardware.radio.subtype", "0"},
+        {"androidboot.dtbo_idx", "2"},
+        {"androidboot.mode", "normal"},
+        {"androidboot.hardware.ddr", "1GB,combuchi,LPDDR4X"},
+        {"androidboot.ddr_info", "combuchiandroidboot.ddr_size=2GB"},
+        {"androidboot.hardware.ufs", "2GB,combushi"},
+        {"androidboot.boottime", "0BLE:58,1BLL:22,1BLE:571,2BLL:105,ODT:0,AVB:123"},
+        {"androidboot.ramdump", "disabled"},
+        {"dm", "1 vroot none ro 1,0 10416 verity 1 624684 fec_start 624684"},
+        {"root", "/dev/dm-0"},
+        {"androidboot.vbmeta.device", "PARTUUID=aa08f1a4-c7c9-402e-9a66-9707cafa9ceb"},
+        {"androidboot.vbmeta.avb_version", "1.1"},
+        {"androidboot.vbmeta.device_state", "unlocked"},
+        {"androidboot.vbmeta.hash_alg", "sha256"},
+        {"androidboot.vbmeta.size", "5248"},
+        {"androidboot.vbmeta.digest",
+         "ac13147e959861c20f2a6da97d25fe79e60e902c022a371c5c039d31e7c68860"},
+        {"androidboot.vbmeta.invalidate_on_error", "yes"},
+        {"androidboot.veritymode", "enforcing"},
+        {"androidboot.verifiedbootstate", "orange"},
+        {"androidboot.space", "sha256 5248 androidboot.nospace=nope"},
+        {"printk.devkmsg", "on"},
+        {"msm_rtb.filter", "0x237"},
+        {"ehci-hcd.park", "3"},
+        {"string ", "string '"},
+        {"service_locator.enable", "1"},
+        {"firmware_class.path", "/vendor/firmware"},
+        {"cgroup.memory", "nokmem"},
+        {"lpm_levels.sleep_disabled", "1"},
+        {"buildvariant", "userdebug"},
+        {"console", "null"},
+        {"terminator", "truncated"},
+};
+
+}  // namespace
+
+TEST(fs_mgr, fs_mgr_parse_boot_config) {
+    EXPECT_EQ(result_space, fs_mgr_parse_boot_config(cmdline));
+}
+
+TEST(fs_mgr, fs_mgr_get_boot_config_from_kernel_cmdline) {
+    std::string content;
+    for (const auto& entry : result_space) {
+        static constexpr char androidboot[] = "androidboot.";
+        if (!android::base::StartsWith(entry.first, androidboot)) continue;
+        auto key = entry.first.substr(strlen(androidboot));
+        EXPECT_TRUE(fs_mgr_get_boot_config_from_kernel(cmdline, key, &content)) << " for " << key;
+        EXPECT_EQ(entry.second, content);
+    }
+    EXPECT_FALSE(fs_mgr_get_boot_config_from_kernel(cmdline, "vbmeta.avb_versio", &content));
+    EXPECT_TRUE(content.empty()) << content;
+    EXPECT_FALSE(fs_mgr_get_boot_config_from_kernel(cmdline, "nospace", &content));
+    EXPECT_TRUE(content.empty()) << content;
+}
+
+TEST(fs_mgr, fs_mgr_read_fstab_file_proc_mounts) {
+    auto fstab = fs_mgr_read_fstab("/proc/mounts");
+    ASSERT_NE(fstab, nullptr);
+
+    std::unique_ptr<std::FILE, int (*)(std::FILE*)> mounts(setmntent("/proc/mounts", "r"),
+                                                           endmntent);
+    ASSERT_NE(mounts, nullptr);
+
+    mntent* mentry;
+    int i = 0;
+    while ((mentry = getmntent(mounts.get())) != nullptr) {
+        ASSERT_LT(i, fstab->num_entries);
+        auto fsrec = &fstab->recs[i];
+
+        std::string mnt_fsname(mentry->mnt_fsname ?: "nullptr");
+        std::string blk_device(fsrec->blk_device ?: "nullptr");
+        EXPECT_EQ(mnt_fsname, blk_device);
+
+        std::string mnt_dir(mentry->mnt_dir ?: "nullptr");
+        std::string mount_point(fsrec->mount_point ?: "nullptr");
+        EXPECT_EQ(mnt_dir, mount_point);
+
+        std::string mnt_type(mentry->mnt_type ?: "nullptr");
+        std::string fs_type(fsrec->fs_type ?: "nullptr");
+        EXPECT_EQ(mnt_type, fs_type);
+
+        std::set<std::string> mnt_opts;
+        for (auto& s : android::base::Split(mentry->mnt_opts ?: "nullptr", ",")) {
+            mnt_opts.emplace(s);
+        }
+        std::set<std::string> fs_options;
+        for (auto& s : android::base::Split(fsrec->fs_options ?: "nullptr", ",")) {
+            fs_options.emplace(s);
+        }
+        // matches private content in fs_mgr_fstab.c
+        static struct flag_list {
+            const char* name;
+            unsigned int flag;
+        } mount_flags[] = {
+                {"noatime", MS_NOATIME},
+                {"noexec", MS_NOEXEC},
+                {"nosuid", MS_NOSUID},
+                {"nodev", MS_NODEV},
+                {"nodiratime", MS_NODIRATIME},
+                {"ro", MS_RDONLY},
+                {"rw", 0},
+                {"remount", MS_REMOUNT},
+                {"bind", MS_BIND},
+                {"rec", MS_REC},
+                {"unbindable", MS_UNBINDABLE},
+                {"private", MS_PRIVATE},
+                {"slave", MS_SLAVE},
+                {"shared", MS_SHARED},
+                {"defaults", 0},
+                {0, 0},
+        };
+        for (auto f = 0; mount_flags[f].name; ++f) {
+            if (mount_flags[f].flag & fsrec->flags) {
+                fs_options.emplace(mount_flags[f].name);
+            }
+        }
+        if (!(fsrec->flags & MS_RDONLY)) fs_options.emplace("rw");
+        EXPECT_EQ(mnt_opts, fs_options);
+        ++i;
+    }
+}
diff --git a/fs_mgr/tools/Android.bp b/fs_mgr/tools/Android.bp
new file mode 100644
index 0000000..4d4aae4
--- /dev/null
+++ b/fs_mgr/tools/Android.bp
@@ -0,0 +1,31 @@
+//
+// 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_binary {
+    name: "dmctl",
+    srcs: ["dmctl.cpp"],
+
+    static_libs: [
+        "libfs_mgr",
+    ],
+
+    shared_libs: [
+        "libbase",
+        "liblog",
+    ],
+
+    cflags: ["-Werror"],
+}
diff --git a/fs_mgr/tools/dmctl.cpp b/fs_mgr/tools/dmctl.cpp
new file mode 100644
index 0000000..45a81af
--- /dev/null
+++ b/fs_mgr/tools/dmctl.cpp
@@ -0,0 +1,322 @@
+/*
+ * 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 <errno.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <linux/dm-ioctl.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <android-base/parseint.h>
+#include <android-base/unique_fd.h>
+#include <libdm/dm.h>
+
+#include <functional>
+#include <iomanip>
+#include <ios>
+#include <iostream>
+#include <map>
+#include <sstream>
+#include <string>
+#include <vector>
+
+using DeviceMapper = ::android::dm::DeviceMapper;
+using DmTable = ::android::dm::DmTable;
+using DmTarget = ::android::dm::DmTarget;
+using DmTargetLinear = ::android::dm::DmTargetLinear;
+using DmTargetZero = ::android::dm::DmTargetZero;
+using DmTargetAndroidVerity = ::android::dm::DmTargetAndroidVerity;
+using DmTargetTypeInfo = ::android::dm::DmTargetTypeInfo;
+using DmBlockDevice = ::android::dm::DeviceMapper::DmBlockDevice;
+
+static int Usage(void) {
+    std::cerr << "usage: dmctl <command> [command options]" << std::endl;
+    std::cerr << "commands:" << std::endl;
+    std::cerr << "  create <dm-name> [-ro] <targets...>" << std::endl;
+    std::cerr << "  delete <dm-name>" << std::endl;
+    std::cerr << "  list <devices | targets>" << std::endl;
+    std::cerr << "  getpath <dm-name>" << std::endl;
+    std::cerr << "  table <dm-name>" << std::endl;
+    std::cerr << "  help" << std::endl;
+    std::cerr << std::endl;
+    std::cerr << "Target syntax:" << std::endl;
+    std::cerr << "  <target_type> <start_sector> <num_sectors> [target_data]" << std::endl;
+    return -EINVAL;
+}
+
+class TargetParser final {
+  public:
+    TargetParser(int argc, char** argv) : arg_index_(0), argc_(argc), argv_(argv) {}
+
+    bool More() const { return arg_index_ < argc_; }
+    std::unique_ptr<DmTarget> Next() {
+        if (!HasArgs(3)) {
+            std::cerr << "Expected <target_type> <start_sector> <num_sectors>" << std::endl;
+            return nullptr;
+        }
+
+        std::string target_type = NextArg();
+        uint64_t start_sector, num_sectors;
+        if (!android::base::ParseUint(NextArg(), &start_sector)) {
+            std::cerr << "Expected start sector, got: " << PreviousArg() << std::endl;
+            return nullptr;
+        }
+        if (!android::base::ParseUint(NextArg(), &num_sectors) || !num_sectors) {
+            std::cerr << "Expected non-zero sector count, got: " << PreviousArg() << std::endl;
+            return nullptr;
+        }
+
+        if (target_type == "zero") {
+            return std::make_unique<DmTargetZero>(start_sector, num_sectors);
+        } else if (target_type == "linear") {
+            if (!HasArgs(2)) {
+                std::cerr << "Expected \"linear\" <block_device> <sector>" << std::endl;
+                return nullptr;
+            }
+
+            std::string block_device = NextArg();
+            uint64_t physical_sector;
+            if (!android::base::ParseUint(NextArg(), &physical_sector)) {
+                std::cerr << "Expected sector, got: \"" << PreviousArg() << "\"" << std::endl;
+                return nullptr;
+            }
+            return std::make_unique<DmTargetLinear>(start_sector, num_sectors, block_device,
+                                                    physical_sector);
+        } else if (target_type == "android-verity") {
+            if (!HasArgs(2)) {
+                std::cerr << "Expected \"android-verity\" <public-key-id> <block_device>"
+                          << std::endl;
+                return nullptr;
+            }
+            std::string keyid = NextArg();
+            std::string block_device = NextArg();
+            return std::make_unique<DmTargetAndroidVerity>(start_sector, num_sectors, keyid,
+                                                           block_device);
+        } else {
+            std::cerr << "Unrecognized target type: " << target_type << std::endl;
+            return nullptr;
+        }
+    }
+
+  private:
+    bool HasArgs(int count) { return arg_index_ + count <= argc_; }
+    const char* NextArg() {
+        CHECK(arg_index_ < argc_);
+        return argv_[arg_index_++];
+    }
+    const char* PreviousArg() {
+        CHECK(arg_index_ >= 0);
+        return argv_[arg_index_ - 1];
+    }
+
+  private:
+    int arg_index_;
+    int argc_;
+    char** argv_;
+};
+
+static int DmCreateCmdHandler(int argc, char** argv) {
+    if (argc < 1) {
+        std::cerr << "Usage: dmctl create <dm-name> [-ro] <targets...>" << std::endl;
+        return -EINVAL;
+    }
+    std::string name = argv[0];
+
+    // Parse extended options first.
+    DmTable table;
+    int arg_index = 1;
+    while (arg_index < argc && argv[arg_index][0] == '-') {
+        if (strcmp(argv[arg_index], "-ro") == 0) {
+            table.set_readonly(true);
+            arg_index++;
+        } else {
+            std::cerr << "Unrecognized option: " << argv[arg_index] << std::endl;
+            return -EINVAL;
+        }
+    }
+
+    // Parse everything else as target information.
+    TargetParser parser(argc - arg_index, argv + arg_index);
+    while (parser.More()) {
+        std::unique_ptr<DmTarget> target = parser.Next();
+        if (!target || !table.AddTarget(std::move(target))) {
+            return -EINVAL;
+        }
+    }
+
+    if (table.num_targets() == 0) {
+        std::cerr << "Must define at least one target." << std::endl;
+        return -EINVAL;
+    }
+
+    DeviceMapper& dm = DeviceMapper::Instance();
+    if (!dm.CreateDevice(name, table)) {
+        std::cerr << "Failed to create device-mapper device with name: " << name << std::endl;
+        return -EIO;
+    }
+    return 0;
+}
+
+static int DmDeleteCmdHandler(int argc, char** argv) {
+    if (argc < 1) {
+        std::cerr << "Usage: dmctl delete <name>" << std::endl;
+        return -EINVAL;
+    }
+
+    std::string name = argv[0];
+    DeviceMapper& dm = DeviceMapper::Instance();
+    if (!dm.DeleteDevice(name)) {
+        std::cerr << "Failed to delete [" << name << "]" << std::endl;
+        return -EIO;
+    }
+
+    return 0;
+}
+
+static int DmListTargets(DeviceMapper& dm) {
+    std::vector<DmTargetTypeInfo> targets;
+    if (!dm.GetAvailableTargets(&targets)) {
+        std::cerr << "Failed to read available device mapper targets" << std::endl;
+        return -errno;
+    }
+
+    std::cout << "Available Device Mapper Targets:" << std::endl;
+    if (targets.empty()) {
+        std::cout << "  <empty>" << std::endl;
+        return 0;
+    }
+
+    for (const auto& target : targets) {
+        std::cout << std::left << std::setw(20) << target.name() << " : " << target.version()
+                  << std::endl;
+    }
+
+    return 0;
+}
+
+static int DmListDevices(DeviceMapper& dm) {
+    std::vector<DmBlockDevice> devices;
+    if (!dm.GetAvailableDevices(&devices)) {
+        std::cerr << "Failed to read available device mapper devices" << std::endl;
+        return -errno;
+    }
+    std::cout << "Available Device Mapper Devices:" << std::endl;
+    if (devices.empty()) {
+        std::cout << "  <empty>" << std::endl;
+        return 0;
+    }
+
+    for (const auto& dev : devices) {
+        std::cout << std::left << std::setw(20) << dev.name() << " : " << dev.Major() << ":"
+                  << dev.Minor() << std::endl;
+    }
+
+    return 0;
+}
+
+static const std::map<std::string, std::function<int(DeviceMapper&)>> listmap = {
+        {"targets", DmListTargets},
+        {"devices", DmListDevices},
+};
+
+static int DmListCmdHandler(int argc, char** argv) {
+    if (argc < 1) {
+        std::cerr << "Invalid arguments, see \'dmctl help\'" << std::endl;
+        return -EINVAL;
+    }
+
+    DeviceMapper& dm = DeviceMapper::Instance();
+    for (const auto& l : listmap) {
+        if (l.first == argv[0]) return l.second(dm);
+    }
+
+    std::cerr << "Invalid argument to \'dmctl list\': " << argv[0] << std::endl;
+    return -EINVAL;
+}
+
+static int HelpCmdHandler(int /* argc */, char** /* argv */) {
+    Usage();
+    return 0;
+}
+
+static int GetPathCmdHandler(int argc, char** argv) {
+    if (argc != 1) {
+        std::cerr << "Invalid arguments, see \'dmctl help\'" << std::endl;
+        return -EINVAL;
+    }
+
+    DeviceMapper& dm = DeviceMapper::Instance();
+    std::string path;
+    if (!dm.GetDmDevicePathByName(argv[0], &path)) {
+        std::cerr << "Could not query path of device \"" << argv[0] << "\"." << std::endl;
+        return -EINVAL;
+    }
+    std::cout << path << std::endl;
+    return 0;
+}
+
+static int TableCmdHandler(int argc, char** argv) {
+    if (argc != 1) {
+        std::cerr << "Invalid arguments, see \'dmctl help\'" << std::endl;
+        return -EINVAL;
+    }
+
+    DeviceMapper& dm = DeviceMapper::Instance();
+    std::vector<DeviceMapper::TargetInfo> table;
+    if (!dm.GetTableStatus(argv[0], &table)) {
+        std::cerr << "Could not query table status of device \"" << argv[0] << "\"." << std::endl;
+        return -EINVAL;
+    }
+    std::cout << "Targets in the device-mapper table for " << argv[0] << ":" << std::endl;
+    for (const auto& target : table) {
+        std::cout << target.spec.sector_start << "-"
+                  << (target.spec.sector_start + target.spec.length) << ": "
+                  << target.spec.target_type;
+        if (!target.data.empty()) {
+            std::cout << ", " << target.data;
+        }
+        std::cout << std::endl;
+    }
+    return 0;
+}
+
+static std::map<std::string, std::function<int(int, char**)>> cmdmap = {
+        // clang-format off
+        {"create", DmCreateCmdHandler},
+        {"delete", DmDeleteCmdHandler},
+        {"list", DmListCmdHandler},
+        {"help", HelpCmdHandler},
+        {"getpath", GetPathCmdHandler},
+        {"table", TableCmdHandler},
+        // clang-format on
+};
+
+int main(int argc, char** argv) {
+    android::base::InitLogging(argv, &android::base::StderrLogger);
+    if (argc < 2) {
+        return Usage();
+    }
+
+    for (const auto& cmd : cmdmap) {
+        if (cmd.first == argv[1]) {
+            return cmd.second(argc - 2, argv + 2);
+        }
+    }
+
+    return Usage();
+}
diff --git a/gatekeeperd/Android.mk b/gatekeeperd/Android.mk
index 3f78955..28f0b07 100644
--- a/gatekeeperd/Android.mk
+++ b/gatekeeperd/Android.mk
@@ -21,8 +21,7 @@
 LOCAL_SRC_FILES := \
 	SoftGateKeeperDevice.cpp \
 	IGateKeeperService.cpp \
-	gatekeeperd.cpp \
-	IUserManager.cpp
+	gatekeeperd.cpp
 
 LOCAL_MODULE := gatekeeperd
 LOCAL_SHARED_LIBRARIES := \
@@ -33,7 +32,12 @@
 	libbase \
 	libutils \
 	libcrypto \
-	libkeystore_binder
+	libkeystore_binder \
+	libhidlbase \
+	libhidltransport \
+	libhwbinder \
+	android.hardware.gatekeeper@1.0 \
+
 LOCAL_STATIC_LIBRARIES := libscrypt_static
 LOCAL_C_INCLUDES := external/scrypt/lib/crypto
 LOCAL_INIT_RC := gatekeeperd.rc
diff --git a/gatekeeperd/IGateKeeperService.cpp b/gatekeeperd/IGateKeeperService.cpp
index 95fbfd1..1c339f4 100644
--- a/gatekeeperd/IGateKeeperService.cpp
+++ b/gatekeeperd/IGateKeeperService.cpp
@@ -158,6 +158,12 @@
             reply->writeNoException();
             return NO_ERROR;
         }
+        case REPORT_DEVICE_SETUP_COMPLETE: {
+            CHECK_INTERFACE(IGateKeeperService, data, reply);
+            reportDeviceSetupComplete();
+            reply->writeNoException();
+            return NO_ERROR;
+        }
         default:
             return BBinder::onTransact(code, data, reply, flags);
     }
diff --git a/gatekeeperd/IGateKeeperService.h b/gatekeeperd/IGateKeeperService.h
index f070486..2816efc 100644
--- a/gatekeeperd/IGateKeeperService.h
+++ b/gatekeeperd/IGateKeeperService.h
@@ -33,6 +33,7 @@
         VERIFY_CHALLENGE = IBinder::FIRST_CALL_TRANSACTION + 2,
         GET_SECURE_USER_ID = IBinder::FIRST_CALL_TRANSACTION + 3,
         CLEAR_SECURE_USER_ID = IBinder::FIRST_CALL_TRANSACTION + 4,
+        REPORT_DEVICE_SETUP_COMPLETE = IBinder::FIRST_CALL_TRANSACTION + 5,
     };
 
     enum {
@@ -95,6 +96,12 @@
      * Clears the secure user ID associated with the user.
      */
     virtual void clearSecureUserId(uint32_t uid) = 0;
+
+    /**
+     * Notifies gatekeeper that device setup has been completed and any potentially still existing
+     * state from before a factory reset can be cleaned up (if it has not been already).
+     */
+    virtual void reportDeviceSetupComplete() = 0;
 };
 
 // ----------------------------------------------------------------------------
diff --git a/gatekeeperd/IUserManager.cpp b/gatekeeperd/IUserManager.cpp
deleted file mode 100644
index 8167d19..0000000
--- a/gatekeeperd/IUserManager.cpp
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright (C) 2015 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.
- */
-
-#define LOG_TAG "IUserManager"
-#include <stdint.h>
-#include <sys/types.h>
-#include <utils/Log.h>
-#include <binder/Parcel.h>
-
-#include "IUserManager.h"
-
-namespace android {
-
-class BpUserManager : public BpInterface<IUserManager>
-{
-public:
-    explicit BpUserManager(const sp<IBinder>& impl) :
-            BpInterface<IUserManager>(impl) {
-    }
-    virtual int32_t getCredentialOwnerProfile(int32_t user_id) {
-        Parcel data, reply;
-        data.writeInterfaceToken(IUserManager::getInterfaceDescriptor());
-        data.writeInt32(user_id);
-        status_t rc = remote()->transact(GET_CREDENTIAL_OWNER_PROFILE, data, &reply, 0);
-        if (rc != NO_ERROR) {
-            ALOGE("%s: failed (%d)\n", __func__, rc);
-            return -1;
-        }
-
-        int32_t exception = reply.readExceptionCode();
-        if (exception != 0) {
-            ALOGE("%s: got exception (%d)\n", __func__, exception);
-            return -1;
-        }
-
-        return reply.readInt32();
-    }
-
-};
-
-IMPLEMENT_META_INTERFACE(UserManager, "android.os.IUserManager");
-
-}; // namespace android
-
diff --git a/gatekeeperd/IUserManager.h b/gatekeeperd/IUserManager.h
deleted file mode 100644
index 640e9b5..0000000
--- a/gatekeeperd/IUserManager.h
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright (C) 2015 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 IUSERMANAGER_H_
-#define IUSERMANAGER_H_
-
-#include <inttypes.h>
-#include <utils/Errors.h>
-#include <binder/IInterface.h>
-#include <binder/Parcel.h>
-#include <utils/Vector.h>
-
-namespace android {
-
-/*
-* Communication channel to UserManager
-*/
-class IUserManager : public IInterface {
-    public:
-        // must be kept in sync with IUserManager.aidl
-        enum {
-            GET_CREDENTIAL_OWNER_PROFILE = IBinder::FIRST_CALL_TRANSACTION + 0,
-        };
-
-        virtual int32_t getCredentialOwnerProfile(int32_t user_id) = 0;
-
-        DECLARE_META_INTERFACE(UserManager);
-};
-
-}; // namespace android
-
-#endif // IUSERMANAGER_H_
-
diff --git a/gatekeeperd/OWNERS b/gatekeeperd/OWNERS
new file mode 100644
index 0000000..9c99c6e
--- /dev/null
+++ b/gatekeeperd/OWNERS
@@ -0,0 +1,2 @@
+swillden@google.com
+jdanis@google.com
diff --git a/gatekeeperd/SoftGateKeeper.h b/gatekeeperd/SoftGateKeeper.h
index cb02a6f..2f4f4d7 100644
--- a/gatekeeperd/SoftGateKeeper.h
+++ b/gatekeeperd/SoftGateKeeper.h
@@ -26,11 +26,11 @@
 }
 
 #include <android-base/memory.h>
-#include <UniquePtr.h>
 #include <gatekeeper/gatekeeper.h>
 
 #include <iostream>
 #include <unordered_map>
+#include <memory>
 
 namespace gatekeeper {
 
@@ -173,7 +173,7 @@
     typedef std::unordered_map<uint32_t, failure_record_t> FailureRecordMap;
     typedef std::unordered_map<uint64_t, fast_hash_t> FastHashMap;
 
-    UniquePtr<uint8_t[]> key_;
+    std::unique_ptr<uint8_t[]> key_;
     FailureRecordMap failure_map_;
     FastHashMap fast_hash_map_;
 };
diff --git a/gatekeeperd/SoftGateKeeperDevice.h b/gatekeeperd/SoftGateKeeperDevice.h
index 3463c29..e3dc068 100644
--- a/gatekeeperd/SoftGateKeeperDevice.h
+++ b/gatekeeperd/SoftGateKeeperDevice.h
@@ -19,7 +19,7 @@
 
 #include "SoftGateKeeper.h"
 
-#include <UniquePtr.h>
+#include <memory>
 
 using namespace gatekeeper;
 
@@ -68,7 +68,7 @@
             const uint8_t *provided_password, uint32_t provided_password_length,
             uint8_t **auth_token, uint32_t *auth_token_length, bool *request_reenroll);
 private:
-    UniquePtr<SoftGateKeeper> impl_;
+    std::unique_ptr<SoftGateKeeper> impl_;
 };
 
 } // namespace gatekeeper
diff --git a/gatekeeperd/gatekeeperd.cpp b/gatekeeperd/gatekeeperd.cpp
index 96bda07..abb387c 100644
--- a/gatekeeperd/gatekeeperd.cpp
+++ b/gatekeeperd/gatekeeperd.cpp
@@ -23,6 +23,7 @@
 #include <inttypes.h>
 #include <stdint.h>
 #include <unistd.h>
+#include <memory>
 
 #include <binder/IPCThreadState.h>
 #include <binder/IServiceManager.h>
@@ -37,7 +38,15 @@
 #include <utils/String16.h>
 
 #include "SoftGateKeeperDevice.h"
-#include "IUserManager.h"
+
+#include <hidl/HidlSupport.h>
+#include <android/hardware/gatekeeper/1.0/IGatekeeper.h>
+
+using android::sp;
+using android::hardware::gatekeeper::V1_0::IGatekeeper;
+using android::hardware::gatekeeper::V1_0::GatekeeperStatusCode;
+using android::hardware::gatekeeper::V1_0::GatekeeperResponse;
+using android::hardware::Return;
 
 namespace android {
 
@@ -47,28 +56,16 @@
 class GateKeeperProxy : public BnGateKeeperService {
 public:
     GateKeeperProxy() {
-        int ret = hw_get_module_by_class(GATEKEEPER_HARDWARE_MODULE_ID, NULL, &module);
-        device = NULL;
+        clear_state_if_needed_done = false;
+        hw_device = IGatekeeper::getService();
 
-        if (ret < 0) {
+        if (hw_device == nullptr) {
             ALOGW("falling back to software GateKeeper");
             soft_device.reset(new SoftGateKeeperDevice());
-        } else {
-            ret = gatekeeper_open(module, &device);
-            if (ret < 0)
-                LOG_ALWAYS_FATAL_IF(ret < 0, "Unable to open GateKeeper HAL");
-        }
-
-        if (mark_cold_boot()) {
-            ALOGI("cold boot: clearing state");
-            if (device != NULL && device->delete_all_users != NULL) {
-                device->delete_all_users(device);
-            }
         }
     }
 
     virtual ~GateKeeperProxy() {
-        if (device) gatekeeper_close(device);
     }
 
     void store_sid(uint32_t uid, uint64_t sid) {
@@ -83,6 +80,21 @@
         close(fd);
     }
 
+    void clear_state_if_needed() {
+        if (clear_state_if_needed_done) {
+            return;
+        }
+
+        if (mark_cold_boot()) {
+            ALOGI("cold boot: clearing state");
+            if (hw_device != nullptr) {
+                hw_device->deleteAllUsers([](const GatekeeperResponse &){});
+            }
+        }
+
+        clear_state_if_needed_done = true;
+    }
+
     bool mark_cold_boot() {
         const char *filename = ".coldboot";
         if (access(filename, F_OK) == -1) {
@@ -137,11 +149,15 @@
             return PERMISSION_DENIED;
         }
 
+        // Make sure to clear any state from before factory reset as soon as a credential is
+        // enrolled (which may happen during device setup).
+        clear_state_if_needed();
+
         // need a desired password to enroll
         if (desired_password_length == 0) return -EINVAL;
 
         int ret;
-        if (device) {
+        if (hw_device != nullptr) {
             const gatekeeper::password_handle_t *handle =
                     reinterpret_cast<const gatekeeper::password_handle_t *>(current_password_handle);
 
@@ -154,10 +170,37 @@
                 current_password_length = 0;
             }
 
-            ret = device->enroll(device, uid, current_password_handle, current_password_handle_length,
-                    current_password, current_password_length,
-                    desired_password, desired_password_length,
-                    enrolled_password_handle, enrolled_password_handle_length);
+            android::hardware::hidl_vec<uint8_t> curPwdHandle;
+            curPwdHandle.setToExternal(const_cast<uint8_t*>(current_password_handle),
+                                       current_password_handle_length);
+            android::hardware::hidl_vec<uint8_t> curPwd;
+            curPwd.setToExternal(const_cast<uint8_t*>(current_password),
+                                 current_password_length);
+            android::hardware::hidl_vec<uint8_t> newPwd;
+            newPwd.setToExternal(const_cast<uint8_t*>(desired_password),
+                                 desired_password_length);
+
+            Return<void> hwRes = hw_device->enroll(uid, curPwdHandle, curPwd, newPwd,
+                              [&ret, enrolled_password_handle, enrolled_password_handle_length]
+                                   (const GatekeeperResponse &rsp) {
+                ret = static_cast<int>(rsp.code); // propagate errors
+                if (rsp.code >= GatekeeperStatusCode::STATUS_OK) {
+                    if (enrolled_password_handle != nullptr &&
+                        enrolled_password_handle_length != nullptr) {
+                        *enrolled_password_handle = new uint8_t[rsp.data.size()];
+                        *enrolled_password_handle_length = rsp.data.size();
+                        memcpy(*enrolled_password_handle, rsp.data.data(),
+                               *enrolled_password_handle_length);
+                    }
+                    ret = 0; // all success states are reported as 0
+                } else if (rsp.code == GatekeeperStatusCode::ERROR_RETRY_TIMEOUT && rsp.timeout > 0) {
+                    ret = rsp.timeout;
+                }
+            });
+            if (!hwRes.isOk()) {
+                ALOGE("enroll transaction failed\n");
+                ret = -1;
+            }
         } else {
             ret = soft_device->enroll(uid,
                     current_password_handle, current_password_handle_length,
@@ -191,11 +234,13 @@
     virtual int verify(uint32_t uid,
             const uint8_t *enrolled_password_handle, uint32_t enrolled_password_handle_length,
             const uint8_t *provided_password, uint32_t provided_password_length, bool *request_reenroll) {
-        uint8_t *auth_token;
+        uint8_t *auth_token = nullptr;
         uint32_t auth_token_length;
-        return verifyChallenge(uid, 0, enrolled_password_handle, enrolled_password_handle_length,
+        int ret = verifyChallenge(uid, 0, enrolled_password_handle, enrolled_password_handle_length,
                 provided_password, provided_password_length,
                 &auth_token, &auth_token_length, request_reenroll);
+        delete [] auth_token;
+        return ret;
     }
 
     virtual int verifyChallenge(uint32_t uid, uint64_t challenge,
@@ -214,16 +259,40 @@
             return -EINVAL;
 
         int ret;
-        if (device) {
+        if (hw_device != nullptr) {
             const gatekeeper::password_handle_t *handle =
                     reinterpret_cast<const gatekeeper::password_handle_t *>(enrolled_password_handle);
             // handle version 0 does not have hardware backed flag, and thus cannot be upgraded to
             // a HAL if there was none before
             if (handle->version == 0 || handle->hardware_backed) {
-                ret = device->verify(device, uid, challenge,
-                    enrolled_password_handle, enrolled_password_handle_length,
-                    provided_password, provided_password_length, auth_token, auth_token_length,
-                    request_reenroll);
+                android::hardware::hidl_vec<uint8_t> curPwdHandle;
+                curPwdHandle.setToExternal(const_cast<uint8_t*>(enrolled_password_handle),
+                                           enrolled_password_handle_length);
+                android::hardware::hidl_vec<uint8_t> enteredPwd;
+                enteredPwd.setToExternal(const_cast<uint8_t*>(provided_password),
+                                         provided_password_length);
+                Return<void> hwRes = hw_device->verify(uid, challenge, curPwdHandle, enteredPwd,
+                                        [&ret, request_reenroll, auth_token, auth_token_length]
+                                             (const GatekeeperResponse &rsp) {
+                    ret = static_cast<int>(rsp.code); // propagate errors
+                    if (auth_token != nullptr && auth_token_length != nullptr &&
+                        rsp.code >= GatekeeperStatusCode::STATUS_OK) {
+                        *auth_token = new uint8_t[rsp.data.size()];
+                        *auth_token_length = rsp.data.size();
+                        memcpy(*auth_token, rsp.data.data(), *auth_token_length);
+                        if (request_reenroll != nullptr) {
+                            *request_reenroll = (rsp.code == GatekeeperStatusCode::STATUS_REENROLL);
+                        }
+                        ret = 0; // all success states are reported as 0
+                    } else if (rsp.code == GatekeeperStatusCode::ERROR_RETRY_TIMEOUT &&
+                               rsp.timeout > 0) {
+                        ret = rsp.timeout;
+                    }
+                });
+                if (!hwRes.isOk()) {
+                    ALOGE("verify transaction failed\n");
+                    ret = -1;
+                }
             } else {
                 // upgrade scenario, a HAL has been added to this device where there was none before
                 SoftGateKeeperDevice soft_dev;
@@ -250,9 +319,9 @@
             sp<IBinder> binder = sm->getService(String16("android.security.keystore"));
             sp<IKeystoreService> service = interface_cast<IKeystoreService>(binder);
             if (service != NULL) {
-                status_t ret = service->addAuthToken(*auth_token, *auth_token_length);
-                if (ret != ResponseCode::NO_ERROR) {
-                    ALOGE("Falure sending auth token to KeyStore: %d", ret);
+                auto ret = service->addAuthToken(*auth_token, *auth_token_length);
+                if (!ret.isOk()) {
+                    ALOGE("Failure sending auth token to KeyStore: %" PRId32, int32_t(ret));
                 }
             } else {
                 ALOGE("Unable to communicate with KeyStore");
@@ -267,23 +336,7 @@
         return ret;
     }
 
-    virtual uint64_t getSecureUserId(uint32_t uid) {
-        uint64_t sid = read_sid(uid);
-         if (sid == 0) {
-            // might be a work profile, look up the parent
-            sp<IServiceManager> sm = defaultServiceManager();
-            sp<IBinder> binder = sm->getService(String16("user"));
-            sp<IUserManager> um = interface_cast<IUserManager>(binder);
-            int32_t parent = um->getCredentialOwnerProfile(uid);
-            if (parent < 0) {
-                return 0;
-            } else if (parent != (int32_t) uid) {
-                return read_sid(parent);
-            }
-        }
-        return sid;
-
-    }
+    virtual uint64_t getSecureUserId(uint32_t uid) { return read_sid(uid); }
 
     virtual void clearSecureUserId(uint32_t uid) {
         IPCThreadState* ipc = IPCThreadState::self();
@@ -295,11 +348,23 @@
         }
         clear_sid(uid);
 
-        if (device != NULL && device->delete_user != NULL) {
-            device->delete_user(device, uid);
+        if (hw_device != nullptr) {
+            hw_device->deleteUser(uid, [] (const GatekeeperResponse &){});
         }
     }
 
+    virtual void reportDeviceSetupComplete() {
+        IPCThreadState* ipc = IPCThreadState::self();
+        const int calling_pid = ipc->getCallingPid();
+        const int calling_uid = ipc->getCallingUid();
+        if (!PermissionCache::checkPermission(KEYGUARD_PERMISSION, calling_pid, calling_uid)) {
+            ALOGE("%s: permission denied for [%d:%d]", __func__, calling_pid, calling_uid);
+            return;
+        }
+
+        clear_state_if_needed();
+    }
+
     virtual status_t dump(int fd, const Vector<String16> &) {
         IPCThreadState* ipc = IPCThreadState::self();
         const int pid = ipc->getCallingPid();
@@ -308,7 +373,7 @@
             return PERMISSION_DENIED;
         }
 
-        if (device == NULL) {
+        if (hw_device == NULL) {
             const char *result = "Device not available";
             write(fd, result, strlen(result) + 1);
         } else {
@@ -320,9 +385,10 @@
     }
 
 private:
-    gatekeeper_device_t *device;
-    UniquePtr<SoftGateKeeperDevice> soft_device;
-    const hw_module_t *module;
+    sp<IGatekeeper> hw_device;
+    std::unique_ptr<SoftGateKeeperDevice> soft_device;
+
+    bool clear_state_if_needed_done;
 };
 }// namespace android
 
diff --git a/gatekeeperd/tests/Android.mk b/gatekeeperd/tests/Android.mk
index a62b1d4..c38c64b 100644
--- a/gatekeeperd/tests/Android.mk
+++ b/gatekeeperd/tests/Android.mk
@@ -19,7 +19,7 @@
 include $(CLEAR_VARS)
 LOCAL_MODULE := gatekeeperd-unit-tests
 LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
-LOCAL_CFLAGS += -g -Wall -Werror -std=gnu++11 -Wno-missing-field-initializers
+LOCAL_CFLAGS += -g -Wall -Werror -Wno-missing-field-initializers
 LOCAL_SHARED_LIBRARIES := libgatekeeper libcrypto libbase
 LOCAL_STATIC_LIBRARIES := libscrypt_static
 LOCAL_C_INCLUDES := external/scrypt/lib/crypto
diff --git a/gatekeeperd/tests/gatekeeper_test.cpp b/gatekeeperd/tests/gatekeeper_test.cpp
index 47a8bfa..100375f 100644
--- a/gatekeeperd/tests/gatekeeper_test.cpp
+++ b/gatekeeperd/tests/gatekeeper_test.cpp
@@ -19,7 +19,6 @@
 
 #include <gtest/gtest.h>
 #include <hardware/hw_auth_token.h>
-#include <UniquePtr.h>
 
 #include "../SoftGateKeeper.h"
 
diff --git a/healthd/Android.bp b/healthd/Android.bp
new file mode 100644
index 0000000..7269b62
--- /dev/null
+++ b/healthd/Android.bp
@@ -0,0 +1,20 @@
+cc_library_headers {
+    name: "libhealthd_headers",
+    vendor_available: true,
+    export_include_dirs: ["include"],
+    header_libs: ["libbatteryservice_headers"],
+    export_header_lib_headers: ["libbatteryservice_headers"],
+}
+
+cc_library_static {
+    name: "libbatterymonitor",
+    srcs: ["BatteryMonitor.cpp"],
+    vendor_available: true,
+    export_include_dirs: ["include"],
+    shared_libs: [
+        "libutils",
+        "libbase",
+    ],
+    header_libs: ["libhealthd_headers"],
+    export_header_lib_headers: ["libhealthd_headers"],
+}
diff --git a/healthd/Android.mk b/healthd/Android.mk
index b292725..1244903 100644
--- a/healthd/Android.mk
+++ b/healthd/Android.mk
@@ -3,124 +3,78 @@
 LOCAL_PATH := $(call my-dir)
 
 include $(CLEAR_VARS)
-LOCAL_SRC_FILES := healthd_board_default.cpp
-LOCAL_MODULE := libhealthd.default
-LOCAL_CFLAGS := -Werror
-LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
-LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
-LOCAL_STATIC_LIBRARIES := libbinder
-LOCAL_EXPORT_STATIC_LIBRARY_HEADERS := libbinder
-include $(BUILD_STATIC_LIBRARY)
-
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES := BatteryMonitor.cpp
-LOCAL_MODULE := libbatterymonitor
-LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
-LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
-LOCAL_STATIC_LIBRARIES := libutils libbase libbinder
-include $(BUILD_STATIC_LIBRARY)
-
-include $(CLEAR_VARS)
-ifeq ($(strip $(BOARD_CHARGER_ENABLE_SUSPEND)),true)
-LOCAL_CFLAGS += -DCHARGER_ENABLE_SUSPEND
-LOCAL_SHARED_LIBRARIES += libsuspend
-endif
 LOCAL_SRC_FILES := \
     healthd_mode_android.cpp \
-    healthd_mode_charger.cpp \
-    AnimationParser.cpp \
-    BatteryPropertiesRegistrar.cpp \
+    BatteryPropertiesRegistrar.cpp
 
-LOCAL_MODULE := libhealthd_internal
-LOCAL_C_INCLUDES := bootable/recovery
+LOCAL_MODULE := libhealthd_android
 LOCAL_EXPORT_C_INCLUDE_DIRS := \
     $(LOCAL_PATH) \
-    $(LOCAL_PATH)/include \
+    $(LOCAL_PATH)/include
 
 LOCAL_STATIC_LIBRARIES := \
     libbatterymonitor \
     libbatteryservice \
-    libbinder \
+    libutils \
+    libbase \
+    libcutils \
+    liblog \
+    libc \
+
+include $(BUILD_STATIC_LIBRARY)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := libhealthd_draw
+
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)
+LOCAL_STATIC_LIBRARIES := \
+	libminui \
+	libbase
+LOCAL_SRC_FILES := healthd_draw.cpp
+
+ifneq ($(TARGET_HEALTHD_DRAW_SPLIT_SCREEN),)
+LOCAL_CFLAGS += -DHEALTHD_DRAW_SPLIT_SCREEN=$(TARGET_HEALTHD_DRAW_SPLIT_SCREEN)
+else
+LOCAL_CFLAGS += -DHEALTHD_DRAW_SPLIT_SCREEN=0
+endif
+
+ifneq ($(TARGET_HEALTHD_DRAW_SPLIT_OFFSET),)
+LOCAL_CFLAGS += -DHEALTHD_DRAW_SPLIT_OFFSET=$(TARGET_HEALTHD_DRAW_SPLIT_OFFSET)
+else
+LOCAL_CFLAGS += -DHEALTHD_DRAW_SPLIT_OFFSET=0
+endif
+
+include $(BUILD_STATIC_LIBRARY)
+
+include $(CLEAR_VARS)
+
+LOCAL_CFLAGS := -Werror
+ifeq ($(strip $(BOARD_CHARGER_DISABLE_INIT_BLANK)),true)
+LOCAL_CFLAGS += -DCHARGER_DISABLE_INIT_BLANK
+endif
+ifeq ($(strip $(BOARD_CHARGER_ENABLE_SUSPEND)),true)
+LOCAL_CFLAGS += -DCHARGER_ENABLE_SUSPEND
+endif
+
+LOCAL_SRC_FILES := \
+    healthd_mode_charger.cpp \
+    AnimationParser.cpp
+
+LOCAL_MODULE := libhealthd_charger
+LOCAL_C_INCLUDES := bootable/recovery $(LOCAL_PATH)/include
+LOCAL_EXPORT_C_INCLUDE_DIRS := \
+    $(LOCAL_PATH) \
+    $(LOCAL_PATH)/include
+
+LOCAL_STATIC_LIBRARIES := \
     libminui \
     libpng \
     libz \
     libutils \
     libbase \
     libcutils \
-    liblog \
-    libm \
-    libc \
-
-include $(BUILD_STATIC_LIBRARY)
-
-
-include $(CLEAR_VARS)
-
-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.cpp \
-	healthd_mode_android.cpp \
-	BatteryPropertiesRegistrar.cpp \
-
-ifneq ($(strip $(LOCAL_CHARGER_NO_UI)),true)
-LOCAL_SRC_FILES += healthd_mode_charger.cpp
-endif
-
-LOCAL_MODULE := healthd
-LOCAL_MODULE_TAGS := optional
-LOCAL_FORCE_STATIC_EXECUTABLE := true
-LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT_SBIN)
-LOCAL_UNSTRIPPED_PATH := $(TARGET_ROOT_OUT_SBIN_UNSTRIPPED)
-
-LOCAL_CFLAGS := -D__STDC_LIMIT_MACROS -Werror
-
-ifeq ($(strip $(BOARD_CHARGER_DISABLE_INIT_BLANK)),true)
-LOCAL_CFLAGS += -DCHARGER_DISABLE_INIT_BLANK
-endif
-
-ifeq ($(strip $(BOARD_CHARGER_ENABLE_SUSPEND)),true)
-LOCAL_CFLAGS += -DCHARGER_ENABLE_SUSPEND
-endif
-
-ifeq ($(strip $(LOCAL_CHARGER_NO_UI)),true)
-LOCAL_CFLAGS += -DCHARGER_NO_UI
-endif
-
-LOCAL_C_INCLUDES := bootable/recovery $(LOCAL_PATH)/include
-
-ifneq ($(BOARD_PERIODIC_CHORES_INTERVAL_FAST),)
-LOCAL_CFLAGS += -DBOARD_PERIODIC_CHORES_INTERVAL_FAST=$(BOARD_PERIODIC_CHORES_INTERVAL_FAST)
-endif
-
-ifneq ($(BOARD_PERIODIC_CHORES_INTERVAL_SLOW),)
-LOCAL_CFLAGS += -DBOARD_PERIODIC_CHORES_INTERVAL_SLOW=$(BOARD_PERIODIC_CHORES_INTERVAL_SLOW)
-endif
-
-LOCAL_STATIC_LIBRARIES := \
-    libhealthd_internal \
-    libbatterymonitor \
-    libbatteryservice \
-    libbinder \
-    libbase \
-
-ifneq ($(strip $(LOCAL_CHARGER_NO_UI)),true)
-LOCAL_STATIC_LIBRARIES += \
-   libminui \
-   libpng \
-   libz \
-
-endif
-
-
-LOCAL_STATIC_LIBRARIES += \
-    libutils \
-    libcutils \
+    libhealthd_draw \
     liblog \
     libm \
     libc \
@@ -129,15 +83,67 @@
 LOCAL_STATIC_LIBRARIES += libsuspend
 endif
 
+include $(BUILD_STATIC_LIBRARY)
+
+### charger ###
+include $(CLEAR_VARS)
+ifeq ($(strip $(BOARD_CHARGER_NO_UI)),true)
+LOCAL_CHARGER_NO_UI := true
+endif
+
+LOCAL_SRC_FILES := \
+    healthd_common.cpp \
+    charger.cpp \
+
+LOCAL_MODULE := charger
+LOCAL_MODULE_TAGS := optional
+LOCAL_FORCE_STATIC_EXECUTABLE := true
+LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT_SBIN)
+LOCAL_UNSTRIPPED_PATH := $(TARGET_ROOT_OUT_SBIN_UNSTRIPPED)
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
+
+LOCAL_CFLAGS := -Werror
+ifeq ($(strip $(LOCAL_CHARGER_NO_UI)),true)
+LOCAL_CFLAGS += -DCHARGER_NO_UI
+endif
+ifneq ($(BOARD_PERIODIC_CHORES_INTERVAL_FAST),)
+LOCAL_CFLAGS += -DBOARD_PERIODIC_CHORES_INTERVAL_FAST=$(BOARD_PERIODIC_CHORES_INTERVAL_FAST)
+endif
+ifneq ($(BOARD_PERIODIC_CHORES_INTERVAL_SLOW),)
+LOCAL_CFLAGS += -DBOARD_PERIODIC_CHORES_INTERVAL_SLOW=$(BOARD_PERIODIC_CHORES_INTERVAL_SLOW)
+endif
+
+LOCAL_STATIC_LIBRARIES := \
+    libhealthd_charger \
+    libhealthd_draw \
+    libbatterymonitor \
+    libbase \
+    libutils \
+    libcutils \
+    liblog \
+    libm \
+    libc \
+
+ifneq ($(strip $(LOCAL_CHARGER_NO_UI)),true)
+LOCAL_STATIC_LIBRARIES += \
+    libminui \
+    libpng \
+    libz \
+
+endif
+
+ifeq ($(strip $(BOARD_CHARGER_ENABLE_SUSPEND)),true)
+LOCAL_STATIC_LIBRARIES += libsuspend
+endif
+
 LOCAL_HAL_STATIC_LIBRARIES := libhealthd
 
-# Symlink /charger to /sbin/healthd
+# Symlink /charger to /sbin/charger
 LOCAL_POST_INSTALL_CMD := $(hide) mkdir -p $(TARGET_ROOT_OUT) \
-    && ln -sf /sbin/healthd $(TARGET_ROOT_OUT)/charger
+    && ln -sf /sbin/charger $(TARGET_ROOT_OUT)/charger
 
 include $(BUILD_EXECUTABLE)
 
-
 ifneq ($(strip $(LOCAL_CHARGER_NO_UI)),true)
 define _add-charger-image
 include $$(CLEAR_VARS)
@@ -165,3 +171,41 @@
 _add-charger-image :=
 _img_modules :=
 endif # LOCAL_CHARGER_NO_UI
+
+### healthd ###
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+    healthd_common.cpp \
+    healthd.cpp \
+
+LOCAL_MODULE := healthd
+LOCAL_MODULE_TAGS := optional
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
+
+ifneq ($(BOARD_PERIODIC_CHORES_INTERVAL_FAST),)
+LOCAL_CFLAGS += -DBOARD_PERIODIC_CHORES_INTERVAL_FAST=$(BOARD_PERIODIC_CHORES_INTERVAL_FAST)
+endif
+ifneq ($(BOARD_PERIODIC_CHORES_INTERVAL_SLOW),)
+LOCAL_CFLAGS += -DBOARD_PERIODIC_CHORES_INTERVAL_SLOW=$(BOARD_PERIODIC_CHORES_INTERVAL_SLOW)
+endif
+
+LOCAL_STATIC_LIBRARIES := \
+    libhealthd_android \
+    libbatterymonitor \
+    libbatteryservice \
+    android.hardware.health@1.0-convert \
+
+LOCAL_SHARED_LIBRARIES := \
+    libbinder \
+    libbase \
+    libutils \
+    libcutils \
+    liblog \
+    libm \
+    libc \
+    libhidlbase \
+    libhidltransport \
+    android.hardware.health@1.0 \
+
+include $(BUILD_EXECUTABLE)
diff --git a/healthd/BatteryMonitor.cpp b/healthd/BatteryMonitor.cpp
index 4e3b885..fa79d0b 100644
--- a/healthd/BatteryMonitor.cpp
+++ b/healthd/BatteryMonitor.cpp
@@ -42,7 +42,6 @@
 #define POWER_SUPPLY_SYSFS_PATH "/sys/class/" POWER_SUPPLY_SUBSYSTEM
 #define FAKE_BATTERY_CAPACITY 42
 #define FAKE_BATTERY_TEMPERATURE 424
-#define ALWAYS_PLUGGED_CAPACITY 100
 #define MILLION 1.0e6
 #define DEFAULT_VBUS_VOLTAGE 5000000
 
@@ -81,8 +80,11 @@
     props->batteryTechnology.clear();
 }
 
-BatteryMonitor::BatteryMonitor() : mHealthdConfig(nullptr), mBatteryDevicePresent(false),
-    mAlwaysPluggedDevice(false), mBatteryFixedCapacity(0), mBatteryFixedTemperature(0) {
+BatteryMonitor::BatteryMonitor()
+    : mHealthdConfig(nullptr),
+      mBatteryDevicePresent(false),
+      mBatteryFixedCapacity(0),
+      mBatteryFixedTemperature(0) {
     initBatteryProperties(&props);
 }
 
@@ -141,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 },
@@ -162,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) {
@@ -223,15 +225,6 @@
         mBatteryFixedTemperature :
         getIntField(mHealthdConfig->batteryTemperaturePath);
 
-    // For devices which do not have battery and are always plugged
-    // into power souce.
-    if (mAlwaysPluggedDevice) {
-        props.chargerAcOnline = true;
-        props.batteryPresent = true;
-        props.batteryStatus = BATTERY_STATUS_CHARGING;
-        props.batteryHealth = BATTERY_HEALTH_GOOD;
-    }
-
     std::string buf;
 
     if (readFromFile(mHealthdConfig->batteryStatusPath, &buf) > 0)
@@ -351,6 +344,7 @@
 
 status_t BatteryMonitor::getProperty(int id, struct BatteryProperty *val) {
     status_t ret = BAD_VALUE;
+    std::string buf;
 
     val->valueInt64 = LONG_MIN;
 
@@ -403,6 +397,11 @@
         }
         break;
 
+    case BATTERY_PROP_BATTERY_STATUS:
+        val->valueInt64 = getChargeStatus();
+        ret = NO_ERROR;
+        break;
+
     default:
         break;
     }
@@ -618,9 +617,6 @@
         KLOG_WARNING(LOG_TAG, "No battery devices found\n");
         hc->periodic_chores_interval_fast = -1;
         hc->periodic_chores_interval_slow = -1;
-        mBatteryFixedCapacity = ALWAYS_PLUGGED_CAPACITY;
-        mBatteryFixedTemperature = FAKE_BATTERY_TEMPERATURE;
-        mAlwaysPluggedDevice = true;
     } else {
         if (mHealthdConfig->batteryStatusPath.isEmpty())
             KLOG_WARNING(LOG_TAG, "BatteryStatusPath not found\n");
diff --git a/healthd/OWNERS b/healthd/OWNERS
new file mode 100644
index 0000000..00df08a
--- /dev/null
+++ b/healthd/OWNERS
@@ -0,0 +1,2 @@
+elsk@google.com
+toddpoynor@google.com
diff --git a/healthd/charger.cpp b/healthd/charger.cpp
new file mode 100644
index 0000000..5a8fe1a
--- /dev/null
+++ b/healthd/charger.cpp
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#define LOG_TAG "charger"
+#define KLOG_LEVEL 6
+
+#include <healthd/healthd.h>
+
+#include <stdlib.h>
+#include <string.h>
+#include <cutils/klog.h>
+
+using namespace android;
+
+// main healthd loop
+extern int healthd_main(void);
+
+// Charger mode
+
+extern void healthd_mode_charger_init(struct healthd_config *config);
+extern int healthd_mode_charger_preparetowait(void);
+extern void healthd_mode_charger_heartbeat(void);
+extern void healthd_mode_charger_battery_update(
+    struct android::BatteryProperties *props);
+
+// NOPs for modes that need no special action
+
+static void healthd_mode_nop_init(struct healthd_config *config);
+static int healthd_mode_nop_preparetowait(void);
+static void healthd_mode_nop_heartbeat(void);
+static void healthd_mode_nop_battery_update(
+    struct android::BatteryProperties *props);
+
+static struct healthd_mode_ops healthd_nops = {
+    .init = healthd_mode_nop_init,
+    .preparetowait = healthd_mode_nop_preparetowait,
+    .heartbeat = healthd_mode_nop_heartbeat,
+    .battery_update = healthd_mode_nop_battery_update,
+};
+
+#ifdef CHARGER_NO_UI
+static struct healthd_mode_ops charger_ops = healthd_nops;
+#else
+static struct healthd_mode_ops charger_ops = {
+    .init = healthd_mode_charger_init,
+    .preparetowait = healthd_mode_charger_preparetowait,
+    .heartbeat = healthd_mode_charger_heartbeat,
+    .battery_update = healthd_mode_charger_battery_update,
+};
+#endif
+
+static void healthd_mode_nop_init(struct healthd_config* /*config*/) {
+}
+
+static int healthd_mode_nop_preparetowait(void) {
+    return -1;
+}
+
+static void healthd_mode_nop_heartbeat(void) {
+}
+
+static void healthd_mode_nop_battery_update(
+    struct android::BatteryProperties* /*props*/) {
+}
+
+int main(int argc, char **argv) {
+    int ch;
+
+    healthd_mode_ops = &charger_ops;
+
+    while ((ch = getopt(argc, argv, "cr")) != -1) {
+        switch (ch) {
+            case 'c':
+                // -c is now a noop
+                break;
+            case 'r':
+                // force nops for recovery
+                healthd_mode_ops = &healthd_nops;
+                break;
+            case '?':
+            default:
+                KLOG_ERROR(LOG_TAG, "Unrecognized charger option: %c\n",
+                        optopt);
+                exit(1);
+        }
+    }
+
+    return healthd_main();
+}
diff --git a/healthd/healthd.cpp b/healthd/healthd.cpp
index aa6735d..ed1971a 100644
--- a/healthd/healthd.cpp
+++ b/healthd/healthd.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2013 The Android Open Source Project
+ * Copyright (C) 2016 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.
@@ -18,369 +18,106 @@
 #define KLOG_LEVEL 6
 
 #include <healthd/healthd.h>
-#include <healthd/BatteryMonitor.h>
 
-#include <errno.h>
-#include <libgen.h>
 #include <stdio.h>
 #include <stdlib.h>
-#include <string.h>
 #include <unistd.h>
-#include <batteryservice/BatteryService.h>
 #include <cutils/klog.h>
-#include <cutils/uevent.h>
-#include <sys/epoll.h>
-#include <sys/timerfd.h>
-#include <utils/Errors.h>
+
+#include <android/hardware/health/1.0/IHealth.h>
+#include <android/hardware/health/1.0/types.h>
+#include <hal_conversion.h>
 
 using namespace android;
 
-#ifndef BOARD_PERIODIC_CHORES_INTERVAL_FAST
-  // Periodic chores fast interval in seconds
-  #define DEFAULT_PERIODIC_CHORES_INTERVAL_FAST (60 * 1)
-#else
-  #define DEFAULT_PERIODIC_CHORES_INTERVAL_FAST (BOARD_PERIODIC_CHORES_INTERVAL_FAST)
-#endif
+using IHealth = ::android::hardware::health::V1_0::IHealth;
+using Result = ::android::hardware::health::V1_0::Result;
+using HealthConfig = ::android::hardware::health::V1_0::HealthConfig;
+using HealthInfo = ::android::hardware::health::V1_0::HealthInfo;
 
-#ifndef BOARD_PERIODIC_CHORES_INTERVAL_SLOW
-  // Periodic chores fast interval in seconds
-  #define DEFAULT_PERIODIC_CHORES_INTERVAL_SLOW (60 * 10)
-#else
-  #define DEFAULT_PERIODIC_CHORES_INTERVAL_SLOW (BOARD_PERIODIC_CHORES_INTERVAL_SLOW)
-#endif
+using ::android::hardware::health::V1_0::hal_conversion::convertToHealthConfig;
+using ::android::hardware::health::V1_0::hal_conversion::convertFromHealthConfig;
+using ::android::hardware::health::V1_0::hal_conversion::convertToHealthInfo;
+using ::android::hardware::health::V1_0::hal_conversion::convertFromHealthInfo;
 
-static struct healthd_config healthd_config = {
-    .periodic_chores_interval_fast = DEFAULT_PERIODIC_CHORES_INTERVAL_FAST,
-    .periodic_chores_interval_slow = DEFAULT_PERIODIC_CHORES_INTERVAL_SLOW,
-    .batteryStatusPath = String8(String8::kEmptyString),
-    .batteryHealthPath = String8(String8::kEmptyString),
-    .batteryPresentPath = String8(String8::kEmptyString),
-    .batteryCapacityPath = String8(String8::kEmptyString),
-    .batteryVoltagePath = String8(String8::kEmptyString),
-    .batteryTemperaturePath = String8(String8::kEmptyString),
-    .batteryTechnologyPath = String8(String8::kEmptyString),
-    .batteryCurrentNowPath = String8(String8::kEmptyString),
-    .batteryCurrentAvgPath = String8(String8::kEmptyString),
-    .batteryChargeCounterPath = String8(String8::kEmptyString),
-    .batteryFullChargePath = String8(String8::kEmptyString),
-    .batteryCycleCountPath = String8(String8::kEmptyString),
-    .energyCounter = NULL,
-    .boot_min_cap = 0,
-    .screen_on = NULL,
-};
+// device specific hal interface;
+static sp<IHealth> gHealth;
 
-static int eventct;
-static int epollfd;
-
-#define POWER_SUPPLY_SUBSYSTEM "power_supply"
-
-// epoll_create() parameter is actually unused
-#define MAX_EPOLL_EVENTS 40
-static int uevent_fd;
-static int wakealarm_fd;
-
-// -1 for no epoll timeout
-static int awake_poll_interval = -1;
-
-static int wakealarm_wake_interval = DEFAULT_PERIODIC_CHORES_INTERVAL_FAST;
-
-static BatteryMonitor* gBatteryMonitor;
-
-struct healthd_mode_ops *healthd_mode_ops;
+// main healthd loop
+extern int healthd_main(void);
 
 // Android mode
-
 extern void healthd_mode_android_init(struct healthd_config *config);
 extern int healthd_mode_android_preparetowait(void);
+extern void healthd_mode_android_heartbeat(void);
 extern void healthd_mode_android_battery_update(
     struct android::BatteryProperties *props);
 
-// Charger mode
-
-extern void healthd_mode_charger_init(struct healthd_config *config);
-extern int healthd_mode_charger_preparetowait(void);
-extern void healthd_mode_charger_heartbeat(void);
-extern void healthd_mode_charger_battery_update(
-    struct android::BatteryProperties *props);
-
-// NOPs for modes that need no special action
-
-static void healthd_mode_nop_init(struct healthd_config *config);
-static int healthd_mode_nop_preparetowait(void);
-static void healthd_mode_nop_heartbeat(void);
-static void healthd_mode_nop_battery_update(
-    struct android::BatteryProperties *props);
-
 static struct healthd_mode_ops android_ops = {
     .init = healthd_mode_android_init,
     .preparetowait = healthd_mode_android_preparetowait,
-    .heartbeat = healthd_mode_nop_heartbeat,
+    .heartbeat = healthd_mode_android_heartbeat,
     .battery_update = healthd_mode_android_battery_update,
 };
 
-static struct healthd_mode_ops charger_ops = {
-#ifdef CHARGER_NO_UI
-    .init = healthd_mode_nop_init,
-    .preparetowait = healthd_mode_nop_preparetowait,
-    .heartbeat = healthd_mode_nop_heartbeat,
-    .battery_update = healthd_mode_nop_battery_update,
-#else
-    .init = healthd_mode_charger_init,
-    .preparetowait = healthd_mode_charger_preparetowait,
-    .heartbeat = healthd_mode_charger_heartbeat,
-    .battery_update = healthd_mode_charger_battery_update,
-#endif
-};
+// default energy counter property redirect to talk to device
+// HAL
+static int healthd_board_get_energy_counter(int64_t *energy) {
 
-static struct healthd_mode_ops recovery_ops = {
-    .init = healthd_mode_nop_init,
-    .preparetowait = healthd_mode_nop_preparetowait,
-    .heartbeat = healthd_mode_nop_heartbeat,
-    .battery_update = healthd_mode_nop_battery_update,
-};
-
-static void healthd_mode_nop_init(struct healthd_config* /*config*/) {
-}
-
-static int healthd_mode_nop_preparetowait(void) {
-    return -1;
-}
-
-static void healthd_mode_nop_heartbeat(void) {
-}
-
-static void healthd_mode_nop_battery_update(
-    struct android::BatteryProperties* /*props*/) {
-}
-
-int healthd_register_event(int fd, void (*handler)(uint32_t), EventWakeup wakeup) {
-    struct epoll_event ev;
-
-    ev.events = EPOLLIN;
-
-    if (wakeup == EVENT_WAKEUP_FD)
-        ev.events |= EPOLLWAKEUP;
-
-    ev.data.ptr = (void *)handler;
-    if (epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &ev) == -1) {
-        KLOG_ERROR(LOG_TAG,
-                   "epoll_ctl failed; errno=%d\n", errno);
-        return -1;
+    if (gHealth == nullptr) {
+        return NAME_NOT_FOUND;
     }
 
-    eventct++;
-    return 0;
+    Result result = Result::NOT_SUPPORTED;
+    gHealth->energyCounter([=, &result] (Result ret, int64_t energyOut) {
+                result = ret;
+                *energy = energyOut;
+            });
+
+    return result == Result::SUCCESS ? OK : NAME_NOT_FOUND;
 }
 
-static void wakealarm_set_interval(int interval) {
-    struct itimerspec itval;
+void healthd_board_init(struct healthd_config *config) {
 
-    if (wakealarm_fd == -1)
-            return;
+    // Initialize the board HAL - Equivalent of healthd_board_init(config)
+    // in charger/recovery mode.
 
-    wakealarm_wake_interval = interval;
-
-    if (interval == -1)
-        interval = 0;
-
-    itval.it_interval.tv_sec = interval;
-    itval.it_interval.tv_nsec = 0;
-    itval.it_value.tv_sec = interval;
-    itval.it_value.tv_nsec = 0;
-
-    if (timerfd_settime(wakealarm_fd, 0, &itval, NULL) == -1)
-        KLOG_ERROR(LOG_TAG, "wakealarm_set_interval: timerfd_settime failed\n");
-}
-
-status_t healthd_get_property(int id, struct BatteryProperty *val) {
-    return gBatteryMonitor->getProperty(id, val);
-}
-
-void healthd_battery_update(void) {
-    // Fast wake interval when on charger (watch for overheat);
-    // slow wake interval when on battery (watch for drained battery).
-
-   int new_wake_interval = gBatteryMonitor->update() ?
-       healthd_config.periodic_chores_interval_fast :
-           healthd_config.periodic_chores_interval_slow;
-
-    if (new_wake_interval != wakealarm_wake_interval)
-            wakealarm_set_interval(new_wake_interval);
-
-    // During awake periods poll at fast rate.  If wake alarm is set at fast
-    // rate then just use the alarm; if wake alarm is set at slow rate then
-    // poll at fast rate while awake and let alarm wake up at slow rate when
-    // asleep.
-
-    if (healthd_config.periodic_chores_interval_fast == -1)
-        awake_poll_interval = -1;
-    else
-        awake_poll_interval =
-            new_wake_interval == healthd_config.periodic_chores_interval_fast ?
-                -1 : healthd_config.periodic_chores_interval_fast * 1000;
-}
-
-void healthd_dump_battery_state(int fd) {
-    gBatteryMonitor->dumpState(fd);
-    fsync(fd);
-}
-
-static void periodic_chores() {
-    healthd_battery_update();
-}
-
-#define UEVENT_MSG_LEN 2048
-static void uevent_event(uint32_t /*epevents*/) {
-    char msg[UEVENT_MSG_LEN+2];
-    char *cp;
-    int n;
-
-    n = uevent_kernel_multicast_recv(uevent_fd, msg, UEVENT_MSG_LEN);
-    if (n <= 0)
-        return;
-    if (n >= UEVENT_MSG_LEN)   /* overflow -- discard */
-        return;
-
-    msg[n] = '\0';
-    msg[n+1] = '\0';
-    cp = msg;
-
-    while (*cp) {
-        if (!strcmp(cp, "SUBSYSTEM=" POWER_SUPPLY_SUBSYSTEM)) {
-            healthd_battery_update();
-            break;
-        }
-
-        /* advance to after the next \0 */
-        while (*cp++)
-            ;
-    }
-}
-
-static void uevent_init(void) {
-    uevent_fd = uevent_open_socket(64*1024, true);
-
-    if (uevent_fd < 0) {
-        KLOG_ERROR(LOG_TAG, "uevent_init: uevent_open_socket failed\n");
+    gHealth = IHealth::getService();
+    if (gHealth == nullptr) {
+        KLOG_WARNING(LOG_TAG, "unable to get HAL interface, using defaults\n");
         return;
     }
 
-    fcntl(uevent_fd, F_SETFL, O_NONBLOCK);
-    if (healthd_register_event(uevent_fd, uevent_event, EVENT_WAKEUP_FD))
-        KLOG_ERROR(LOG_TAG,
-                   "register for uevent events failed\n");
+    HealthConfig halConfig;
+    convertToHealthConfig(config, halConfig);
+    gHealth->init(halConfig, [=] (const auto &halConfigOut) {
+            convertFromHealthConfig(halConfigOut, config);
+            // always redirect energy counter queries
+            config->energyCounter = healthd_board_get_energy_counter;
+            });
 }
 
-static void wakealarm_event(uint32_t /*epevents*/) {
-    unsigned long long wakeups;
+int healthd_board_battery_update(struct android::BatteryProperties *props) {
+    int logthis = 0;
 
-    if (read(wakealarm_fd, &wakeups, sizeof(wakeups)) == -1) {
-        KLOG_ERROR(LOG_TAG, "wakealarm_event: read wakealarm fd failed\n");
-        return;
+    if (gHealth == nullptr) {
+        return logthis;
     }
 
-    periodic_chores();
+    HealthInfo info;
+    convertToHealthInfo(props, info);
+    gHealth->update(info,
+            [=, &logthis] (int32_t ret, const auto &infoOut) {
+                logthis = ret;
+                convertFromHealthInfo(infoOut, props);
+            });
+
+    return logthis;
 }
 
-static void wakealarm_init(void) {
-    wakealarm_fd = timerfd_create(CLOCK_BOOTTIME_ALARM, TFD_NONBLOCK);
-    if (wakealarm_fd == -1) {
-        KLOG_ERROR(LOG_TAG, "wakealarm_init: timerfd_create failed\n");
-        return;
-    }
+int main(int /*argc*/, char ** /*argv*/) {
 
-    if (healthd_register_event(wakealarm_fd, wakealarm_event, EVENT_WAKEUP_FD))
-        KLOG_ERROR(LOG_TAG,
-                   "Registration of wakealarm event failed\n");
-
-    wakealarm_set_interval(healthd_config.periodic_chores_interval_fast);
-}
-
-static void healthd_mainloop(void) {
-    int nevents = 0;
-    while (1) {
-        struct epoll_event events[eventct];
-        int timeout = awake_poll_interval;
-        int mode_timeout;
-
-        /* Don't wait for first timer timeout to run periodic chores */
-        if (!nevents)
-            periodic_chores();
-
-        healthd_mode_ops->heartbeat();
-
-        mode_timeout = healthd_mode_ops->preparetowait();
-        if (timeout < 0 || (mode_timeout > 0 && mode_timeout < timeout))
-            timeout = mode_timeout;
-        nevents = epoll_wait(epollfd, events, eventct, timeout);
-        if (nevents == -1) {
-            if (errno == EINTR)
-                continue;
-            KLOG_ERROR(LOG_TAG, "healthd_mainloop: epoll_wait failed\n");
-            break;
-        }
-
-        for (int n = 0; n < nevents; ++n) {
-            if (events[n].data.ptr)
-                (*(void (*)(int))events[n].data.ptr)(events[n].events);
-        }
-    }
-
-    return;
-}
-
-static int healthd_init() {
-    epollfd = epoll_create(MAX_EPOLL_EVENTS);
-    if (epollfd == -1) {
-        KLOG_ERROR(LOG_TAG,
-                   "epoll_create failed; errno=%d\n",
-                   errno);
-        return -1;
-    }
-
-    healthd_board_init(&healthd_config);
-    healthd_mode_ops->init(&healthd_config);
-    wakealarm_init();
-    uevent_init();
-    gBatteryMonitor = new BatteryMonitor();
-    gBatteryMonitor->init(&healthd_config);
-    return 0;
-}
-
-int main(int argc, char **argv) {
-    int ch;
-    int ret;
-
-    klog_set_level(KLOG_LEVEL);
     healthd_mode_ops = &android_ops;
 
-    if (!strcmp(basename(argv[0]), "charger")) {
-        healthd_mode_ops = &charger_ops;
-    } else {
-        while ((ch = getopt(argc, argv, "cr")) != -1) {
-            switch (ch) {
-            case 'c':
-                healthd_mode_ops = &charger_ops;
-                break;
-            case 'r':
-                healthd_mode_ops = &recovery_ops;
-                break;
-            case '?':
-            default:
-                KLOG_ERROR(LOG_TAG, "Unrecognized healthd option: %c\n",
-                           optopt);
-                exit(1);
-            }
-        }
-    }
-
-    ret = healthd_init();
-    if (ret) {
-        KLOG_ERROR("Initialization failed, exiting\n");
-        exit(2);
-    }
-
-    healthd_mainloop();
-    KLOG_ERROR("Main loop terminated, exiting\n");
-    return 3;
+    return healthd_main();
 }
diff --git a/healthd/healthd_board_default.cpp b/healthd/healthd_board_default.cpp
deleted file mode 100644
index eb55773..0000000
--- a/healthd/healthd_board_default.cpp
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Copyright (C) 2013 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 <healthd/healthd.h>
-
-void healthd_board_init(struct healthd_config*)
-{
-    // use defaults
-}
-
-
-int healthd_board_battery_update(struct android::BatteryProperties*)
-{
-    // return 0 to log periodic polled battery status to kernel log
-    return 0;
-}
diff --git a/healthd/healthd_common.cpp b/healthd/healthd_common.cpp
new file mode 100644
index 0000000..6599919
--- /dev/null
+++ b/healthd/healthd_common.cpp
@@ -0,0 +1,304 @@
+/*
+ * Copyright (C) 2013 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.
+ */
+
+#define LOG_TAG "healthd-common"
+#define KLOG_LEVEL 6
+
+#include <healthd/healthd.h>
+#include <healthd/BatteryMonitor.h>
+
+#include <errno.h>
+#include <libgen.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <batteryservice/BatteryService.h>
+#include <cutils/klog.h>
+#include <cutils/uevent.h>
+#include <sys/epoll.h>
+#include <sys/timerfd.h>
+#include <utils/Errors.h>
+
+using namespace android;
+
+#ifndef BOARD_PERIODIC_CHORES_INTERVAL_FAST
+  // Periodic chores fast interval in seconds
+  #define DEFAULT_PERIODIC_CHORES_INTERVAL_FAST (60 * 1)
+#else
+  #define DEFAULT_PERIODIC_CHORES_INTERVAL_FAST (BOARD_PERIODIC_CHORES_INTERVAL_FAST)
+#endif
+
+#ifndef BOARD_PERIODIC_CHORES_INTERVAL_SLOW
+  // Periodic chores fast interval in seconds
+  #define DEFAULT_PERIODIC_CHORES_INTERVAL_SLOW (60 * 10)
+#else
+  #define DEFAULT_PERIODIC_CHORES_INTERVAL_SLOW (BOARD_PERIODIC_CHORES_INTERVAL_SLOW)
+#endif
+
+static struct healthd_config healthd_config = {
+    .periodic_chores_interval_fast = DEFAULT_PERIODIC_CHORES_INTERVAL_FAST,
+    .periodic_chores_interval_slow = DEFAULT_PERIODIC_CHORES_INTERVAL_SLOW,
+    .batteryStatusPath = String8(String8::kEmptyString),
+    .batteryHealthPath = String8(String8::kEmptyString),
+    .batteryPresentPath = String8(String8::kEmptyString),
+    .batteryCapacityPath = String8(String8::kEmptyString),
+    .batteryVoltagePath = String8(String8::kEmptyString),
+    .batteryTemperaturePath = String8(String8::kEmptyString),
+    .batteryTechnologyPath = String8(String8::kEmptyString),
+    .batteryCurrentNowPath = String8(String8::kEmptyString),
+    .batteryCurrentAvgPath = String8(String8::kEmptyString),
+    .batteryChargeCounterPath = String8(String8::kEmptyString),
+    .batteryFullChargePath = String8(String8::kEmptyString),
+    .batteryCycleCountPath = String8(String8::kEmptyString),
+    .energyCounter = NULL,
+    .boot_min_cap = 0,
+    .screen_on = NULL,
+};
+
+static int eventct;
+static int epollfd;
+
+#define POWER_SUPPLY_SUBSYSTEM "power_supply"
+
+// epoll_create() parameter is actually unused
+#define MAX_EPOLL_EVENTS 40
+static int uevent_fd;
+static int wakealarm_fd;
+
+// -1 for no epoll timeout
+static int awake_poll_interval = -1;
+
+static int wakealarm_wake_interval = DEFAULT_PERIODIC_CHORES_INTERVAL_FAST;
+
+static BatteryMonitor* gBatteryMonitor;
+
+struct healthd_mode_ops *healthd_mode_ops;
+
+int healthd_register_event(int fd, void (*handler)(uint32_t), EventWakeup wakeup) {
+    struct epoll_event ev;
+
+    ev.events = EPOLLIN;
+
+    if (wakeup == EVENT_WAKEUP_FD)
+        ev.events |= EPOLLWAKEUP;
+
+    ev.data.ptr = (void *)handler;
+    if (epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &ev) == -1) {
+        KLOG_ERROR(LOG_TAG,
+                   "epoll_ctl failed; errno=%d\n", errno);
+        return -1;
+    }
+
+    eventct++;
+    return 0;
+}
+
+static void wakealarm_set_interval(int interval) {
+    struct itimerspec itval;
+
+    if (wakealarm_fd == -1)
+            return;
+
+    wakealarm_wake_interval = interval;
+
+    if (interval == -1)
+        interval = 0;
+
+    itval.it_interval.tv_sec = interval;
+    itval.it_interval.tv_nsec = 0;
+    itval.it_value.tv_sec = interval;
+    itval.it_value.tv_nsec = 0;
+
+    if (timerfd_settime(wakealarm_fd, 0, &itval, NULL) == -1)
+        KLOG_ERROR(LOG_TAG, "wakealarm_set_interval: timerfd_settime failed\n");
+}
+
+status_t healthd_get_property(int id, struct BatteryProperty *val) {
+    return gBatteryMonitor->getProperty(id, val);
+}
+
+void healthd_battery_update(void) {
+    // Fast wake interval when on charger (watch for overheat);
+    // slow wake interval when on battery (watch for drained battery).
+
+   int new_wake_interval = gBatteryMonitor->update() ?
+       healthd_config.periodic_chores_interval_fast :
+           healthd_config.periodic_chores_interval_slow;
+
+    if (new_wake_interval != wakealarm_wake_interval)
+            wakealarm_set_interval(new_wake_interval);
+
+    // During awake periods poll at fast rate.  If wake alarm is set at fast
+    // rate then just use the alarm; if wake alarm is set at slow rate then
+    // poll at fast rate while awake and let alarm wake up at slow rate when
+    // asleep.
+
+    if (healthd_config.periodic_chores_interval_fast == -1)
+        awake_poll_interval = -1;
+    else
+        awake_poll_interval =
+            new_wake_interval == healthd_config.periodic_chores_interval_fast ?
+                -1 : healthd_config.periodic_chores_interval_fast * 1000;
+}
+
+void healthd_dump_battery_state(int fd) {
+    gBatteryMonitor->dumpState(fd);
+    fsync(fd);
+}
+
+static void periodic_chores() {
+    healthd_battery_update();
+}
+
+#define UEVENT_MSG_LEN 2048
+static void uevent_event(uint32_t /*epevents*/) {
+    char msg[UEVENT_MSG_LEN+2];
+    char *cp;
+    int n;
+
+    n = uevent_kernel_multicast_recv(uevent_fd, msg, UEVENT_MSG_LEN);
+    if (n <= 0)
+        return;
+    if (n >= UEVENT_MSG_LEN)   /* overflow -- discard */
+        return;
+
+    msg[n] = '\0';
+    msg[n+1] = '\0';
+    cp = msg;
+
+    while (*cp) {
+        if (!strcmp(cp, "SUBSYSTEM=" POWER_SUPPLY_SUBSYSTEM)) {
+            healthd_battery_update();
+            break;
+        }
+
+        /* advance to after the next \0 */
+        while (*cp++)
+            ;
+    }
+}
+
+static void uevent_init(void) {
+    uevent_fd = uevent_open_socket(64*1024, true);
+
+    if (uevent_fd < 0) {
+        KLOG_ERROR(LOG_TAG, "uevent_init: uevent_open_socket failed\n");
+        return;
+    }
+
+    fcntl(uevent_fd, F_SETFL, O_NONBLOCK);
+    if (healthd_register_event(uevent_fd, uevent_event, EVENT_WAKEUP_FD))
+        KLOG_ERROR(LOG_TAG,
+                   "register for uevent events failed\n");
+}
+
+static void wakealarm_event(uint32_t /*epevents*/) {
+    unsigned long long wakeups;
+
+    if (read(wakealarm_fd, &wakeups, sizeof(wakeups)) == -1) {
+        KLOG_ERROR(LOG_TAG, "wakealarm_event: read wakealarm fd failed\n");
+        return;
+    }
+
+    periodic_chores();
+}
+
+static void wakealarm_init(void) {
+    wakealarm_fd = timerfd_create(CLOCK_BOOTTIME_ALARM, TFD_NONBLOCK);
+    if (wakealarm_fd == -1) {
+        KLOG_ERROR(LOG_TAG, "wakealarm_init: timerfd_create failed\n");
+        return;
+    }
+
+    if (healthd_register_event(wakealarm_fd, wakealarm_event, EVENT_WAKEUP_FD))
+        KLOG_ERROR(LOG_TAG,
+                   "Registration of wakealarm event failed\n");
+
+    wakealarm_set_interval(healthd_config.periodic_chores_interval_fast);
+}
+
+static void healthd_mainloop(void) {
+    int nevents = 0;
+    while (1) {
+        struct epoll_event events[eventct];
+        int timeout = awake_poll_interval;
+        int mode_timeout;
+
+        /* Don't wait for first timer timeout to run periodic chores */
+        if (!nevents)
+            periodic_chores();
+
+        healthd_mode_ops->heartbeat();
+
+        mode_timeout = healthd_mode_ops->preparetowait();
+        if (timeout < 0 || (mode_timeout > 0 && mode_timeout < timeout))
+            timeout = mode_timeout;
+        nevents = epoll_wait(epollfd, events, eventct, timeout);
+        if (nevents == -1) {
+            if (errno == EINTR)
+                continue;
+            KLOG_ERROR(LOG_TAG, "healthd_mainloop: epoll_wait failed\n");
+            break;
+        }
+
+        for (int n = 0; n < nevents; ++n) {
+            if (events[n].data.ptr)
+                (*(void (*)(int))events[n].data.ptr)(events[n].events);
+        }
+    }
+
+    return;
+}
+
+static int healthd_init() {
+    epollfd = epoll_create(MAX_EPOLL_EVENTS);
+    if (epollfd == -1) {
+        KLOG_ERROR(LOG_TAG,
+                   "epoll_create failed; errno=%d\n",
+                   errno);
+        return -1;
+    }
+
+    healthd_board_init(&healthd_config);
+    healthd_mode_ops->init(&healthd_config);
+    wakealarm_init();
+    uevent_init();
+    gBatteryMonitor = new BatteryMonitor();
+    gBatteryMonitor->init(&healthd_config);
+    return 0;
+}
+
+int healthd_main() {
+    int ret;
+
+    klog_set_level(KLOG_LEVEL);
+
+    if (!healthd_mode_ops) {
+        KLOG_ERROR("healthd ops not set, exiting\n");
+        exit(1);
+    }
+
+    ret = healthd_init();
+    if (ret) {
+        KLOG_ERROR("Initialization failed, exiting\n");
+        exit(2);
+    }
+
+    healthd_mainloop();
+    KLOG_ERROR("Main loop terminated, exiting\n");
+    return 3;
+}
diff --git a/healthd/healthd_draw.cpp b/healthd/healthd_draw.cpp
new file mode 100644
index 0000000..30f2cf3
--- /dev/null
+++ b/healthd/healthd_draw.cpp
@@ -0,0 +1,217 @@
+/*
+ * Copyright (C) 2017 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/stringprintf.h>
+#include <batteryservice/BatteryService.h>
+#include <cutils/klog.h>
+
+#include "healthd_draw.h"
+
+#define LOGE(x...) KLOG_ERROR("charger", x);
+#define LOGW(x...) KLOG_WARNING("charger", x);
+#define LOGV(x...) KLOG_DEBUG("charger", x);
+
+HealthdDraw::HealthdDraw(animation* anim)
+  : kSplitScreen(HEALTHD_DRAW_SPLIT_SCREEN),
+    kSplitOffset(HEALTHD_DRAW_SPLIT_OFFSET) {
+    int ret = gr_init();
+
+    if (ret < 0) {
+        LOGE("gr_init failed\n");
+        graphics_available = false;
+        return;
+    }
+
+    graphics_available = true;
+    sys_font = gr_sys_font();
+    if (sys_font == nullptr) {
+        LOGW("No system font, screen fallback text not available\n");
+    } else {
+        gr_font_size(sys_font, &char_width_, &char_height_);
+    }
+
+    screen_width_ = gr_fb_width() / (kSplitScreen ? 2 : 1);
+    screen_height_ = gr_fb_height();
+
+    int res;
+    if (!anim->text_clock.font_file.empty() &&
+        (res = gr_init_font(anim->text_clock.font_file.c_str(), &anim->text_clock.font)) < 0) {
+        LOGE("Could not load time font (%d)\n", res);
+    }
+    if (!anim->text_percent.font_file.empty() &&
+        (res = gr_init_font(anim->text_percent.font_file.c_str(), &anim->text_percent.font)) < 0) {
+        LOGE("Could not load percent font (%d)\n", res);
+    }
+}
+
+HealthdDraw::~HealthdDraw() {}
+
+void HealthdDraw::redraw_screen(const animation* batt_anim, GRSurface* surf_unknown) {
+    if (!graphics_available) return;
+    clear_screen();
+
+    /* try to display *something* */
+    if (batt_anim->cur_level < 0 || batt_anim->num_frames == 0)
+        draw_unknown(surf_unknown);
+    else
+        draw_battery(batt_anim);
+    gr_flip();
+}
+
+void HealthdDraw::blank_screen(bool blank) {
+    if (!graphics_available) return;
+    gr_fb_blank(blank);
+}
+
+void HealthdDraw::clear_screen(void) {
+    if (!graphics_available) return;
+    gr_color(0, 0, 0, 255);
+    gr_clear();
+}
+
+int HealthdDraw::draw_surface_centered(GRSurface* surface) {
+    if (!graphics_available) return 0;
+
+    int w = gr_get_width(surface);
+    int h = gr_get_height(surface);
+    int x = (screen_width_ - w) / 2 + kSplitOffset;
+    int y = (screen_height_ - h) / 2;
+
+    LOGV("drawing surface %dx%d+%d+%d\n", w, h, x, y);
+    gr_blit(surface, 0, 0, w, h, x, y);
+    if (kSplitScreen) {
+        x += screen_width_ - 2 * kSplitOffset;
+        LOGV("drawing surface %dx%d+%d+%d\n", w, h, x, y);
+        gr_blit(surface, 0, 0, w, h, x, y);
+    }
+
+    return y + h;
+}
+
+int HealthdDraw::draw_text(const GRFont* font, int x, int y, const char* str) {
+    if (!graphics_available) return 0;
+    int str_len_px = gr_measure(font, str);
+
+    if (x < 0) x = (screen_width_ - str_len_px) / 2;
+    if (y < 0) y = (screen_height_ - char_height_) / 2;
+    gr_text(font, x + kSplitOffset, y, str, false /* bold */);
+    if (kSplitScreen) gr_text(font, x - kSplitOffset + screen_width_, y, str, false /* bold */);
+
+    return y + char_height_;
+}
+
+void HealthdDraw::determine_xy(const animation::text_field& field,
+                               const int length, int* x, int* y) {
+  *x = field.pos_x;
+
+  int str_len_px = length * field.font->char_width;
+  if (field.pos_x == CENTER_VAL) {
+    *x = (screen_width_ - str_len_px) / 2;
+  } else if (field.pos_x >= 0) {
+    *x = field.pos_x;
+  } else {  // position from max edge
+    *x = screen_width_ + field.pos_x - str_len_px - kSplitOffset;
+  }
+
+  *y = field.pos_y;
+
+  if (field.pos_y == CENTER_VAL) {
+    *y = (screen_height_ - field.font->char_height) / 2;
+  } else if (field.pos_y >= 0) {
+    *y = field.pos_y;
+  } else {  // position from max edge
+    *y = screen_height_ + field.pos_y - field.font->char_height;
+  }
+}
+
+void HealthdDraw::draw_clock(const animation* anim) {
+    static constexpr char CLOCK_FORMAT[] = "%H:%M";
+    static constexpr int CLOCK_LENGTH = 6;
+
+    const animation::text_field& field = anim->text_clock;
+
+    if (!graphics_available || field.font == nullptr || field.font->char_width == 0 ||
+        field.font->char_height == 0)
+        return;
+
+    time_t rawtime;
+    time(&rawtime);
+    tm* time_info = localtime(&rawtime);
+
+    char clock_str[CLOCK_LENGTH];
+    size_t length = strftime(clock_str, CLOCK_LENGTH, CLOCK_FORMAT, time_info);
+    if (length != CLOCK_LENGTH - 1) {
+        LOGE("Could not format time\n");
+        return;
+    }
+
+    int x, y;
+    determine_xy(field, length, &x, &y);
+
+    LOGV("drawing clock %s %d %d\n", clock_str, x, y);
+    gr_color(field.color_r, field.color_g, field.color_b, field.color_a);
+    draw_text(field.font, x, y, clock_str);
+}
+
+void HealthdDraw::draw_percent(const animation* anim) {
+    if (!graphics_available) return;
+    int cur_level = anim->cur_level;
+    if (anim->cur_status == BATTERY_STATUS_FULL) {
+        cur_level = 100;
+    }
+
+    if (cur_level <= 0) return;
+
+    const animation::text_field& field = anim->text_percent;
+    if (field.font == nullptr || field.font->char_width == 0 || field.font->char_height == 0) {
+        return;
+    }
+
+    std::string str = base::StringPrintf("%d%%", cur_level);
+
+    int x, y;
+    determine_xy(field, str.size(), &x, &y);
+
+    LOGV("drawing percent %s %d %d\n", str.c_str(), x, y);
+    gr_color(field.color_r, field.color_g, field.color_b, field.color_a);
+    draw_text(field.font, x, y, str.c_str());
+}
+
+void HealthdDraw::draw_battery(const animation* anim) {
+    if (!graphics_available) return;
+    const animation::frame& frame = anim->frames[anim->cur_frame];
+
+    if (anim->num_frames != 0) {
+        draw_surface_centered(frame.surface);
+        LOGV("drawing frame #%d min_cap=%d time=%d\n", anim->cur_frame, frame.min_level,
+             frame.disp_time);
+    }
+    draw_clock(anim);
+    draw_percent(anim);
+}
+
+void HealthdDraw::draw_unknown(GRSurface* surf_unknown) {
+  int y;
+  if (surf_unknown) {
+      draw_surface_centered(surf_unknown);
+  } else if (sys_font) {
+      gr_color(0xa4, 0xc6, 0x39, 255);
+      y = draw_text(sys_font, -1, -1, "Charging!");
+      draw_text(sys_font, -1, y + 25, "?\?/100");
+  } else {
+      LOGW("Charging, level unknown\n");
+  }
+}
diff --git a/healthd/healthd_draw.h b/healthd/healthd_draw.h
new file mode 100644
index 0000000..7c847bd
--- /dev/null
+++ b/healthd/healthd_draw.h
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2017 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 HEALTHD_DRAW_H
+#define HEALTHD_DRAW_H
+
+#include <linux/input.h>
+#include <minui/minui.h>
+
+#include "animation.h"
+
+using namespace android;
+
+class HealthdDraw {
+ public:
+  // Configures font using given animation.
+  HealthdDraw(animation* anim);
+  virtual ~HealthdDraw();
+
+  // Redraws screen.
+  void redraw_screen(const animation* batt_anim, GRSurface* surf_unknown);
+
+  // Blanks screen if true, unblanks if false.
+  virtual void blank_screen(bool blank);
+
+ protected:
+  virtual void clear_screen();
+
+  // returns the last y-offset of where the surface ends.
+  virtual int draw_surface_centered(GRSurface* surface);
+  // Negative x or y coordinates center text.
+  virtual int draw_text(const GRFont* font, int x, int y, const char* str);
+
+  // Negative x or y coordinates position the text away from the opposite edge
+  // that positive ones do.
+  virtual void determine_xy(const animation::text_field& field,
+                            const int length, int* x, int* y);
+
+  // Draws battery animation, if it exists.
+  virtual void draw_battery(const animation* anim);
+  // Draws clock text, if animation contains text_field data.
+  virtual void draw_clock(const animation* anim);
+  // Draws battery percentage text if animation contains text_field data.
+  virtual void draw_percent(const animation* anim);
+  // Draws charger->surf_unknown or basic text.
+  virtual void draw_unknown(GRSurface* surf_unknown);
+
+  // Pixel sizes of characters for default font.
+  int char_width_;
+  int char_height_;
+
+  // Width and height of screen in pixels.
+  int screen_width_;
+  int screen_height_;
+
+  // Device screen is split vertically.
+  const bool kSplitScreen;
+  // Pixels to offset graphics towards center split.
+  const int kSplitOffset;
+
+  // system text font, may be nullptr
+  const GRFont* sys_font;
+
+  // true if minui init'ed OK, false if minui init failed
+  bool graphics_available;
+};
+
+#endif  // HEALTHD_DRAW_H
diff --git a/healthd/healthd_mode_android.cpp b/healthd/healthd_mode_android.cpp
index 323ef52..c612313 100644
--- a/healthd/healthd_mode_android.cpp
+++ b/healthd/healthd_mode_android.cpp
@@ -42,6 +42,9 @@
     return -1;
 }
 
+void healthd_mode_android_heartbeat(void) {
+}
+
 static void binder_event(uint32_t /*epevents*/) {
     IPCThreadState::self()->handlePolledCommands();
 }
diff --git a/healthd/healthd_mode_charger.cpp b/healthd/healthd_mode_charger.cpp
index d7d1454..f1fe5cd 100644
--- a/healthd/healthd_mode_charger.cpp
+++ b/healthd/healthd_mode_charger.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2011-2013 The Android Open Source Project
+ * Copyright (C) 2011-2017 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.
@@ -18,8 +18,6 @@
 #include <errno.h>
 #include <fcntl.h>
 #include <inttypes.h>
-#include <linux/input.h>
-#include <stdbool.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -34,56 +32,54 @@
 
 #include <android-base/file.h>
 #include <android-base/macros.h>
-#include <android-base/stringprintf.h>
 
-#include <sys/socket.h>
 #include <linux/netlink.h>
+#include <sys/socket.h>
 
-#include <batteryservice/BatteryService.h>
-#include <cutils/android_reboot.h>
 #include <cutils/klog.h>
 #include <cutils/misc.h>
-#include <cutils/uevent.h>
 #include <cutils/properties.h>
-#include <minui/minui.h>
+#include <cutils/uevent.h>
+#include <sys/reboot.h>
 
 #ifdef CHARGER_ENABLE_SUSPEND
 #include <suspend/autosuspend.h>
 #endif
 
-#include "animation.h"
 #include "AnimationParser.h"
+#include "healthd_draw.h"
 
 #include <healthd/healthd.h>
 
 using namespace android;
 
-char *locale;
+char* locale;
 
 #ifndef max
-#define max(a,b) ((a) > (b) ? (a) : (b))
+#define max(a, b) ((a) > (b) ? (a) : (b))
 #endif
 
 #ifndef min
-#define min(a,b) ((a) < (b) ? (a) : (b))
+#define min(a, b) ((a) < (b) ? (a) : (b))
 #endif
 
-#define ARRAY_SIZE(x)           (sizeof(x)/sizeof((x)[0]))
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
 
-#define MSEC_PER_SEC            (1000LL)
-#define NSEC_PER_MSEC           (1000000LL)
+#define MSEC_PER_SEC (1000LL)
+#define NSEC_PER_MSEC (1000000LL)
 
-#define BATTERY_UNKNOWN_TIME    (2 * MSEC_PER_SEC)
-#define POWER_ON_KEY_TIME       (2 * MSEC_PER_SEC)
+#define BATTERY_UNKNOWN_TIME (2 * MSEC_PER_SEC)
+#define POWER_ON_KEY_TIME (2 * MSEC_PER_SEC)
 #define UNPLUGGED_SHUTDOWN_TIME (10 * MSEC_PER_SEC)
 
-#define LAST_KMSG_MAX_SZ        (32 * 1024)
+#define LAST_KMSG_MAX_SZ (32 * 1024)
 
-#define LOGE(x...) do { KLOG_ERROR("charger", x); } while (0)
-#define LOGW(x...) do { KLOG_WARNING("charger", x); } while (0)
-#define LOGV(x...) do { KLOG_DEBUG("charger", x); } while (0)
+#define LOGE(x...) KLOG_ERROR("charger", x);
+#define LOGW(x...) KLOG_WARNING("charger", x);
+#define LOGV(x...) KLOG_DEBUG("charger", x);
 
-static constexpr const char* animation_desc_path = "/res/values/charger/animation.txt";
+static constexpr const char* animation_desc_path =
+    "/res/values/charger/animation.txt";
 
 struct key_state {
     bool pending;
@@ -98,34 +94,36 @@
     int64_t next_key_check;
     int64_t next_pwr_check;
 
-    struct key_state keys[KEY_MAX + 1];
+    key_state keys[KEY_MAX + 1];
 
-    struct animation *batt_anim;
+    animation* batt_anim;
     GRSurface* surf_unknown;
     int boot_min_cap;
 };
 
-static const struct animation BASE_ANIMATION = {
-    .text_clock = {
-        .pos_x = 0,
-        .pos_y = 0,
+static const animation BASE_ANIMATION = {
+    .text_clock =
+        {
+            .pos_x = 0,
+            .pos_y = 0,
 
-        .color_r = 255,
-        .color_g = 255,
-        .color_b = 255,
-        .color_a = 255,
+            .color_r = 255,
+            .color_g = 255,
+            .color_b = 255,
+            .color_a = 255,
 
-        .font = nullptr,
-    },
-    .text_percent = {
-        .pos_x = 0,
-        .pos_y = 0,
+            .font = nullptr,
+        },
+    .text_percent =
+        {
+            .pos_x = 0,
+            .pos_y = 0,
 
-        .color_r = 255,
-        .color_g = 255,
-        .color_b = 255,
-        .color_a = 255,
-    },
+            .color_r = 255,
+            .color_g = 255,
+            .color_b = 255,
+            .color_a = 255,
+        },
 
     .run = false,
 
@@ -141,8 +139,7 @@
     .cur_status = BATTERY_STATUS_UNKNOWN,
 };
 
-
-static struct animation::frame default_animation_frames[] = {
+static animation::frame default_animation_frames[] = {
     {
         .disp_time = 750,
         .min_level = 0,
@@ -181,35 +178,25 @@
     },
 };
 
-static struct animation battery_animation = BASE_ANIMATION;
+static animation battery_animation = BASE_ANIMATION;
 
-static struct charger charger_state;
-static struct healthd_config *healthd_config;
-static struct android::BatteryProperties *batt_prop;
-static int char_width;
-static int char_height;
-static bool minui_inited;
+static charger charger_state;
+static healthd_config* healthd_config;
+static android::BatteryProperties* batt_prop;
+static std::unique_ptr<HealthdDraw> healthd_draw;
 
 /* current time in milliseconds */
-static int64_t curr_time_ms(void)
-{
-    struct timespec tm;
+static int64_t curr_time_ms() {
+    timespec tm;
     clock_gettime(CLOCK_MONOTONIC, &tm);
     return tm.tv_sec * MSEC_PER_SEC + (tm.tv_nsec / NSEC_PER_MSEC);
 }
 
-static void clear_screen(void)
-{
-    gr_color(0, 0, 0, 255);
-    gr_clear();
-}
-
 #define MAX_KLOG_WRITE_BUF_SZ 256
 
-static void dump_last_kmsg(void)
-{
-    char *buf;
-    char *ptr;
+static void dump_last_kmsg(void) {
+    char* buf;
+    char* ptr;
     unsigned sz = 0;
     int len;
 
@@ -239,11 +226,10 @@
     while (len > 0) {
         int cnt = min(len, MAX_KLOG_WRITE_BUF_SZ);
         char yoink;
-        char *nl;
+        char* nl;
 
-        nl = (char *)memrchr(ptr, '\n', cnt - 1);
-        if (nl)
-            cnt = nl - ptr + 1;
+        nl = (char*)memrchr(ptr, '\n', cnt - 1);
+        if (nl) cnt = nl - ptr + 1;
 
         yoink = ptr[cnt];
         ptr[cnt] = '\0';
@@ -263,249 +249,69 @@
 }
 
 #ifdef CHARGER_ENABLE_SUSPEND
-static int request_suspend(bool enable)
-{
+static int request_suspend(bool enable) {
     if (enable)
         return autosuspend_enable();
     else
         return autosuspend_disable();
 }
 #else
-static int request_suspend(bool /*enable*/)
-{
+static int request_suspend(bool /*enable*/) {
     return 0;
 }
 #endif
 
-static int draw_text(const char *str, int x, int y)
-{
-    int str_len_px = gr_measure(gr_sys_font(), str);
-
-    if (x < 0)
-        x = (gr_fb_width() - str_len_px) / 2;
-    if (y < 0)
-        y = (gr_fb_height() - char_height) / 2;
-    gr_text(gr_sys_font(), x, y, str, 0);
-
-    return y + char_height;
-}
-
-static void android_green(void)
-{
-    gr_color(0xa4, 0xc6, 0x39, 255);
-}
-
-// Negative x or y coordinates position the text away from the opposite edge that positive ones do.
-void determine_xy(const animation::text_field& field, const int length, int* x, int* y)
-{
-    *x = field.pos_x;
-    *y = field.pos_y;
-
-    int str_len_px = length * field.font->char_width;
-    if (field.pos_x == CENTER_VAL) {
-        *x = (gr_fb_width() - str_len_px) / 2;
-    } else if (field.pos_x >= 0) {
-        *x = field.pos_x;
-    } else {  // position from max edge
-        *x = gr_fb_width() + field.pos_x - str_len_px;
-    }
-
-    if (field.pos_y == CENTER_VAL) {
-        *y = (gr_fb_height() - field.font->char_height) / 2;
-    } else if (field.pos_y >= 0) {
-        *y = field.pos_y;
-    } else {  // position from max edge
-        *y = gr_fb_height() + field.pos_y - field.font->char_height;
-    }
-}
-
-static void draw_clock(const animation& anim)
-{
-    static constexpr char CLOCK_FORMAT[] = "%H:%M";
-    static constexpr int CLOCK_LENGTH = 6;
-
-    const animation::text_field& field = anim.text_clock;
-
-    if (field.font == nullptr || field.font->char_width == 0 || field.font->char_height == 0) return;
-
-    time_t rawtime;
-    time(&rawtime);
-    struct tm* time_info = localtime(&rawtime);
-
-    char clock_str[CLOCK_LENGTH];
-    size_t length = strftime(clock_str, CLOCK_LENGTH, CLOCK_FORMAT, time_info);
-    if (length != CLOCK_LENGTH - 1) {
-        LOGE("Could not format time\n");
-        return;
-    }
-
-    int x, y;
-    determine_xy(field, length, &x, &y);
-
-    LOGV("drawing clock %s %d %d\n", clock_str, x, y);
-    gr_color(field.color_r, field.color_g, field.color_b, field.color_a);
-    gr_text(field.font, x, y, clock_str, false);
-}
-
-static void draw_percent(const animation& anim)
-{
-    int cur_level = anim.cur_level;
-    if (anim.cur_status == BATTERY_STATUS_FULL) {
-        cur_level = 100;
-    }
-
-    if (cur_level <= 0) return;
-
-    const animation::text_field& field = anim.text_percent;
-    if (field.font == nullptr || field.font->char_width == 0 || field.font->char_height == 0) {
-        return;
-    }
-
-    std::string str = base::StringPrintf("%d%%", cur_level);
-
-    int x, y;
-    determine_xy(field, str.size(), &x, &y);
-
-    LOGV("drawing percent %s %d %d\n", str.c_str(), x, y);
-    gr_color(field.color_r, field.color_g, field.color_b, field.color_a);
-    gr_text(field.font, x, y, str.c_str(), false);
-}
-
-/* returns the last y-offset of where the surface ends */
-static int draw_surface_centered(GRSurface* surface)
-{
-    int w;
-    int h;
-    int x;
-    int y;
-
-    w = gr_get_width(surface);
-    h = gr_get_height(surface);
-    x = (gr_fb_width() - w) / 2 ;
-    y = (gr_fb_height() - h) / 2 ;
-
-    LOGV("drawing surface %dx%d+%d+%d\n", w, h, x, y);
-    gr_blit(surface, 0, 0, w, h, x, y);
-    return y + h;
-}
-
-static void draw_unknown(struct charger *charger)
-{
-    int y;
-    if (charger->surf_unknown) {
-        draw_surface_centered(charger->surf_unknown);
-    } else {
-        android_green();
-        y = draw_text("Charging!", -1, -1);
-        draw_text("?\?/100", -1, y + 25);
-    }
-}
-
-static void draw_battery(const struct charger* charger)
-{
-    const struct animation& anim = *charger->batt_anim;
-    const struct animation::frame& frame = anim.frames[anim.cur_frame];
-
-    if (anim.num_frames != 0) {
-        draw_surface_centered(frame.surface);
-        LOGV("drawing frame #%d min_cap=%d time=%d\n",
-             anim.cur_frame, frame.min_level,
-             frame.disp_time);
-    }
-    draw_clock(anim);
-    draw_percent(anim);
-}
-
-static void redraw_screen(struct charger *charger)
-{
-    struct animation *batt_anim = charger->batt_anim;
-
-    clear_screen();
-
-    /* try to display *something* */
-    if (batt_anim->cur_level < 0 || batt_anim->num_frames == 0)
-        draw_unknown(charger);
-    else
-        draw_battery(charger);
-    gr_flip();
-}
-
-static void kick_animation(struct animation *anim)
-{
+static void kick_animation(animation* anim) {
     anim->run = true;
 }
 
-static void reset_animation(struct animation *anim)
-{
+static void reset_animation(animation* anim) {
     anim->cur_cycle = 0;
     anim->cur_frame = 0;
     anim->run = false;
 }
 
-static void init_status_display(struct animation* anim)
-{
-    int res;
-
-    if (!anim->text_clock.font_file.empty()) {
-        if ((res =
-                gr_init_font(anim->text_clock.font_file.c_str(), &anim->text_clock.font)) < 0) {
-            LOGE("Could not load time font (%d)\n", res);
-        }
-    }
-
-    if (!anim->text_percent.font_file.empty()) {
-        if ((res =
-                gr_init_font(anim->text_percent.font_file.c_str(), &anim->text_percent.font)) < 0) {
-            LOGE("Could not load percent font (%d)\n", res);
-        }
-    }
-}
-
-static void update_screen_state(struct charger *charger, int64_t now)
-{
-    struct animation *batt_anim = charger->batt_anim;
+static void update_screen_state(charger* charger, int64_t now) {
+    animation* batt_anim = charger->batt_anim;
     int disp_time;
 
     if (!batt_anim->run || now < charger->next_screen_transition) return;
 
-    if (!minui_inited) {
+    if (healthd_draw == nullptr) {
         if (healthd_config && healthd_config->screen_on) {
             if (!healthd_config->screen_on(batt_prop)) {
                 LOGV("[%" PRId64 "] leave screen off\n", now);
                 batt_anim->run = false;
                 charger->next_screen_transition = -1;
-                if (charger->charger_connected)
-                    request_suspend(true);
+                if (charger->charger_connected) request_suspend(true);
                 return;
             }
         }
 
-        gr_init();
-        gr_font_size(gr_sys_font(), &char_width, &char_height);
-        init_status_display(batt_anim);
+        healthd_draw.reset(new HealthdDraw(batt_anim));
 
 #ifndef CHARGER_DISABLE_INIT_BLANK
-        gr_fb_blank(true);
+        healthd_draw->blank_screen(true);
 #endif
-        minui_inited = true;
     }
 
     /* animation is over, blank screen and leave */
     if (batt_anim->num_cycles > 0 && batt_anim->cur_cycle == batt_anim->num_cycles) {
         reset_animation(batt_anim);
         charger->next_screen_transition = -1;
-        gr_fb_blank(true);
+        healthd_draw->blank_screen(true);
         LOGV("[%" PRId64 "] animation done\n", now);
-        if (charger->charger_connected)
-            request_suspend(true);
+        if (charger->charger_connected) request_suspend(true);
         return;
     }
 
     disp_time = batt_anim->frames[batt_anim->cur_frame].disp_time;
 
+    /* unblank the screen on first cycle and first frame */
+    if (batt_anim->cur_cycle == 0 && batt_anim->cur_frame == 0) healthd_draw->blank_screen(false);
+
     /* animation starting, set up the animation */
     if (batt_anim->cur_frame == 0) {
-
         LOGV("[%" PRId64 "] animation starting\n", now);
         if (batt_prop) {
             batt_anim->cur_level = batt_prop->batteryLevel;
@@ -522,17 +328,13 @@
 
                 // repeat the first frame first_frame_repeats times
                 disp_time = batt_anim->frames[batt_anim->cur_frame].disp_time *
-                    batt_anim->first_frame_repeats;
+                            batt_anim->first_frame_repeats;
             }
         }
     }
 
-    /* unblank the screen  on first cycle */
-    if (batt_anim->cur_cycle == 0)
-        gr_fb_blank(false);
-
     /* draw the new frame (@ cur_frame) */
-    redraw_screen(charger);
+    healthd_draw->redraw_screen(charger->batt_anim, charger->surf_unknown);
 
     /* if we don't have anim frames, we only have one image, so just bump
      * the cycle counter and exit
@@ -576,22 +378,18 @@
     }
 }
 
-static int set_key_callback(struct charger *charger, int code, int value)
-{
+static int set_key_callback(charger* charger, int code, int value) {
     int64_t now = curr_time_ms();
     int down = !!value;
 
-    if (code > KEY_MAX)
-        return -1;
+    if (code > KEY_MAX) return -1;
 
     /* ignore events that don't modify our state */
-    if (charger->keys[code].down == down)
-        return 0;
+    if (charger->keys[code].down == down) return 0;
 
     /* only record the down even timestamp, as the amount
      * of time the key spent not being pressed is not useful */
-    if (down)
-        charger->keys[code].timestamp = now;
+    if (down) charger->keys[code].timestamp = now;
     charger->keys[code].down = down;
     charger->keys[code].pending = true;
     if (down) {
@@ -600,34 +398,27 @@
         int64_t duration = now - charger->keys[code].timestamp;
         int64_t secs = duration / 1000;
         int64_t msecs = duration - secs * 1000;
-        LOGV("[%" PRId64 "] key[%d] up (was down for %" PRId64 ".%" PRId64 "sec)\n",
-             now, code, secs, msecs);
+        LOGV("[%" PRId64 "] key[%d] up (was down for %" PRId64 ".%" PRId64 "sec)\n", now, code,
+             secs, msecs);
     }
 
     return 0;
 }
 
-static void update_input_state(struct charger *charger,
-                               struct input_event *ev)
-{
-    if (ev->type != EV_KEY)
-        return;
+static void update_input_state(charger* charger, input_event* ev) {
+    if (ev->type != EV_KEY) return;
     set_key_callback(charger, ev->code, ev->value);
 }
 
-static void set_next_key_check(struct charger *charger,
-                               struct key_state *key,
-                               int64_t timeout)
-{
+static void set_next_key_check(charger* charger, key_state* key, int64_t timeout) {
     int64_t then = key->timestamp + timeout;
 
     if (charger->next_key_check == -1 || then < charger->next_key_check)
         charger->next_key_check = then;
 }
 
-static void process_key(struct charger *charger, int code, int64_t now)
-{
-    struct key_state *key = &charger->keys[code];
+static void process_key(charger* charger, int code, int64_t now) {
+    key_state* key = &charger->keys[code];
 
     if (code == KEY_POWER) {
         if (key->down) {
@@ -642,10 +433,12 @@
                 } else {
                     if (charger->batt_anim->cur_level >= charger->boot_min_cap) {
                         LOGW("[%" PRId64 "] rebooting\n", now);
-                        android_reboot(ANDROID_RB_RESTART, 0, 0);
+                        reboot(RB_AUTOBOOT);
                     } else {
-                        LOGV("[%" PRId64 "] ignore power-button press, battery level "
-                            "less than minimum\n", now);
+                        LOGV("[%" PRId64
+                             "] ignore power-button press, battery level "
+                             "less than minimum\n",
+                             now);
                     }
                 }
             } else {
@@ -654,9 +447,9 @@
                  */
                 set_next_key_check(charger, key, POWER_ON_KEY_TIME);
 
-               /* Turn on the display and kick animation on power-key press
-                * rather than on key release
-                */
+                /* Turn on the display and kick animation on power-key press
+                 * rather than on key release
+                 */
                 kick_animation(charger->batt_anim);
                 request_suspend(false);
             }
@@ -671,21 +464,17 @@
     key->pending = false;
 }
 
-static void handle_input_state(struct charger *charger, int64_t now)
-{
+static void handle_input_state(charger* charger, int64_t now) {
     process_key(charger, KEY_POWER, now);
 
     if (charger->next_key_check != -1 && now > charger->next_key_check)
         charger->next_key_check = -1;
 }
 
-static void handle_power_supply_state(struct charger *charger, int64_t now)
-{
-    if (!charger->have_battery_state)
-        return;
+static void handle_power_supply_state(charger* charger, int64_t now) {
+    if (!charger->have_battery_state) return;
 
     if (!charger->charger_connected) {
-
         /* Last cycle would have stopped at the extreme top of battery-icon
          * Need to show the correct level corresponding to capacity.
          */
@@ -697,7 +486,7 @@
                  now, (int64_t)UNPLUGGED_SHUTDOWN_TIME, charger->next_pwr_check);
         } else if (now >= charger->next_pwr_check) {
             LOGW("[%" PRId64 "] shutting down\n", now);
-            android_reboot(ANDROID_RB_POWEROFF, 0, 0);
+            reboot(RB_POWER_OFF);
         } else {
             /* otherwise we already have a shutdown timer scheduled */
         }
@@ -711,9 +500,8 @@
     }
 }
 
-void healthd_mode_charger_heartbeat()
-{
-    struct charger *charger = &charger_state;
+void healthd_mode_charger_heartbeat() {
+    charger* charger = &charger_state;
     int64_t now = curr_time_ms();
 
     handle_input_state(charger, now);
@@ -725,14 +513,11 @@
     update_screen_state(charger, now);
 }
 
-void healthd_mode_charger_battery_update(
-    struct android::BatteryProperties *props)
-{
-    struct charger *charger = &charger_state;
+void healthd_mode_charger_battery_update(android::BatteryProperties* props) {
+    charger* charger = &charger_state;
 
     charger->charger_connected =
-        props->chargerAcOnline || props->chargerUsbOnline ||
-        props->chargerWirelessOnline;
+        props->chargerAcOnline || props->chargerUsbOnline || props->chargerWirelessOnline;
 
     if (!charger->have_battery_state) {
         charger->have_battery_state = true;
@@ -743,19 +528,16 @@
     batt_prop = props;
 }
 
-int healthd_mode_charger_preparetowait(void)
-{
-    struct charger *charger = &charger_state;
+int healthd_mode_charger_preparetowait(void) {
+    charger* charger = &charger_state;
     int64_t now = curr_time_ms();
     int64_t next_event = INT64_MAX;
     int64_t timeout;
 
-    LOGV("[%" PRId64 "] next screen: %" PRId64 " next key: %" PRId64 " next pwr: %" PRId64 "\n", now,
-         charger->next_screen_transition, charger->next_key_check,
-         charger->next_pwr_check);
+    LOGV("[%" PRId64 "] next screen: %" PRId64 " next key: %" PRId64 " next pwr: %" PRId64 "\n",
+         now, charger->next_screen_transition, charger->next_key_check, charger->next_pwr_check);
 
-    if (charger->next_screen_transition != -1)
-        next_event = charger->next_screen_transition;
+    if (charger->next_screen_transition != -1) next_event = charger->next_screen_transition;
     if (charger->next_key_check != -1 && charger->next_key_check < next_event)
         next_event = charger->next_key_check;
     if (charger->next_pwr_check != -1 && charger->next_pwr_check < next_event)
@@ -766,32 +548,27 @@
     else
         timeout = -1;
 
-   return (int)timeout;
+    return (int)timeout;
 }
 
-static int input_callback(struct charger *charger, int fd, unsigned int epevents)
-{
-    struct input_event ev;
+static int input_callback(charger* charger, int fd, unsigned int epevents) {
+    input_event ev;
     int ret;
 
     ret = ev_get_input(fd, epevents, &ev);
-    if (ret)
-        return -1;
+    if (ret) return -1;
     update_input_state(charger, &ev);
     return 0;
 }
 
-static void charger_event_handler(uint32_t /*epevents*/)
-{
+static void charger_event_handler(uint32_t /*epevents*/) {
     int ret;
 
     ret = ev_wait(-1);
-    if (!ret)
-        ev_dispatch();
+    if (!ret) ev_dispatch();
 }
 
-animation* init_animation()
-{
+animation* init_animation() {
     bool parse_success;
 
     std::string content;
@@ -814,32 +591,29 @@
     }
 
     LOGV("Animation Description:\n");
-    LOGV("  animation: %d %d '%s' (%d)\n",
-        battery_animation.num_cycles, battery_animation.first_frame_repeats,
-        battery_animation.animation_file.c_str(), battery_animation.num_frames);
+    LOGV("  animation: %d %d '%s' (%d)\n", battery_animation.num_cycles,
+         battery_animation.first_frame_repeats, battery_animation.animation_file.c_str(),
+         battery_animation.num_frames);
     LOGV("  fail_file: '%s'\n", battery_animation.fail_file.c_str());
-    LOGV("  clock: %d %d %d %d %d %d '%s'\n",
-        battery_animation.text_clock.pos_x, battery_animation.text_clock.pos_y,
-        battery_animation.text_clock.color_r, battery_animation.text_clock.color_g,
-        battery_animation.text_clock.color_b, battery_animation.text_clock.color_a,
-        battery_animation.text_clock.font_file.c_str());
-    LOGV("  percent: %d %d %d %d %d %d '%s'\n",
-        battery_animation.text_percent.pos_x, battery_animation.text_percent.pos_y,
-        battery_animation.text_percent.color_r, battery_animation.text_percent.color_g,
-        battery_animation.text_percent.color_b, battery_animation.text_percent.color_a,
-        battery_animation.text_percent.font_file.c_str());
+    LOGV("  clock: %d %d %d %d %d %d '%s'\n", battery_animation.text_clock.pos_x,
+         battery_animation.text_clock.pos_y, battery_animation.text_clock.color_r,
+         battery_animation.text_clock.color_g, battery_animation.text_clock.color_b,
+         battery_animation.text_clock.color_a, battery_animation.text_clock.font_file.c_str());
+    LOGV("  percent: %d %d %d %d %d %d '%s'\n", battery_animation.text_percent.pos_x,
+         battery_animation.text_percent.pos_y, battery_animation.text_percent.color_r,
+         battery_animation.text_percent.color_g, battery_animation.text_percent.color_b,
+         battery_animation.text_percent.color_a, battery_animation.text_percent.font_file.c_str());
     for (int i = 0; i < battery_animation.num_frames; i++) {
         LOGV("  frame %.2d: %d %d %d\n", i, battery_animation.frames[i].disp_time,
-            battery_animation.frames[i].min_level, battery_animation.frames[i].max_level);
+             battery_animation.frames[i].min_level, battery_animation.frames[i].max_level);
     }
 
     return &battery_animation;
 }
 
-void healthd_mode_charger_init(struct healthd_config* config)
-{
+void healthd_mode_charger_init(struct healthd_config* config) {
     int ret;
-    struct charger *charger = &charger_state;
+    charger* charger = &charger_state;
     int i;
     int epollfd;
 
@@ -847,19 +621,18 @@
 
     LOGW("--------------- STARTING CHARGER MODE ---------------\n");
 
-    ret = ev_init(std::bind(&input_callback, charger, std::placeholders::_1,
-                            std::placeholders::_2));
+    ret = ev_init(std::bind(&input_callback, charger, std::placeholders::_1, std::placeholders::_2));
     if (!ret) {
         epollfd = ev_get_epollfd();
         healthd_register_event(epollfd, charger_event_handler, EVENT_WAKEUP_FD);
     }
 
-    struct animation* anim = init_animation();
+    animation* anim = init_animation();
     charger->batt_anim = anim;
 
     ret = res_create_display_surface(anim->fail_file.c_str(), &charger->surf_unknown);
     if (ret < 0) {
-        LOGE("Cannot load custom battery_fail image. Reverting to built in.\n");
+        LOGE("Cannot load custom battery_fail image. Reverting to built in: %d\n", ret);
         ret = res_create_display_surface("charger/battery_fail", &charger->surf_unknown);
         if (ret < 0) {
             LOGE("Cannot load built in battery_fail image\n");
@@ -871,15 +644,15 @@
     int scale_count;
     int scale_fps;  // Not in use (charger/battery_scale doesn't have FPS text
                     // chunk). We are using hard-coded frame.disp_time instead.
-    ret = res_create_multi_display_surface(anim->animation_file.c_str(),
-        &scale_count, &scale_fps, &scale_frames);
+    ret = res_create_multi_display_surface(anim->animation_file.c_str(), &scale_count, &scale_fps,
+                                           &scale_frames);
     if (ret < 0) {
         LOGE("Cannot load battery_scale image\n");
         anim->num_frames = 0;
         anim->num_cycles = 1;
     } else if (scale_count != anim->num_frames) {
-        LOGE("battery_scale image has unexpected frame count (%d, expected %d)\n",
-             scale_count, anim->num_frames);
+        LOGE("battery_scale image has unexpected frame count (%d, expected %d)\n", scale_count,
+             anim->num_frames);
         anim->num_frames = 0;
         anim->num_cycles = 1;
     } else {
@@ -887,8 +660,8 @@
             anim->frames[i].surface = scale_frames[i];
         }
     }
-    ev_sync_key_state(std::bind(&set_key_callback, charger, std::placeholders::_1,
-                                std::placeholders::_2));
+    ev_sync_key_state(
+        std::bind(&set_key_callback, charger, std::placeholders::_1, std::placeholders::_2));
 
     charger->next_screen_transition = -1;
     charger->next_key_check = -1;
diff --git a/healthd/include/healthd/BatteryMonitor.h b/healthd/include/healthd/BatteryMonitor.h
index 8865a7d..97435c7 100644
--- a/healthd/include/healthd/BatteryMonitor.h
+++ b/healthd/include/healthd/BatteryMonitor.h
@@ -18,7 +18,6 @@
 #define HEALTHD_BATTERYMONITOR_H
 
 #include <batteryservice/BatteryService.h>
-#include <binder/IInterface.h>
 #include <utils/String8.h>
 #include <utils/Vector.h>
 
@@ -48,7 +47,6 @@
     struct healthd_config *mHealthdConfig;
     Vector<String8> mChargerNames;
     bool mBatteryDevicePresent;
-    bool mAlwaysPluggedDevice;
     int mBatteryFixedCapacity;
     int mBatteryFixedTemperature;
     struct BatteryProperties props;
diff --git a/include/private/fs_config.h b/include/private/fs_config.h
new file mode 100644
index 0000000..e9868a4
--- /dev/null
+++ b/include/private/fs_config.h
@@ -0,0 +1,4 @@
+// TODO(b/63135587) remove this file after the transitive dependency
+// from private/android_filesystem_config.h is resolved. All files that use
+// libcutils/include/private/fs_config.h should include the file directly, not
+// indirectly via private/android_filesystem_config.h.
diff --git a/init/Android.bp b/init/Android.bp
index fce424e..660d586 100644
--- a/init/Android.bp
+++ b/init/Android.bp
@@ -18,9 +18,9 @@
     name: "init_defaults",
     cpp_std: "experimental",
     sanitize: {
-        misc_undefined: ["integer"],
+        misc_undefined: ["signed-integer-overflow"],
     },
-    cppflags: [
+    cflags: [
         "-DLOG_UEVENTS=0",
         "-Wall",
         "-Wextra",
@@ -54,7 +54,40 @@
                 "-DSHUTDOWN_ZERO_TIMEOUT=1",
             ],
         },
+        uml: {
+            cppflags: ["-DUSER_MODE_LINUX"],
+        },
     },
+    static_libs: [
+        "libbootloader_message",
+        "libfs_mgr",
+        "libfec",
+        "libfec_rs",
+        "libhidl-gen-utils",
+        "libsquashfs_utils",
+        "liblogwrap",
+        "libext4_utils",
+        "libseccomp_policy",
+        "libcrypto_utils",
+        "libsparse",
+        "libprocessgroup",
+        "libavb",
+        "libkeyutils",
+        "libprotobuf-cpp-lite",
+        "libpropertyinfoserializer",
+        "libpropertyinfoparser",
+    ],
+    shared_libs: [
+        "libcutils",
+        "libbase",
+        "libc",
+        "liblog",
+        "libcrypto",
+        "libc++",
+        "libdl",
+        "libz",
+        "libselinux",
+    ],
 }
 
 cc_library_static {
@@ -62,26 +95,46 @@
     defaults: ["init_defaults"],
     srcs: [
         "action.cpp",
+        "action_manager.cpp",
+        "action_parser.cpp",
+        "bootchart.cpp",
+        "builtins.cpp",
         "capabilities.cpp",
         "descriptors.cpp",
         "devices.cpp",
+        "epoll.cpp",
         "firmware_handler.cpp",
+        "first_stage_mount.cpp",
         "import_parser.cpp",
-        "init_parser.cpp",
-        "log.cpp",
+        "init.cpp",
+        "keychords.cpp",
+        "modalias_handler.cpp",
         "parser.cpp",
+        "persistent_properties.cpp",
+        "persistent_properties.proto",
+        "property_service.cpp",
+        "property_type.cpp",
+        "reboot.cpp",
+        "security.cpp",
+        "selinux.cpp",
         "service.cpp",
+        "sigchld_handler.cpp",
+        "subcontext.cpp",
+        "subcontext.proto",
+        "rlimit_parser.cpp",
+        "tokenizer.cpp",
         "uevent_listener.cpp",
+        "ueventd.cpp",
         "ueventd_parser.cpp",
         "util.cpp",
+        "watchdogd.cpp",
     ],
     whole_static_libs: ["libcap"],
-    static_libs: [
-        "libbase",
-        "libselinux",
-        "liblog",
-        "libprocessgroup",
-    ],
+    header_libs: ["bootimg_headers"],
+    proto: {
+        type: "lite",
+        export_proto_headers: true,
+    },
 }
 
 /*
@@ -94,46 +147,14 @@
 
     name: "init",
     defaults: ["init_defaults"],
+    required: [
+        "e2fsdroid",
+        "mke2fs",
+        "sload_f2fs",
+        "make_f2fs",
+    ],
     static_executable: true,
-    srcs: [
-        "bootchart.cpp",
-        "builtins.cpp",
-        "init.cpp",
-        "init_first_stage.cpp",
-        "keychords.cpp",
-        "property_service.cpp",
-        "reboot.cpp",
-        "signal_handler.cpp",
-        "ueventd.cpp",
-        "watchdogd.cpp",
-    ],
-    include_dirs: [
-        "system/core/mkbootimg"
-    ],
-    static_libs: [
-        "libinit",
-        "libbootloader_message",
-        "libfs_mgr",
-        "libfec",
-        "libfec_rs",
-        "libsquashfs_utils",
-        "liblogwrap",
-        "libext4_utils",
-        "libcutils",
-        "libbase",
-        "libc",
-        "libselinux",
-        "liblog",
-        "libcrypto_utils",
-        "libcrypto",
-        "libc++_static",
-        "libdl",
-        "libsparse",
-        "libz",
-        "libprocessgroup",
-        "libavb",
-        "libkeyutils",
-    ],
+    srcs: ["main.cpp"],
     symlinks: [
         "sbin/ueventd",
         "sbin/watchdogd",
@@ -149,18 +170,100 @@
     defaults: ["init_defaults"],
     srcs: [
         "devices_test.cpp",
-        "init_parser_test.cpp",
         "init_test.cpp",
+        "keychords_test.cpp",
+        "persistent_properties_test.cpp",
         "property_service_test.cpp",
+        "property_type_test.cpp",
+        "result_test.cpp",
+        "rlimit_parser_test.cpp",
         "service_test.cpp",
+        "subcontext_test.cpp",
+        "tokenizer_test.cpp",
+        "ueventd_parser_test.cpp",
+        "ueventd_test.cpp",
         "util_test.cpp",
     ],
-    shared_libs: [
-        "libbase",
-        "libcutils",
-        "libselinux",
+    static_libs: ["libinit"],
+    test_suites: ["device-tests"],
+}
+
+cc_benchmark {
+    name: "init_benchmarks",
+    defaults: ["init_defaults"],
+    srcs: [
+        "subcontext_benchmark.cpp",
     ],
     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",
+        "epoll.cpp",
+        "keychords.cpp",
+        "import_parser.cpp",
+        "host_import_parser.cpp",
+        "host_init_verifier.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",
+        "generated_android_ids"
+    ],
+    target: {
+        android: {
+            enabled: false,
+        },
+        darwin: {
+            enabled: false,
+        },
+    },
+}
+
 subdirs = ["*"]
diff --git a/init/Android.mk b/init/Android.mk
index 6cd47f4..d0cb820 100644
--- a/init/Android.mk
+++ b/init/Android.mk
@@ -41,23 +41,10 @@
 
 include $(CLEAR_VARS)
 LOCAL_CPPFLAGS := $(init_cflags)
-LOCAL_SRC_FILES:= \
-    bootchart.cpp \
-    builtins.cpp \
-    init.cpp \
-    init_first_stage.cpp \
-    keychords.cpp \
-    property_service.cpp \
-    reboot.cpp \
-    signal_handler.cpp \
-    ueventd.cpp \
-    watchdogd.cpp \
+LOCAL_SRC_FILES := main.cpp
 
 LOCAL_MODULE:= init
-LOCAL_C_INCLUDES += \
-    system/core/mkbootimg
 
-LOCAL_FORCE_STATIC_EXECUTABLE := true
 LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT)
 LOCAL_UNSTRIPPED_PATH := $(TARGET_ROOT_OUT_UNSTRIPPED)
 
@@ -67,29 +54,51 @@
     libfs_mgr \
     libfec \
     libfec_rs \
+    libhidl-gen-utils \
     libsquashfs_utils \
     liblogwrap \
     libext4_utils \
-    libcutils \
-    libbase \
-    libc \
-    libselinux \
-    liblog \
+    libseccomp_policy \
     libcrypto_utils \
-    libcrypto \
-    libc++_static \
-    libdl \
     libsparse \
-    libz \
     libprocessgroup \
     libavb \
     libkeyutils \
+    libprotobuf-cpp-lite \
+    libpropertyinfoserializer \
+    libpropertyinfoparser \
+    liblp \
+
+shared_libs := \
+    libcutils \
+    libbase \
+    liblog \
+    libcrypto \
+    libdl \
+    libz \
+    libselinux \
+
+ifneq ($(BOARD_BUILD_SYSTEM_ROOT_IMAGE),true)
+# init is static executable for non-system-as-root devices, because the dynamic linker
+# and shared libs are not available before /system is mounted, but init has to run
+# before the partition is mounted.
+LOCAL_STATIC_LIBRARIES += $(shared_libs) libc++_static
+LOCAL_FORCE_STATIC_EXECUTABLE := true
+else
+LOCAL_SHARED_LIBRARIES := $(shared_libs) libc++
+endif
+shared_libs :=
+
+LOCAL_REQUIRED_MODULES := \
+    e2fsdroid \
+    mke2fs \
+    sload_f2fs \
+    make_f2fs \
 
 # Create symlinks.
 LOCAL_POST_INSTALL_CMD := $(hide) mkdir -p $(TARGET_ROOT_OUT)/sbin; \
     ln -sf ../init $(TARGET_ROOT_OUT)/sbin/ueventd; \
     ln -sf ../init $(TARGET_ROOT_OUT)/sbin/watchdogd
 
-LOCAL_SANITIZE := integer
-LOCAL_CLANG := true
+LOCAL_SANITIZE := signed-integer-overflow
 include $(BUILD_EXECUTABLE)
diff --git a/init/OWNERS b/init/OWNERS
new file mode 100644
index 0000000..babbe4d
--- /dev/null
+++ b/init/OWNERS
@@ -0,0 +1 @@
+tomcherry@google.com
diff --git a/init/README.md b/init/README.md
index 422fdad..b0a73b9 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
@@ -170,11 +193,103 @@
 
 `disabled`
 > This service will not automatically start with its class.
-  It must be explicitly started by name.
+  It must be explicitly started by name or by interface name.
+
+`enter_namespace <type> <path>`
+> Enters the namespace of type _type_ located at _path_. Only network namespaces are supported with
+  _type_ set to "net". Note that only one namespace of a given _type_ may be entered.
+
+`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.
+
+`sigstop`
+> Send SIGSTOP to the service immediately before exec is called. This is intended for debugging.
+  See the below section on debugging for how this can be used.
+
 `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 +298,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,70 +314,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.
-
-`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.
-
-`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
 --------
@@ -363,6 +415,11 @@
   within _argument_.
   Init halts executing commands until the forked process exits.
 
+`exec_background [ <seclabel> [ <user> [ <group>\* ] ] ] -- <command> [ <argument>\* ]`
+> Fork and execute command with the given arguments. This is handled similarly
+  to the `exec` command. The difference is that init does not halt executing
+  commands until the process exits for `exec_background`.
+
 `exec_start <service>`
 > Start a given service and halt the processing of additional init commands
   until it returns.  The command functions similarly to the `exec` command,
@@ -435,12 +492,20 @@
 `rmdir <path>`
 > Calls rmdir(2) on the given path.
 
+`readahead <file|dir> [--fully]`
+> Calls readahead(2) on the file or files within given directory.
+  Use option --fully to read the full file content.
+
 `setprop <name> <value>`
 > Set system property _name_ to _value_. Properties are expanded
   within _value_.
 
 `setrlimit <resource> <cur> <max>`
-> Set the rlimit for a resource.
+> Set the rlimit for a resource. This applies to all processes launched after
+  the limit is set. It is intended to be set early in init and applied globally.
+  _resource_ is best specified using its text representation ('cpu', 'rtio', etc
+  or 'RLIM_CPU', 'RLIM_RTIO', etc). It also may be specified as the int value
+  that the resource enum corresponds to.
 
 `start <service>`
 > Start a service running if it is not already running.
@@ -651,23 +716,58 @@
 
 Debugging init
 --------------
-By default, programs executed by init will drop stdout and stderr into
-/dev/null. To help with debugging, you can execute your program via the
-Android program logwrapper. This will redirect stdout/stderr into the
-Android logging system (accessed via logcat).
+Launching init services without init is not recommended as init sets up a significant amount of
+environment (user, groups, security label, capabilities, etc) that is hard to replicate manually.
 
-For example
-service akmd /system/bin/logwrapper /sbin/akmd
+If it is required to debug a service from its very start, the `sigstop` service option is added.
+This option will send SIGSTOP to a service immediately before calling exec. This gives a window
+where developers can attach a debugger, strace, etc before continuing the service with SIGCONT.
 
-For quicker turnaround when working on init itself, use:
+This flag can also be dynamically controled via the ctl.sigstop_on and ctl.sigstop_off properties.
 
-    mm -j &&
-    m ramdisk-nodeps &&
-    m bootimage-nodeps &&
-    adb reboot bootloader &&
-    fastboot boot $ANDROID_PRODUCT_OUT/boot.img
+Below is an example of dynamically debugging logd via the above:
 
-Alternatively, use the emulator:
+    stop logd
+    setprop ctl.sigstop_on logd
+    start logd
+    ps -e | grep logd
+    > logd          4343     1   18156   1684 do_signal_stop 538280 T init
+    gdbclient.py -p 4343
+    b main
+    c
+    c
+    c
+    > Breakpoint 1, main (argc=1, argv=0x7ff8c9a488) at system/core/logd/main.cpp:427
 
-    emulator -partition-size 1024 \
-        -verbose -show-kernel -no-window
+Below is an example of doing the same but with strace
+
+    stop logd
+    setprop ctl.sigstop_on logd
+    start logd
+    ps -e | grep logd
+    > logd          4343     1   18156   1684 do_signal_stop 538280 T init
+    strace -p 4343
+
+    (From a different shell)
+    kill -SIGCONT 4343
+
+    > strace runs
+
+Host Init Script Verification
+-----------------------------
+
+Init scripts are checked for correctness during build time. Specifically the below is checked.
+
+1) Well formatted action, service and import sections, e.g. no actions without a preceding 'on'
+line, and no extraneous lines after an 'import' statement.
+2) All commands map to a valid keyword and the argument count is within the correct range.
+3) All service options are valid. This is stricter than how commands are checked as the service
+options' arguments are fully parsed, e.g. UIDs and GIDs must resolve.
+
+There are other parts of init scripts that are only parsed at runtime and therefore not checked
+during build time, among them are the below.
+
+1) The validity of the arguments of commands, e.g. no checking if file paths actually exist, if
+SELinux would permit the operation, or if the UIDs and GIDs resolve.
+2) No checking if a service exists or has a valid SELinux domain defined
+3) No checking if a service has not been previously defined in a different init script.
diff --git a/init/action.cpp b/init/action.cpp
index 4ec5f17..11335ca 100644
--- a/init/action.cpp
+++ b/init/action.cpp
@@ -28,49 +28,72 @@
 namespace android {
 namespace init {
 
-Command::Command(BuiltinFunction f, const std::vector<std::string>& args, int line)
-    : func_(f), args_(args), line_(line) {}
+Result<Success> RunBuiltinFunction(const BuiltinFunction& function,
+                                   const std::vector<std::string>& args,
+                                   const std::string& context) {
+    auto builtin_arguments = BuiltinArguments(context);
 
-int Command::InvokeFunc() const {
-    std::vector<std::string> expanded_args;
-    expanded_args.resize(args_.size());
-    expanded_args[0] = args_[0];
-    for (std::size_t i = 1; i < args_.size(); ++i) {
-        if (!expand_props(args_[i], &expanded_args[i])) {
-            LOG(ERROR) << args_[0] << ": cannot expand '" << args_[i] << "'";
-            return -EINVAL;
+    builtin_arguments.args.resize(args.size());
+    builtin_arguments.args[0] = args[0];
+    for (std::size_t i = 1; i < args.size(); ++i) {
+        if (!expand_props(args[i], &builtin_arguments.args[i])) {
+            return Error() << "cannot expand '" << args[i] << "'";
         }
     }
 
-    return func_(expanded_args);
+    return function(builtin_arguments);
+}
+
+Command::Command(BuiltinFunction f, bool execute_in_subcontext,
+                 const std::vector<std::string>& args, int line)
+    : func_(std::move(f)), execute_in_subcontext_(execute_in_subcontext), args_(args), line_(line) {}
+
+Result<Success> Command::InvokeFunc(Subcontext* subcontext) const {
+    if (subcontext) {
+        if (execute_in_subcontext_) {
+            return subcontext->Execute(args_);
+        }
+
+        auto expanded_args = subcontext->ExpandArgs(args_);
+        if (!expanded_args) {
+            return expanded_args.error();
+        }
+        return RunBuiltinFunction(func_, *expanded_args, subcontext->context());
+    }
+
+    return RunBuiltinFunction(func_, args_, kInitContext);
 }
 
 std::string Command::BuildCommandString() const {
     return Join(args_, ' ');
 }
 
-Action::Action(bool oneshot, const std::string& filename, int line)
-    : oneshot_(oneshot), filename_(filename), line_(line) {}
+Action::Action(bool oneshot, Subcontext* subcontext, const std::string& filename, int line,
+               const std::string& event_trigger,
+               const std::map<std::string, std::string>& property_triggers)
+    : property_triggers_(property_triggers),
+      event_trigger_(event_trigger),
+      oneshot_(oneshot),
+      subcontext_(subcontext),
+      filename_(filename),
+      line_(line) {}
 
-const KeywordMap<BuiltinFunction>* Action::function_map_ = nullptr;
+const KeywordFunctionMap* Action::function_map_ = nullptr;
 
-bool Action::AddCommand(const std::vector<std::string>& args, int line, std::string* err) {
+Result<Success> Action::AddCommand(const std::vector<std::string>& args, int line) {
     if (!function_map_) {
-        *err = "no function map available";
-        return false;
+        return Error() << "no function map available";
     }
 
-    auto function = function_map_->FindFunction(args, err);
-    if (!function) {
-        return false;
-    }
+    auto function = function_map_->FindFunction(args);
+    if (!function) return Error() << function.error();
 
-    AddCommand(function, args, line);
-    return true;
+    commands_.emplace_back(function->second, function->first, args, line);
+    return Success();
 }
 
 void Action::AddCommand(BuiltinFunction f, const std::vector<std::string>& args, int line) {
-    commands_.emplace_back(f, args, line);
+    commands_.emplace_back(f, false, args, line);
 }
 
 std::size_t Action::NumCommands() const {
@@ -92,83 +115,31 @@
 
 void Action::ExecuteCommand(const Command& command) const {
     android::base::Timer t;
-    int result = command.InvokeFunc();
-
+    auto result = command.InvokeFunc(subcontext_);
     auto duration = t.duration();
+
+    // There are many legacy paths in rootdir/init.rc that will virtually never exist on a new
+    // device, such as '/sys/class/leds/jogball-backlight/brightness'.  As of this writing, there
+    // are 198 such failures on bullhead.  Instead of spamming the log reporting them, we do not
+    // report such failures unless we're running at the DEBUG log level.
+    bool report_failure = !result.has_value();
+    if (report_failure && android::base::GetMinimumLogSeverity() > android::base::DEBUG &&
+        result.error_errno() == ENOENT) {
+        report_failure = false;
+    }
+
     // Any action longer than 50ms will be warned to user as slow operation
-    if (duration > 50ms || android::base::GetMinimumLogSeverity() <= android::base::DEBUG) {
+    if (report_failure || duration > 50ms ||
+        android::base::GetMinimumLogSeverity() <= android::base::DEBUG) {
         std::string trigger_name = BuildTriggersString();
         std::string cmd_str = command.BuildCommandString();
 
         LOG(INFO) << "Command '" << cmd_str << "' action=" << trigger_name << " (" << filename_
-                  << ":" << command.line() << ") returned " << result << " took "
-                  << duration.count() << "ms.";
+                  << ":" << command.line() << ") took " << duration.count() << "ms and "
+                  << (result ? "succeeded" : "failed: " + result.error_string());
     }
 }
 
-bool Action::ParsePropertyTrigger(const std::string& trigger, std::string* err) {
-    const static std::string prop_str("property:");
-    std::string prop_name(trigger.substr(prop_str.length()));
-    size_t equal_pos = prop_name.find('=');
-    if (equal_pos == std::string::npos) {
-        *err = "property trigger found without matching '='";
-        return false;
-    }
-
-    std::string prop_value(prop_name.substr(equal_pos + 1));
-    prop_name.erase(equal_pos);
-
-    if (auto [it, inserted] = property_triggers_.emplace(prop_name, prop_value); !inserted) {
-        *err = "multiple property triggers found for same property";
-        return false;
-    }
-    return true;
-}
-
-bool Action::InitTriggers(const std::vector<std::string>& args, std::string* err) {
-    const static std::string prop_str("property:");
-    for (std::size_t i = 0; i < args.size(); ++i) {
-        if (args[i].empty()) {
-            *err = "empty trigger is not valid";
-            return false;
-        }
-
-        if (i % 2) {
-            if (args[i] != "&&") {
-                *err = "&& is the only symbol allowed to concatenate actions";
-                return false;
-            } else {
-                continue;
-            }
-        }
-
-        if (!args[i].compare(0, prop_str.length(), prop_str)) {
-            if (!ParsePropertyTrigger(args[i], err)) {
-                return false;
-            }
-        } else {
-            if (!event_trigger_.empty()) {
-                *err = "multiple event triggers are not allowed";
-                return false;
-            }
-
-            event_trigger_ = args[i];
-        }
-    }
-
-    return true;
-}
-
-bool Action::InitSingleTrigger(const std::string& trigger) {
-    std::vector<std::string> name_vector{trigger};
-    std::string err;
-    bool ret = InitTriggers(name_vector, &err);
-    if (!ret) {
-        LOG(ERROR) << "InitSingleTrigger failed due to: " << err;
-    }
-    return ret;
-}
-
 // This function checks that all property triggers are satisfied, that is
 // for each (name, value) in property_triggers_, check that the current
 // value of the property 'name' == value.
@@ -236,129 +207,5 @@
     }
 }
 
-ActionManager::ActionManager() : current_command_(0) {
-}
-
-ActionManager& ActionManager::GetInstance() {
-    static ActionManager instance;
-    return instance;
-}
-
-void ActionManager::AddAction(std::unique_ptr<Action> action) {
-    actions_.emplace_back(std::move(action));
-}
-
-void ActionManager::QueueEventTrigger(const std::string& trigger) {
-    event_queue_.emplace(trigger);
-}
-
-void ActionManager::QueuePropertyChange(const std::string& name, const std::string& value) {
-    event_queue_.emplace(std::make_pair(name, value));
-}
-
-void ActionManager::QueueAllPropertyActions() {
-    QueuePropertyChange("", "");
-}
-
-void ActionManager::QueueBuiltinAction(BuiltinFunction func, const std::string& name) {
-    auto action = std::make_unique<Action>(true, "<Builtin Action>", 0);
-    std::vector<std::string> name_vector{name};
-
-    if (!action->InitSingleTrigger(name)) {
-        return;
-    }
-
-    action->AddCommand(func, name_vector, 0);
-
-    event_queue_.emplace(action.get());
-    actions_.emplace_back(std::move(action));
-}
-
-void ActionManager::ExecuteOneCommand() {
-    // Loop through the event queue until we have an action to execute
-    while (current_executing_actions_.empty() && !event_queue_.empty()) {
-        for (const auto& action : actions_) {
-            if (std::visit([&action](const auto& event) { return action->CheckEvent(event); },
-                           event_queue_.front())) {
-                current_executing_actions_.emplace(action.get());
-            }
-        }
-        event_queue_.pop();
-    }
-
-    if (current_executing_actions_.empty()) {
-        return;
-    }
-
-    auto action = current_executing_actions_.front();
-
-    if (current_command_ == 0) {
-        std::string trigger_name = action->BuildTriggersString();
-        LOG(INFO) << "processing action (" << trigger_name << ") from (" << action->filename()
-                  << ":" << action->line() << ")";
-    }
-
-    action->ExecuteOneCommand(current_command_);
-
-    // If this was the last command in the current action, then remove
-    // the action from the executing list.
-    // If this action was oneshot, then also remove it from actions_.
-    ++current_command_;
-    if (current_command_ == action->NumCommands()) {
-        current_executing_actions_.pop();
-        current_command_ = 0;
-        if (action->oneshot()) {
-            auto eraser = [&action] (std::unique_ptr<Action>& a) {
-                return a.get() == action;
-            };
-            actions_.erase(std::remove_if(actions_.begin(), actions_.end(), eraser));
-        }
-    }
-}
-
-bool ActionManager::HasMoreCommands() const {
-    return !current_executing_actions_.empty() || !event_queue_.empty();
-}
-
-void ActionManager::DumpState() const {
-    for (const auto& a : actions_) {
-        a->DumpState();
-    }
-}
-
-void ActionManager::ClearQueue() {
-    // We are shutting down so don't claim the oneshot builtin actions back
-    current_executing_actions_ = {};
-    event_queue_ = {};
-    current_command_ = 0;
-}
-
-bool ActionParser::ParseSection(std::vector<std::string>&& args, const std::string& filename,
-                                int line, std::string* err) {
-    std::vector<std::string> triggers(args.begin() + 1, args.end());
-    if (triggers.size() < 1) {
-        *err = "actions must have a trigger";
-        return false;
-    }
-
-    auto action = std::make_unique<Action>(false, filename, line);
-    if (!action->InitTriggers(triggers, err)) {
-        return false;
-    }
-
-    action_ = std::move(action);
-    return true;
-}
-
-bool ActionParser::ParseLineSection(std::vector<std::string>&& args, int line, std::string* err) {
-    return action_ ? action_->AddCommand(std::move(args), line, err) : false;
-}
-
-void ActionParser::EndSection() {
-    if (action_ && action_->NumCommands() > 0) {
-        action_manager_->AddAction(std::move(action_));
-    }
-}
-
 }  // namespace init
 }  // namespace android
diff --git a/init/action.h b/init/action.h
index ad15f3f..4f063cc 100644
--- a/init/action.h
+++ b/init/action.h
@@ -24,23 +24,29 @@
 #include <vector>
 
 #include "builtins.h"
-#include "init_parser.h"
 #include "keyword_map.h"
+#include "result.h"
+#include "subcontext.h"
 
 namespace android {
 namespace init {
 
+Result<Success> RunBuiltinFunction(const BuiltinFunction& function,
+                                   const std::vector<std::string>& args, const std::string& context);
+
 class Command {
   public:
-    Command(BuiltinFunction f, const std::vector<std::string>& args, int line);
+    Command(BuiltinFunction f, bool execute_in_subcontext, const std::vector<std::string>& args,
+            int line);
 
-    int InvokeFunc() const;
+    Result<Success> InvokeFunc(Subcontext* subcontext) const;
     std::string BuildCommandString() const;
 
     int line() const { return line_; }
 
   private:
     BuiltinFunction func_;
+    bool execute_in_subcontext_;
     std::vector<std::string> args_;
     int line_;
 };
@@ -51,12 +57,12 @@
 
 class Action {
   public:
-    explicit Action(bool oneshot, const std::string& filename, int line);
+    Action(bool oneshot, Subcontext* subcontext, const std::string& filename, int line,
+           const std::string& event_trigger,
+           const std::map<std::string, std::string>& property_triggers);
 
-    bool AddCommand(const std::vector<std::string>& args, int line, std::string* err);
+    Result<Success> AddCommand(const std::vector<std::string>& args, int line);
     void AddCommand(BuiltinFunction f, const std::vector<std::string>& args, int line);
-    bool InitTriggers(const std::vector<std::string>& args, std::string* err);
-    bool InitSingleTrigger(const std::string& trigger);
     std::size_t NumCommands() const;
     void ExecuteOneCommand(std::size_t command) const;
     void ExecuteAllCommands() const;
@@ -69,65 +75,23 @@
     bool oneshot() const { return oneshot_; }
     const std::string& filename() const { return filename_; }
     int line() const { return line_; }
-    static void set_function_map(const KeywordMap<BuiltinFunction>* function_map) {
+    static void set_function_map(const KeywordFunctionMap* function_map) {
         function_map_ = function_map;
     }
 
-
-private:
+  private:
     void ExecuteCommand(const Command& command) const;
     bool CheckPropertyTriggers(const std::string& name = "",
                                const std::string& value = "") const;
-    bool ParsePropertyTrigger(const std::string& trigger, std::string* err);
 
     std::map<std::string, std::string> property_triggers_;
     std::string event_trigger_;
     std::vector<Command> commands_;
     bool oneshot_;
+    Subcontext* subcontext_;
     std::string filename_;
     int line_;
-    static const KeywordMap<BuiltinFunction>* function_map_;
-};
-
-class ActionManager {
-  public:
-    static ActionManager& GetInstance();
-
-    // Exposed for testing
-    ActionManager();
-
-    void AddAction(std::unique_ptr<Action> action);
-    void QueueEventTrigger(const std::string& trigger);
-    void QueuePropertyChange(const std::string& name, const std::string& value);
-    void QueueAllPropertyActions();
-    void QueueBuiltinAction(BuiltinFunction func, const std::string& name);
-    void ExecuteOneCommand();
-    bool HasMoreCommands() const;
-    void DumpState() const;
-    void ClearQueue();
-
-  private:
-    ActionManager(ActionManager const&) = delete;
-    void operator=(ActionManager const&) = delete;
-
-    std::vector<std::unique_ptr<Action>> actions_;
-    std::queue<std::variant<EventTrigger, PropertyChange, BuiltinAction>> event_queue_;
-    std::queue<const Action*> current_executing_actions_;
-    std::size_t current_command_;
-};
-
-class ActionParser : public SectionParser {
-  public:
-    ActionParser(ActionManager* action_manager)
-        : action_manager_(action_manager), action_(nullptr) {}
-    bool ParseSection(std::vector<std::string>&& args, const std::string& filename, int line,
-                      std::string* err) override;
-    bool ParseLineSection(std::vector<std::string>&& args, int line, std::string* err) override;
-    void EndSection() override;
-
-  private:
-    ActionManager* action_manager_;
-    std::unique_ptr<Action> action_;
+    static const KeywordFunctionMap* function_map_;
 };
 
 }  // namespace init
diff --git a/init/action_manager.cpp b/init/action_manager.cpp
new file mode 100644
index 0000000..22977bb
--- /dev/null
+++ b/init/action_manager.cpp
@@ -0,0 +1,116 @@
+/*
+ * 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 "action_manager.h"
+
+#include <android-base/logging.h>
+
+namespace android {
+namespace init {
+
+ActionManager::ActionManager() : current_command_(0) {}
+
+ActionManager& ActionManager::GetInstance() {
+    static ActionManager instance;
+    return instance;
+}
+
+void ActionManager::AddAction(std::unique_ptr<Action> action) {
+    actions_.emplace_back(std::move(action));
+}
+
+void ActionManager::QueueEventTrigger(const std::string& trigger) {
+    event_queue_.emplace(trigger);
+}
+
+void ActionManager::QueuePropertyChange(const std::string& name, const std::string& value) {
+    event_queue_.emplace(std::make_pair(name, value));
+}
+
+void ActionManager::QueueAllPropertyActions() {
+    QueuePropertyChange("", "");
+}
+
+void ActionManager::QueueBuiltinAction(BuiltinFunction func, const std::string& name) {
+    auto action = std::make_unique<Action>(true, nullptr, "<Builtin Action>", 0, name,
+                                           std::map<std::string, std::string>{});
+    std::vector<std::string> name_vector{name};
+
+    action->AddCommand(func, name_vector, 0);
+
+    event_queue_.emplace(action.get());
+    actions_.emplace_back(std::move(action));
+}
+
+void ActionManager::ExecuteOneCommand() {
+    // Loop through the event queue until we have an action to execute
+    while (current_executing_actions_.empty() && !event_queue_.empty()) {
+        for (const auto& action : actions_) {
+            if (std::visit([&action](const auto& event) { return action->CheckEvent(event); },
+                           event_queue_.front())) {
+                current_executing_actions_.emplace(action.get());
+            }
+        }
+        event_queue_.pop();
+    }
+
+    if (current_executing_actions_.empty()) {
+        return;
+    }
+
+    auto action = current_executing_actions_.front();
+
+    if (current_command_ == 0) {
+        std::string trigger_name = action->BuildTriggersString();
+        LOG(INFO) << "processing action (" << trigger_name << ") from (" << action->filename()
+                  << ":" << action->line() << ")";
+    }
+
+    action->ExecuteOneCommand(current_command_);
+
+    // If this was the last command in the current action, then remove
+    // the action from the executing list.
+    // If this action was oneshot, then also remove it from actions_.
+    ++current_command_;
+    if (current_command_ == action->NumCommands()) {
+        current_executing_actions_.pop();
+        current_command_ = 0;
+        if (action->oneshot()) {
+            auto eraser = [&action](std::unique_ptr<Action>& a) { return a.get() == action; };
+            actions_.erase(std::remove_if(actions_.begin(), actions_.end(), eraser));
+        }
+    }
+}
+
+bool ActionManager::HasMoreCommands() const {
+    return !current_executing_actions_.empty() || !event_queue_.empty();
+}
+
+void ActionManager::DumpState() const {
+    for (const auto& a : actions_) {
+        a->DumpState();
+    }
+}
+
+void ActionManager::ClearQueue() {
+    // We are shutting down so don't claim the oneshot builtin actions back
+    current_executing_actions_ = {};
+    event_queue_ = {};
+    current_command_ = 0;
+}
+
+}  // namespace init
+}  // namespace android
diff --git a/init/action_manager.h b/init/action_manager.h
new file mode 100644
index 0000000..5f47a6d
--- /dev/null
+++ b/init/action_manager.h
@@ -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.
+ */
+
+#ifndef _INIT_ACTION_MANAGER_H
+#define _INIT_ACTION_MANAGER_H
+
+#include <string>
+#include <vector>
+
+#include "action.h"
+#include "builtins.h"
+
+namespace android {
+namespace init {
+
+class ActionManager {
+  public:
+    static ActionManager& GetInstance();
+
+    // Exposed for testing
+    ActionManager();
+
+    void AddAction(std::unique_ptr<Action> action);
+    void QueueEventTrigger(const std::string& trigger);
+    void QueuePropertyChange(const std::string& name, const std::string& value);
+    void QueueAllPropertyActions();
+    void QueueBuiltinAction(BuiltinFunction func, const std::string& name);
+    void ExecuteOneCommand();
+    bool HasMoreCommands() const;
+    void DumpState() const;
+    void ClearQueue();
+
+  private:
+    ActionManager(ActionManager const&) = delete;
+    void operator=(ActionManager const&) = delete;
+
+    std::vector<std::unique_ptr<Action>> actions_;
+    std::queue<std::variant<EventTrigger, PropertyChange, BuiltinAction>> event_queue_;
+    std::queue<const Action*> current_executing_actions_;
+    std::size_t current_command_;
+};
+
+}  // namespace init
+}  // namespace android
+
+#endif
diff --git a/init/action_parser.cpp b/init/action_parser.cpp
new file mode 100644
index 0000000..1481162
--- /dev/null
+++ b/init/action_parser.cpp
@@ -0,0 +1,153 @@
+/*
+ * 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 "action_parser.h"
+
+#include <android-base/properties.h>
+#include <android-base/strings.h>
+
+#include "stable_properties.h"
+
+using android::base::GetBoolProperty;
+using android::base::StartsWith;
+
+namespace android {
+namespace init {
+
+namespace {
+
+bool IsActionableProperty(Subcontext* subcontext, const std::string& prop_name) {
+    static bool enabled = GetBoolProperty("ro.actionable_compatible_property.enabled", false);
+
+    if (subcontext == nullptr || !enabled) {
+        return true;
+    }
+
+    if (kExportedActionableProperties.count(prop_name) == 1) {
+        return true;
+    }
+    for (const auto& prefix : kPartnerPrefixes) {
+        if (android::base::StartsWith(prop_name, prefix)) {
+            return true;
+        }
+    }
+    return false;
+}
+
+Result<Success> ParsePropertyTrigger(const std::string& trigger, Subcontext* subcontext,
+                                     std::map<std::string, std::string>* property_triggers) {
+    const static std::string prop_str("property:");
+    std::string prop_name(trigger.substr(prop_str.length()));
+    size_t equal_pos = prop_name.find('=');
+    if (equal_pos == std::string::npos) {
+        return Error() << "property trigger found without matching '='";
+    }
+
+    std::string prop_value(prop_name.substr(equal_pos + 1));
+    prop_name.erase(equal_pos);
+
+    if (!IsActionableProperty(subcontext, prop_name)) {
+        return Error() << "unexported property trigger found: " << prop_name;
+    }
+
+    if (auto [it, inserted] = property_triggers->emplace(prop_name, prop_value); !inserted) {
+        return Error() << "multiple property triggers found for same property";
+    }
+    return Success();
+}
+
+Result<Success> ParseTriggers(const std::vector<std::string>& args, Subcontext* subcontext,
+                              std::string* event_trigger,
+                              std::map<std::string, std::string>* property_triggers) {
+    const static std::string prop_str("property:");
+    for (std::size_t i = 0; i < args.size(); ++i) {
+        if (args[i].empty()) {
+            return Error() << "empty trigger is not valid";
+        }
+
+        if (i % 2) {
+            if (args[i] != "&&") {
+                return Error() << "&& is the only symbol allowed to concatenate actions";
+            } else {
+                continue;
+            }
+        }
+
+        if (!args[i].compare(0, prop_str.length(), prop_str)) {
+            if (auto result = ParsePropertyTrigger(args[i], subcontext, property_triggers);
+                !result) {
+                return result;
+            }
+        } else {
+            if (!event_trigger->empty()) {
+                return Error() << "multiple event triggers are not allowed";
+            }
+
+            *event_trigger = args[i];
+        }
+    }
+
+    return Success();
+}
+
+}  // namespace
+
+Result<Success> ActionParser::ParseSection(std::vector<std::string>&& args,
+                                           const std::string& filename, int line) {
+    std::vector<std::string> triggers(args.begin() + 1, args.end());
+    if (triggers.size() < 1) {
+        return Error() << "Actions must have a trigger";
+    }
+
+    Subcontext* action_subcontext = nullptr;
+    if (subcontexts_) {
+        for (auto& subcontext : *subcontexts_) {
+            if (StartsWith(filename, subcontext.path_prefix())) {
+                action_subcontext = &subcontext;
+                break;
+            }
+        }
+    }
+
+    std::string event_trigger;
+    std::map<std::string, std::string> property_triggers;
+
+    if (auto result = ParseTriggers(triggers, action_subcontext, &event_trigger, &property_triggers);
+        !result) {
+        return Error() << "ParseTriggers() failed: " << result.error();
+    }
+
+    auto action = std::make_unique<Action>(false, action_subcontext, filename, line, event_trigger,
+                                           property_triggers);
+
+    action_ = std::move(action);
+    return Success();
+}
+
+Result<Success> ActionParser::ParseLineSection(std::vector<std::string>&& args, int line) {
+    return action_ ? action_->AddCommand(std::move(args), line) : Success();
+}
+
+Result<Success> ActionParser::EndSection() {
+    if (action_ && action_->NumCommands() > 0) {
+        action_manager_->AddAction(std::move(action_));
+    }
+
+    return Success();
+}
+
+}  // namespace init
+}  // namespace android
diff --git a/init/action_parser.h b/init/action_parser.h
new file mode 100644
index 0000000..b7f7074
--- /dev/null
+++ b/init/action_parser.h
@@ -0,0 +1,49 @@
+/*
+ * 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_ACTION_PARSER_H
+#define _INIT_ACTION_PARSER_H
+
+#include <string>
+#include <vector>
+
+#include "action.h"
+#include "action_manager.h"
+#include "parser.h"
+#include "subcontext.h"
+
+namespace android {
+namespace init {
+
+class ActionParser : public SectionParser {
+  public:
+    ActionParser(ActionManager* action_manager, std::vector<Subcontext>* subcontexts)
+        : action_manager_(action_manager), subcontexts_(subcontexts), action_(nullptr) {}
+    Result<Success> ParseSection(std::vector<std::string>&& args, const std::string& filename,
+                                 int line) override;
+    Result<Success> ParseLineSection(std::vector<std::string>&& args, int line) override;
+    Result<Success> EndSection() override;
+
+  private:
+    ActionManager* action_manager_;
+    std::vector<Subcontext>* subcontexts_;
+    std::unique_ptr<Action> action_;
+};
+
+}  // namespace init
+}  // namespace android
+
+#endif
diff --git a/init/bootchart.cpp b/init/bootchart.cpp
index 4727f92..c2cf573 100644
--- a/init/bootchart.cpp
+++ b/init/bootchart.cpp
@@ -32,12 +32,14 @@
 #include <mutex>
 #include <thread>
 
+#include <android-base/chrono_utils.h>
 #include <android-base/file.h>
 #include <android-base/logging.h>
 #include <android-base/properties.h>
 #include <android-base/stringprintf.h>
 
 using android::base::StringPrintf;
+using android::base::boot_clock;
 using namespace std::chrono_literals;
 
 namespace android {
@@ -50,9 +52,9 @@
 static bool g_bootcharting_finished;
 
 static long long get_uptime_jiffies() {
-  std::string uptime;
-  if (!android::base::ReadFileToString("/proc/uptime", &uptime)) return 0;
-  return 100LL * strtod(uptime.c_str(), NULL);
+    constexpr int64_t kNanosecondsPerJiffy = 10000000;
+    boot_clock::time_point uptime = boot_clock::now();
+    return uptime.time_since_epoch().count() / kNanosecondsPerJiffy;
 }
 
 static std::unique_ptr<FILE, decltype(&fclose)> fopen_unique(const char* filename,
@@ -163,37 +165,37 @@
   LOG(INFO) << "Bootcharting finished";
 }
 
-static int do_bootchart_start() {
-  // We don't care about the content, but we do care that /data/bootchart/enabled actually exists.
-  std::string start;
-  if (!android::base::ReadFileToString("/data/bootchart/enabled", &start)) {
-    LOG(VERBOSE) << "Not bootcharting";
-    return 0;
-  }
+static Result<Success> do_bootchart_start() {
+    // We don't care about the content, but we do care that /data/bootchart/enabled actually exists.
+    std::string start;
+    if (!android::base::ReadFileToString("/data/bootchart/enabled", &start)) {
+        LOG(VERBOSE) << "Not bootcharting";
+        return Success();
+    }
 
-  g_bootcharting_thread = new std::thread(bootchart_thread_main);
-  return 0;
+    g_bootcharting_thread = new std::thread(bootchart_thread_main);
+    return Success();
 }
 
-static int do_bootchart_stop() {
-  if (!g_bootcharting_thread) return 0;
+static Result<Success> do_bootchart_stop() {
+    if (!g_bootcharting_thread) return Success();
 
-  // Tell the worker thread it's time to quit.
-  {
-    std::lock_guard<std::mutex> lock(g_bootcharting_finished_mutex);
-    g_bootcharting_finished = true;
-    g_bootcharting_finished_cv.notify_one();
-  }
+    // Tell the worker thread it's time to quit.
+    {
+        std::lock_guard<std::mutex> lock(g_bootcharting_finished_mutex);
+        g_bootcharting_finished = true;
+        g_bootcharting_finished_cv.notify_one();
+    }
 
-  g_bootcharting_thread->join();
-  delete g_bootcharting_thread;
-  g_bootcharting_thread = nullptr;
-  return 0;
+    g_bootcharting_thread->join();
+    delete g_bootcharting_thread;
+    g_bootcharting_thread = nullptr;
+    return Success();
 }
 
-int do_bootchart(const std::vector<std::string>& args) {
-  if (args[1] == "start") return do_bootchart_start();
-  return do_bootchart_stop();
+Result<Success> do_bootchart(const BuiltinArguments& args) {
+    if (args[1] == "start") return do_bootchart_start();
+    return do_bootchart_stop();
 }
 
 }  // namespace init
diff --git a/init/bootchart.h b/init/bootchart.h
index e4f7b59..05474ca 100644
--- a/init/bootchart.h
+++ b/init/bootchart.h
@@ -20,10 +20,13 @@
 #include <string>
 #include <vector>
 
+#include "builtin_arguments.h"
+#include "result.h"
+
 namespace android {
 namespace init {
 
-int do_bootchart(const std::vector<std::string>& args);
+Result<Success> do_bootchart(const BuiltinArguments& args);
 
 }  // namespace init
 }  // namespace android
diff --git a/init/builtin_arguments.h b/init/builtin_arguments.h
new file mode 100644
index 0000000..1742b78
--- /dev/null
+++ b/init/builtin_arguments.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2017 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_BUILTIN_ARGUMENTS_H
+#define _INIT_BUILTIN_ARGUMENTS_H
+
+#include <string>
+#include <vector>
+
+namespace android {
+namespace init {
+
+struct BuiltinArguments {
+    BuiltinArguments(const std::string& context) : context(context) {}
+    BuiltinArguments(std::vector<std::string> args, const std::string& context)
+        : args(std::move(args)), context(context) {}
+
+    const std::string& operator[](std::size_t i) const { return args[i]; }
+    auto begin() const { return args.begin(); }
+    auto end() const { return args.end(); }
+    auto size() const { return args.size(); }
+
+    std::vector<std::string> args;
+    const std::string& context;
+};
+
+}  // namespace init
+}  // namespace android
+
+#endif
diff --git a/init/builtins.cpp b/init/builtins.cpp
index dfd7b73..17d34e1 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -19,6 +19,7 @@
 #include <dirent.h>
 #include <errno.h>
 #include <fcntl.h>
+#include <fts.h>
 #include <linux/loop.h>
 #include <linux/module.h>
 #include <mntent.h>
@@ -44,7 +45,9 @@
 #include <android-base/logging.h>
 #include <android-base/parseint.h>
 #include <android-base/properties.h>
+#include <android-base/stringprintf.h>
 #include <android-base/strings.h>
+#include <android-base/unique_fd.h>
 #include <bootloader_message/bootloader_message.h>
 #include <cutils/android_reboot.h>
 #include <ext4_utils/ext4_crypt.h>
@@ -53,19 +56,24 @@
 #include <selinux/android.h>
 #include <selinux/label.h>
 #include <selinux/selinux.h>
+#include <system/thread_defs.h>
 
-#include "action.h"
+#include "action_manager.h"
 #include "bootchart.h"
 #include "init.h"
-#include "init_parser.h"
+#include "parser.h"
 #include "property_service.h"
 #include "reboot.h"
+#include "rlimit_parser.h"
+#include "selinux.h"
 #include "service.h"
-#include "signal_handler.h"
+#include "subcontext.h"
 #include "util.h"
 
 using namespace std::literals::string_literals;
 
+using android::base::unique_fd;
+
 #define chmod DO_NOT_USE_CHMOD_USE_FCHMODAT_SYMLINK_NOFOLLOW
 
 namespace android {
@@ -73,128 +81,145 @@
 
 static constexpr std::chrono::nanoseconds kCommandRetryTimeout = 5s;
 
-static int insmod(const char *filename, const char *options, int flags) {
-    int fd = open(filename, O_RDONLY | O_NOFOLLOW | O_CLOEXEC);
-    if (fd == -1) {
-        PLOG(ERROR) << "insmod: open(\"" << filename << "\") failed";
-        return -1;
-    }
-    int rc = syscall(__NR_finit_module, fd, options, flags);
-    if (rc == -1) {
-        PLOG(ERROR) << "finit_module for \"" << filename << "\" failed";
-    }
-    close(fd);
-    return rc;
-}
-
-static int __ifupdown(const char *interface, int up) {
-    struct ifreq ifr;
-    int s, ret;
-
-    strlcpy(ifr.ifr_name, interface, IFNAMSIZ);
-
-    s = socket(AF_INET, SOCK_DGRAM, 0);
-    if (s < 0)
-        return -1;
-
-    ret = ioctl(s, SIOCGIFFLAGS, &ifr);
-    if (ret < 0) {
-        goto done;
-    }
-
-    if (up)
-        ifr.ifr_flags |= IFF_UP;
-    else
-        ifr.ifr_flags &= ~IFF_UP;
-
-    ret = ioctl(s, SIOCSIFFLAGS, &ifr);
-
-done:
-    close(s);
-    return ret;
-}
-
-static int reboot_into_recovery(const std::vector<std::string>& options) {
+static Result<Success> reboot_into_recovery(const std::vector<std::string>& options) {
+    LOG(ERROR) << "Rebooting into recovery";
     std::string err;
     if (!write_bootloader_message(options, &err)) {
-        LOG(ERROR) << "failed to set bootloader message: " << err;
-        return -1;
+        return Error() << "Failed to set bootloader message: " << err;
     }
     property_set("sys.powerctl", "reboot,recovery");
-    return 0;
+    return Success();
 }
 
-static int do_class_start(const std::vector<std::string>& args) {
-        /* Starting a class does not start services
-         * which are explicitly disabled.  They must
-         * be started individually.
-         */
-    ServiceManager::GetInstance().
-        ForEachServiceInClass(args[1], [] (Service* s) { s->StartIfNotDisabled(); });
-    return 0;
-}
-
-static int do_class_stop(const std::vector<std::string>& args) {
-    ServiceManager::GetInstance().
-        ForEachServiceInClass(args[1], [] (Service* s) { s->Stop(); });
-    return 0;
-}
-
-static int do_class_reset(const std::vector<std::string>& args) {
-    ServiceManager::GetInstance().
-        ForEachServiceInClass(args[1], [] (Service* s) { s->Reset(); });
-    return 0;
-}
-
-static int do_class_restart(const std::vector<std::string>& args) {
-    ServiceManager::GetInstance().
-        ForEachServiceInClass(args[1], [] (Service* s) { s->Restart(); });
-    return 0;
-}
-
-static int do_domainname(const std::vector<std::string>& args) {
-    std::string err;
-    if (!WriteFile("/proc/sys/kernel/domainname", args[1], &err)) {
-        LOG(ERROR) << err;
-        return -1;
+template <typename F>
+static void ForEachServiceInClass(const std::string& classname, F function) {
+    for (const auto& service : ServiceList::GetInstance()) {
+        if (service->classnames().count(classname)) std::invoke(function, service);
     }
-    return 0;
 }
 
-static int do_enable(const std::vector<std::string>& args) {
-    Service* svc = ServiceManager::GetInstance().FindServiceByName(args[1]);
-    if (!svc) {
-        return -1;
+static Result<Success> do_class_start(const BuiltinArguments& args) {
+    // Starting a class does not start services which are explicitly disabled.
+    // They must  be started individually.
+    for (const auto& service : ServiceList::GetInstance()) {
+        if (service->classnames().count(args[1])) {
+            if (auto result = service->StartIfNotDisabled(); !result) {
+                LOG(ERROR) << "Could not start service '" << service->name()
+                           << "' as part of class '" << args[1] << "': " << result.error();
+            }
+        }
     }
-    return svc->Enable();
+    return Success();
 }
 
-static int do_exec(const std::vector<std::string>& args) {
-    return ServiceManager::GetInstance().Exec(args) ? 0 : -1;
+static Result<Success> do_class_stop(const BuiltinArguments& args) {
+    ForEachServiceInClass(args[1], &Service::Stop);
+    return Success();
 }
 
-static int do_exec_start(const std::vector<std::string>& args) {
-    return ServiceManager::GetInstance().ExecStart(args[1]) ? 0 : -1;
+static Result<Success> do_class_reset(const BuiltinArguments& args) {
+    ForEachServiceInClass(args[1], &Service::Reset);
+    return Success();
 }
 
-static int do_export(const std::vector<std::string>& args) {
-    return add_environment(args[1].c_str(), args[2].c_str());
+static Result<Success> do_class_restart(const BuiltinArguments& args) {
+    ForEachServiceInClass(args[1], &Service::Restart);
+    return Success();
 }
 
-static int do_hostname(const std::vector<std::string>& args) {
-    std::string err;
-    if (!WriteFile("/proc/sys/kernel/hostname", args[1], &err)) {
-        LOG(ERROR) << err;
-        return -1;
+static Result<Success> do_domainname(const BuiltinArguments& args) {
+    if (auto result = WriteFile("/proc/sys/kernel/domainname", args[1]); !result) {
+        return Error() << "Unable to write to /proc/sys/kernel/domainname: " << result.error();
     }
-    return 0;
+    return Success();
 }
 
-static int do_ifup(const std::vector<std::string>& args) {
-    return __ifupdown(args[1].c_str(), 1);
+static Result<Success> do_enable(const BuiltinArguments& args) {
+    Service* svc = ServiceList::GetInstance().FindService(args[1]);
+    if (!svc) return Error() << "Could not find service";
+
+    if (auto result = svc->Enable(); !result) {
+        return Error() << "Could not enable service: " << result.error();
+    }
+
+    return Success();
 }
 
-static int do_insmod(const std::vector<std::string>& args) {
+static Result<Success> do_exec(const BuiltinArguments& args) {
+    auto service = Service::MakeTemporaryOneshotService(args.args);
+    if (!service) {
+        return Error() << "Could not create exec service";
+    }
+    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_exec_background(const BuiltinArguments& args) {
+    auto service = Service::MakeTemporaryOneshotService(args.args);
+    if (!service) {
+        return Error() << "Could not create exec background service";
+    }
+    if (auto result = service->Start(); !result) {
+        return Error() << "Could not start exec background service: " << result.error();
+    }
+
+    ServiceList::GetInstance().AddService(std::move(service));
+    return Success();
+}
+
+static Result<Success> do_exec_start(const BuiltinArguments& args) {
+    Service* service = ServiceList::GetInstance().FindService(args[1]);
+    if (!service) {
+        return Error() << "Service not found";
+    }
+
+    if (auto result = service->ExecStart(); !result) {
+        return Error() << "Could not start exec service: " << result.error();
+    }
+
+    return Success();
+}
+
+static Result<Success> do_export(const BuiltinArguments& args) {
+    if (setenv(args[1].c_str(), args[2].c_str(), 1) == -1) {
+        return ErrnoError() << "setenv() failed";
+    }
+    return Success();
+}
+
+static Result<Success> do_hostname(const BuiltinArguments& args) {
+    if (auto result = WriteFile("/proc/sys/kernel/hostname", args[1]); !result) {
+        return Error() << "Unable to write to /proc/sys/kernel/hostname: " << result.error();
+    }
+    return Success();
+}
+
+static Result<Success> do_ifup(const BuiltinArguments& args) {
+    struct ifreq ifr;
+
+    strlcpy(ifr.ifr_name, args[1].c_str(), IFNAMSIZ);
+
+    unique_fd s(TEMP_FAILURE_RETRY(socket(AF_INET, SOCK_DGRAM, 0)));
+    if (s < 0) return ErrnoError() << "opening socket failed";
+
+    if (ioctl(s, SIOCGIFFLAGS, &ifr) < 0) {
+        return ErrnoError() << "ioctl(..., SIOCGIFFLAGS, ...) failed";
+    }
+
+    ifr.ifr_flags |= IFF_UP;
+
+    if (ioctl(s, SIOCSIFFLAGS, &ifr) < 0) {
+        return ErrnoError() << "ioctl(..., SIOCSIFFLAGS, ...) failed";
+    }
+
+    return Success();
+}
+
+static Result<Success> do_insmod(const BuiltinArguments& args) {
     int flags = 0;
     auto it = args.begin() + 1;
 
@@ -205,72 +230,98 @@
 
     std::string filename = *it++;
     std::string options = android::base::Join(std::vector<std::string>(it, args.end()), ' ');
-    return insmod(filename.c_str(), options.c_str(), flags);
+
+    unique_fd fd(TEMP_FAILURE_RETRY(open(filename.c_str(), O_RDONLY | O_NOFOLLOW | O_CLOEXEC)));
+    if (fd == -1) return ErrnoError() << "open(\"" << filename << "\") failed";
+
+    int rc = syscall(__NR_finit_module, fd.get(), options.c_str(), flags);
+    if (rc == -1) return ErrnoError() << "finit_module for \"" << filename << "\" failed";
+
+    return Success();
 }
 
-static int do_mkdir(const std::vector<std::string>& args) {
+static Result<Success> do_interface_restart(const BuiltinArguments& args) {
+    Service* svc = ServiceList::GetInstance().FindInterface(args[1]);
+    if (!svc) return Error() << "interface " << args[1] << " not found";
+    svc->Restart();
+    return Success();
+}
+
+static Result<Success> do_interface_start(const BuiltinArguments& args) {
+    Service* svc = ServiceList::GetInstance().FindInterface(args[1]);
+    if (!svc) return Error() << "interface " << args[1] << " not found";
+    if (auto result = svc->Start(); !result) {
+        return Error() << "Could not start interface: " << result.error();
+    }
+    return Success();
+}
+
+static Result<Success> do_interface_stop(const BuiltinArguments& args) {
+    Service* svc = ServiceList::GetInstance().FindInterface(args[1]);
+    if (!svc) return Error() << "interface " << args[1] << " not found";
+    svc->Stop();
+    return Success();
+}
+
+// mkdir <path> [mode] [owner] [group]
+static Result<Success> do_mkdir(const BuiltinArguments& args) {
     mode_t mode = 0755;
-    int ret;
-
-    /* mkdir <path> [mode] [owner] [group] */
-
     if (args.size() >= 3) {
         mode = std::strtoul(args[2].c_str(), 0, 8);
     }
 
-    ret = make_dir(args[1].c_str(), mode, sehandle);
-    /* chmod in case the directory already exists */
-    if (ret == -1 && errno == EEXIST) {
-        ret = fchmodat(AT_FDCWD, args[1].c_str(), mode, AT_SYMLINK_NOFOLLOW);
-    }
-    if (ret == -1) {
-        return -errno;
+    if (!make_dir(args[1], mode)) {
+        /* chmod in case the directory already exists */
+        if (errno == EEXIST) {
+            if (fchmodat(AT_FDCWD, args[1].c_str(), mode, AT_SYMLINK_NOFOLLOW) == -1) {
+                return ErrnoError() << "fchmodat() failed";
+            }
+        } else {
+            return ErrnoError() << "mkdir() failed";
+        }
     }
 
     if (args.size() >= 4) {
-        uid_t uid;
-        std::string decode_uid_err;
-        if (!DecodeUid(args[3], &uid, &decode_uid_err)) {
-            LOG(ERROR) << "Unable to find UID for '" << args[3] << "': " << decode_uid_err;
-            return -1;
+        auto uid = DecodeUid(args[3]);
+        if (!uid) {
+            return Error() << "Unable to decode UID for '" << args[3] << "': " << uid.error();
         }
-        gid_t gid = -1;
+        Result<gid_t> gid = -1;
 
         if (args.size() == 5) {
-            if (!DecodeUid(args[4], &gid, &decode_uid_err)) {
-                LOG(ERROR) << "Unable to find GID for '" << args[3] << "': " << decode_uid_err;
-                return -1;
+            gid = DecodeUid(args[4]);
+            if (!gid) {
+                return Error() << "Unable to decode GID for '" << args[3] << "': " << gid.error();
             }
         }
 
-        if (lchown(args[1].c_str(), uid, gid) == -1) {
-            return -errno;
+        if (lchown(args[1].c_str(), *uid, *gid) == -1) {
+            return ErrnoError() << "lchown failed";
         }
 
         /* chown may have cleared S_ISUID and S_ISGID, chmod again */
         if (mode & (S_ISUID | S_ISGID)) {
-            ret = fchmodat(AT_FDCWD, args[1].c_str(), mode, AT_SYMLINK_NOFOLLOW);
-            if (ret == -1) {
-                return -errno;
+            if (fchmodat(AT_FDCWD, args[1].c_str(), mode, AT_SYMLINK_NOFOLLOW) == -1) {
+                return ErrnoError() << "fchmodat failed";
             }
         }
     }
 
     if (e4crypt_is_native()) {
         if (e4crypt_set_directory_policy(args[1].c_str())) {
-            const std::vector<std::string> options = {
-                "--prompt_and_wipe_data",
-                "--reason=set_policy_failed:"s + args[1]};
-            reboot_into_recovery(options);
-            return -1;
+            return reboot_into_recovery(
+                {"--prompt_and_wipe_data", "--reason=set_policy_failed:"s + args[1]});
         }
     }
-    return 0;
+    return Success();
 }
 
 /* umount <path> */
-static int do_umount(const std::vector<std::string>& args) {
-  return umount(args[1].c_str());
+static Result<Success> do_umount(const BuiltinArguments& args) {
+    if (umount(args[1].c_str()) < 0) {
+        return ErrnoError() << "umount() failed";
+    }
+    return Success();
 }
 
 static struct {
@@ -298,16 +349,13 @@
 #define DATA_MNT_POINT "/data"
 
 /* mount <type> <device> <path> <flags ...> <options> */
-static int do_mount(const std::vector<std::string>& args) {
-    char tmp[64];
-    const char *source, *target, *system;
-    const char *options = NULL;
+static Result<Success> do_mount(const BuiltinArguments& args) {
+    const char* options = nullptr;
     unsigned flags = 0;
-    std::size_t na = 0;
-    int n, i;
-    int wait = 0;
+    bool wait = false;
 
-    for (na = 4; na < args.size(); na++) {
+    for (size_t na = 4; na < args.size(); na++) {
+        size_t i;
         for (i = 0; mount_flags[i].name; i++) {
             if (!args[na].compare(mount_flags[i].name)) {
                 flags |= mount_flags[i].flag;
@@ -316,71 +364,54 @@
         }
 
         if (!mount_flags[i].name) {
-            if (!args[na].compare("wait"))
-                wait = 1;
-            /* if our last argument isn't a flag, wolf it up as an option string */
-            else if (na + 1 == args.size())
+            if (!args[na].compare("wait")) {
+                wait = true;
+                // If our last argument isn't a flag, wolf it up as an option string.
+            } else if (na + 1 == args.size()) {
                 options = args[na].c_str();
+            }
         }
     }
 
-    system = args[1].c_str();
-    source = args[2].c_str();
-    target = args[3].c_str();
+    const char* system = args[1].c_str();
+    const char* source = args[2].c_str();
+    const char* target = args[3].c_str();
 
-    if (!strncmp(source, "loop@", 5)) {
-        int mode, loop, fd;
-        struct loop_info info;
+    if (android::base::StartsWith(source, "loop@")) {
+        int mode = (flags & MS_RDONLY) ? O_RDONLY : O_RDWR;
+        unique_fd fd(TEMP_FAILURE_RETRY(open(source + 5, mode | O_CLOEXEC)));
+        if (fd < 0) return ErrnoError() << "open(" << source + 5 << ", " << mode << ") failed";
 
-        mode = (flags & MS_RDONLY) ? O_RDONLY : O_RDWR;
-        fd = open(source + 5, mode | O_CLOEXEC);
-        if (fd < 0) {
-            return -1;
-        }
+        for (size_t n = 0;; n++) {
+            std::string tmp = android::base::StringPrintf("/dev/block/loop%zu", n);
+            unique_fd loop(TEMP_FAILURE_RETRY(open(tmp.c_str(), mode | O_CLOEXEC)));
+            if (loop < 0) return ErrnoError() << "open(" << tmp << ", " << mode << ") failed";
 
-        for (n = 0; ; n++) {
-            snprintf(tmp, sizeof(tmp), "/dev/block/loop%d", n);
-            loop = open(tmp, mode | O_CLOEXEC);
-            if (loop < 0) {
-                close(fd);
-                return -1;
-            }
-
+            loop_info info;
             /* if it is a blank loop device */
             if (ioctl(loop, LOOP_GET_STATUS, &info) < 0 && errno == ENXIO) {
                 /* if it becomes our loop device */
-                if (ioctl(loop, LOOP_SET_FD, fd) >= 0) {
-                    close(fd);
-
-                    if (mount(tmp, target, system, flags, options) < 0) {
+                if (ioctl(loop, LOOP_SET_FD, fd.get()) >= 0) {
+                    if (mount(tmp.c_str(), target, system, flags, options) < 0) {
                         ioctl(loop, LOOP_CLR_FD, 0);
-                        close(loop);
-                        return -1;
+                        return ErrnoError() << "mount() failed";
                     }
-
-                    close(loop);
-                    goto exit_success;
+                    return Success();
                 }
             }
-
-            close(loop);
         }
 
-        close(fd);
-        LOG(ERROR) << "out of loopback devices";
-        return -1;
+        return Error() << "out of loopback devices";
     } else {
         if (wait)
             wait_for_file(source, kCommandRetryTimeout);
         if (mount(source, target, system, flags, options) < 0) {
-            return -1;
+            return ErrnoError() << "mount() failed";
         }
 
     }
 
-exit_success:
-    return 0;
-
+    return Success();
 }
 
 /* Imports .rc files from the specified paths. Default ones are applied if none is given.
@@ -388,21 +419,15 @@
  * start_index: index of the first path in the args list
  */
 static void import_late(const std::vector<std::string>& args, size_t start_index, size_t end_index) {
-    Parser& parser = Parser::GetInstance();
+    auto& action_manager = ActionManager::GetInstance();
+    auto& service_list = ServiceList::GetInstance();
+    Parser parser = CreateParser(action_manager, service_list);
     if (end_index <= start_index) {
         // Fallbacks for partitions on which early mount isn't enabled.
-        if (!parser.is_system_etc_init_loaded()) {
-            parser.ParseConfig("/system/etc/init");
-            parser.set_is_system_etc_init_loaded(true);
+        for (const auto& path : late_import_paths) {
+            parser.ParseConfig(path);
         }
-        if (!parser.is_vendor_etc_init_loaded()) {
-            parser.ParseConfig("/vendor/etc/init");
-            parser.set_is_vendor_etc_init_loaded(true);
-        }
-        if (!parser.is_odm_etc_init_loaded()) {
-            parser.ParseConfig("/odm/etc/init");
-            parser.set_is_odm_etc_init_loaded(true);
-        }
+        late_import_paths.clear();
     } else {
         for (size_t i = start_index; i < end_index; ++i) {
             parser.ParseConfig(args[i]);
@@ -418,9 +443,7 @@
  *
  *  Call fs_mgr_mount_all() to mount the given fstab
  */
-static int mount_fstab(const char* fstabfile, int mount_mode) {
-    int ret = -1;
-
+static Result<int> mount_fstab(const char* fstabfile, int mount_mode) {
     /*
      * Call fs_mgr_mount_all() to mount all filesystems.  We fork(2) and
      * do the call in the child to provide protection to the main init
@@ -438,9 +461,9 @@
         }
 
         if (WIFEXITED(status)) {
-            ret = WEXITSTATUS(status);
+            return WEXITSTATUS(status);
         } else {
-            ret = -1;
+            return Error() << "child aborted";
         }
     } else if (pid == 0) {
         /* child, call fs_mgr_mount_all() */
@@ -457,10 +480,8 @@
         }
         _exit(child_ret);
     } else {
-        /* fork failed, return an error */
-        return -1;
+        return Error() << "fork() failed";
     }
-    return ret;
 }
 
 /* Queue event based on fs_mgr return code.
@@ -472,29 +493,32 @@
  *
  * return code is processed based on input code
  */
-static int queue_fs_event(int code) {
-    int ret = code;
+static Result<Success> queue_fs_event(int code) {
     if (code == FS_MGR_MNTALL_DEV_NEEDS_ENCRYPTION) {
         ActionManager::GetInstance().QueueEventTrigger("encrypt");
+        return Success();
     } else if (code == FS_MGR_MNTALL_DEV_MIGHT_BE_ENCRYPTED) {
         property_set("ro.crypto.state", "encrypted");
         property_set("ro.crypto.type", "block");
         ActionManager::GetInstance().QueueEventTrigger("defaultcrypto");
+        return Success();
     } else if (code == FS_MGR_MNTALL_DEV_NOT_ENCRYPTED) {
         property_set("ro.crypto.state", "unencrypted");
         ActionManager::GetInstance().QueueEventTrigger("nonencrypted");
+        return Success();
     } else if (code == FS_MGR_MNTALL_DEV_NOT_ENCRYPTABLE) {
         property_set("ro.crypto.state", "unsupported");
         ActionManager::GetInstance().QueueEventTrigger("nonencrypted");
+        return Success();
     } else if (code == FS_MGR_MNTALL_DEV_NEEDS_RECOVERY) {
         /* Setup a wipe via recovery, and reboot into recovery */
         PLOG(ERROR) << "fs_mgr_mount_all suggested recovery, so wiping data via recovery.";
         const std::vector<std::string> options = {"--wipe_data", "--reason=fs_mgr_mount_all" };
-        ret = reboot_into_recovery(options);
+        return reboot_into_recovery(options);
         /* If reboot worked, there is no return. */
     } else if (code == FS_MGR_MNTALL_DEV_FILE_ENCRYPTED) {
         if (e4crypt_install_keyring()) {
-            return -1;
+            return Error() << "e4crypt_install_keyring() failed";
         }
         property_set("ro.crypto.state", "encrypted");
         property_set("ro.crypto.type", "file");
@@ -502,12 +526,35 @@
         // Although encrypted, we have device key, so we do not need to
         // do anything different from the nonencrypted case.
         ActionManager::GetInstance().QueueEventTrigger("nonencrypted");
+        return Success();
+    } else if (code == FS_MGR_MNTALL_DEV_IS_METADATA_ENCRYPTED) {
+        if (e4crypt_install_keyring()) {
+            return Error() << "e4crypt_install_keyring() failed";
+        }
+        property_set("ro.crypto.state", "encrypted");
+        property_set("ro.crypto.type", "file");
+
+        // Although encrypted, vold has already set the device up, so we do not need to
+        // do anything different from the nonencrypted case.
+        ActionManager::GetInstance().QueueEventTrigger("nonencrypted");
+        return Success();
+    } else if (code == FS_MGR_MNTALL_DEV_NEEDS_METADATA_ENCRYPTION) {
+        if (e4crypt_install_keyring()) {
+            return Error() << "e4crypt_install_keyring() failed";
+        }
+        property_set("ro.crypto.state", "encrypted");
+        property_set("ro.crypto.type", "file");
+
+        // Although encrypted, vold has already set the device up, so we do not need to
+        // do anything different from the nonencrypted case.
+        ActionManager::GetInstance().QueueEventTrigger("nonencrypted");
+        return Success();
     } else if (code > 0) {
-        PLOG(ERROR) << "fs_mgr_mount_all returned unexpected error " << code;
+        Error() << "fs_mgr_mount_all() returned unexpected error " << code;
     }
     /* else ... < 0: error */
 
-    return ret;
+    return Error() << "Invalid code: " << code;
 }
 
 /* mount_all <fstab> [ <path> ]* [--<options>]*
@@ -515,7 +562,7 @@
  * This function might request a reboot, in which case it will
  * not return.
  */
-static int do_mount_all(const std::vector<std::string>& args) {
+static Result<Success> do_mount_all(const BuiltinArguments& args) {
     std::size_t na = 0;
     bool import_rc = true;
     bool queue_event = true;
@@ -540,24 +587,30 @@
 
     std::string prop_name = "ro.boottime.init.mount_all."s + prop_post_fix;
     android::base::Timer t;
-    int ret =  mount_fstab(fstabfile, mount_mode);
+    auto mount_fstab_return_code = mount_fstab(fstabfile, mount_mode);
+    if (!mount_fstab_return_code) {
+        return Error() << "mount_fstab() failed " << mount_fstab_return_code.error();
+    }
     property_set(prop_name, std::to_string(t.duration().count()));
 
     if (import_rc) {
         /* Paths of .rc files are specified at the 2nd argument and beyond */
-        import_late(args, 2, path_arg_end);
+        import_late(args.args, 2, path_arg_end);
     }
 
     if (queue_event) {
         /* queue_fs_event will queue event based on mount_fstab return code
          * and return processed return code*/
-        ret = queue_fs_event(ret);
+        auto queue_fs_result = queue_fs_event(*mount_fstab_return_code);
+        if (!queue_fs_result) {
+            return Error() << "queue_fs_event() failed: " << queue_fs_result.error();
+        }
     }
 
-    return ret;
+    return Success();
 }
 
-static int do_swapon_all(const std::vector<std::string>& args) {
+static Result<Success> do_swapon_all(const BuiltinArguments& args) {
     struct fstab *fstab;
     int ret;
 
@@ -565,89 +618,118 @@
     ret = fs_mgr_swapon_all(fstab);
     fs_mgr_free_fstab(fstab);
 
-    return ret;
+    if (ret != 0) return Error() << "fs_mgr_swapon_all() failed";
+    return Success();
 }
 
-static int do_setprop(const std::vector<std::string>& args) {
+static Result<Success> do_setprop(const BuiltinArguments& args) {
     property_set(args[1], args[2]);
-    return 0;
+    return Success();
 }
 
-static int do_setrlimit(const std::vector<std::string>& args) {
-    struct rlimit limit;
-    int resource;
-    if (android::base::ParseInt(args[1], &resource) &&
-        android::base::ParseUint(args[2], &limit.rlim_cur) &&
-        android::base::ParseUint(args[3], &limit.rlim_max)) {
-        return setrlimit(resource, &limit);
+static Result<Success> do_setrlimit(const BuiltinArguments& args) {
+    auto rlimit = ParseRlimit(args.args);
+    if (!rlimit) return rlimit.error();
+
+    if (setrlimit(rlimit->first, &rlimit->second) == -1) {
+        return ErrnoError() << "setrlimit failed";
     }
-    LOG(WARNING) << "ignoring setrlimit " << args[1] << " " << args[2] << " " << args[3];
-    return -1;
+    return Success();
 }
 
-static int do_start(const std::vector<std::string>& args) {
-    Service* svc = ServiceManager::GetInstance().FindServiceByName(args[1]);
-    if (!svc) {
-        LOG(ERROR) << "do_start: Service " << args[1] << " not found";
-        return -1;
+static Result<Success> do_start(const BuiltinArguments& args) {
+    Service* svc = ServiceList::GetInstance().FindService(args[1]);
+    if (!svc) return Error() << "service " << args[1] << " not found";
+    if (auto result = svc->Start(); !result) {
+        return Error() << "Could not start service: " << result.error();
     }
-    if (!svc->Start())
-        return -1;
-    return 0;
+    return Success();
 }
 
-static int do_stop(const std::vector<std::string>& args) {
-    Service* svc = ServiceManager::GetInstance().FindServiceByName(args[1]);
-    if (!svc) {
-        LOG(ERROR) << "do_stop: Service " << args[1] << " not found";
-        return -1;
-    }
+static Result<Success> do_stop(const BuiltinArguments& args) {
+    Service* svc = ServiceList::GetInstance().FindService(args[1]);
+    if (!svc) return Error() << "service " << args[1] << " not found";
     svc->Stop();
-    return 0;
+    return Success();
 }
 
-static int do_restart(const std::vector<std::string>& args) {
-    Service* svc = ServiceManager::GetInstance().FindServiceByName(args[1]);
-    if (!svc) {
-        LOG(ERROR) << "do_restart: Service " << args[1] << " not found";
-        return -1;
-    }
+static Result<Success> do_restart(const BuiltinArguments& args) {
+    Service* svc = ServiceList::GetInstance().FindService(args[1]);
+    if (!svc) return Error() << "service " << args[1] << " not found";
     svc->Restart();
-    return 0;
+    return Success();
 }
 
-static int do_trigger(const std::vector<std::string>& args) {
+static Result<Success> do_trigger(const BuiltinArguments& args) {
     ActionManager::GetInstance().QueueEventTrigger(args[1]);
-    return 0;
+    return Success();
 }
 
-static int do_symlink(const std::vector<std::string>& args) {
-    return symlink(args[1].c_str(), args[2].c_str());
-}
-
-static int do_rm(const std::vector<std::string>& args) {
-    return unlink(args[1].c_str());
-}
-
-static int do_rmdir(const std::vector<std::string>& args) {
-    return rmdir(args[1].c_str());
-}
-
-static int do_sysclktz(const std::vector<std::string>& args) {
-    struct timezone tz = {};
-    if (android::base::ParseInt(args[1], &tz.tz_minuteswest) && settimeofday(NULL, &tz) != -1) {
-        return 0;
+static int MakeSymlink(const std::string& target, const std::string& linkpath) {
+    std::string secontext;
+    // Passing 0 for mode should work.
+    if (SelabelLookupFileContext(linkpath, 0, &secontext) && !secontext.empty()) {
+        setfscreatecon(secontext.c_str());
     }
-    return -1;
+
+    int rc = symlink(target.c_str(), linkpath.c_str());
+
+    if (!secontext.empty()) {
+        int save_errno = errno;
+        setfscreatecon(nullptr);
+        errno = save_errno;
+    }
+
+    return rc;
 }
 
-static int do_verity_load_state(const std::vector<std::string>& args) {
+static Result<Success> do_symlink(const BuiltinArguments& args) {
+    if (MakeSymlink(args[1], args[2]) < 0) {
+        // The symlink builtin is often used to create symlinks for older devices to be backwards
+        // compatible with new paths, therefore we skip reporting this error.
+        if (errno == EEXIST && android::base::GetMinimumLogSeverity() > android::base::DEBUG) {
+            return Success();
+        }
+        return ErrnoError() << "symlink() failed";
+    }
+    return Success();
+}
+
+static Result<Success> do_rm(const BuiltinArguments& args) {
+    if (unlink(args[1].c_str()) < 0) {
+        return ErrnoError() << "unlink() failed";
+    }
+    return Success();
+}
+
+static Result<Success> do_rmdir(const BuiltinArguments& args) {
+    if (rmdir(args[1].c_str()) < 0) {
+        return ErrnoError() << "rmdir() failed";
+    }
+    return Success();
+}
+
+static Result<Success> do_sysclktz(const BuiltinArguments& args) {
+    struct timezone tz = {};
+    if (!android::base::ParseInt(args[1], &tz.tz_minuteswest)) {
+        return Error() << "Unable to parse mins_west_of_gmt";
+    }
+
+    if (settimeofday(nullptr, &tz) == -1) {
+        return ErrnoError() << "settimeofday() failed";
+    }
+    return Success();
+}
+
+static Result<Success> do_verity_load_state(const BuiltinArguments& args) {
     int mode = -1;
     bool loaded = fs_mgr_load_verity_state(&mode);
     if (loaded && mode != VERITY_MODE_DEFAULT) {
         ActionManager::GetInstance().QueueEventTrigger("verity-logging");
     }
-    return loaded ? 0 : 1;
+    if (!loaded) return Error() << "Could not load verity state";
+
+    return Success();
 }
 
 static void verity_update_property(fstab_rec *fstab, const char *mount_point,
@@ -655,55 +737,137 @@
     property_set("partition."s + mount_point + ".verified", std::to_string(mode));
 }
 
-static int do_verity_update_state(const std::vector<std::string>& args) {
-    return fs_mgr_update_verity_state(verity_update_property) ? 0 : 1;
+static Result<Success> do_verity_update_state(const BuiltinArguments& args) {
+    if (!fs_mgr_update_verity_state(verity_update_property)) {
+        return Error() << "fs_mgr_update_verity_state() failed";
+    }
+    return Success();
 }
 
-static int do_write(const std::vector<std::string>& args) {
-    std::string err;
-    if (!WriteFile(args[1], args[2], &err)) {
-        LOG(ERROR) << err;
-        return -1;
+static Result<Success> do_write(const BuiltinArguments& args) {
+    if (auto result = WriteFile(args[1], args[2]); !result) {
+        return Error() << "Unable to write to file '" << args[1] << "': " << result.error();
     }
-    return 0;
+
+    return Success();
 }
 
-static int do_copy(const std::vector<std::string>& args) {
-    std::string data;
-    std::string err;
-    if (!ReadFile(args[1], &data, &err)) {
-        LOG(ERROR) << err;
-        return -1;
+static Result<Success> readahead_file(const std::string& filename, bool fully) {
+    android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(filename.c_str(), O_RDONLY)));
+    if (fd == -1) {
+        return ErrnoError() << "Error opening file";
     }
-    if (!WriteFile(args[2], data, &err)) {
-        LOG(ERROR) << err;
-        return -1;
+    if (posix_fadvise(fd, 0, 0, POSIX_FADV_WILLNEED)) {
+        return ErrnoError() << "Error posix_fadvise file";
     }
-    return 0;
+    if (readahead(fd, 0, std::numeric_limits<size_t>::max())) {
+        return ErrnoError() << "Error readahead file";
+    }
+    if (fully) {
+        char buf[BUFSIZ];
+        ssize_t n;
+        while ((n = TEMP_FAILURE_RETRY(read(fd, &buf[0], sizeof(buf)))) > 0) {
+        }
+        if (n != 0) {
+            return ErrnoError() << "Error reading file";
+        }
+    }
+    return Success();
 }
 
-static int do_chown(const std::vector<std::string>& args) {
-    uid_t uid;
-    std::string decode_uid_err;
-    if (!DecodeUid(args[1], &uid, &decode_uid_err)) {
-        LOG(ERROR) << "Unable to find UID for '" << args[1] << "': " << decode_uid_err;
-        return -1;
+static Result<Success> do_readahead(const BuiltinArguments& args) {
+    struct stat sb;
+
+    if (stat(args[1].c_str(), &sb)) {
+        return ErrnoError() << "Error opening " << args[1];
+    }
+
+    bool readfully = false;
+    if (args.size() == 3 && args[2] == "--fully") {
+        readfully = true;
+    }
+    // We will do readahead in a forked process in order not to block init
+    // since it may block while it reads the
+    // filesystem metadata needed to locate the requested blocks.  This
+    // occurs frequently with ext[234] on large files using indirect blocks
+    // instead of extents, giving the appearance that the call blocks until
+    // the requested data has been read.
+    pid_t pid = fork();
+    if (pid == 0) {
+        if (setpriority(PRIO_PROCESS, 0, static_cast<int>(ANDROID_PRIORITY_LOWEST)) != 0) {
+            PLOG(WARNING) << "setpriority failed";
+        }
+        if (android_set_ioprio(0, IoSchedClass_IDLE, 7)) {
+            PLOG(WARNING) << "ioprio_get failed";
+        }
+        android::base::Timer t;
+        if (S_ISREG(sb.st_mode)) {
+            if (auto result = readahead_file(args[1], readfully); !result) {
+                LOG(WARNING) << "Unable to readahead '" << args[1] << "': " << result.error();
+                _exit(EXIT_FAILURE);
+            }
+        } else if (S_ISDIR(sb.st_mode)) {
+            char* paths[] = {const_cast<char*>(args[1].data()), nullptr};
+            std::unique_ptr<FTS, decltype(&fts_close)> fts(
+                fts_open(paths, FTS_PHYSICAL | FTS_NOCHDIR | FTS_XDEV, nullptr), fts_close);
+            if (!fts) {
+                PLOG(ERROR) << "Error opening directory: " << args[1];
+                _exit(EXIT_FAILURE);
+            }
+            // Traverse the entire hierarchy and do readahead
+            for (FTSENT* ftsent = fts_read(fts.get()); ftsent != nullptr;
+                 ftsent = fts_read(fts.get())) {
+                if (ftsent->fts_info & FTS_F) {
+                    const std::string filename = ftsent->fts_accpath;
+                    if (auto result = readahead_file(filename, readfully); !result) {
+                        LOG(WARNING)
+                            << "Unable to readahead '" << filename << "': " << result.error();
+                    }
+                }
+            }
+        }
+        LOG(INFO) << "Readahead " << args[1] << " took " << t << " asynchronously";
+        _exit(0);
+    } else if (pid < 0) {
+        return ErrnoError() << "Fork failed";
+    }
+    return Success();
+}
+
+static Result<Success> do_copy(const BuiltinArguments& args) {
+    auto file_contents = ReadFile(args[1]);
+    if (!file_contents) {
+        return Error() << "Could not read input file '" << args[1] << "': " << file_contents.error();
+    }
+    if (auto result = WriteFile(args[2], *file_contents); !result) {
+        return Error() << "Could not write to output file '" << args[2] << "': " << result.error();
+    }
+
+    return Success();
+}
+
+static Result<Success> do_chown(const BuiltinArguments& args) {
+    auto uid = DecodeUid(args[1]);
+    if (!uid) {
+        return Error() << "Unable to decode UID for '" << args[1] << "': " << uid.error();
     }
 
     // GID is optional and pushes the index of path out by one if specified.
     const std::string& path = (args.size() == 4) ? args[3] : args[2];
-    gid_t gid = -1;
+    Result<gid_t> gid = -1;
 
     if (args.size() == 4) {
-        if (!DecodeUid(args[2], &gid, &decode_uid_err)) {
-            LOG(ERROR) << "Unable to find GID for '" << args[2] << "': " << decode_uid_err;
-            return -1;
+        gid = DecodeUid(args[2]);
+        if (!gid) {
+            return Error() << "Unable to decode GID for '" << args[2] << "': " << gid.error();
         }
     }
 
-    if (lchown(path.c_str(), uid, gid) == -1) return -errno;
+    if (lchown(path.c_str(), *uid, *gid) == -1) {
+        return ErrnoError() << "lchown() failed";
+    }
 
-    return 0;
+    return Success();
 }
 
 static mode_t get_mode(const char *s) {
@@ -719,15 +883,15 @@
     return mode;
 }
 
-static int do_chmod(const std::vector<std::string>& args) {
+static Result<Success> do_chmod(const BuiltinArguments& args) {
     mode_t mode = get_mode(args[1].c_str());
     if (fchmodat(AT_FDCWD, args[2].c_str(), mode, AT_SYMLINK_NOFOLLOW) < 0) {
-        return -errno;
+        return ErrnoError() << "fchmodat() failed";
     }
-    return 0;
+    return Success();
 }
 
-static int do_restorecon(const std::vector<std::string>& args) {
+static Result<Success> do_restorecon(const BuiltinArguments& args) {
     int ret = 0;
 
     struct flag_type {const char* name; int value;};
@@ -744,8 +908,7 @@
     for (size_t i = 1; i < args.size(); ++i) {
         if (android::base::StartsWith(args[i], "--")) {
             if (!in_flags) {
-                LOG(ERROR) << "restorecon - flags must precede paths";
-                return -1;
+                return Error() << "flags must precede paths";
             }
             bool found = false;
             for (size_t j = 0; flags[j].name; ++j) {
@@ -756,26 +919,27 @@
                 }
             }
             if (!found) {
-                LOG(ERROR) << "restorecon - bad flag " << args[i];
-                return -1;
+                return Error() << "bad flag " << args[i];
             }
         } else {
             in_flags = false;
             if (selinux_android_restorecon(args[i].c_str(), flag) < 0) {
-                ret = -errno;
+                ret = errno;
             }
         }
     }
-    return ret;
+
+    if (ret) return ErrnoError() << "selinux_android_restorecon() failed";
+    return Success();
 }
 
-static int do_restorecon_recursive(const std::vector<std::string>& args) {
-    std::vector<std::string> non_const_args(args);
+static Result<Success> do_restorecon_recursive(const BuiltinArguments& args) {
+    std::vector<std::string> non_const_args(args.args);
     non_const_args.insert(std::next(non_const_args.begin()), "--recursive");
-    return do_restorecon(non_const_args);
+    return do_restorecon({std::move(non_const_args), args.context});
 }
 
-static int do_loglevel(const std::vector<std::string>& args) {
+static Result<Success> do_loglevel(const BuiltinArguments& args) {
     // TODO: support names instead/as well?
     int log_level = -1;
     android::base::ParseInt(args[1], &log_level);
@@ -790,144 +954,163 @@
         case 1:
         case 0: severity = android::base::FATAL; break;
         default:
-            LOG(ERROR) << "loglevel: invalid log level " << log_level;
-            return -EINVAL;
+            return Error() << "invalid log level " << log_level;
     }
     android::base::SetMinimumLogSeverity(severity);
-    return 0;
+    return Success();
 }
 
-static int do_load_persist_props(const std::vector<std::string>& args) {
+static Result<Success> do_load_persist_props(const BuiltinArguments& args) {
     load_persist_props();
-    return 0;
+    return Success();
 }
 
-static int do_load_system_props(const std::vector<std::string>& args) {
+static Result<Success> do_load_system_props(const BuiltinArguments& args) {
     load_system_props();
-    return 0;
+    return Success();
 }
 
-static int do_wait(const std::vector<std::string>& args) {
-    if (args.size() == 2) {
-        return wait_for_file(args[1].c_str(), kCommandRetryTimeout);
-    } else if (args.size() == 3) {
-        int timeout;
-        if (android::base::ParseInt(args[2], &timeout)) {
-            return wait_for_file(args[1].c_str(), std::chrono::seconds(timeout));
+static Result<Success> do_wait(const BuiltinArguments& args) {
+    auto timeout = kCommandRetryTimeout;
+    if (args.size() == 3) {
+        int timeout_int;
+        if (!android::base::ParseInt(args[2], &timeout_int)) {
+            return Error() << "failed to parse timeout";
         }
+        timeout = std::chrono::seconds(timeout_int);
     }
-    return -1;
+
+    if (wait_for_file(args[1].c_str(), timeout) != 0) {
+        return Error() << "wait_for_file() failed";
+    }
+
+    return Success();
 }
 
-static int do_wait_for_prop(const std::vector<std::string>& args) {
+static Result<Success> do_wait_for_prop(const BuiltinArguments& args) {
     const char* name = args[1].c_str();
     const char* value = args[2].c_str();
     size_t value_len = strlen(value);
 
-    if (!is_legal_property_name(name)) {
-        LOG(ERROR) << "do_wait_for_prop(\"" << name << "\", \"" << value
-                   << "\") failed: bad name";
-        return -1;
+    if (!IsLegalPropertyName(name)) {
+        return Error() << "IsLegalPropertyName(" << name << ") failed";
     }
     if (value_len >= PROP_VALUE_MAX) {
-        LOG(ERROR) << "do_wait_for_prop(\"" << name << "\", \"" << value
-                   << "\") failed: value too long";
-        return -1;
+        return Error() << "value too long";
     }
     if (!start_waiting_for_property(name, value)) {
-        LOG(ERROR) << "do_wait_for_prop(\"" << name << "\", \"" << value
-                   << "\") failed: init already in waiting";
-        return -1;
+        return Error() << "already waiting for a property";
     }
-    return 0;
-}
-
-/*
- * Callback to make a directory from the ext4 code
- */
-static int do_installkeys_ensure_dir_exists(const char* dir) {
-    if (make_dir(dir, 0700, sehandle) && errno != EEXIST) {
-        return -1;
-    }
-
-    return 0;
+    return Success();
 }
 
 static bool is_file_crypto() {
     return android::base::GetProperty("ro.crypto.type", "") == "file";
 }
 
-static int do_installkey(const std::vector<std::string>& args) {
-    if (!is_file_crypto()) {
-        return 0;
+static Result<Success> ExecWithRebootOnFailure(const std::string& reboot_reason,
+                                               const BuiltinArguments& args) {
+    auto service = Service::MakeTemporaryOneshotService(args.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) {
+            if (e4crypt_is_native()) {
+                LOG(ERROR) << "Rebooting into recovery, reason: " << reboot_reason;
+                reboot_into_recovery({"--prompt_and_wipe_data", "--reason="s + reboot_reason});
+            } else {
+                LOG(ERROR) << "Failure (reboot suppressed): " << 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();
+
     auto unencrypted_dir = args[1] + e4crypt_unencrypted_folder;
-    if (do_installkeys_ensure_dir_exists(unencrypted_dir.c_str())) {
-        PLOG(ERROR) << "Failed to create " << unencrypted_dir;
-        return -1;
+    if (!make_dir(unencrypted_dir, 0700) && errno != EEXIST) {
+        return ErrnoError() << "Failed to create " << unencrypted_dir;
     }
-    std::vector<std::string> exec_args = {"exec", "/system/bin/vdc", "--wait", "cryptfs",
-                                          "enablefilecrypto"};
-    return do_exec(exec_args);
+    return ExecWithRebootOnFailure(
+        "enablefilecrypto_failed",
+        {{"exec", "/system/bin/vdc", "--wait", "cryptfs", "enablefilecrypto"}, args.context});
 }
 
-static int do_init_user0(const std::vector<std::string>& args) {
-    std::vector<std::string> exec_args = {"exec", "/system/bin/vdc", "--wait", "cryptfs",
-                                          "init_user0"};
-    return do_exec(exec_args);
+static Result<Success> do_init_user0(const BuiltinArguments& args) {
+    return ExecWithRebootOnFailure(
+        "init_user0_failed",
+        {{"exec", "/system/bin/vdc", "--wait", "cryptfs", "init_user0"}, 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
     static const Map builtin_functions = {
-        {"bootchart",               {1,     1,    do_bootchart}},
-        {"chmod",                   {2,     2,    do_chmod}},
-        {"chown",                   {2,     3,    do_chown}},
-        {"class_reset",             {1,     1,    do_class_reset}},
-        {"class_restart",           {1,     1,    do_class_restart}},
-        {"class_start",             {1,     1,    do_class_start}},
-        {"class_stop",              {1,     1,    do_class_stop}},
-        {"copy",                    {2,     2,    do_copy}},
-        {"domainname",              {1,     1,    do_domainname}},
-        {"enable",                  {1,     1,    do_enable}},
-        {"exec",                    {1,     kMax, do_exec}},
-        {"exec_start",              {1,     1,    do_exec_start}},
-        {"export",                  {2,     2,    do_export}},
-        {"hostname",                {1,     1,    do_hostname}},
-        {"ifup",                    {1,     1,    do_ifup}},
-        {"init_user0",              {0,     0,    do_init_user0}},
-        {"insmod",                  {1,     kMax, do_insmod}},
-        {"installkey",              {1,     1,    do_installkey}},
-        {"load_persist_props",      {0,     0,    do_load_persist_props}},
-        {"load_system_props",       {0,     0,    do_load_system_props}},
-        {"loglevel",                {1,     1,    do_loglevel}},
-        {"mkdir",                   {1,     4,    do_mkdir}},
-        {"mount_all",               {1,     kMax, do_mount_all}},
-        {"mount",                   {3,     kMax, do_mount}},
-        {"umount",                  {1,     1,    do_umount}},
-        {"restart",                 {1,     1,    do_restart}},
-        {"restorecon",              {1,     kMax, do_restorecon}},
-        {"restorecon_recursive",    {1,     kMax, do_restorecon_recursive}},
-        {"rm",                      {1,     1,    do_rm}},
-        {"rmdir",                   {1,     1,    do_rmdir}},
-        {"setprop",                 {2,     2,    do_setprop}},
-        {"setrlimit",               {3,     3,    do_setrlimit}},
-        {"start",                   {1,     1,    do_start}},
-        {"stop",                    {1,     1,    do_stop}},
-        {"swapon_all",              {1,     1,    do_swapon_all}},
-        {"symlink",                 {2,     2,    do_symlink}},
-        {"sysclktz",                {1,     1,    do_sysclktz}},
-        {"trigger",                 {1,     1,    do_trigger}},
-        {"verity_load_state",       {0,     0,    do_verity_load_state}},
-        {"verity_update_state",     {0,     0,    do_verity_update_state}},
-        {"wait",                    {1,     2,    do_wait}},
-        {"wait_for_prop",           {2,     2,    do_wait_for_prop}},
-        {"write",                   {2,     2,    do_write}},
+        {"bootchart",               {1,     1,    {false,  do_bootchart}}},
+        {"chmod",                   {2,     2,    {true,   do_chmod}}},
+        {"chown",                   {2,     3,    {true,   do_chown}}},
+        {"class_reset",             {1,     1,    {false,  do_class_reset}}},
+        {"class_restart",           {1,     1,    {false,  do_class_restart}}},
+        {"class_start",             {1,     1,    {false,  do_class_start}}},
+        {"class_stop",              {1,     1,    {false,  do_class_stop}}},
+        {"copy",                    {2,     2,    {true,   do_copy}}},
+        {"domainname",              {1,     1,    {true,   do_domainname}}},
+        {"enable",                  {1,     1,    {false,  do_enable}}},
+        {"exec",                    {1,     kMax, {false,  do_exec}}},
+        {"exec_background",         {1,     kMax, {false,  do_exec_background}}},
+        {"exec_start",              {1,     1,    {false,  do_exec_start}}},
+        {"export",                  {2,     2,    {false,  do_export}}},
+        {"hostname",                {1,     1,    {true,   do_hostname}}},
+        {"ifup",                    {1,     1,    {true,   do_ifup}}},
+        {"init_user0",              {0,     0,    {false,  do_init_user0}}},
+        {"insmod",                  {1,     kMax, {true,   do_insmod}}},
+        {"installkey",              {1,     1,    {false,  do_installkey}}},
+        {"interface_restart",       {1,     1,    {false,  do_interface_restart}}},
+        {"interface_start",         {1,     1,    {false,  do_interface_start}}},
+        {"interface_stop",          {1,     1,    {false,  do_interface_stop}}},
+        {"load_persist_props",      {0,     0,    {false,  do_load_persist_props}}},
+        {"load_system_props",       {0,     0,    {false,  do_load_system_props}}},
+        {"loglevel",                {1,     1,    {false,  do_loglevel}}},
+        {"mkdir",                   {1,     4,    {true,   do_mkdir}}},
+        // TODO: Do mount operations in vendor_init.
+        // mount_all is currently too complex to run in vendor_init as it queues action triggers,
+        // imports rc scripts, etc.  It should be simplified and run in vendor_init context.
+        // mount and umount are run in the same context as mount_all for symmetry.
+        {"mount_all",               {1,     kMax, {false,  do_mount_all}}},
+        {"mount",                   {3,     kMax, {false,  do_mount}}},
+        {"umount",                  {1,     1,    {false,  do_umount}}},
+        {"readahead",               {1,     2,    {true,   do_readahead}}},
+        {"restart",                 {1,     1,    {false,  do_restart}}},
+        {"restorecon",              {1,     kMax, {true,   do_restorecon}}},
+        {"restorecon_recursive",    {1,     kMax, {true,   do_restorecon_recursive}}},
+        {"rm",                      {1,     1,    {true,   do_rm}}},
+        {"rmdir",                   {1,     1,    {true,   do_rmdir}}},
+        {"setprop",                 {2,     2,    {true,   do_setprop}}},
+        {"setrlimit",               {3,     3,    {false,  do_setrlimit}}},
+        {"start",                   {1,     1,    {false,  do_start}}},
+        {"stop",                    {1,     1,    {false,  do_stop}}},
+        {"swapon_all",              {1,     1,    {false,  do_swapon_all}}},
+        {"symlink",                 {2,     2,    {true,   do_symlink}}},
+        {"sysclktz",                {1,     1,    {false,  do_sysclktz}}},
+        {"trigger",                 {1,     1,    {false,  do_trigger}}},
+        {"verity_load_state",       {0,     0,    {false,  do_verity_load_state}}},
+        {"verity_update_state",     {0,     0,    {false,  do_verity_update_state}}},
+        {"wait",                    {1,     2,    {true,   do_wait}}},
+        {"wait_for_prop",           {2,     2,    {false,  do_wait_for_prop}}},
+        {"write",                   {2,     2,    {true,   do_write}}},
     };
     // clang-format on
     return builtin_functions;
 }
+// Builtin-function-map end
 
 }  // namespace init
 }  // namespace android
diff --git a/init/builtins.h b/init/builtins.h
index b110f61..814b2d5 100644
--- a/init/builtins.h
+++ b/init/builtins.h
@@ -22,13 +22,17 @@
 #include <string>
 #include <vector>
 
+#include "builtin_arguments.h"
 #include "keyword_map.h"
+#include "result.h"
 
 namespace android {
 namespace init {
 
-using BuiltinFunction = std::function<int(const std::vector<std::string>&)>;
-class BuiltinFunctionMap : public KeywordMap<BuiltinFunction> {
+using BuiltinFunction = std::function<Result<Success>(const BuiltinArguments&)>;
+
+using KeywordFunctionMap = KeywordMap<std::pair<bool, BuiltinFunction>>;
+class BuiltinFunctionMap : public KeywordFunctionMap {
   public:
     BuiltinFunctionMap() {}
 
diff --git a/init/capabilities.cpp b/init/capabilities.cpp
index 642a364..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;
 }
 
@@ -194,5 +203,18 @@
     return SetAmbientCaps(to_keep);
 }
 
+bool DropInheritableCaps() {
+    ScopedCaps caps(cap_get_proc());
+    if (cap_clear_flag(caps.get(), CAP_INHERITABLE) == -1) {
+        PLOG(ERROR) << "cap_clear_flag(INHERITABLE) failed";
+        return false;
+    }
+    if (cap_set_proc(caps.get()) != 0) {
+        PLOG(ERROR) << "cap_set_proc() failed";
+        return false;
+    }
+    return true;
+}
+
 }  // namespace init
 }  // namespace android
diff --git a/init/capabilities.h b/init/capabilities.h
index ede85c3..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 {
 
@@ -35,6 +46,7 @@
 bool CapAmbientSupported();
 unsigned int GetLastValidCap();
 bool SetCapsForExec(const CapSet& to_keep);
+bool DropInheritableCaps();
 
 }  // namespace init
 }  // namespace android
diff --git a/init/descriptors.cpp b/init/descriptors.cpp
index 0cb639a..6265687 100644
--- a/init/descriptors.cpp
+++ b/init/descriptors.cpp
@@ -28,7 +28,6 @@
 #include <cutils/android_get_control_file.h>
 #include <cutils/sockets.h>
 
-#include "init.h"
 #include "util.h"
 
 namespace android {
@@ -62,7 +61,7 @@
                 [] (char& c) { c = isalnum(c) ? c : '_'; });
 
   std::string val = std::to_string(fd);
-  add_environment(publishedName.c_str(), val.c_str());
+  setenv(publishedName.c_str(), val.c_str(), 1);
 
   // make sure we don't close on exec
   fcntl(fd, F_SETFD, 0);
@@ -86,8 +85,7 @@
     int flags =
         ((types[0] == "stream" ? SOCK_STREAM : (types[0] == "dgram" ? SOCK_DGRAM : SOCK_SEQPACKET)));
     bool passcred = types.size() > 1 && types[1] == "passcred";
-    return CreateSocket(name().c_str(), flags, passcred, perm(), uid(), gid(), context.c_str(),
-                        sehandle);
+    return CreateSocket(name().c_str(), flags, passcred, perm(), uid(), gid(), context.c_str());
 }
 
 const std::string SocketInfo::key() const {
diff --git a/init/devices.cpp b/init/devices.cpp
index 13cf991..ed4a739 100644
--- a/init/devices.cpp
+++ b/init/devices.cpp
@@ -30,7 +30,7 @@
 #include <selinux/android.h>
 #include <selinux/selinux.h>
 
-#include "ueventd.h"
+#include "selinux.h"
 #include "util.h"
 
 #ifdef _INIT_INIT_H
@@ -126,7 +126,7 @@
 }
 
 bool Permissions::Match(const std::string& path) const {
-    if (prefix_) return StartsWith(path, name_.c_str());
+    if (prefix_) return StartsWith(path, name_);
     if (wildcard_) return fnmatch(name_.c_str(), path.c_str(), FNM_PATHNAME) == 0;
     return path == name_;
 }
@@ -224,18 +224,13 @@
     auto[mode, uid, gid] = GetDevicePermissions(path, links);
     mode |= (block ? S_IFBLK : S_IFCHR);
 
-    char* secontext = nullptr;
-    if (sehandle_) {
-        std::vector<const char*> c_links;
-        for (const auto& link : links) {
-            c_links.emplace_back(link.c_str());
-        }
-        c_links.emplace_back(nullptr);
-        if (selabel_lookup_best_match(sehandle_, &secontext, path.c_str(), &c_links[0], mode)) {
-            PLOG(ERROR) << "Device '" << path << "' not created; cannot find SELinux label";
-            return;
-        }
-        setfscreatecon(secontext);
+    std::string secontext;
+    if (!SelabelLookupFileContextBestMatch(path, links, mode, &secontext)) {
+        PLOG(ERROR) << "Device '" << path << "' not created; cannot find SELinux label";
+        return;
+    }
+    if (!secontext.empty()) {
+        setfscreatecon(secontext.c_str());
     }
 
     dev_t dev = makedev(major, minor);
@@ -250,7 +245,7 @@
     }
     /* If the node already exists update its SELinux label to handle cases when
      * it was created with the wrong context during coldboot procedure. */
-    if (mknod(path.c_str(), mode, dev) && (errno == EEXIST) && secontext) {
+    if (mknod(path.c_str(), mode, dev) && (errno == EEXIST) && !secontext.empty()) {
         char* fcon = nullptr;
         int rc = lgetfilecon(path.c_str(), &fcon);
         if (rc < 0) {
@@ -258,10 +253,10 @@
             goto out;
         }
 
-        bool different = strcmp(fcon, secontext) != 0;
+        bool different = fcon != secontext;
         freecon(fcon);
 
-        if (different && lsetfilecon(path.c_str(), secontext)) {
+        if (different && lsetfilecon(path.c_str(), secontext.c_str())) {
             PLOG(ERROR) << "Cannot set '" << secontext << "' SELinux label on '" << path
                         << "' device";
         }
@@ -273,8 +268,7 @@
         PLOG(FATAL) << "setegid(AID_ROOT) failed";
     }
 
-    if (secontext) {
-        freecon(secontext);
+    if (!secontext.empty()) {
         setfscreatecon(nullptr);
     }
 }
@@ -305,9 +299,9 @@
         static const std::string devices_platform_prefix = "/devices/platform/";
         static const std::string devices_prefix = "/devices/";
 
-        if (StartsWith(device, devices_platform_prefix.c_str())) {
+        if (StartsWith(device, devices_platform_prefix)) {
             device = device.substr(devices_platform_prefix.length());
-        } else if (StartsWith(device, devices_prefix.c_str())) {
+        } else if (StartsWith(device, devices_prefix)) {
             device = device.substr(devices_prefix.length());
         }
 
@@ -334,10 +328,10 @@
                          << partition_name_sanitized << "'";
         }
         links.emplace_back(link_path + "/by-name/" + partition_name_sanitized);
-    }
-
-    if (uevent.partition_num >= 0) {
-        links.emplace_back(link_path + "/by-num/p" + std::to_string(uevent.partition_num));
+        // Adds symlink: /dev/block/by-name/<partition_name>.
+        if (boot_devices_.find(device) != boot_devices_.end()) {
+            links.emplace_back("/dev/block/by-name/" + partition_name_sanitized);
+        }
     }
 
     auto last_slash = uevent.path.rfind('/');
@@ -351,12 +345,18 @@
     if (action == "add") {
         MakeDevice(devpath, block, major, minor, links);
         for (const auto& link : links) {
-            if (mkdir_recursive(Dirname(link), 0755, sehandle_)) {
+            if (!mkdir_recursive(Dirname(link), 0755)) {
                 PLOG(ERROR) << "Failed to create directory " << Dirname(link);
             }
 
-            if (symlink(devpath.c_str(), link.c_str()) && errno != EEXIST) {
-                PLOG(ERROR) << "Failed to symlink " << devpath << " to " << link;
+            if (symlink(devpath.c_str(), link.c_str())) {
+                if (errno != EEXIST) {
+                    PLOG(ERROR) << "Failed to symlink " << devpath << " to " << link;
+                } else if (std::string link_path;
+                           Readlink(link, &link_path) && link_path != devpath) {
+                    PLOG(ERROR) << "Failed to symlink " << devpath << " to " << link
+                                << ", which already links to: " << link_path;
+                }
             }
         }
     }
@@ -391,48 +391,47 @@
         if (StartsWith(uevent.path, "/devices")) {
             links = GetBlockDeviceSymlinks(uevent);
         }
-    } else if (StartsWith(uevent.subsystem, "usb")) {
-        if (uevent.subsystem == "usb") {
-            if (!uevent.device_name.empty()) {
-                devpath = "/dev/" + uevent.device_name;
-            } else {
-                // This imitates the file system that would be created
-                // if we were using devfs instead.
-                // Minors are broken up into groups of 128, starting at "001"
-                int bus_id = uevent.minor / 128 + 1;
-                int device_id = uevent.minor % 128 + 1;
-                devpath = StringPrintf("/dev/bus/usb/%03d/%03d", bus_id, device_id);
-            }
-        } else {
-            // ignore other USB events
-            return;
-        }
     } else if (const auto subsystem =
                    std::find(subsystems_.cbegin(), subsystems_.cend(), uevent.subsystem);
                subsystem != subsystems_.cend()) {
         devpath = subsystem->ParseDevPath(uevent);
+    } else if (uevent.subsystem == "usb") {
+        if (!uevent.device_name.empty()) {
+            devpath = "/dev/" + uevent.device_name;
+        } else {
+            // This imitates the file system that would be created
+            // if we were using devfs instead.
+            // Minors are broken up into groups of 128, starting at "001"
+            int bus_id = uevent.minor / 128 + 1;
+            int device_id = uevent.minor % 128 + 1;
+            devpath = StringPrintf("/dev/bus/usb/%03d/%03d", bus_id, device_id);
+        }
+    } else if (StartsWith(uevent.subsystem, "usb")) {
+        // ignore other USB events
+        return;
     } else {
         devpath = "/dev/" + Basename(uevent.path);
     }
 
-    mkdir_recursive(Dirname(devpath), 0755, sehandle_);
+    mkdir_recursive(Dirname(devpath), 0755);
 
     HandleDevice(uevent.action, devpath, block, uevent.major, uevent.minor, links);
 }
 
 DeviceHandler::DeviceHandler(std::vector<Permissions> dev_permissions,
                              std::vector<SysfsPermissions> sysfs_permissions,
-                             std::vector<Subsystem> subsystems, bool skip_restorecon)
+                             std::vector<Subsystem> subsystems, std::set<std::string> boot_devices,
+                             bool skip_restorecon)
     : dev_permissions_(std::move(dev_permissions)),
       sysfs_permissions_(std::move(sysfs_permissions)),
       subsystems_(std::move(subsystems)),
-      sehandle_(selinux_android_file_context_handle()),
+      boot_devices_(std::move(boot_devices)),
       skip_restorecon_(skip_restorecon),
       sysfs_mount_point_("/sys") {}
 
 DeviceHandler::DeviceHandler()
     : DeviceHandler(std::vector<Permissions>{}, std::vector<SysfsPermissions>{},
-                    std::vector<Subsystem>{}, false) {}
+                    std::vector<Subsystem>{}, std::set<std::string>{}, false) {}
 
 }  // namespace init
 }  // namespace android
diff --git a/init/devices.h b/init/devices.h
index c64f5fb..0be660f 100644
--- a/init/devices.h
+++ b/init/devices.h
@@ -21,6 +21,7 @@
 #include <sys/types.h>
 
 #include <algorithm>
+#include <set>
 #include <string>
 #include <vector>
 
@@ -34,6 +35,8 @@
 
 class Permissions {
   public:
+    friend void TestPermissions(const Permissions& expected, const Permissions& test);
+
     Permissions(const std::string& name, mode_t perm, uid_t uid, gid_t gid);
 
     bool Match(const std::string& path) const;
@@ -56,6 +59,8 @@
 
 class SysfsPermissions : public Permissions {
   public:
+    friend void TestSysfsPermissions(const SysfsPermissions& expected, const SysfsPermissions& test);
+
     SysfsPermissions(const std::string& name, const std::string& attribute, mode_t perm, uid_t uid,
                      gid_t gid)
         : Permissions(name, perm, uid, gid), attribute_(attribute) {}
@@ -70,15 +75,24 @@
 class Subsystem {
   public:
     friend class SubsystemParser;
+    friend void TestSubsystems(const Subsystem& expected, const Subsystem& test);
+
+    enum DevnameSource {
+        DEVNAME_UEVENT_DEVNAME,
+        DEVNAME_UEVENT_DEVPATH,
+    };
 
     Subsystem() {}
+    Subsystem(const std::string& name) : name_(name) {}
+    Subsystem(const std::string& name, DevnameSource source, const std::string& dir_name)
+        : name_(name), devname_source_(source), dir_name_(dir_name) {}
 
     // Returns the full path for a uevent of a device that is a member of this subsystem,
     // according to the rules parsed from ueventd.rc
     std::string ParseDevPath(const Uevent& uevent) const {
-        std::string devname = devname_source_ == DevnameSource::DEVNAME_UEVENT_DEVNAME
-                                  ? uevent.device_name
-                                  : android::base::Basename(uevent.path);
+        std::string devname = devname_source_ == DEVNAME_UEVENT_DEVNAME
+                                      ? uevent.device_name
+                                      : android::base::Basename(uevent.path);
 
         return dir_name_ + "/" + devname;
     }
@@ -86,14 +100,9 @@
     bool operator==(const std::string& string_name) const { return name_ == string_name; }
 
   private:
-    enum class DevnameSource {
-        DEVNAME_UEVENT_DEVNAME,
-        DEVNAME_UEVENT_DEVPATH,
-    };
-
     std::string name_;
+    DevnameSource devname_source_ = DEVNAME_UEVENT_DEVNAME;
     std::string dir_name_ = "/dev";
-    DevnameSource devname_source_;
 };
 
 class DeviceHandler {
@@ -102,9 +111,8 @@
 
     DeviceHandler();
     DeviceHandler(std::vector<Permissions> dev_permissions,
-                  std::vector<SysfsPermissions> sysfs_permissions,
-                  std::vector<Subsystem> subsystems, bool skip_restorecon);
-    ~DeviceHandler(){};
+                  std::vector<SysfsPermissions> sysfs_permissions, std::vector<Subsystem> subsystems,
+                  std::set<std::string> boot_devices, bool skip_restorecon);
 
     void HandleDeviceEvent(const Uevent& uevent);
 
@@ -124,7 +132,7 @@
     std::vector<Permissions> dev_permissions_;
     std::vector<SysfsPermissions> sysfs_permissions_;
     std::vector<Subsystem> subsystems_;
-    selabel_handle* sehandle_;
+    std::set<std::string> boot_devices_;
     bool skip_restorecon_;
     std::string sysfs_mount_point_;
 };
diff --git a/init/devices_test.cpp b/init/devices_test.cpp
index ac4ab9b..d658f4d 100644
--- a/init/devices_test.cpp
+++ b/init/devices_test.cpp
@@ -35,13 +35,13 @@
         device_handler_.sysfs_mount_point_ = fake_sys_root.path;
 
         std::string platform_device_dir = fake_sys_root.path + platform_device;
-        mkdir_recursive(platform_device_dir, 0777, nullptr);
+        mkdir_recursive(platform_device_dir, 0777);
 
         std::string platform_bus = fake_sys_root.path + "/bus/platform"s;
-        mkdir_recursive(platform_bus, 0777, nullptr);
+        mkdir_recursive(platform_bus, 0777);
         symlink(platform_bus.c_str(), (platform_device_dir + "/subsystem").c_str());
 
-        mkdir_recursive(android::base::Dirname(fake_sys_root.path + uevent.path), 0777, nullptr);
+        mkdir_recursive(android::base::Dirname(fake_sys_root.path + uevent.path), 0777);
 
         std::vector<std::string> result;
         result = device_handler_.GetBlockDeviceSymlinks(uevent);
@@ -84,7 +84,6 @@
     };
     std::vector<std::string> expected_result{
         "/dev/block/platform/soc.0/f9824900.sdhci/by-name/modem",
-        "/dev/block/platform/soc.0/f9824900.sdhci/by-num/p1",
         "/dev/block/platform/soc.0/f9824900.sdhci/mmcblk0p1",
     };
 
@@ -100,7 +99,6 @@
         .partition_num = 1,
     };
     std::vector<std::string> expected_result{
-        "/dev/block/platform/soc.0/f9824900.sdhci/by-num/p1",
         "/dev/block/platform/soc.0/f9824900.sdhci/mmcblk0p1",
     };
 
diff --git a/init/epoll.cpp b/init/epoll.cpp
new file mode 100644
index 0000000..4bca09e
--- /dev/null
+++ b/init/epoll.cpp
@@ -0,0 +1,84 @@
+/*
+ * 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 "epoll.h"
+
+#include <sys/epoll.h>
+
+#include <chrono>
+#include <functional>
+#include <map>
+
+namespace android {
+namespace init {
+
+Epoll::Epoll() {}
+
+Result<Success> Epoll::Open() {
+    if (epoll_fd_ >= 0) return Success();
+    epoll_fd_.reset(epoll_create1(EPOLL_CLOEXEC));
+
+    if (epoll_fd_ == -1) {
+        return ErrnoError() << "epoll_create1 failed";
+    }
+    return Success();
+}
+
+Result<Success> Epoll::RegisterHandler(int fd, std::function<void()> handler) {
+    auto [it, inserted] = epoll_handlers_.emplace(fd, std::move(handler));
+    if (!inserted) {
+        return Error() << "Cannot specify two epoll handlers for a given FD";
+    }
+    epoll_event ev;
+    ev.events = EPOLLIN;
+    // std::map's iterators do not get invalidated until erased, so we use the
+    // pointer to the std::function in the map directly for epoll_ctl.
+    ev.data.ptr = reinterpret_cast<void*>(&it->second);
+    if (epoll_ctl(epoll_fd_, EPOLL_CTL_ADD, fd, &ev) == -1) {
+        Result<Success> result = ErrnoError() << "epoll_ctl failed to add fd";
+        epoll_handlers_.erase(fd);
+        return result;
+    }
+    return Success();
+}
+
+Result<Success> Epoll::UnregisterHandler(int fd) {
+    if (epoll_ctl(epoll_fd_, EPOLL_CTL_DEL, fd, nullptr) == -1) {
+        return ErrnoError() << "epoll_ctl failed to remove fd";
+    }
+    if (epoll_handlers_.erase(fd) != 1) {
+        return Error() << "Attempting to remove epoll handler for FD without an existing handler";
+    }
+    return Success();
+}
+
+Result<Success> Epoll::Wait(std::optional<std::chrono::milliseconds> timeout) {
+    int timeout_ms = -1;
+    if (timeout && timeout->count() < INT_MAX) {
+        timeout_ms = timeout->count();
+    }
+    epoll_event ev;
+    auto nr = TEMP_FAILURE_RETRY(epoll_wait(epoll_fd_, &ev, 1, timeout_ms));
+    if (nr == -1) {
+        return ErrnoError() << "epoll_wait failed";
+    } else if (nr == 1) {
+        std::invoke(*reinterpret_cast<std::function<void()>*>(ev.data.ptr));
+    }
+    return Success();
+}
+
+}  // namespace init
+}  // namespace android
diff --git a/init/epoll.h b/init/epoll.h
new file mode 100644
index 0000000..85a791c
--- /dev/null
+++ b/init/epoll.h
@@ -0,0 +1,49 @@
+/*
+ * 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_EPOLL_H
+#define _INIT_EPOLL_H
+
+#include <chrono>
+#include <functional>
+#include <map>
+#include <optional>
+
+#include <android-base/unique_fd.h>
+
+#include "result.h"
+
+namespace android {
+namespace init {
+
+class Epoll {
+  public:
+    Epoll();
+
+    Result<Success> Open();
+    Result<Success> RegisterHandler(int fd, std::function<void()> handler);
+    Result<Success> UnregisterHandler(int fd);
+    Result<Success> Wait(std::optional<std::chrono::milliseconds> timeout);
+
+  private:
+    android::base::unique_fd epoll_fd_;
+    std::map<int, std::function<void()>> epoll_handlers_;
+};
+
+}  // namespace init
+}  // namespace android
+
+#endif
diff --git a/init/firmware_handler.cpp b/init/firmware_handler.cpp
index b686885..28bda34 100644
--- a/init/firmware_handler.cpp
+++ b/init/firmware_handler.cpp
@@ -21,7 +21,6 @@
 #include <sys/wait.h>
 #include <unistd.h>
 
-#include <string>
 #include <thread>
 
 #include <android-base/chrono_utils.h>
@@ -36,6 +35,8 @@
 namespace android {
 namespace init {
 
+std::vector<std::string> firmware_directories;
+
 static void LoadFirmware(const Uevent& uevent, const std::string& root, int fw_fd, size_t fw_size,
                          int loading_fd, int data_fd) {
     // Start transfer.
@@ -78,12 +79,9 @@
         return;
     }
 
-    static const char* firmware_dirs[] = {"/etc/firmware/", "/vendor/firmware/",
-                                          "/firmware/image/"};
-
 try_loading_again:
-    for (size_t i = 0; i < arraysize(firmware_dirs); i++) {
-        std::string file = firmware_dirs[i] + uevent.firmware;
+    for (const auto& firmware_directory : firmware_directories) {
+        std::string file = firmware_directory + uevent.firmware;
         unique_fd fw_fd(open(file.c_str(), O_RDONLY | O_CLOEXEC));
         struct stat sb;
         if (fw_fd != -1 && fstat(fw_fd, &sb) != -1) {
diff --git a/init/firmware_handler.h b/init/firmware_handler.h
index e456ac4..6081511 100644
--- a/init/firmware_handler.h
+++ b/init/firmware_handler.h
@@ -17,11 +17,16 @@
 #ifndef _INIT_FIRMWARE_HANDLER_H
 #define _INIT_FIRMWARE_HANDLER_H
 
+#include <string>
+#include <vector>
+
 #include "uevent.h"
 
 namespace android {
 namespace init {
 
+extern std::vector<std::string> firmware_directories;
+
 void HandleFirmwareEvent(const Uevent& uevent);
 
 }  // namespace init
diff --git a/init/first_stage_mount.cpp b/init/first_stage_mount.cpp
new file mode 100644
index 0000000..43075b2
--- /dev/null
+++ b/init/first_stage_mount.cpp
@@ -0,0 +1,598 @@
+/*
+ * Copyright (C) 2017 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 "first_stage_mount.h"
+
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <chrono>
+#include <memory>
+#include <set>
+#include <string>
+#include <vector>
+
+#include <android-base/chrono_utils.h>
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/strings.h>
+#include <liblp/metadata_format.h>
+
+#include "devices.h"
+#include "fs_mgr.h"
+#include "fs_mgr_avb.h"
+#include "fs_mgr_dm_linear.h"
+#include "uevent.h"
+#include "uevent_listener.h"
+#include "util.h"
+
+using android::base::Timer;
+
+namespace android {
+namespace init {
+
+// Class Declarations
+// ------------------
+class FirstStageMount {
+  public:
+    FirstStageMount();
+    virtual ~FirstStageMount() = default;
+
+    // The factory method to create either FirstStageMountVBootV1 or FirstStageMountVBootV2
+    // based on device tree configurations.
+    static std::unique_ptr<FirstStageMount> Create();
+    bool DoFirstStageMount();  // Mounts fstab entries read from device tree.
+    bool InitDevices();
+
+  protected:
+    ListenerAction HandleBlockDevice(const std::string& name, const Uevent&);
+    bool InitRequiredDevices();
+    bool InitMappedDevice(const std::string& verity_device);
+    bool CreateLogicalPartitions();
+    bool MountPartitions();
+    bool GetBackingDmLinearDevices();
+
+    virtual ListenerAction UeventCallback(const Uevent& uevent);
+
+    // Pure virtual functions.
+    virtual bool GetDmVerityDevices() = 0;
+    virtual bool SetUpDmVerity(fstab_rec* fstab_rec) = 0;
+
+    bool need_dm_verity_;
+
+    std::unique_ptr<fstab, decltype(&fs_mgr_free_fstab)> device_tree_fstab_;
+    std::string lp_metadata_partition_;
+    std::vector<fstab_rec*> mount_fstab_recs_;
+    std::set<std::string> required_devices_partition_names_;
+    std::unique_ptr<DeviceHandler> device_handler_;
+    UeventListener uevent_listener_;
+};
+
+class FirstStageMountVBootV1 : public FirstStageMount {
+  public:
+    FirstStageMountVBootV1() = default;
+    ~FirstStageMountVBootV1() override = default;
+
+  protected:
+    bool GetDmVerityDevices() override;
+    bool SetUpDmVerity(fstab_rec* fstab_rec) override;
+};
+
+class FirstStageMountVBootV2 : public FirstStageMount {
+  public:
+    friend void SetInitAvbVersionInRecovery();
+
+    FirstStageMountVBootV2();
+    ~FirstStageMountVBootV2() override = default;
+
+  protected:
+    ListenerAction UeventCallback(const Uevent& uevent) override;
+    bool GetDmVerityDevices() override;
+    bool SetUpDmVerity(fstab_rec* fstab_rec) override;
+    bool InitAvbHandle();
+
+    std::string device_tree_vbmeta_parts_;
+    FsManagerAvbUniquePtr avb_handle_;
+    ByNameSymlinkMap by_name_symlink_map_;
+};
+
+// Static Functions
+// ----------------
+static inline bool IsDtVbmetaCompatible() {
+    return is_android_dt_value_expected("vbmeta/compatible", "android,vbmeta");
+}
+
+static bool inline IsRecoveryMode() {
+    return access("/system/bin/recovery", F_OK) == 0;
+}
+
+static inline bool IsDmLinearEnabled() {
+    static bool checked = false;
+    static bool enabled = false;
+    if (checked) {
+        return enabled;
+    }
+    import_kernel_cmdline(false,
+                          [](const std::string& key, const std::string& value, bool in_qemu) {
+                              if (key == "androidboot.logical_partitions" && value == "1") {
+                                  enabled = true;
+                              }
+                          });
+    checked = true;
+    return enabled;
+}
+
+// Class Definitions
+// -----------------
+FirstStageMount::FirstStageMount()
+    : need_dm_verity_(false), device_tree_fstab_(fs_mgr_read_fstab_dt(), fs_mgr_free_fstab) {
+    if (device_tree_fstab_) {
+        // Stores device_tree_fstab_->recs[] into mount_fstab_recs_ (vector<fstab_rec*>)
+        // for easier manipulation later, e.g., range-base for loop.
+        for (int i = 0; i < device_tree_fstab_->num_entries; i++) {
+            mount_fstab_recs_.push_back(&device_tree_fstab_->recs[i]);
+        }
+    } else {
+        LOG(INFO) << "Failed to read fstab from device tree";
+    }
+
+    auto boot_devices = fs_mgr_get_boot_devices();
+    device_handler_ = std::make_unique<DeviceHandler>(
+            std::vector<Permissions>{}, std::vector<SysfsPermissions>{}, std::vector<Subsystem>{},
+            std::move(boot_devices), false);
+}
+
+std::unique_ptr<FirstStageMount> FirstStageMount::Create() {
+    if (IsDtVbmetaCompatible()) {
+        return std::make_unique<FirstStageMountVBootV2>();
+    } else {
+        return std::make_unique<FirstStageMountVBootV1>();
+    }
+}
+
+bool FirstStageMount::DoFirstStageMount() {
+    if (!IsDmLinearEnabled() && mount_fstab_recs_.empty()) {
+        // Nothing to mount.
+        LOG(INFO) << "First stage mount skipped (missing/incompatible/empty fstab in device tree)";
+        return true;
+    }
+
+    if (!InitDevices()) return false;
+
+    if (!CreateLogicalPartitions()) return false;
+
+    if (!MountPartitions()) return false;
+
+    return true;
+}
+
+bool FirstStageMount::InitDevices() {
+    return GetBackingDmLinearDevices() && GetDmVerityDevices() && InitRequiredDevices();
+}
+
+bool FirstStageMount::GetBackingDmLinearDevices() {
+    // Add any additional devices required for dm-linear mappings.
+    if (!IsDmLinearEnabled()) {
+        return true;
+    }
+
+    required_devices_partition_names_.emplace(LP_METADATA_PARTITION_NAME);
+    return true;
+}
+
+// Creates devices with uevent->partition_name matching one in the member variable
+// required_devices_partition_names_. Found partitions will then be removed from it
+// for the subsequent member function to check which devices are NOT created.
+bool FirstStageMount::InitRequiredDevices() {
+    if (required_devices_partition_names_.empty()) {
+        return true;
+    }
+
+    if (IsDmLinearEnabled() || need_dm_verity_) {
+        const std::string dm_path = "/devices/virtual/misc/device-mapper";
+        bool found = false;
+        auto dm_callback = [this, &dm_path, &found](const Uevent& uevent) {
+            if (uevent.path == dm_path) {
+                device_handler_->HandleDeviceEvent(uevent);
+                found = true;
+                return ListenerAction::kStop;
+            }
+            return ListenerAction::kContinue;
+        };
+        uevent_listener_.RegenerateUeventsForPath("/sys" + dm_path, dm_callback);
+        if (!found) {
+            LOG(INFO) << "device-mapper device not found in /sys, waiting for its uevent";
+            Timer t;
+            uevent_listener_.Poll(dm_callback, 10s);
+            LOG(INFO) << "Wait for device-mapper returned after " << t;
+        }
+        if (!found) {
+            LOG(ERROR) << "device-mapper device not found after polling timeout";
+            return false;
+        }
+    }
+
+    auto uevent_callback = [this](const Uevent& uevent) { return UeventCallback(uevent); };
+    uevent_listener_.RegenerateUevents(uevent_callback);
+
+    // UeventCallback() will remove found partitions from required_devices_partition_names_.
+    // So if it isn't empty here, it means some partitions are not found.
+    if (!required_devices_partition_names_.empty()) {
+        LOG(INFO) << __PRETTY_FUNCTION__
+                  << ": partition(s) not found in /sys, waiting for their uevent(s): "
+                  << android::base::Join(required_devices_partition_names_, ", ");
+        Timer t;
+        uevent_listener_.Poll(uevent_callback, 10s);
+        LOG(INFO) << "Wait for partitions returned after " << t;
+    }
+
+    if (!required_devices_partition_names_.empty()) {
+        LOG(ERROR) << __PRETTY_FUNCTION__ << ": partition(s) not found after polling timeout: "
+                   << android::base::Join(required_devices_partition_names_, ", ");
+        return false;
+    }
+
+    return true;
+}
+
+bool FirstStageMount::CreateLogicalPartitions() {
+    if (!IsDmLinearEnabled()) {
+        return true;
+    }
+
+    if (lp_metadata_partition_.empty()) {
+        LOG(ERROR) << "Could not locate logical partition tables in partition "
+                   << LP_METADATA_PARTITION_NAME;
+        return false;
+    }
+    return android::fs_mgr::CreateLogicalPartitions(lp_metadata_partition_);
+}
+
+ListenerAction FirstStageMount::HandleBlockDevice(const std::string& name, const Uevent& uevent) {
+    // Matches partition name to create device nodes.
+    // Both required_devices_partition_names_ and uevent->partition_name have A/B
+    // suffix when A/B is used.
+    auto iter = required_devices_partition_names_.find(name);
+    if (iter != required_devices_partition_names_.end()) {
+        LOG(VERBOSE) << __PRETTY_FUNCTION__ << ": found partition: " << *iter;
+        if (IsDmLinearEnabled() && name == LP_METADATA_PARTITION_NAME) {
+            std::vector<std::string> links = device_handler_->GetBlockDeviceSymlinks(uevent);
+            lp_metadata_partition_ = links[0];
+        }
+        required_devices_partition_names_.erase(iter);
+        device_handler_->HandleDeviceEvent(uevent);
+        if (required_devices_partition_names_.empty()) {
+            return ListenerAction::kStop;
+        } else {
+            return ListenerAction::kContinue;
+        }
+    }
+    return ListenerAction::kContinue;
+}
+
+ListenerAction FirstStageMount::UeventCallback(const Uevent& uevent) {
+    // Ignores everything that is not a block device.
+    if (uevent.subsystem != "block") {
+        return ListenerAction::kContinue;
+    }
+
+    if (!uevent.partition_name.empty()) {
+        return HandleBlockDevice(uevent.partition_name, uevent);
+    } else {
+        size_t base_idx = uevent.path.rfind('/');
+        if (base_idx != std::string::npos) {
+            return HandleBlockDevice(uevent.path.substr(base_idx + 1), uevent);
+        }
+    }
+    // Not found a partition or find an unneeded partition, continue to find others.
+    return ListenerAction::kContinue;
+}
+
+// Creates "/dev/block/dm-XX" for dm-verity by running coldboot on /sys/block/dm-XX.
+bool FirstStageMount::InitMappedDevice(const std::string& dm_device) {
+    const std::string device_name(basename(dm_device.c_str()));
+    const std::string syspath = "/sys/block/" + device_name;
+    bool found = false;
+
+    auto verity_callback = [&device_name, &dm_device, this, &found](const Uevent& uevent) {
+        if (uevent.device_name == device_name) {
+            LOG(VERBOSE) << "Creating device-mapper device : " << dm_device;
+            device_handler_->HandleDeviceEvent(uevent);
+            found = true;
+            return ListenerAction::kStop;
+        }
+        return ListenerAction::kContinue;
+    };
+
+    uevent_listener_.RegenerateUeventsForPath(syspath, verity_callback);
+    if (!found) {
+        LOG(INFO) << "dm-verity device not found in /sys, waiting for its uevent";
+        Timer t;
+        uevent_listener_.Poll(verity_callback, 10s);
+        LOG(INFO) << "wait for dm-verity device returned after " << t;
+    }
+    if (!found) {
+        LOG(ERROR) << "dm-verity device not found after polling timeout";
+        return false;
+    }
+
+    return true;
+}
+
+bool FirstStageMount::MountPartitions() {
+    for (auto fstab_rec : mount_fstab_recs_) {
+        if (fs_mgr_is_logical(fstab_rec)) {
+            if (!fs_mgr_update_logical_partition(fstab_rec)) {
+                return false;
+            }
+            if (!InitMappedDevice(fstab_rec->blk_device)) {
+                return false;
+            }
+        }
+        if (!SetUpDmVerity(fstab_rec)) {
+            PLOG(ERROR) << "Failed to setup verity for '" << fstab_rec->mount_point << "'";
+            return false;
+        }
+        if (fs_mgr_do_mount_one(fstab_rec)) {
+            PLOG(ERROR) << "Failed to mount '" << fstab_rec->mount_point << "'";
+            return false;
+        }
+    }
+    return true;
+}
+
+bool FirstStageMountVBootV1::GetDmVerityDevices() {
+    std::string verity_loc_device;
+    need_dm_verity_ = false;
+
+    for (auto fstab_rec : mount_fstab_recs_) {
+        // Don't allow verifyatboot in the first stage.
+        if (fs_mgr_is_verifyatboot(fstab_rec)) {
+            LOG(ERROR) << "Partitions can't be verified at boot";
+            return false;
+        }
+        // Checks for verified partitions.
+        if (fs_mgr_is_verified(fstab_rec)) {
+            need_dm_verity_ = true;
+        }
+        // Checks if verity metadata is on a separate partition. Note that it is
+        // not partition specific, so there must be only one additional partition
+        // that carries verity state.
+        if (fstab_rec->verity_loc) {
+            if (verity_loc_device.empty()) {
+                verity_loc_device = fstab_rec->verity_loc;
+            } else if (verity_loc_device != fstab_rec->verity_loc) {
+                LOG(ERROR) << "More than one verity_loc found: " << verity_loc_device << ", "
+                           << fstab_rec->verity_loc;
+                return false;
+            }
+        }
+    }
+
+    // Includes the partition names of fstab records and verity_loc_device (if any).
+    // Notes that fstab_rec->blk_device has A/B suffix updated by fs_mgr when A/B is used.
+    for (auto fstab_rec : mount_fstab_recs_) {
+        required_devices_partition_names_.emplace(basename(fstab_rec->blk_device));
+    }
+
+    if (!verity_loc_device.empty()) {
+        required_devices_partition_names_.emplace(basename(verity_loc_device.c_str()));
+    }
+
+    return true;
+}
+
+bool FirstStageMountVBootV1::SetUpDmVerity(fstab_rec* fstab_rec) {
+    if (fs_mgr_is_verified(fstab_rec)) {
+        int ret = fs_mgr_setup_verity(fstab_rec, false /* wait_for_verity_dev */);
+        switch (ret) {
+            case FS_MGR_SETUP_VERITY_SKIPPED:
+            case FS_MGR_SETUP_VERITY_DISABLED:
+                LOG(INFO) << "Verity disabled/skipped for '" << fstab_rec->mount_point << "'";
+                return true;
+            case FS_MGR_SETUP_VERITY_SUCCESS:
+                // The exact block device name (fstab_rec->blk_device) is changed to
+                // "/dev/block/dm-XX". Needs to create it because ueventd isn't started in init
+                // first stage.
+                return InitMappedDevice(fstab_rec->blk_device);
+            default:
+                return false;
+        }
+    }
+    return true;  // Returns true to mount the partition.
+}
+
+// FirstStageMountVBootV2 constructor.
+// Gets the vbmeta partitions from device tree.
+// /{
+//     firmware {
+//         android {
+//             vbmeta {
+//                 compatible = "android,vbmeta";
+//                 parts = "vbmeta,boot,system,vendor"
+//             };
+//         };
+//     };
+//  }
+FirstStageMountVBootV2::FirstStageMountVBootV2() : avb_handle_(nullptr) {
+    if (!read_android_dt_file("vbmeta/parts", &device_tree_vbmeta_parts_)) {
+        PLOG(ERROR) << "Failed to read vbmeta/parts from device tree";
+        return;
+    }
+}
+
+bool FirstStageMountVBootV2::GetDmVerityDevices() {
+    need_dm_verity_ = false;
+
+    std::set<std::string> logical_partitions;
+
+    // fstab_rec->blk_device has A/B suffix.
+    for (auto fstab_rec : mount_fstab_recs_) {
+        if (fs_mgr_is_avb(fstab_rec)) {
+            need_dm_verity_ = true;
+        }
+        if (fs_mgr_is_logical(fstab_rec)) {
+            // Don't try to find logical partitions via uevent regeneration.
+            logical_partitions.emplace(basename(fstab_rec->blk_device));
+        } else {
+            required_devices_partition_names_.emplace(basename(fstab_rec->blk_device));
+        }
+    }
+
+    // libavb verifies AVB metadata on all verified partitions at once.
+    // e.g., The device_tree_vbmeta_parts_ will be "vbmeta,boot,system,vendor"
+    // for libavb to verify metadata, even if there is only /vendor in the
+    // above mount_fstab_recs_.
+    if (need_dm_verity_) {
+        if (device_tree_vbmeta_parts_.empty()) {
+            LOG(ERROR) << "Missing vbmeta parts in device tree";
+            return false;
+        }
+        std::vector<std::string> partitions = android::base::Split(device_tree_vbmeta_parts_, ",");
+        std::string ab_suffix = fs_mgr_get_slot_suffix();
+        for (const auto& partition : partitions) {
+            std::string partition_name = partition + ab_suffix;
+            if (logical_partitions.count(partition_name)) {
+                continue;
+            }
+            // required_devices_partition_names_ is of type std::set so it's not an issue
+            // to emplace a partition twice. e.g., /vendor might be in both places:
+            //   - device_tree_vbmeta_parts_ = "vbmeta,boot,system,vendor"
+            //   - mount_fstab_recs_: /vendor_a
+            required_devices_partition_names_.emplace(partition_name);
+        }
+    }
+    return true;
+}
+
+ListenerAction FirstStageMountVBootV2::UeventCallback(const Uevent& uevent) {
+    // Check if this uevent corresponds to one of the required partitions and store its symlinks if
+    // so, in order to create FsManagerAvbHandle later.
+    // Note that the parent callback removes partitions from the list of required partitions
+    // as it finds them, so this must happen first.
+    if (!uevent.partition_name.empty() &&
+        required_devices_partition_names_.find(uevent.partition_name) !=
+                required_devices_partition_names_.end()) {
+        // GetBlockDeviceSymlinks() will return three symlinks at most, depending on
+        // the content of uevent. by-name symlink will be at [0] if uevent->partition_name
+        // is not empty. e.g.,
+        //   - /dev/block/platform/soc.0/f9824900.sdhci/by-name/modem
+        //   - /dev/block/platform/soc.0/f9824900.sdhci/mmcblk0p1
+        std::vector<std::string> links = device_handler_->GetBlockDeviceSymlinks(uevent);
+        if (!links.empty()) {
+            auto [it, inserted] = by_name_symlink_map_.emplace(uevent.partition_name, links[0]);
+            if (!inserted) {
+                LOG(ERROR) << "Partition '" << uevent.partition_name
+                           << "' already existed in the by-name symlink map with a value of '"
+                           << it->second << "', new value '" << links[0] << "' will be ignored.";
+            }
+        }
+    }
+
+    return FirstStageMount::UeventCallback(uevent);
+}
+
+bool FirstStageMountVBootV2::SetUpDmVerity(fstab_rec* fstab_rec) {
+    if (fs_mgr_is_avb(fstab_rec)) {
+        if (!InitAvbHandle()) return false;
+        SetUpAvbHashtreeResult hashtree_result =
+                avb_handle_->SetUpAvbHashtree(fstab_rec, false /* wait_for_verity_dev */);
+        switch (hashtree_result) {
+            case SetUpAvbHashtreeResult::kDisabled:
+                return true;  // Returns true to mount the partition.
+            case SetUpAvbHashtreeResult::kSuccess:
+                // The exact block device name (fstab_rec->blk_device) is changed to
+                // "/dev/block/dm-XX". Needs to create it because ueventd isn't started in init
+                // first stage.
+                return InitMappedDevice(fstab_rec->blk_device);
+            default:
+                return false;
+        }
+    }
+    return true;  // Returns true to mount the partition.
+}
+
+bool FirstStageMountVBootV2::InitAvbHandle() {
+    if (avb_handle_) return true;  // Returns true if the handle is already initialized.
+
+    if (by_name_symlink_map_.empty()) {
+        LOG(ERROR) << "by_name_symlink_map_ is empty";
+        return false;
+    }
+
+    avb_handle_ = FsManagerAvbHandle::Open(std::move(by_name_symlink_map_));
+    by_name_symlink_map_.clear();  // Removes all elements after the above std::move().
+
+    if (!avb_handle_) {
+        PLOG(ERROR) << "Failed to open FsManagerAvbHandle";
+        return false;
+    }
+    // Sets INIT_AVB_VERSION here for init to set ro.boot.avb_version in the second stage.
+    setenv("INIT_AVB_VERSION", avb_handle_->avb_version().c_str(), 1);
+    return true;
+}
+
+// Public functions
+// ----------------
+// Mounts partitions specified by fstab in device tree.
+bool DoFirstStageMount() {
+    // Skips first stage mount if we're in recovery mode.
+    if (IsRecoveryMode()) {
+        LOG(INFO) << "First stage mount skipped (recovery mode)";
+        return true;
+    }
+
+    std::unique_ptr<FirstStageMount> handle = FirstStageMount::Create();
+    if (!handle) {
+        LOG(ERROR) << "Failed to create FirstStageMount";
+        return false;
+    }
+    return handle->DoFirstStageMount();
+}
+
+void SetInitAvbVersionInRecovery() {
+    if (!IsRecoveryMode()) {
+        LOG(INFO) << "Skipped setting INIT_AVB_VERSION (not in recovery mode)";
+        return;
+    }
+
+    if (!IsDtVbmetaCompatible()) {
+        LOG(INFO) << "Skipped setting INIT_AVB_VERSION (not vbmeta compatible)";
+        return;
+    }
+
+    // Initializes required devices for the subsequent FsManagerAvbHandle::Open()
+    // to verify AVB metadata on all partitions in the verified chain.
+    // We only set INIT_AVB_VERSION when the AVB verification succeeds, i.e., the
+    // Open() function returns a valid handle.
+    // We don't need to mount partitions here in recovery mode.
+    FirstStageMountVBootV2 avb_first_mount;
+    if (!avb_first_mount.InitDevices()) {
+        LOG(ERROR) << "Failed to init devices for INIT_AVB_VERSION";
+        return;
+    }
+
+    FsManagerAvbUniquePtr avb_handle =
+            FsManagerAvbHandle::Open(std::move(avb_first_mount.by_name_symlink_map_));
+    if (!avb_handle) {
+        PLOG(ERROR) << "Failed to open FsManagerAvbHandle for INIT_AVB_VERSION";
+        return;
+    }
+    setenv("INIT_AVB_VERSION", avb_handle->avb_version().c_str(), 1);
+}
+
+}  // namespace init
+}  // namespace android
diff --git a/init/first_stage_mount.h b/init/first_stage_mount.h
new file mode 100644
index 0000000..21d87fd
--- /dev/null
+++ b/init/first_stage_mount.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#pragma once
+
+namespace android {
+namespace init {
+
+bool DoFirstStageMount();
+void SetInitAvbVersionInRecovery();
+
+}  // namespace init
+}  // namespace android
diff --git a/init/host_import_parser.cpp b/init/host_import_parser.cpp
new file mode 100644
index 0000000..93e363f
--- /dev/null
+++ b/init/host_import_parser.cpp
@@ -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.
+ */
+
+#include "host_import_parser.h"
+
+#include <android-base/strings.h>
+
+using android::base::StartsWith;
+
+namespace android {
+namespace init {
+
+Result<Success> HostImportParser::ParseSection(std::vector<std::string>&& args, const std::string&,
+                                               int) {
+    if (args.size() != 2) {
+        return Error() << "single argument needed for import\n";
+    }
+
+    return Success();
+}
+
+Result<Success> HostImportParser::ParseLineSection(std::vector<std::string>&&, int) {
+    return Error() << "Unexpected line found after import statement";
+}
+
+}  // namespace init
+}  // namespace android
diff --git a/init/host_import_parser.h b/init/host_import_parser.h
new file mode 100644
index 0000000..52b8891
--- /dev/null
+++ b/init/host_import_parser.h
@@ -0,0 +1,35 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <string>
+#include <vector>
+
+#include "parser.h"
+
+namespace android {
+namespace init {
+
+class HostImportParser : public SectionParser {
+  public:
+    HostImportParser() {}
+    Result<Success> ParseSection(std::vector<std::string>&& args, const std::string&, int) override;
+    Result<Success> ParseLineSection(std::vector<std::string>&&, int) override;
+};
+
+}  // namespace init
+}  // namespace android
diff --git a/init/host_init_stubs.cpp b/init/host_init_stubs.cpp
new file mode 100644
index 0000000..2352fc7
--- /dev/null
+++ b/init/host_init_stubs.cpp
@@ -0,0 +1,55 @@
+/*
+ * 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"
+
+#include <android-base/properties.h>
+
+// unistd.h
+int setgroups(size_t __size, const gid_t* __list) {
+    return 0;
+}
+
+namespace android {
+namespace init {
+
+// init.h
+std::string default_console = "/dev/console";
+
+// property_service.h
+uint32_t SetProperty(const std::string& key, const std::string& value) {
+    android::base::SetProperty(key, value);
+    return 0;
+}
+uint32_t (*property_set)(const std::string& name, const std::string& value) = SetProperty;
+uint32_t HandlePropertySet(const std::string&, const std::string&, const std::string&, const ucred&,
+                           std::string*) {
+    return 0;
+}
+
+// selinux.h
+bool SelinuxHasVendorInit() {
+    return true;
+}
+
+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..f0e1f07
--- /dev/null
+++ b/init/host_init_stubs.h
@@ -0,0 +1,51 @@
+/*
+ * 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);
+
+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
+bool SelinuxHasVendorInit();
+void SelabelInitialize();
+bool SelabelLookupFileContext(const std::string& key, int type, std::string* result);
+
+}  // namespace init
+}  // namespace android
+
+#endif
diff --git a/init/host_init_verifier.cpp b/init/host_init_verifier.cpp
new file mode 100644
index 0000000..8407729
--- /dev/null
+++ b/init/host_init_verifier.cpp
@@ -0,0 +1,166 @@
+//
+// 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 <errno.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <iostream>
+#include <string>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/parseint.h>
+#include <android-base/strings.h>
+
+#include "action.h"
+#include "action_manager.h"
+#include "action_parser.h"
+#include "host_import_parser.h"
+#include "host_init_stubs.h"
+#include "parser.h"
+#include "result.h"
+#include "service.h"
+
+#define EXCLUDE_FS_CONFIG_STRUCTURES
+#include "generated_android_ids.h"
+
+using namespace std::literals;
+
+using android::base::ParseInt;
+using android::base::ReadFileToString;
+using android::base::Split;
+
+static std::string passwd_file;
+
+static std::vector<std::pair<std::string, int>> GetVendorPasswd() {
+    std::string passwd;
+    if (!ReadFileToString(passwd_file, &passwd)) {
+        return {};
+    }
+
+    std::vector<std::pair<std::string, int>> result;
+    auto passwd_lines = Split(passwd, "\n");
+    for (const auto& line : passwd_lines) {
+        auto split_line = Split(line, ":");
+        if (split_line.size() < 3) {
+            continue;
+        }
+        int uid = 0;
+        if (!ParseInt(split_line[2], &uid)) {
+            continue;
+        }
+        result.emplace_back(split_line[0], uid);
+    }
+    return result;
+}
+
+passwd* getpwnam(const char* login) {  // NOLINT: implementing bad function.
+    // This isn't thread safe, but that's okay for our purposes.
+    static char static_name[32] = "";
+    static char static_dir[32] = "/";
+    static char static_shell[32] = "/system/bin/sh";
+    static passwd static_passwd = {
+        .pw_name = static_name,
+        .pw_dir = static_dir,
+        .pw_shell = static_shell,
+        .pw_uid = 0,
+        .pw_gid = 0,
+    };
+
+    for (size_t n = 0; n < android_id_count; ++n) {
+        if (!strcmp(android_ids[n].name, login)) {
+            snprintf(static_name, sizeof(static_name), "%s", android_ids[n].name);
+            static_passwd.pw_uid = android_ids[n].aid;
+            static_passwd.pw_gid = android_ids[n].aid;
+            return &static_passwd;
+        }
+    }
+
+    static const auto vendor_passwd = GetVendorPasswd();
+
+    for (const auto& [name, uid] : vendor_passwd) {
+        if (name == login) {
+            snprintf(static_name, sizeof(static_name), "%s", name.c_str());
+            static_passwd.pw_uid = uid;
+            static_passwd.pw_gid = uid;
+            return &static_passwd;
+        }
+    }
+
+    unsigned int oem_uid;
+    if (sscanf(login, "oem_%u", &oem_uid) == 1) {
+        snprintf(static_name, sizeof(static_name), "%s", login);
+        static_passwd.pw_uid = oem_uid;
+        static_passwd.pw_gid = oem_uid;
+        return &static_passwd;
+    }
+
+    errno = ENOENT;
+    return nullptr;
+}
+
+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::StdioLogger);
+    android::base::SetMinimumLogSeverity(android::base::ERROR);
+
+    if (argc != 2 && argc != 3) {
+        LOG(ERROR) << "Usage: " << argv[0] << " <init rc file> [passwd file]";
+        return EXIT_FAILURE;
+    }
+
+    if (argc == 3) {
+        passwd_file = argv[2];
+    }
+
+    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));
+    parser.AddSectionParser("import", std::make_unique<HostImportParser>());
+
+    if (!parser.ParseConfigFileInsecure(argv[1])) {
+        LOG(ERROR) << "Failed to open init rc script '" << argv[1] << "'";
+        return EXIT_FAILURE;
+    }
+    if (parser.parse_error_count() > 0) {
+        LOG(ERROR) << "Failed to parse init script '" << argv[1] << "' with "
+                   << parser.parse_error_count() << " errors";
+        return EXIT_FAILURE;
+    }
+    return EXIT_SUCCESS;
+}
+
+}  // namespace init
+}  // namespace android
+
+int main(int argc, char** argv) {
+    return android::init::main(argc, argv);
+}
diff --git a/init/import_parser.cpp b/init/import_parser.cpp
index b9fa2ce..fb3185e 100644
--- a/init/import_parser.cpp
+++ b/init/import_parser.cpp
@@ -23,34 +23,33 @@
 namespace android {
 namespace init {
 
-bool ImportParser::ParseSection(std::vector<std::string>&& args, const std::string& filename,
-                                int line, std::string* err) {
+Result<Success> ImportParser::ParseSection(std::vector<std::string>&& args,
+                                           const std::string& filename, int line) {
     if (args.size() != 2) {
-        *err = "single argument needed for import\n";
-        return false;
+        return Error() << "single argument needed for import\n";
     }
 
     std::string conf_file;
     bool ret = expand_props(args[1], &conf_file);
     if (!ret) {
-        *err = "error while expanding import";
-        return false;
+        return Error() << "error while expanding import";
     }
 
     LOG(INFO) << "Added '" << conf_file << "' to import list";
     if (filename_.empty()) filename_ = filename;
     imports_.emplace_back(std::move(conf_file), line);
-    return true;
+    return Success();
+}
+
+Result<Success> ImportParser::ParseLineSection(std::vector<std::string>&&, int) {
+    return Error() << "Unexpected line found after import statement";
 }
 
 void ImportParser::EndFile() {
     auto current_imports = std::move(imports_);
     imports_.clear();
     for (const auto& [import, line_num] : current_imports) {
-        if (!parser_->ParseConfig(import)) {
-            PLOG(ERROR) << filename_ << ": " << line_num << ": Could not import file '" << import
-                        << "'";
-        }
+        parser_->ParseConfig(import);
     }
 }
 
diff --git a/init/import_parser.h b/init/import_parser.h
index b774c57..7bc72e6 100644
--- a/init/import_parser.h
+++ b/init/import_parser.h
@@ -17,19 +17,20 @@
 #ifndef _INIT_IMPORT_PARSER_H
 #define _INIT_IMPORT_PARSER_H
 
-#include "init_parser.h"
-
 #include <string>
 #include <vector>
 
+#include "parser.h"
+
 namespace android {
 namespace init {
 
 class ImportParser : public SectionParser {
   public:
     ImportParser(Parser* parser) : parser_(parser) {}
-    bool ParseSection(std::vector<std::string>&& args, const std::string& filename, int line,
-                      std::string* err) override;
+    Result<Success> ParseSection(std::vector<std::string>&& args, const std::string& filename,
+                                 int line) override;
+    Result<Success> ParseLineSection(std::vector<std::string>&&, int) override;
     void EndFile() override;
 
   private:
diff --git a/init/init.cpp b/init/init.cpp
index bf251ff..7ad9ec3 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -16,131 +16,121 @@
 
 #include "init.h"
 
-#include <ctype.h>
 #include <dirent.h>
-#include <errno.h>
 #include <fcntl.h>
-#include <inttypes.h>
-#include <libgen.h>
 #include <paths.h>
+#include <pthread.h>
+#include <seccomp_policy.h>
 #include <signal.h>
-#include <stdarg.h>
-#include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
-#include <sys/epoll.h>
 #include <sys/mount.h>
-#include <sys/socket.h>
-#include <sys/stat.h>
+#include <sys/signalfd.h>
 #include <sys/sysmacros.h>
 #include <sys/types.h>
-#include <sys/un.h>
-#include <sys/wait.h>
 #include <unistd.h>
 
+#include <map>
+#include <memory>
+#include <optional>
+
 #include <android-base/chrono_utils.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 <android-base/unique_fd.h>
+#include <cutils/android_reboot.h>
 #include <keyutils.h>
 #include <libavb/libavb.h>
 #include <private/android_filesystem_config.h>
 #include <selinux/android.h>
-#include <selinux/selinux.h>
 
-#include <fstream>
-#include <memory>
-#include <vector>
-
-#include "action.h"
-#include "bootchart.h"
+#include "action_parser.h"
+#include "epoll.h"
+#include "first_stage_mount.h"
 #include "import_parser.h"
-#include "init_first_stage.h"
-#include "init_parser.h"
 #include "keychords.h"
-#include "log.h"
 #include "property_service.h"
 #include "reboot.h"
-#include "service.h"
-#include "signal_handler.h"
+#include "security.h"
+#include "selinux.h"
+#include "sigchld_handler.h"
 #include "ueventd.h"
 #include "util.h"
 #include "watchdogd.h"
 
+using namespace std::chrono_literals;
 using namespace std::string_literals;
 
 using android::base::boot_clock;
 using android::base::GetProperty;
+using android::base::ReadFileToString;
+using android::base::StringPrintf;
 using android::base::Timer;
+using android::base::Trim;
 
 namespace android {
 namespace init {
 
-struct selabel_handle *sehandle;
-struct selabel_handle *sehandle_prop;
-
 static int property_triggers_enabled = 0;
 
 static char qemu[32];
 
 std::string default_console = "/dev/console";
-static time_t process_needs_restart_at;
 
-const char *ENV[32];
-
-static int epoll_fd = -1;
+static int signal_fd = -1;
 
 static std::unique_ptr<Timer> waiting_for_prop(nullptr);
 static std::string wait_prop_name;
 static std::string wait_prop_value;
 static bool shutting_down;
+static std::string shutdown_command;
+static bool do_shutdown = false;
+
+std::vector<std::string> late_import_paths;
+
+static std::vector<Subcontext>* subcontexts;
 
 void DumpState() {
-    ServiceManager::GetInstance().DumpState();
+    ServiceList::GetInstance().DumpState();
     ActionManager::GetInstance().DumpState();
 }
 
-void register_epoll_handler(int fd, void (*fn)()) {
-    epoll_event ev;
-    ev.events = EPOLLIN;
-    ev.data.ptr = reinterpret_cast<void*>(fn);
-    if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd, &ev) == -1) {
-        PLOG(ERROR) << "epoll_ctl failed";
-    }
+Parser CreateParser(ActionManager& action_manager, ServiceList& service_list) {
+    Parser parser;
+
+    parser.AddSectionParser("service", std::make_unique<ServiceParser>(&service_list, subcontexts));
+    parser.AddSectionParser("on", std::make_unique<ActionParser>(&action_manager, subcontexts));
+    parser.AddSectionParser("import", std::make_unique<ImportParser>(&parser));
+
+    return parser;
 }
 
-/* add_environment - add "key=value" to the current environment */
-int add_environment(const char *key, const char *val)
-{
-    size_t n;
-    size_t key_len = strlen(key);
+static void LoadBootScripts(ActionManager& action_manager, ServiceList& service_list) {
+    Parser parser = CreateParser(action_manager, service_list);
 
-    /* The last environment entry is reserved to terminate the list */
-    for (n = 0; n < (arraysize(ENV) - 1); n++) {
-
-        /* Delete any existing entry for this key */
-        if (ENV[n] != NULL) {
-            size_t entry_key_len = strcspn(ENV[n], "=");
-            if ((entry_key_len == key_len) && (strncmp(ENV[n], key, entry_key_len) == 0)) {
-                free((char*)ENV[n]);
-                ENV[n] = NULL;
-            }
+    std::string bootscript = GetProperty("ro.boot.init_rc", "");
+    if (bootscript.empty()) {
+        parser.ParseConfig("/init.rc");
+        if (!parser.ParseConfig("/system/etc/init")) {
+            late_import_paths.emplace_back("/system/etc/init");
         }
-
-        /* Add entry if a free slot is available */
-        if (ENV[n] == NULL) {
-            char* entry;
-            asprintf(&entry, "%s=%s", key, val);
-            ENV[n] = entry;
-            return 0;
+        if (!parser.ParseConfig("/product/etc/init")) {
+            late_import_paths.emplace_back("/product/etc/init");
         }
+        if (!parser.ParseConfig("/product-services/etc/init")) {
+            late_import_paths.emplace_back("/product-services/etc/init");
+        }
+        if (!parser.ParseConfig("/odm/etc/init")) {
+            late_import_paths.emplace_back("/odm/etc/init");
+        }
+        if (!parser.ParseConfig("/vendor/etc/init")) {
+            late_import_paths.emplace_back("/vendor/etc/init");
+        }
+    } else {
+        parser.ParseConfig(bootscript);
     }
-
-    LOG(ERROR) << "No env. room to store: '" << key << "':'" << val << "'";
-
-    return -1;
 }
 
 bool start_waiting_for_property(const char *name, const char *value)
@@ -174,9 +164,16 @@
     // In non-thermal-shutdown case, 'shutdown' trigger will be fired to let device specific
     // commands to be executed.
     if (name == "sys.powerctl") {
-        if (HandlePowerctlMessage(value)) {
-            shutting_down = true;
-        }
+        // Despite the above comment, we can't call HandlePowerctlMessage() in this function,
+        // because it modifies the contents of the action queue, which can cause the action queue
+        // to get into a bad state if this function is called from a command being executed by the
+        // action queue.  Instead we set this flag and ensure that shutdown happens before the next
+        // command is run in the main init loop.
+        // TODO: once property service is removed from init, this will never happen from a builtin,
+        // but rather from a callback from the property service socket, in which case this hack can
+        // go away.
+        shutdown_command = value;
+        do_shutdown = true;
     }
 
     if (property_triggers_enabled) ActionManager::GetInstance().QueuePropertyChange(name, value);
@@ -189,33 +186,117 @@
     }
 }
 
-static void restart_processes()
-{
-    process_needs_restart_at = 0;
-    ServiceManager::GetInstance().ForEachServiceWithFlags(SVC_RESTARTING, [](Service* s) {
-        s->RestartIfNeeded(&process_needs_restart_at);
-    });
+static std::optional<boot_clock::time_point> RestartProcesses() {
+    std::optional<boot_clock::time_point> next_process_restart_time;
+    for (const auto& s : ServiceList::GetInstance()) {
+        if (!(s->flags() & SVC_RESTARTING)) continue;
+
+        auto restart_time = s->time_started() + 5s;
+        if (boot_clock::now() > restart_time) {
+            if (auto result = s->Start(); !result) {
+                LOG(ERROR) << "Could not restart process '" << s->name() << "': " << result.error();
+            }
+        } else {
+            if (!next_process_restart_time || restart_time < *next_process_restart_time) {
+                next_process_restart_time = restart_time;
+            }
+        }
+    }
+    return next_process_restart_time;
 }
 
-void handle_control_message(const std::string& msg, const std::string& name) {
-    Service* svc = ServiceManager::GetInstance().FindServiceByName(name);
-    if (svc == nullptr) {
-        LOG(ERROR) << "no such service '" << name << "'";
+static Result<Success> DoControlStart(Service* service) {
+    return service->Start();
+}
+
+static Result<Success> DoControlStop(Service* service) {
+    service->Stop();
+    return Success();
+}
+
+static Result<Success> DoControlRestart(Service* service) {
+    service->Restart();
+    return Success();
+}
+
+enum class ControlTarget {
+    SERVICE,    // function gets called for the named service
+    INTERFACE,  // action gets called for every service that holds this interface
+};
+
+struct ControlMessageFunction {
+    ControlTarget target;
+    std::function<Result<Success>(Service*)> action;
+};
+
+static const std::map<std::string, ControlMessageFunction>& get_control_message_map() {
+    // clang-format off
+    static const std::map<std::string, ControlMessageFunction> control_message_functions = {
+        {"sigstop_on",        {ControlTarget::SERVICE,
+                               [](auto* service) { service->set_sigstop(true); return Success(); }}},
+        {"sigstop_off",       {ControlTarget::SERVICE,
+                               [](auto* service) { service->set_sigstop(false); return Success(); }}},
+        {"start",             {ControlTarget::SERVICE,   DoControlStart}},
+        {"stop",              {ControlTarget::SERVICE,   DoControlStop}},
+        {"restart",           {ControlTarget::SERVICE,   DoControlRestart}},
+        {"interface_start",   {ControlTarget::INTERFACE, DoControlStart}},
+        {"interface_stop",    {ControlTarget::INTERFACE, DoControlStop}},
+        {"interface_restart", {ControlTarget::INTERFACE, DoControlRestart}},
+    };
+    // clang-format on
+
+    return control_message_functions;
+}
+
+void HandleControlMessage(const std::string& msg, const std::string& name, pid_t pid) {
+    const auto& map = get_control_message_map();
+    const auto it = map.find(msg);
+
+    if (it == map.end()) {
+        LOG(ERROR) << "Unknown control msg '" << msg << "'";
         return;
     }
 
-    if (msg == "start") {
-        svc->Start();
-    } else if (msg == "stop") {
-        svc->Stop();
-    } else if (msg == "restart") {
-        svc->Restart();
+    std::string cmdline_path = StringPrintf("proc/%d/cmdline", pid);
+    std::string process_cmdline;
+    if (ReadFileToString(cmdline_path, &process_cmdline)) {
+        std::replace(process_cmdline.begin(), process_cmdline.end(), '\0', ' ');
+        process_cmdline = Trim(process_cmdline);
     } else {
-        LOG(ERROR) << "unknown control msg '" << msg << "'";
+        process_cmdline = "unknown process";
+    }
+
+    LOG(INFO) << "Received control message '" << msg << "' for '" << name << "' from pid: " << pid
+              << " (" << process_cmdline << ")";
+
+    const ControlMessageFunction& function = it->second;
+
+    Service* svc = nullptr;
+
+    switch (function.target) {
+        case ControlTarget::SERVICE:
+            svc = ServiceList::GetInstance().FindService(name);
+            break;
+        case ControlTarget::INTERFACE:
+            svc = ServiceList::GetInstance().FindInterface(name);
+            break;
+        default:
+            LOG(ERROR) << "Invalid function target from static map key '" << msg << "': "
+                       << static_cast<std::underlying_type<ControlTarget>::type>(function.target);
+            return;
+    }
+
+    if (svc == nullptr) {
+        LOG(ERROR) << "Could not find '" << name << "' for ctl." << msg;
+        return;
+    }
+
+    if (auto result = function.action(svc); !result) {
+        LOG(ERROR) << "Could not ctl." << msg << " for '" << name << "': " << result.error();
     }
 }
 
-static int wait_for_coldboot_done_action(const std::vector<std::string>& args) {
+static Result<Success> wait_for_coldboot_done_action(const BuiltinArguments& args) {
     Timer t;
 
     LOG(VERBOSE) << "Waiting for " COLDBOOT_DONE "...";
@@ -229,233 +310,19 @@
     // because any build that slow isn't likely to boot at all, and we'd
     // rather any test lab devices fail back to the bootloader.
     if (wait_for_file(COLDBOOT_DONE, 60s) < 0) {
-        LOG(ERROR) << "Timed out waiting for " COLDBOOT_DONE;
-        panic();
+        LOG(FATAL) << "Timed out waiting for " COLDBOOT_DONE;
     }
 
     property_set("ro.boottime.init.cold_boot_wait", std::to_string(t.duration().count()));
-    return 0;
+    return Success();
 }
 
-/*
- * Writes 512 bytes of output from Hardware RNG (/dev/hw_random, backed
- * by Linux kernel's hw_random framework) into Linux RNG's via /dev/urandom.
- * Does nothing if Hardware RNG is not present.
- *
- * Since we don't yet trust the quality of Hardware RNG, these bytes are not
- * mixed into the primary pool of Linux RNG and the entropy estimate is left
- * unmodified.
- *
- * If the HW RNG device /dev/hw_random is present, we require that at least
- * 512 bytes read from it are written into Linux RNG. QA is expected to catch
- * devices/configurations where these I/O operations are blocking for a long
- * time. We do not reboot or halt on failures, as this is a best-effort
- * attempt.
- */
-static int mix_hwrng_into_linux_rng_action(const std::vector<std::string>& args)
-{
-    int result = -1;
-    int hwrandom_fd = -1;
-    int urandom_fd = -1;
-    char buf[512];
-    ssize_t chunk_size;
-    size_t total_bytes_written = 0;
-
-    hwrandom_fd = TEMP_FAILURE_RETRY(
-            open("/dev/hw_random", O_RDONLY | O_NOFOLLOW | O_CLOEXEC));
-    if (hwrandom_fd == -1) {
-        if (errno == ENOENT) {
-            LOG(ERROR) << "/dev/hw_random not found";
-            // It's not an error to not have a Hardware RNG.
-            result = 0;
-        } else {
-            PLOG(ERROR) << "Failed to open /dev/hw_random";
-        }
-        goto ret;
-    }
-
-    urandom_fd = TEMP_FAILURE_RETRY(
-            open("/dev/urandom", O_WRONLY | O_NOFOLLOW | O_CLOEXEC));
-    if (urandom_fd == -1) {
-        PLOG(ERROR) << "Failed to open /dev/urandom";
-        goto ret;
-    }
-
-    while (total_bytes_written < sizeof(buf)) {
-        chunk_size = TEMP_FAILURE_RETRY(
-                read(hwrandom_fd, buf, sizeof(buf) - total_bytes_written));
-        if (chunk_size == -1) {
-            PLOG(ERROR) << "Failed to read from /dev/hw_random";
-            goto ret;
-        } else if (chunk_size == 0) {
-            LOG(ERROR) << "Failed to read from /dev/hw_random: EOF";
-            goto ret;
-        }
-
-        chunk_size = TEMP_FAILURE_RETRY(write(urandom_fd, buf, chunk_size));
-        if (chunk_size == -1) {
-            PLOG(ERROR) << "Failed to write to /dev/urandom";
-            goto ret;
-        }
-        total_bytes_written += chunk_size;
-    }
-
-    LOG(INFO) << "Mixed " << total_bytes_written << " bytes from /dev/hw_random into /dev/urandom";
-    result = 0;
-
-ret:
-    if (hwrandom_fd != -1) {
-        close(hwrandom_fd);
-    }
-    if (urandom_fd != -1) {
-        close(urandom_fd);
-    }
-    return result;
-}
-
-static void security_failure() {
-    LOG(ERROR) << "Security failure...";
-    panic();
-}
-
-static bool set_highest_available_option_value(std::string path, int min, int max)
-{
-    std::ifstream inf(path, std::fstream::in);
-    if (!inf) {
-        LOG(ERROR) << "Cannot open for reading: " << path;
-        return false;
-    }
-
-    int current = max;
-    while (current >= min) {
-        // try to write out new value
-        std::string str_val = std::to_string(current);
-        std::ofstream of(path, std::fstream::out);
-        if (!of) {
-            LOG(ERROR) << "Cannot open for writing: " << path;
-            return false;
-        }
-        of << str_val << std::endl;
-        of.close();
-
-        // check to make sure it was recorded
-        inf.seekg(0);
-        std::string str_rec;
-        inf >> str_rec;
-        if (str_val.compare(str_rec) == 0) {
-            break;
-        }
-        current--;
-    }
-    inf.close();
-
-    if (current < min) {
-        LOG(ERROR) << "Unable to set minimum option value " << min << " in " << path;
-        return false;
-    }
-    return true;
-}
-
-#define MMAP_RND_PATH "/proc/sys/vm/mmap_rnd_bits"
-#define MMAP_RND_COMPAT_PATH "/proc/sys/vm/mmap_rnd_compat_bits"
-
-/* __attribute__((unused)) due to lack of mips support: see mips block
- * in set_mmap_rnd_bits_action */
-static bool __attribute__((unused)) set_mmap_rnd_bits_min(int start, int min, bool compat) {
-    std::string path;
-    if (compat) {
-        path = MMAP_RND_COMPAT_PATH;
-    } else {
-        path = MMAP_RND_PATH;
-    }
-
-    return set_highest_available_option_value(path, min, start);
-}
-
-/*
- * Set /proc/sys/vm/mmap_rnd_bits and potentially
- * /proc/sys/vm/mmap_rnd_compat_bits to the maximum supported values.
- * Returns -1 if unable to set these to an acceptable value.
- *
- * To support this sysctl, the following upstream commits are needed:
- *
- * d07e22597d1d mm: mmap: add new /proc tunable for mmap_base ASLR
- * e0c25d958f78 arm: mm: support ARCH_MMAP_RND_BITS
- * 8f0d3aa9de57 arm64: mm: support ARCH_MMAP_RND_BITS
- * 9e08f57d684a x86: mm: support ARCH_MMAP_RND_BITS
- * ec9ee4acd97c drivers: char: random: add get_random_long()
- * 5ef11c35ce86 mm: ASLR: use get_random_long()
- */
-static int set_mmap_rnd_bits_action(const std::vector<std::string>& args)
-{
-    int ret = -1;
-
-    /* values are arch-dependent */
-#if defined(__aarch64__)
-    /* arm64 supports 18 - 33 bits depending on pagesize and VA_SIZE */
-    if (set_mmap_rnd_bits_min(33, 24, false)
-            && set_mmap_rnd_bits_min(16, 16, true)) {
-        ret = 0;
-    }
-#elif defined(__x86_64__)
-    /* x86_64 supports 28 - 32 bits */
-    if (set_mmap_rnd_bits_min(32, 32, false)
-            && set_mmap_rnd_bits_min(16, 16, true)) {
-        ret = 0;
-    }
-#elif defined(__arm__) || defined(__i386__)
-    /* check to see if we're running on 64-bit kernel */
-    bool h64 = !access(MMAP_RND_COMPAT_PATH, F_OK);
-    /* supported 32-bit architecture must have 16 bits set */
-    if (set_mmap_rnd_bits_min(16, 16, h64)) {
-        ret = 0;
-    }
-#elif defined(__mips__) || defined(__mips64__)
-    // TODO: add mips support b/27788820
-    ret = 0;
-#else
-    LOG(ERROR) << "Unknown architecture";
-#endif
-
-    if (ret == -1) {
-        LOG(ERROR) << "Unable to set adequate mmap entropy value!";
-        security_failure();
-    }
-    return ret;
-}
-
-#define KPTR_RESTRICT_PATH "/proc/sys/kernel/kptr_restrict"
-#define KPTR_RESTRICT_MINVALUE 2
-#define KPTR_RESTRICT_MAXVALUE 4
-
-/* Set kptr_restrict to the highest available level.
- *
- * Aborts if unable to set this to an acceptable value.
- */
-static int set_kptr_restrict_action(const std::vector<std::string>& args)
-{
-    std::string path = KPTR_RESTRICT_PATH;
-
-    if (!set_highest_available_option_value(path, KPTR_RESTRICT_MINVALUE, KPTR_RESTRICT_MAXVALUE)) {
-        LOG(ERROR) << "Unable to set adequate kptr_restrict value!";
-        security_failure();
-    }
-    return 0;
-}
-
-static int keychord_init_action(const std::vector<std::string>& args)
-{
-    keychord_init();
-    return 0;
-}
-
-static int console_init_action(const std::vector<std::string>& args)
-{
+static Result<Success> console_init_action(const BuiltinArguments& args) {
     std::string console = GetProperty("ro.boot.console", "");
     if (!console.empty()) {
         default_console = "/dev/" + console;
     }
-    return 0;
+    return Success();
 }
 
 static void import_kernel_nv(const std::string& key, const std::string& value, bool for_emulator) {
@@ -487,21 +354,23 @@
 }
 
 static void export_kernel_boot_props() {
+    constexpr const char* UNSET = "";
     struct {
         const char *src_prop;
         const char *dst_prop;
         const char *default_value;
     } prop_map[] = {
-        { "ro.boot.serialno",   "ro.serialno",   "", },
+        { "ro.boot.serialno",   "ro.serialno",   UNSET, },
         { "ro.boot.mode",       "ro.bootmode",   "unknown", },
         { "ro.boot.baseband",   "ro.baseband",   "unknown", },
         { "ro.boot.bootloader", "ro.bootloader", "unknown", },
         { "ro.boot.hardware",   "ro.hardware",   "unknown", },
         { "ro.boot.revision",   "ro.revision",   "0", },
     };
-    for (size_t i = 0; i < arraysize(prop_map); i++) {
-        std::string value = GetProperty(prop_map[i].src_prop, "");
-        property_set(prop_map[i].dst_prop, (!value.empty()) ? value : prop_map[i].default_value);
+    for (const auto& prop : prop_map) {
+        std::string value = GetProperty(prop.src_prop, prop.default_value);
+        if (value != UNSET)
+            property_set(prop.dst_prop, value);
     }
 }
 
@@ -510,7 +379,7 @@
         return;
     }
 
-    std::unique_ptr<DIR, int (*)(DIR*)> dir(opendir(kAndroidDtDir.c_str()), closedir);
+    std::unique_ptr<DIR, int (*)(DIR*)> dir(opendir(get_android_dt_dir().c_str()), closedir);
     if (!dir) return;
 
     std::string dt_file;
@@ -520,7 +389,7 @@
             continue;
         }
 
-        std::string file_name = kAndroidDtDir + dp->d_name;
+        std::string file_name = get_android_dt_dir() + dp->d_name;
 
         android::base::ReadFileToString(file_name, &dt_file);
         std::replace(dt_file.begin(), dt_file.end(), ',', '.');
@@ -537,367 +406,24 @@
     if (qemu[0]) import_kernel_cmdline(true, import_kernel_nv);
 }
 
-static int property_enable_triggers_action(const std::vector<std::string>& args)
-{
+static Result<Success> property_enable_triggers_action(const BuiltinArguments& args) {
     /* Enable property triggers. */
     property_triggers_enabled = 1;
-    return 0;
+    return Success();
 }
 
-static int queue_property_triggers_action(const std::vector<std::string>& args)
-{
+static Result<Success> queue_property_triggers_action(const BuiltinArguments& args) {
     ActionManager::GetInstance().QueueBuiltinAction(property_enable_triggers_action, "enable_property_trigger");
     ActionManager::GetInstance().QueueAllPropertyActions();
-    return 0;
+    return Success();
 }
 
-static void selinux_init_all_handles(void)
-{
-    sehandle = selinux_android_file_context_handle();
-    selinux_android_set_sehandle(sehandle);
-    sehandle_prop = selinux_android_prop_context_handle();
-}
-
-enum selinux_enforcing_status { SELINUX_PERMISSIVE, SELINUX_ENFORCING };
-
-static selinux_enforcing_status selinux_status_from_cmdline() {
-    selinux_enforcing_status status = SELINUX_ENFORCING;
-
-    import_kernel_cmdline(false, [&](const std::string& key, const std::string& value, bool in_qemu) {
-        if (key == "androidboot.selinux" && value == "permissive") {
-            status = SELINUX_PERMISSIVE;
+static void global_seccomp() {
+    import_kernel_cmdline(false, [](const std::string& key, const std::string& value, bool in_qemu) {
+        if (key == "androidboot.seccomp" && value == "global" && !set_global_seccomp_filter()) {
+            LOG(FATAL) << "Failed to globally enable seccomp!";
         }
     });
-
-    return status;
-}
-
-static bool selinux_is_enforcing(void)
-{
-    if (ALLOW_PERMISSIVE_SELINUX) {
-        return selinux_status_from_cmdline() == SELINUX_ENFORCING;
-    }
-    return true;
-}
-
-static int audit_callback(void *data, security_class_t /*cls*/, char *buf, size_t len) {
-
-    property_audit_data *d = reinterpret_cast<property_audit_data*>(data);
-
-    if (!d || !d->name || !d->cr) {
-        LOG(ERROR) << "audit_callback invoked with null data arguments!";
-        return 0;
-    }
-
-    snprintf(buf, len, "property=%s pid=%d uid=%d gid=%d", d->name,
-            d->cr->pid, d->cr->uid, d->cr->gid);
-    return 0;
-}
-
-/*
- * Forks, executes the provided program in the child, and waits for the completion in the parent.
- * Child's stderr is captured and logged using LOG(ERROR).
- *
- * Returns true if the child exited with status code 0, returns false otherwise.
- */
-static bool fork_execve_and_wait_for_completion(const char* filename, char* const argv[],
-                                                char* const envp[]) {
-    // Create a pipe used for redirecting child process's output.
-    // * pipe_fds[0] is the FD the parent will use for reading.
-    // * pipe_fds[1] is the FD the child will use for writing.
-    int pipe_fds[2];
-    if (pipe(pipe_fds) == -1) {
-        PLOG(ERROR) << "Failed to create pipe";
-        return false;
-    }
-
-    pid_t child_pid = fork();
-    if (child_pid == -1) {
-        PLOG(ERROR) << "Failed to fork for " << filename;
-        return false;
-    }
-
-    if (child_pid == 0) {
-        // fork succeeded -- this is executing in the child process
-
-        // Close the pipe FD not used by this process
-        TEMP_FAILURE_RETRY(close(pipe_fds[0]));
-
-        // Redirect stderr to the pipe FD provided by the parent
-        if (TEMP_FAILURE_RETRY(dup2(pipe_fds[1], STDERR_FILENO)) == -1) {
-            PLOG(ERROR) << "Failed to redirect stderr of " << filename;
-            _exit(127);
-            return false;
-        }
-        TEMP_FAILURE_RETRY(close(pipe_fds[1]));
-
-        if (execve(filename, argv, envp) == -1) {
-            PLOG(ERROR) << "Failed to execve " << filename;
-            return false;
-        }
-        // Unreachable because execve will have succeeded and replaced this code
-        // with child process's code.
-        _exit(127);
-        return false;
-    } else {
-        // fork succeeded -- this is executing in the original/parent process
-
-        // Close the pipe FD not used by this process
-        TEMP_FAILURE_RETRY(close(pipe_fds[1]));
-
-        // Log the redirected output of the child process.
-        // It's unfortunate that there's no standard way to obtain an istream for a file descriptor.
-        // As a result, we're buffering all output and logging it in one go at the end of the
-        // invocation, instead of logging it as it comes in.
-        const int child_out_fd = pipe_fds[0];
-        std::string child_output;
-        if (!android::base::ReadFdToString(child_out_fd, &child_output)) {
-            PLOG(ERROR) << "Failed to capture full output of " << filename;
-        }
-        TEMP_FAILURE_RETRY(close(child_out_fd));
-        if (!child_output.empty()) {
-            // Log captured output, line by line, because LOG expects to be invoked for each line
-            std::istringstream in(child_output);
-            std::string line;
-            while (std::getline(in, line)) {
-                LOG(ERROR) << filename << ": " << line;
-            }
-        }
-
-        // Wait for child to terminate
-        int status;
-        if (TEMP_FAILURE_RETRY(waitpid(child_pid, &status, 0)) != child_pid) {
-            PLOG(ERROR) << "Failed to wait for " << filename;
-            return false;
-        }
-
-        if (WIFEXITED(status)) {
-            int status_code = WEXITSTATUS(status);
-            if (status_code == 0) {
-                return true;
-            } else {
-                LOG(ERROR) << filename << " exited with status " << status_code;
-            }
-        } else if (WIFSIGNALED(status)) {
-            LOG(ERROR) << filename << " killed by signal " << WTERMSIG(status);
-        } else if (WIFSTOPPED(status)) {
-            LOG(ERROR) << filename << " stopped by signal " << WSTOPSIG(status);
-        } else {
-            LOG(ERROR) << "waitpid for " << filename << " returned unexpected status: " << status;
-        }
-
-        return false;
-    }
-}
-
-static bool read_first_line(const char* file, std::string* line) {
-    line->clear();
-
-    std::string contents;
-    if (!android::base::ReadFileToString(file, &contents, true /* follow symlinks */)) {
-        return false;
-    }
-    std::istringstream in(contents);
-    std::getline(in, *line);
-    return true;
-}
-
-static bool selinux_find_precompiled_split_policy(std::string* file) {
-    file->clear();
-
-    static constexpr const char precompiled_sepolicy[] = "/vendor/etc/selinux/precompiled_sepolicy";
-    if (access(precompiled_sepolicy, R_OK) == -1) {
-        return false;
-    }
-    std::string actual_plat_id;
-    if (!read_first_line("/system/etc/selinux/plat_and_mapping_sepolicy.cil.sha256",
-                         &actual_plat_id)) {
-        PLOG(INFO) << "Failed to read "
-                      "/system/etc/selinux/plat_and_mapping_sepolicy.cil.sha256";
-        return false;
-    }
-    std::string precompiled_plat_id;
-    if (!read_first_line("/vendor/etc/selinux/precompiled_sepolicy.plat_and_mapping.sha256",
-                         &precompiled_plat_id)) {
-        PLOG(INFO) << "Failed to read "
-                      "/vendor/etc/selinux/"
-                      "precompiled_sepolicy.plat_and_mapping.sha256";
-        return false;
-    }
-    if ((actual_plat_id.empty()) || (actual_plat_id != precompiled_plat_id)) {
-        return false;
-    }
-
-    *file = precompiled_sepolicy;
-    return true;
-}
-
-static constexpr const char plat_policy_cil_file[] = "/system/etc/selinux/plat_sepolicy.cil";
-
-static bool selinux_is_split_policy_device() { return access(plat_policy_cil_file, R_OK) != -1; }
-
-/*
- * Loads SELinux policy split across platform/system and non-platform/vendor files.
- *
- * Returns true upon success, false otherwise (failure cause is logged).
- */
-static bool selinux_load_split_policy() {
-    // IMPLEMENTATION NOTE: Split policy consists of three CIL files:
-    // * platform -- policy needed due to logic contained in the system image,
-    // * non-platform -- policy needed due to logic contained in the vendor image,
-    // * mapping -- mapping policy which helps preserve forward-compatibility of non-platform policy
-    //   with newer versions of platform policy.
-    //
-    // secilc is invoked to compile the above three policy files into a single monolithic policy
-    // file. This file is then loaded into the kernel.
-
-    // Load precompiled policy from vendor image, if a matching policy is found there. The policy
-    // must match the platform policy on the system image.
-    std::string precompiled_sepolicy_file;
-    if (selinux_find_precompiled_split_policy(&precompiled_sepolicy_file)) {
-        android::base::unique_fd fd(
-            open(precompiled_sepolicy_file.c_str(), O_RDONLY | O_CLOEXEC | O_BINARY));
-        if (fd != -1) {
-            if (selinux_android_load_policy_from_fd(fd, precompiled_sepolicy_file.c_str()) < 0) {
-                LOG(ERROR) << "Failed to load SELinux policy from " << precompiled_sepolicy_file;
-                return false;
-            }
-            return true;
-        }
-    }
-    // No suitable precompiled policy could be loaded
-
-    LOG(INFO) << "Compiling SELinux policy";
-
-    // Determine the highest policy language version supported by the kernel
-    set_selinuxmnt("/sys/fs/selinux");
-    int max_policy_version = security_policyvers();
-    if (max_policy_version == -1) {
-        PLOG(ERROR) << "Failed to determine highest policy version supported by kernel";
-        return false;
-    }
-
-    // We store the output of the compilation on /dev because this is the most convenient tmpfs
-    // storage mount available this early in the boot sequence.
-    char compiled_sepolicy[] = "/dev/sepolicy.XXXXXX";
-    android::base::unique_fd compiled_sepolicy_fd(mkostemp(compiled_sepolicy, O_CLOEXEC));
-    if (compiled_sepolicy_fd < 0) {
-        PLOG(ERROR) << "Failed to create temporary file " << compiled_sepolicy;
-        return false;
-    }
-
-    // clang-format off
-    const char* compile_args[] = {
-        "/system/bin/secilc",
-        plat_policy_cil_file,
-        "-M", "true",
-        // Target the highest policy language version supported by the kernel
-        "-c", std::to_string(max_policy_version).c_str(),
-        "/system/etc/selinux/mapping_sepolicy.cil",
-        "/vendor/etc/selinux/nonplat_sepolicy.cil",
-        "-o", compiled_sepolicy,
-        // We don't care about file_contexts output by the compiler
-        "-f", "/sys/fs/selinux/null",  // /dev/null is not yet available
-        nullptr};
-    // clang-format on
-
-    if (!fork_execve_and_wait_for_completion(compile_args[0], (char**)compile_args, (char**)ENV)) {
-        unlink(compiled_sepolicy);
-        return false;
-    }
-    unlink(compiled_sepolicy);
-
-    LOG(INFO) << "Loading compiled SELinux policy";
-    if (selinux_android_load_policy_from_fd(compiled_sepolicy_fd, compiled_sepolicy) < 0) {
-        LOG(ERROR) << "Failed to load SELinux policy from " << compiled_sepolicy;
-        return false;
-    }
-
-    return true;
-}
-
-/*
- * Loads SELinux policy from a monolithic file.
- *
- * Returns true upon success, false otherwise (failure cause is logged).
- */
-static bool selinux_load_monolithic_policy() {
-    LOG(VERBOSE) << "Loading SELinux policy from monolithic file";
-    if (selinux_android_load_policy() < 0) {
-        PLOG(ERROR) << "Failed to load monolithic SELinux policy";
-        return false;
-    }
-    return true;
-}
-
-/*
- * Loads SELinux policy into the kernel.
- *
- * Returns true upon success, false otherwise (failure cause is logged).
- */
-static bool selinux_load_policy() {
-    return selinux_is_split_policy_device() ? selinux_load_split_policy()
-                                            : selinux_load_monolithic_policy();
-}
-
-static void selinux_initialize(bool in_kernel_domain) {
-    Timer t;
-
-    selinux_callback cb;
-    cb.func_log = selinux_klog_callback;
-    selinux_set_callback(SELINUX_CB_LOG, cb);
-    cb.func_audit = audit_callback;
-    selinux_set_callback(SELINUX_CB_AUDIT, cb);
-
-    if (in_kernel_domain) {
-        LOG(INFO) << "Loading SELinux policy";
-        if (!selinux_load_policy()) {
-            panic();
-        }
-
-        bool kernel_enforcing = (security_getenforce() == 1);
-        bool is_enforcing = selinux_is_enforcing();
-        if (kernel_enforcing != is_enforcing) {
-            if (security_setenforce(is_enforcing)) {
-                PLOG(ERROR) << "security_setenforce(%s) failed" << (is_enforcing ? "true" : "false");
-                security_failure();
-            }
-        }
-
-        std::string err;
-        if (!WriteFile("/sys/fs/selinux/checkreqprot", "0", &err)) {
-            LOG(ERROR) << err;
-            security_failure();
-        }
-
-        // init's first stage can't set properties, so pass the time to the second stage.
-        setenv("INIT_SELINUX_TOOK", std::to_string(t.duration().count()).c_str(), 1);
-    } else {
-        selinux_init_all_handles();
-    }
-}
-
-// The files and directories that were created before initial sepolicy load or
-// files on ramdisk need to have their security context restored to the proper
-// value. This must happen before /dev is populated by ueventd.
-static void selinux_restore_context() {
-    LOG(INFO) << "Running restorecon...";
-    selinux_android_restorecon("/dev", 0);
-    selinux_android_restorecon("/dev/kmsg", 0);
-    if constexpr (WORLD_WRITABLE_KMSG) {
-      selinux_android_restorecon("/dev/kmsg_debug", 0);
-    }
-    selinux_android_restorecon("/dev/socket", 0);
-    selinux_android_restorecon("/dev/random", 0);
-    selinux_android_restorecon("/dev/urandom", 0);
-    selinux_android_restorecon("/dev/__properties__", 0);
-    selinux_android_restorecon("/plat_property_contexts", 0);
-    selinux_android_restorecon("/nonplat_property_contexts", 0);
-    selinux_android_restorecon("/dev/block", SELINUX_ANDROID_RESTORECON_RECURSE);
-    selinux_android_restorecon("/dev/device-mapper", 0);
-
-    selinux_android_restorecon("/sbin/mke2fs", 0);
-    selinux_android_restorecon("/sbin/e2fsdroid", 0);
 }
 
 // Set the UDC controller for the ConfigFS USB Gadgets.
@@ -916,7 +442,7 @@
     }
 }
 
-static void install_reboot_signal_handlers() {
+static void InstallRebootSignalHandlers() {
     // Instead of panic'ing the kernel as is the default behavior when init crashes,
     // we prefer to reboot to bootloader on development builds, as this will prevent
     // boot looping bad configurations and allow both developers and test farms to easily
@@ -924,9 +450,18 @@
     struct sigaction action;
     memset(&action, 0, sizeof(action));
     sigfillset(&action.sa_mask);
-    action.sa_handler = [](int) {
-        // panic() reboots to bootloader
-        panic();
+    action.sa_handler = [](int signal) {
+        // These signal handlers are also caught for processes forked from init, however we do not
+        // want them to trigger reboot, so we directly call _exit() for children processes here.
+        if (getpid() != 1) {
+            _exit(signal);
+        }
+
+        // Calling DoReboot() or LOG(FATAL) is not a good option as this is a signal handler.
+        // RebootSystem uses syscall() which isn't actually async-signal-safe, but our only option
+        // and probably good enough given this is already an error case and only enabled for
+        // development builds.
+        RebootSystem(ANDROID_RB_RESTART2, "bootloader");
     };
     action.sa_flags = SA_RESTART;
     sigaction(SIGABRT, &action, nullptr);
@@ -941,6 +476,142 @@
     sigaction(SIGTRAP, &action, nullptr);
 }
 
+static void HandleSigtermSignal(const signalfd_siginfo& siginfo) {
+    if (siginfo.ssi_pid != 0) {
+        // Drop any userspace SIGTERM requests.
+        LOG(DEBUG) << "Ignoring SIGTERM from pid " << siginfo.ssi_pid;
+        return;
+    }
+
+    HandlePowerctlMessage("shutdown,container");
+}
+
+static void HandleSignalFd() {
+    signalfd_siginfo siginfo;
+    ssize_t bytes_read = TEMP_FAILURE_RETRY(read(signal_fd, &siginfo, sizeof(siginfo)));
+    if (bytes_read != sizeof(siginfo)) {
+        PLOG(ERROR) << "Failed to read siginfo from signal_fd";
+        return;
+    }
+
+    switch (siginfo.ssi_signo) {
+        case SIGCHLD:
+            ReapAnyOutstandingChildren();
+            break;
+        case SIGTERM:
+            HandleSigtermSignal(siginfo);
+            break;
+        default:
+            PLOG(ERROR) << "signal_fd: received unexpected signal " << siginfo.ssi_signo;
+            break;
+    }
+}
+
+static void UnblockSignals() {
+    const struct sigaction act { .sa_handler = SIG_DFL };
+    sigaction(SIGCHLD, &act, nullptr);
+
+    sigset_t mask;
+    sigemptyset(&mask);
+    sigaddset(&mask, SIGCHLD);
+    sigaddset(&mask, SIGTERM);
+
+    if (sigprocmask(SIG_UNBLOCK, &mask, nullptr) == -1) {
+        PLOG(FATAL) << "failed to unblock signals for PID " << getpid();
+    }
+}
+
+static void InstallSignalFdHandler(Epoll* epoll) {
+    // Applying SA_NOCLDSTOP to a defaulted SIGCHLD handler prevents the signalfd from receiving
+    // SIGCHLD when a child process stops or continues (b/77867680#comment9).
+    const struct sigaction act { .sa_handler = SIG_DFL, .sa_flags = SA_NOCLDSTOP };
+    sigaction(SIGCHLD, &act, nullptr);
+
+    sigset_t mask;
+    sigemptyset(&mask);
+    sigaddset(&mask, SIGCHLD);
+
+    if (!IsRebootCapable()) {
+        // If init does not have the CAP_SYS_BOOT capability, it is running in a container.
+        // In that case, receiving SIGTERM will cause the system to shut down.
+        sigaddset(&mask, SIGTERM);
+    }
+
+    if (sigprocmask(SIG_BLOCK, &mask, nullptr) == -1) {
+        PLOG(FATAL) << "failed to block signals";
+    }
+
+    // Register a handler to unblock signals in the child processes.
+    const int result = pthread_atfork(nullptr, nullptr, &UnblockSignals);
+    if (result != 0) {
+        LOG(FATAL) << "Failed to register a fork handler: " << strerror(result);
+    }
+
+    signal_fd = signalfd(-1, &mask, SFD_CLOEXEC);
+    if (signal_fd == -1) {
+        PLOG(FATAL) << "failed to create signalfd";
+    }
+
+    if (auto result = epoll->RegisterHandler(signal_fd, HandleSignalFd); !result) {
+        LOG(FATAL) << result.error();
+    }
+}
+
+void HandleKeychord(const std::vector<int>& keycodes) {
+    // Only handle keychords if adb is enabled.
+    std::string adb_enabled = android::base::GetProperty("init.svc.adbd", "");
+    if (adb_enabled != "running") {
+        LOG(WARNING) << "Not starting service for keychord " << android::base::Join(keycodes, ' ')
+                     << " because ADB is disabled";
+        return;
+    }
+
+    auto found = false;
+    for (const auto& service : ServiceList::GetInstance()) {
+        auto svc = service.get();
+        if (svc->keycodes() == keycodes) {
+            found = true;
+            LOG(INFO) << "Starting service '" << svc->name() << "' from keychord "
+                      << android::base::Join(keycodes, ' ');
+            if (auto result = svc->Start(); !result) {
+                LOG(ERROR) << "Could not start service '" << svc->name() << "' from keychord "
+                           << android::base::Join(keycodes, ' ') << ": " << result.error();
+            }
+        }
+    }
+    if (!found) {
+        LOG(ERROR) << "Service for keychord " << android::base::Join(keycodes, ' ') << " not found";
+    }
+}
+
+static void InitAborter(const char* abort_message) {
+    // When init forks, it continues to use this aborter for LOG(FATAL), but we want children to
+    // simply abort instead of trying to reboot the system.
+    if (getpid() != 1) {
+        android::base::DefaultAborter(abort_message);
+        return;
+    }
+
+    RebootSystem(ANDROID_RB_RESTART2, "bootloader");
+}
+
+static void InitKernelLogging(char* argv[]) {
+    // Make stdin/stdout/stderr all point to /dev/null.
+    int fd = open("/sys/fs/selinux/null", O_RDWR);
+    if (fd == -1) {
+        int saved_errno = errno;
+        android::base::InitLogging(argv, &android::base::KernelLogger, InitAborter);
+        errno = saved_errno;
+        PLOG(FATAL) << "Couldn't open /sys/fs/selinux/null";
+    }
+    dup2(fd, 0);
+    dup2(fd, 1);
+    dup2(fd, 2);
+    if (fd > 2) close(fd);
+
+    android::base::InitLogging(argv, &android::base::KernelLogger, InitAborter);
+}
+
 int main(int argc, char** argv) {
     if (!strcmp(basename(argv[0]), "ueventd")) {
         return ueventd_main(argc, argv);
@@ -950,65 +621,102 @@
         return watchdogd_main(argc, argv);
     }
 
-    if (REBOOT_BOOTLOADER_ON_PANIC) {
-        install_reboot_signal_handlers();
+    if (argc > 1 && !strcmp(argv[1], "subcontext")) {
+        android::base::InitLogging(argv, &android::base::KernelLogger);
+        const BuiltinFunctionMap function_map;
+        return SubcontextMain(argc, argv, &function_map);
     }
 
-    add_environment("PATH", _PATH_DEFPATH);
+    if (REBOOT_BOOTLOADER_ON_PANIC) {
+        InstallRebootSignalHandlers();
+    }
 
     bool is_first_stage = (getenv("INIT_SECOND_STAGE") == nullptr);
 
     if (is_first_stage) {
         boot_clock::time_point start_time = boot_clock::now();
 
+        std::vector<std::pair<std::string, int>> errors;
+#define CHECKCALL(x) \
+    if (x != 0) errors.emplace_back(#x " failed", errno);
+
         // Clear the umask.
         umask(0);
 
+        CHECKCALL(clearenv());
+        CHECKCALL(setenv("PATH", _PATH_DEFPATH, 1));
         // Get the basic filesystem setup we need put together in the initramdisk
         // on / and then we'll let the rc file figure out the rest.
-        mount("tmpfs", "/dev", "tmpfs", MS_NOSUID, "mode=0755");
-        mkdir("/dev/pts", 0755);
-        mkdir("/dev/socket", 0755);
-        mount("devpts", "/dev/pts", "devpts", 0, NULL);
-        #define MAKE_STR(x) __STRING(x)
-        mount("proc", "/proc", "proc", 0, "hidepid=2,gid=" MAKE_STR(AID_READPROC));
+        CHECKCALL(mount("tmpfs", "/dev", "tmpfs", MS_NOSUID, "mode=0755"));
+        CHECKCALL(mkdir("/dev/pts", 0755));
+        CHECKCALL(mkdir("/dev/socket", 0755));
+        CHECKCALL(mount("devpts", "/dev/pts", "devpts", 0, NULL));
+#define MAKE_STR(x) __STRING(x)
+        CHECKCALL(mount("proc", "/proc", "proc", 0, "hidepid=2,gid=" MAKE_STR(AID_READPROC)));
+#undef MAKE_STR
         // Don't expose the raw commandline to unprivileged processes.
-        chmod("/proc/cmdline", 0440);
+        CHECKCALL(chmod("/proc/cmdline", 0440));
         gid_t groups[] = { AID_READPROC };
-        setgroups(arraysize(groups), groups);
-        mount("sysfs", "/sys", "sysfs", 0, NULL);
-        mount("selinuxfs", "/sys/fs/selinux", "selinuxfs", 0, NULL);
+        CHECKCALL(setgroups(arraysize(groups), groups));
+        CHECKCALL(mount("sysfs", "/sys", "sysfs", 0, NULL));
+        CHECKCALL(mount("selinuxfs", "/sys/fs/selinux", "selinuxfs", 0, NULL));
 
-        mknod("/dev/kmsg", S_IFCHR | 0600, makedev(1, 11));
+        CHECKCALL(mknod("/dev/kmsg", S_IFCHR | 0600, makedev(1, 11)));
 
         if constexpr (WORLD_WRITABLE_KMSG) {
-          mknod("/dev/kmsg_debug", S_IFCHR | 0622, makedev(1, 11));
+            CHECKCALL(mknod("/dev/kmsg_debug", S_IFCHR | 0622, makedev(1, 11)));
         }
 
-        mknod("/dev/random", S_IFCHR | 0666, makedev(1, 8));
-        mknod("/dev/urandom", S_IFCHR | 0666, makedev(1, 9));
+        CHECKCALL(mknod("/dev/random", S_IFCHR | 0666, makedev(1, 8)));
+        CHECKCALL(mknod("/dev/urandom", S_IFCHR | 0666, makedev(1, 9)));
+
+        // This is needed for log wrapper, which gets called before ueventd runs.
+        CHECKCALL(mknod("/dev/ptmx", S_IFCHR | 0666, makedev(5, 2)));
+        CHECKCALL(mknod("/dev/null", S_IFCHR | 0666, makedev(1, 3)));
+
+        // Mount staging areas for devices managed by vold
+        // See storage config details at http://source.android.com/devices/storage/
+        CHECKCALL(mount("tmpfs", "/mnt", "tmpfs", MS_NOEXEC | MS_NOSUID | MS_NODEV,
+                        "mode=0755,uid=0,gid=1000"));
+        // /mnt/vendor is used to mount vendor-specific partitions that can not be
+        // part of the vendor partition, e.g. because they are mounted read-write.
+        CHECKCALL(mkdir("/mnt/vendor", 0755));
+        // /mnt/product is used to mount product-specific partitions that can not be
+        // part of the product partition, e.g. because they are mounted read-write.
+        CHECKCALL(mkdir("/mnt/product", 0755));
+
+#undef CHECKCALL
 
         // Now that tmpfs is mounted on /dev and we have /dev/kmsg, we can actually
         // talk to the outside world...
         InitKernelLogging(argv);
 
+        if (!errors.empty()) {
+            for (const auto& [error_string, error_errno] : errors) {
+                LOG(ERROR) << error_string << " " << strerror(error_errno);
+            }
+            LOG(FATAL) << "Init encountered errors starting first stage, aborting";
+        }
+
         LOG(INFO) << "init first stage started!";
 
         if (!DoFirstStageMount()) {
-            LOG(ERROR) << "Failed to mount required partitions early ...";
-            panic();
+            LOG(FATAL) << "Failed to mount required partitions early ...";
         }
 
         SetInitAvbVersionInRecovery();
 
+        // Enable seccomp if global boot option was passed (otherwise it is enabled in zygote).
+        global_seccomp();
+
         // Set up SELinux, loading the SELinux policy.
-        selinux_initialize(true);
+        SelinuxSetupKernelLogging();
+        SelinuxInitialize();
 
         // We're in the kernel domain, so re-exec init to transition to the init domain now
         // that the SELinux policy has been loaded.
         if (selinux_android_restorecon("/init", 0) == -1) {
-            PLOG(ERROR) << "restorecon failed";
-            security_failure();
+            PLOG(FATAL) << "restorecon failed of /init failed";
         }
 
         setenv("INIT_SECOND_STAGE", "true", 1);
@@ -1023,8 +731,7 @@
 
         // execv() only returns if an error happened, in which case we
         // panic and never fall through this conditional.
-        PLOG(ERROR) << "execv(\"" << path << "\") failed";
-        security_failure();
+        PLOG(FATAL) << "execv(\"" << path << "\") failed";
     }
 
     // At this point we're in the second stage of init.
@@ -1065,46 +772,31 @@
     unsetenv("INIT_AVB_VERSION");
 
     // Now set up SELinux for second stage.
-    selinux_initialize(false);
-    selinux_restore_context();
+    SelinuxSetupKernelLogging();
+    SelabelInitialize();
+    SelinuxRestoreContext();
 
-    epoll_fd = epoll_create1(EPOLL_CLOEXEC);
-    if (epoll_fd == -1) {
-        PLOG(ERROR) << "epoll_create1 failed";
-        exit(1);
+    Epoll epoll;
+    if (auto result = epoll.Open(); !result) {
+        PLOG(FATAL) << result.error();
     }
 
-    signal_handler_init();
+    InstallSignalFdHandler(&epoll);
 
     property_load_boot_defaults();
     export_oem_lock_status();
-    start_property_service();
+    StartPropertyService(&epoll);
     set_usb_controller();
 
     const BuiltinFunctionMap function_map;
     Action::set_function_map(&function_map);
 
-    ActionManager& am = ActionManager::GetInstance();
-    ServiceManager& sm = ServiceManager::GetInstance();
-    Parser& parser = Parser::GetInstance();
+    subcontexts = InitializeSubcontexts();
 
-    parser.AddSectionParser("service", std::make_unique<ServiceParser>(&sm));
-    parser.AddSectionParser("on", std::make_unique<ActionParser>(&am));
-    parser.AddSectionParser("import", std::make_unique<ImportParser>(&parser));
-    std::string bootscript = GetProperty("ro.boot.init_rc", "");
-    if (bootscript.empty()) {
-        parser.ParseConfig("/init.rc");
-        parser.set_is_system_etc_init_loaded(
-                parser.ParseConfig("/system/etc/init"));
-        parser.set_is_vendor_etc_init_loaded(
-                parser.ParseConfig("/vendor/etc/init"));
-        parser.set_is_odm_etc_init_loaded(parser.ParseConfig("/odm/etc/init"));
-    } else {
-        parser.ParseConfig(bootscript);
-        parser.set_is_system_etc_init_loaded(true);
-        parser.set_is_vendor_etc_init_loaded(true);
-        parser.set_is_odm_etc_init_loaded(true);
-    }
+    ActionManager& am = ActionManager::GetInstance();
+    ServiceList& sm = ServiceList::GetInstance();
+
+    LoadBootScripts(am, sm);
 
     // Turning this on and letting the INFO logging be discarded adds 0.2s to
     // Nexus 9 boot time, so it's disabled by default.
@@ -1115,10 +807,19 @@
     // Queue an action that waits for coldboot done so we know ueventd has set up all of /dev...
     am.QueueBuiltinAction(wait_for_coldboot_done_action, "wait_for_coldboot_done");
     // ... so that we can start queuing up actions that require stuff from /dev.
-    am.QueueBuiltinAction(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng");
-    am.QueueBuiltinAction(set_mmap_rnd_bits_action, "set_mmap_rnd_bits");
-    am.QueueBuiltinAction(set_kptr_restrict_action, "set_kptr_restrict");
-    am.QueueBuiltinAction(keychord_init_action, "keychord_init");
+    am.QueueBuiltinAction(MixHwrngIntoLinuxRngAction, "MixHwrngIntoLinuxRng");
+    am.QueueBuiltinAction(SetMmapRndBitsAction, "SetMmapRndBits");
+    am.QueueBuiltinAction(SetKptrRestrictAction, "SetKptrRestrict");
+    Keychords keychords;
+    am.QueueBuiltinAction(
+        [&epoll, &keychords](const BuiltinArguments& args) -> Result<Success> {
+            for (const auto& svc : ServiceList::GetInstance()) {
+                keychords.Register(svc->keycodes());
+            }
+            keychords.Start(&epoll, HandleKeychord);
+            return Success();
+        },
+        "KeychordInit");
     am.QueueBuiltinAction(console_init_action, "console_init");
 
     // Trigger all the boot actions to get us started.
@@ -1126,7 +827,7 @@
 
     // Repeat mix_hwrng_into_linux_rng in case /dev/hw_random or /dev/random
     // wasn't ready immediately after wait_for_coldboot_done
-    am.QueueBuiltinAction(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng");
+    am.QueueBuiltinAction(MixHwrngIntoLinuxRngAction, "MixHwrngIntoLinuxRng");
 
     // Don't mount filesystems or start core system services in charger mode.
     std::string bootmode = GetProperty("ro.bootmode", "");
@@ -1141,30 +842,36 @@
 
     while (true) {
         // By default, sleep until something happens.
-        int epoll_timeout_ms = -1;
+        auto epoll_timeout = std::optional<std::chrono::milliseconds>{};
 
-        if (!(waiting_for_prop || sm.IsWaitingForExec())) {
+        if (do_shutdown && !shutting_down) {
+            do_shutdown = false;
+            if (HandlePowerctlMessage(shutdown_command)) {
+                shutting_down = true;
+            }
+        }
+
+        if (!(waiting_for_prop || Service::is_exec_service_running())) {
             am.ExecuteOneCommand();
         }
-        if (!(waiting_for_prop || sm.IsWaitingForExec())) {
-            if (!shutting_down) restart_processes();
+        if (!(waiting_for_prop || Service::is_exec_service_running())) {
+            if (!shutting_down) {
+                auto next_process_restart_time = RestartProcesses();
 
-            // If there's a process that needs restarting, wake up in time for that.
-            if (process_needs_restart_at != 0) {
-                epoll_timeout_ms = (process_needs_restart_at - time(nullptr)) * 1000;
-                if (epoll_timeout_ms < 0) epoll_timeout_ms = 0;
+                // If there's a process that needs restarting, wake up in time for that.
+                if (next_process_restart_time) {
+                    epoll_timeout = std::chrono::ceil<std::chrono::milliseconds>(
+                        *next_process_restart_time - boot_clock::now());
+                    if (*epoll_timeout < 0ms) epoll_timeout = 0ms;
+                }
             }
 
             // If there's more work to do, wake up again immediately.
-            if (am.HasMoreCommands()) epoll_timeout_ms = 0;
+            if (am.HasMoreCommands()) epoll_timeout = 0ms;
         }
 
-        epoll_event ev;
-        int nr = TEMP_FAILURE_RETRY(epoll_wait(epoll_fd, &ev, 1, epoll_timeout_ms));
-        if (nr == -1) {
-            PLOG(ERROR) << "epoll_wait failed";
-        } else if (nr == 1) {
-            ((void (*)()) ev.data.ptr)();
+        if (auto result = epoll.Wait(epoll_timeout); !result) {
+            LOG(ERROR) << result.error();
         }
     }
 
@@ -1173,7 +880,3 @@
 
 }  // namespace init
 }  // namespace android
-
-int main(int argc, char** argv) {
-    android::init::main(argc, argv);
-}
diff --git a/init/init.h b/init/init.h
index aaab523..6c82fa1 100644
--- a/init/init.h
+++ b/init/init.h
@@ -17,9 +17,16 @@
 #ifndef _INIT_INIT_H
 #define _INIT_INIT_H
 
-#include <string>
+#include <sys/types.h>
 
-#include <selinux/label.h>
+#include <functional>
+#include <string>
+#include <vector>
+
+#include "action.h"
+#include "action_manager.h"
+#include "parser.h"
+#include "service.h"
 
 namespace android {
 namespace init {
@@ -27,25 +34,23 @@
 // Note: These globals are *only* valid in init, so they should not be used in ueventd,
 // watchdogd, or any files that may be included in those, such as devices.cpp and util.cpp.
 // TODO: Have an Init class and remove all globals.
-extern const char *ENV[32];
 extern std::string default_console;
-extern struct selabel_handle *sehandle;
-extern struct selabel_handle *sehandle_prop;
+extern std::vector<std::string> late_import_paths;
 
-void handle_control_message(const std::string& msg, const std::string& arg);
+Parser CreateParser(ActionManager& action_manager, ServiceList& service_list);
+
+void HandleControlMessage(const std::string& msg, const std::string& arg, pid_t pid);
 
 void property_changed(const std::string& name, const std::string& value);
 
-void register_epoll_handler(int fd, void (*fn)());
-
-int add_environment(const char* key, const char* val);
-
 bool start_waiting_for_property(const char *name, const char *value);
 
 void DumpState();
 
 void ResetWaitForProp();
 
+int main(int argc, char** argv);
+
 }  // namespace init
 }  // namespace android
 
diff --git a/init/init_first_stage.cpp b/init/init_first_stage.cpp
deleted file mode 100644
index 0f7e38f..0000000
--- a/init/init_first_stage.cpp
+++ /dev/null
@@ -1,517 +0,0 @@
-/*
- * Copyright (C) 2017 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 "init_first_stage.h"
-
-#include <stdlib.h>
-#include <unistd.h>
-
-#include <chrono>
-#include <memory>
-#include <set>
-#include <string>
-#include <vector>
-
-#include <android-base/chrono_utils.h>
-#include <android-base/file.h>
-#include <android-base/logging.h>
-#include <android-base/strings.h>
-
-#include "devices.h"
-#include "fs_mgr.h"
-#include "fs_mgr_avb.h"
-#include "uevent.h"
-#include "uevent_listener.h"
-#include "util.h"
-
-using android::base::Timer;
-
-namespace android {
-namespace init {
-
-// Class Declarations
-// ------------------
-class FirstStageMount {
-  public:
-    FirstStageMount();
-    virtual ~FirstStageMount() = default;
-
-    // The factory method to create either FirstStageMountVBootV1 or FirstStageMountVBootV2
-    // based on device tree configurations.
-    static std::unique_ptr<FirstStageMount> Create();
-    bool DoFirstStageMount();  // Mounts fstab entries read from device tree.
-    bool InitDevices();
-
-  protected:
-    bool InitRequiredDevices();
-    bool InitVerityDevice(const std::string& verity_device);
-    bool MountPartitions();
-
-    virtual ListenerAction UeventCallback(const Uevent& uevent);
-
-    // Pure virtual functions.
-    virtual bool GetRequiredDevices() = 0;
-    virtual bool SetUpDmVerity(fstab_rec* fstab_rec) = 0;
-
-    bool need_dm_verity_;
-
-    std::unique_ptr<fstab, decltype(&fs_mgr_free_fstab)> device_tree_fstab_;
-    std::vector<fstab_rec*> mount_fstab_recs_;
-    std::set<std::string> required_devices_partition_names_;
-    DeviceHandler device_handler_;
-    UeventListener uevent_listener_;
-};
-
-class FirstStageMountVBootV1 : public FirstStageMount {
-  public:
-    FirstStageMountVBootV1() = default;
-    ~FirstStageMountVBootV1() override = default;
-
-  protected:
-    bool GetRequiredDevices() override;
-    bool SetUpDmVerity(fstab_rec* fstab_rec) override;
-};
-
-class FirstStageMountVBootV2 : public FirstStageMount {
-  public:
-    friend void SetInitAvbVersionInRecovery();
-
-    FirstStageMountVBootV2();
-    ~FirstStageMountVBootV2() override = default;
-
-  protected:
-    ListenerAction UeventCallback(const Uevent& uevent) override;
-    bool GetRequiredDevices() override;
-    bool SetUpDmVerity(fstab_rec* fstab_rec) override;
-    bool InitAvbHandle();
-
-    std::string device_tree_vbmeta_parts_;
-    FsManagerAvbUniquePtr avb_handle_;
-    ByNameSymlinkMap by_name_symlink_map_;
-};
-
-// Static Functions
-// ----------------
-static inline bool IsDtVbmetaCompatible() {
-    return is_android_dt_value_expected("vbmeta/compatible", "android,vbmeta");
-}
-
-static bool inline IsRecoveryMode() {
-    return access("/sbin/recovery", F_OK) == 0;
-}
-
-// Class Definitions
-// -----------------
-FirstStageMount::FirstStageMount()
-    : need_dm_verity_(false), device_tree_fstab_(fs_mgr_read_fstab_dt(), fs_mgr_free_fstab) {
-    if (!device_tree_fstab_) {
-        LOG(ERROR) << "Failed to read fstab from device tree";
-        return;
-    }
-    // Stores device_tree_fstab_->recs[] into mount_fstab_recs_ (vector<fstab_rec*>)
-    // for easier manipulation later, e.g., range-base for loop.
-    for (int i = 0; i < device_tree_fstab_->num_entries; i++) {
-        mount_fstab_recs_.push_back(&device_tree_fstab_->recs[i]);
-    }
-}
-
-std::unique_ptr<FirstStageMount> FirstStageMount::Create() {
-    if (IsDtVbmetaCompatible()) {
-        return std::make_unique<FirstStageMountVBootV2>();
-    } else {
-        return std::make_unique<FirstStageMountVBootV1>();
-    }
-}
-
-bool FirstStageMount::DoFirstStageMount() {
-    // Nothing to mount.
-    if (mount_fstab_recs_.empty()) return true;
-
-    if (!InitDevices()) return false;
-
-    if (!MountPartitions()) return false;
-
-    return true;
-}
-
-bool FirstStageMount::InitDevices() {
-    return GetRequiredDevices() && InitRequiredDevices();
-}
-
-// Creates devices with uevent->partition_name matching one in the member variable
-// required_devices_partition_names_. Found partitions will then be removed from it
-// for the subsequent member function to check which devices are NOT created.
-bool FirstStageMount::InitRequiredDevices() {
-    if (required_devices_partition_names_.empty()) {
-        return true;
-    }
-
-    if (need_dm_verity_) {
-        const std::string dm_path = "/devices/virtual/misc/device-mapper";
-        bool found = false;
-        auto dm_callback = [this, &dm_path, &found](const Uevent& uevent) {
-            if (uevent.path == dm_path) {
-                device_handler_.HandleDeviceEvent(uevent);
-                found = true;
-                return ListenerAction::kStop;
-            }
-            return ListenerAction::kContinue;
-        };
-        uevent_listener_.RegenerateUeventsForPath("/sys" + dm_path, dm_callback);
-        if (!found) {
-            LOG(INFO) << "device-mapper device not found in /sys, waiting for its uevent";
-            Timer t;
-            uevent_listener_.Poll(dm_callback, 10s);
-            LOG(INFO) << "Wait for device-mapper returned after " << t;
-        }
-        if (!found) {
-            LOG(ERROR) << "device-mapper device not found after polling timeout";
-            return false;
-        }
-    }
-
-    auto uevent_callback = [this](const Uevent& uevent) { return UeventCallback(uevent); };
-    uevent_listener_.RegenerateUevents(uevent_callback);
-
-    // UeventCallback() will remove found partitions from required_devices_partition_names_.
-    // So if it isn't empty here, it means some partitions are not found.
-    if (!required_devices_partition_names_.empty()) {
-        LOG(INFO) << __PRETTY_FUNCTION__
-                  << ": partition(s) not found in /sys, waiting for their uevent(s): "
-                  << android::base::Join(required_devices_partition_names_, ", ");
-        Timer t;
-        uevent_listener_.Poll(uevent_callback, 10s);
-        LOG(INFO) << "Wait for partitions returned after " << t;
-    }
-
-    if (!required_devices_partition_names_.empty()) {
-        LOG(ERROR) << __PRETTY_FUNCTION__ << ": partition(s) not found after polling timeout: "
-                   << android::base::Join(required_devices_partition_names_, ", ");
-        return false;
-    }
-
-    return true;
-}
-
-ListenerAction FirstStageMount::UeventCallback(const Uevent& uevent) {
-    // Ignores everything that is not a block device.
-    if (uevent.subsystem != "block") {
-        return ListenerAction::kContinue;
-    }
-
-    if (!uevent.partition_name.empty()) {
-        // Matches partition name to create device nodes.
-        // Both required_devices_partition_names_ and uevent->partition_name have A/B
-        // suffix when A/B is used.
-        auto iter = required_devices_partition_names_.find(uevent.partition_name);
-        if (iter != required_devices_partition_names_.end()) {
-            LOG(VERBOSE) << __PRETTY_FUNCTION__ << ": found partition: " << *iter;
-            required_devices_partition_names_.erase(iter);
-            device_handler_.HandleDeviceEvent(uevent);
-            if (required_devices_partition_names_.empty()) {
-                return ListenerAction::kStop;
-            } else {
-                return ListenerAction::kContinue;
-            }
-        }
-    }
-    // Not found a partition or find an unneeded partition, continue to find others.
-    return ListenerAction::kContinue;
-}
-
-// Creates "/dev/block/dm-XX" for dm-verity by running coldboot on /sys/block/dm-XX.
-bool FirstStageMount::InitVerityDevice(const std::string& verity_device) {
-    const std::string device_name(basename(verity_device.c_str()));
-    const std::string syspath = "/sys/block/" + device_name;
-    bool found = false;
-
-    auto verity_callback = [&device_name, &verity_device, this, &found](const Uevent& uevent) {
-        if (uevent.device_name == device_name) {
-            LOG(VERBOSE) << "Creating dm-verity device : " << verity_device;
-            device_handler_.HandleDeviceEvent(uevent);
-            found = true;
-            return ListenerAction::kStop;
-        }
-        return ListenerAction::kContinue;
-    };
-
-    uevent_listener_.RegenerateUeventsForPath(syspath, verity_callback);
-    if (!found) {
-        LOG(INFO) << "dm-verity device not found in /sys, waiting for its uevent";
-        Timer t;
-        uevent_listener_.Poll(verity_callback, 10s);
-        LOG(INFO) << "wait for dm-verity device returned after " << t;
-    }
-    if (!found) {
-        LOG(ERROR) << "dm-verity device not found after polling timeout";
-        return false;
-    }
-
-    return true;
-}
-
-bool FirstStageMount::MountPartitions() {
-    for (auto fstab_rec : mount_fstab_recs_) {
-        if (!SetUpDmVerity(fstab_rec)) {
-            PLOG(ERROR) << "Failed to setup verity for '" << fstab_rec->mount_point << "'";
-            return false;
-        }
-        if (fs_mgr_do_mount_one(fstab_rec)) {
-            PLOG(ERROR) << "Failed to mount '" << fstab_rec->mount_point << "'";
-            return false;
-        }
-    }
-    return true;
-}
-
-bool FirstStageMountVBootV1::GetRequiredDevices() {
-    std::string verity_loc_device;
-    need_dm_verity_ = false;
-
-    for (auto fstab_rec : mount_fstab_recs_) {
-        // Don't allow verifyatboot in the first stage.
-        if (fs_mgr_is_verifyatboot(fstab_rec)) {
-            LOG(ERROR) << "Partitions can't be verified at boot";
-            return false;
-        }
-        // Checks for verified partitions.
-        if (fs_mgr_is_verified(fstab_rec)) {
-            need_dm_verity_ = true;
-        }
-        // Checks if verity metadata is on a separate partition. Note that it is
-        // not partition specific, so there must be only one additional partition
-        // that carries verity state.
-        if (fstab_rec->verity_loc) {
-            if (verity_loc_device.empty()) {
-                verity_loc_device = fstab_rec->verity_loc;
-            } else if (verity_loc_device != fstab_rec->verity_loc) {
-                LOG(ERROR) << "More than one verity_loc found: " << verity_loc_device << ", "
-                           << fstab_rec->verity_loc;
-                return false;
-            }
-        }
-    }
-
-    // Includes the partition names of fstab records and verity_loc_device (if any).
-    // Notes that fstab_rec->blk_device has A/B suffix updated by fs_mgr when A/B is used.
-    for (auto fstab_rec : mount_fstab_recs_) {
-        required_devices_partition_names_.emplace(basename(fstab_rec->blk_device));
-    }
-
-    if (!verity_loc_device.empty()) {
-        required_devices_partition_names_.emplace(basename(verity_loc_device.c_str()));
-    }
-
-    return true;
-}
-
-bool FirstStageMountVBootV1::SetUpDmVerity(fstab_rec* fstab_rec) {
-    if (fs_mgr_is_verified(fstab_rec)) {
-        int ret = fs_mgr_setup_verity(fstab_rec, false /* wait_for_verity_dev */);
-        switch (ret) {
-            case FS_MGR_SETUP_VERITY_SKIPPED:
-            case FS_MGR_SETUP_VERITY_DISABLED:
-                LOG(INFO) << "Verity disabled/skipped for '" << fstab_rec->mount_point << "'";
-                return true;
-            case FS_MGR_SETUP_VERITY_SUCCESS:
-                // The exact block device name (fstab_rec->blk_device) is changed to
-                // "/dev/block/dm-XX". Needs to create it because ueventd isn't started in init
-                // first stage.
-                return InitVerityDevice(fstab_rec->blk_device);
-            default:
-                return false;
-        }
-    }
-    return true;  // Returns true to mount the partition.
-}
-
-// FirstStageMountVBootV2 constructor.
-// Gets the vbmeta partitions from device tree.
-// /{
-//     firmware {
-//         android {
-//             vbmeta {
-//                 compatible = "android,vbmeta";
-//                 parts = "vbmeta,boot,system,vendor"
-//             };
-//         };
-//     };
-//  }
-FirstStageMountVBootV2::FirstStageMountVBootV2() : avb_handle_(nullptr) {
-    if (!read_android_dt_file("vbmeta/parts", &device_tree_vbmeta_parts_)) {
-        PLOG(ERROR) << "Failed to read vbmeta/parts from device tree";
-        return;
-    }
-}
-
-bool FirstStageMountVBootV2::GetRequiredDevices() {
-    need_dm_verity_ = false;
-
-    // fstab_rec->blk_device has A/B suffix.
-    for (auto fstab_rec : mount_fstab_recs_) {
-        if (fs_mgr_is_avb(fstab_rec)) {
-            need_dm_verity_ = true;
-        }
-        required_devices_partition_names_.emplace(basename(fstab_rec->blk_device));
-    }
-
-    // libavb verifies AVB metadata on all verified partitions at once.
-    // e.g., The device_tree_vbmeta_parts_ will be "vbmeta,boot,system,vendor"
-    // for libavb to verify metadata, even if there is only /vendor in the
-    // above mount_fstab_recs_.
-    if (need_dm_verity_) {
-        if (device_tree_vbmeta_parts_.empty()) {
-            LOG(ERROR) << "Missing vbmeta parts in device tree";
-            return false;
-        }
-        std::vector<std::string> partitions = android::base::Split(device_tree_vbmeta_parts_, ",");
-        std::string ab_suffix = fs_mgr_get_slot_suffix();
-        for (const auto& partition : partitions) {
-            // required_devices_partition_names_ is of type std::set so it's not an issue
-            // to emplace a partition twice. e.g., /vendor might be in both places:
-            //   - device_tree_vbmeta_parts_ = "vbmeta,boot,system,vendor"
-            //   - mount_fstab_recs_: /vendor_a
-            required_devices_partition_names_.emplace(partition + ab_suffix);
-        }
-    }
-    return true;
-}
-
-ListenerAction FirstStageMountVBootV2::UeventCallback(const Uevent& uevent) {
-    // Check if this uevent corresponds to one of the required partitions and store its symlinks if
-    // so, in order to create FsManagerAvbHandle later.
-    // Note that the parent callback removes partitions from the list of required partitions
-    // as it finds them, so this must happen first.
-    if (!uevent.partition_name.empty() &&
-        required_devices_partition_names_.find(uevent.partition_name) !=
-            required_devices_partition_names_.end()) {
-        // GetBlockDeviceSymlinks() will return three symlinks at most, depending on
-        // the content of uevent. by-name symlink will be at [0] if uevent->partition_name
-        // is not empty. e.g.,
-        //   - /dev/block/platform/soc.0/f9824900.sdhci/by-name/modem
-        //   - /dev/block/platform/soc.0/f9824900.sdhci/by-num/p1
-        //   - /dev/block/platform/soc.0/f9824900.sdhci/mmcblk0p1
-        std::vector<std::string> links = device_handler_.GetBlockDeviceSymlinks(uevent);
-        if (!links.empty()) {
-            auto[it, inserted] = by_name_symlink_map_.emplace(uevent.partition_name, links[0]);
-            if (!inserted) {
-                LOG(ERROR) << "Partition '" << uevent.partition_name
-                           << "' already existed in the by-name symlink map with a value of '"
-                           << it->second << "', new value '" << links[0] << "' will be ignored.";
-            }
-        }
-    }
-
-    return FirstStageMount::UeventCallback(uevent);
-}
-
-bool FirstStageMountVBootV2::SetUpDmVerity(fstab_rec* fstab_rec) {
-    if (fs_mgr_is_avb(fstab_rec)) {
-        if (!InitAvbHandle()) return false;
-        SetUpAvbHashtreeResult hashtree_result =
-            avb_handle_->SetUpAvbHashtree(fstab_rec, false /* wait_for_verity_dev */);
-        switch (hashtree_result) {
-            case SetUpAvbHashtreeResult::kDisabled:
-                return true;  // Returns true to mount the partition.
-            case SetUpAvbHashtreeResult::kSuccess:
-                // The exact block device name (fstab_rec->blk_device) is changed to
-                // "/dev/block/dm-XX". Needs to create it because ueventd isn't started in init
-                // first stage.
-                return InitVerityDevice(fstab_rec->blk_device);
-            default:
-                return false;
-        }
-    }
-    return true;  // Returns true to mount the partition.
-}
-
-bool FirstStageMountVBootV2::InitAvbHandle() {
-    if (avb_handle_) return true;  // Returns true if the handle is already initialized.
-
-    if (by_name_symlink_map_.empty()) {
-        LOG(ERROR) << "by_name_symlink_map_ is empty";
-        return false;
-    }
-
-    avb_handle_ = FsManagerAvbHandle::Open(std::move(by_name_symlink_map_));
-    by_name_symlink_map_.clear();  // Removes all elements after the above std::move().
-
-    if (!avb_handle_) {
-        PLOG(ERROR) << "Failed to open FsManagerAvbHandle";
-        return false;
-    }
-    // Sets INIT_AVB_VERSION here for init to set ro.boot.avb_version in the second stage.
-    setenv("INIT_AVB_VERSION", avb_handle_->avb_version().c_str(), 1);
-    return true;
-}
-
-// Public functions
-// ----------------
-// Mounts partitions specified by fstab in device tree.
-bool DoFirstStageMount() {
-    // Skips first stage mount if we're in recovery mode.
-    if (IsRecoveryMode()) {
-        LOG(INFO) << "First stage mount skipped (recovery mode)";
-        return true;
-    }
-
-    // Firstly checks if device tree fstab entries are compatible.
-    if (!is_android_dt_value_expected("fstab/compatible", "android,fstab")) {
-        LOG(INFO) << "First stage mount skipped (missing/incompatible fstab in device tree)";
-        return true;
-    }
-
-    std::unique_ptr<FirstStageMount> handle = FirstStageMount::Create();
-    if (!handle) {
-        LOG(ERROR) << "Failed to create FirstStageMount";
-        return false;
-    }
-    return handle->DoFirstStageMount();
-}
-
-void SetInitAvbVersionInRecovery() {
-    if (!IsRecoveryMode()) {
-        LOG(INFO) << "Skipped setting INIT_AVB_VERSION (not in recovery mode)";
-        return;
-    }
-
-    if (!IsDtVbmetaCompatible()) {
-        LOG(INFO) << "Skipped setting INIT_AVB_VERSION (not vbmeta compatible)";
-        return;
-    }
-
-    // Initializes required devices for the subsequent FsManagerAvbHandle::Open()
-    // to verify AVB metadata on all partitions in the verified chain.
-    // We only set INIT_AVB_VERSION when the AVB verification succeeds, i.e., the
-    // Open() function returns a valid handle.
-    // We don't need to mount partitions here in recovery mode.
-    FirstStageMountVBootV2 avb_first_mount;
-    if (!avb_first_mount.InitDevices()) {
-        LOG(ERROR) << "Failed to init devices for INIT_AVB_VERSION";
-        return;
-    }
-
-    FsManagerAvbUniquePtr avb_handle =
-        FsManagerAvbHandle::Open(std::move(avb_first_mount.by_name_symlink_map_));
-    if (!avb_handle) {
-        PLOG(ERROR) << "Failed to open FsManagerAvbHandle for INIT_AVB_VERSION";
-        return;
-    }
-    setenv("INIT_AVB_VERSION", avb_handle->avb_version().c_str(), 1);
-}
-
-}  // namespace init
-}  // namespace android
diff --git a/init/init_first_stage.h b/init/init_first_stage.h
deleted file mode 100644
index c7a3867..0000000
--- a/init/init_first_stage.h
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Copyright (C) 2017 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_FIRST_STAGE_H
-#define _INIT_FIRST_STAGE_H
-
-namespace android {
-namespace init {
-
-bool DoFirstStageMount();
-void SetInitAvbVersionInRecovery();
-
-}  // namespace init
-}  // namespace android
-
-#endif
diff --git a/init/init_parser.cpp b/init/init_parser.cpp
deleted file mode 100644
index 9f7089b..0000000
--- a/init/init_parser.cpp
+++ /dev/null
@@ -1,168 +0,0 @@
-/*
- * Copyright (C) 2010 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 "init_parser.h"
-
-#include <dirent.h>
-
-#include <android-base/chrono_utils.h>
-#include <android-base/logging.h>
-#include <android-base/stringprintf.h>
-#include <android-base/strings.h>
-
-#include "parser.h"
-#include "util.h"
-
-namespace android {
-namespace init {
-
-Parser::Parser() {
-}
-
-Parser& Parser::GetInstance() {
-    static Parser instance;
-    return instance;
-}
-
-void Parser::AddSectionParser(const std::string& name,
-                              std::unique_ptr<SectionParser> parser) {
-    section_parsers_[name] = std::move(parser);
-}
-
-void Parser::AddSingleLineParser(const std::string& prefix, LineCallback callback) {
-    line_callbacks_.emplace_back(prefix, callback);
-}
-
-void Parser::ParseData(const std::string& filename, const std::string& data) {
-    //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');
-
-    parse_state state;
-    state.line = 0;
-    state.ptr = &data_copy[0];
-    state.nexttoken = 0;
-
-    SectionParser* section_parser = nullptr;
-    std::vector<std::string> args;
-
-    for (;;) {
-        switch (next_token(&state)) {
-        case T_EOF:
-            if (section_parser) {
-                section_parser->EndSection();
-            }
-            return;
-        case T_NEWLINE:
-            state.line++;
-            if (args.empty()) {
-                break;
-            }
-            // If we have a line matching a prefix we recognize, call its callback and unset any
-            // current section parsers.  This is meant for /sys/ and /dev/ line entries for uevent.
-            for (const auto& [prefix, callback] : line_callbacks_) {
-                if (android::base::StartsWith(args[0], prefix.c_str())) {
-                    if (section_parser) section_parser->EndSection();
-
-                    std::string ret_err;
-                    if (!callback(std::move(args), &ret_err)) {
-                        LOG(ERROR) << filename << ": " << state.line << ": " << ret_err;
-                    }
-                    section_parser = nullptr;
-                    break;
-                }
-            }
-            if (section_parsers_.count(args[0])) {
-                if (section_parser) {
-                    section_parser->EndSection();
-                }
-                section_parser = section_parsers_[args[0]].get();
-                std::string ret_err;
-                if (!section_parser->ParseSection(std::move(args), filename, state.line, &ret_err)) {
-                    LOG(ERROR) << filename << ": " << state.line << ": " << ret_err;
-                    section_parser = nullptr;
-                }
-            } else if (section_parser) {
-                std::string ret_err;
-                if (!section_parser->ParseLineSection(std::move(args), state.line, &ret_err)) {
-                    LOG(ERROR) << filename << ": " << state.line << ": " << ret_err;
-                }
-            }
-            args.clear();
-            break;
-        case T_TEXT:
-            args.emplace_back(state.text);
-            break;
-        }
-    }
-}
-
-bool Parser::ParseConfigFile(const std::string& path) {
-    LOG(INFO) << "Parsing file " << path << "...";
-    android::base::Timer t;
-    std::string data;
-    std::string err;
-    if (!ReadFile(path, &data, &err)) {
-        LOG(ERROR) << err;
-        return false;
-    }
-
-    data.push_back('\n'); // TODO: fix parse_config.
-    ParseData(path, data);
-    for (const auto& [section_name, section_parser] : section_parsers_) {
-        section_parser->EndFile();
-    }
-
-    LOG(VERBOSE) << "(Parsing " << path << " took " << t << ".)";
-    return true;
-}
-
-bool Parser::ParseConfigDir(const std::string& path) {
-    LOG(INFO) << "Parsing directory " << path << "...";
-    std::unique_ptr<DIR, int(*)(DIR*)> config_dir(opendir(path.c_str()), closedir);
-    if (!config_dir) {
-        PLOG(ERROR) << "Could not import directory '" << path << "'";
-        return false;
-    }
-    dirent* current_file;
-    std::vector<std::string> files;
-    while ((current_file = readdir(config_dir.get()))) {
-        // Ignore directories and only process regular files.
-        if (current_file->d_type == DT_REG) {
-            std::string current_path =
-                android::base::StringPrintf("%s/%s", path.c_str(), current_file->d_name);
-            files.emplace_back(current_path);
-        }
-    }
-    // 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)) {
-            LOG(ERROR) << "could not import file '" << file << "'";
-        }
-    }
-    return true;
-}
-
-bool Parser::ParseConfig(const std::string& path) {
-    if (is_dir(path.c_str())) {
-        return ParseConfigDir(path);
-    }
-    return ParseConfigFile(path);
-}
-
-}  // namespace init
-}  // namespace android
diff --git a/init/init_parser.h b/init/init_parser.h
deleted file mode 100644
index c07a699..0000000
--- a/init/init_parser.h
+++ /dev/null
@@ -1,102 +0,0 @@
-/*
- * Copyright (C) 2010 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_INIT_PARSER_H_
-#define _INIT_INIT_PARSER_H_
-
-#include <map>
-#include <memory>
-#include <string>
-#include <vector>
-
-//  SectionParser is an interface that can parse a given 'section' in init.
-//
-//  You can implement up to 4 functions below, with ParseSection() being mandatory.
-//  The first two function return bool with false indicating a failure and has a std::string* err
-//  parameter into which an error string can be written.  It will be reported along with the
-//  filename and line number of where the error occurred.
-//
-//  1) bool ParseSection(std::vector<std::string>&& args, const std::string& filename,
-//                       int line, std::string* err)
-//    This function is called when a section is first encountered.
-//
-//  2) bool ParseLineSection(std::vector<std::string>&& args, int line, std::string* err)
-//    This function is called on each subsequent line until the next section is encountered.
-//
-//  3) bool EndSection()
-//    This function is called either when a new section is found or at the end of the file.
-//    It indicates that parsing of the current section is complete and any relevant objects should
-//    be committed.
-//
-//  4) bool EndFile()
-//    This function is called at the end of the file.
-//    It indicates that the parsing has completed and any relevant objects should be committed.
-
-namespace android {
-namespace init {
-
-class SectionParser {
-  public:
-    virtual ~SectionParser() {}
-    virtual bool ParseSection(std::vector<std::string>&& args, const std::string& filename,
-                              int line, std::string* err) = 0;
-    virtual bool ParseLineSection(std::vector<std::string>&&, int, std::string*) { return true; };
-    virtual void EndSection(){};
-    virtual void EndFile(){};
-};
-
-class Parser {
-  public:
-    //  LineCallback is the type for callbacks that can parse a line starting with a given prefix.
-    //
-    //  They take the form of bool Callback(std::vector<std::string>&& args, std::string* err)
-    //
-    //  Similar to ParseSection() and ParseLineSection(), this function returns bool with false
-    //  indicating a failure and has an std::string* err parameter into which an error string can
-    //  be written.
-    using LineCallback = std::function<bool(std::vector<std::string>&&, std::string*)>;
-
-    // TODO: init is the only user of this as a singleton; remove it.
-    static Parser& GetInstance();
-
-    Parser();
-
-    bool ParseConfig(const std::string& path);
-    void AddSectionParser(const std::string& name, std::unique_ptr<SectionParser> parser);
-    void AddSingleLineParser(const std::string& prefix, LineCallback callback);
-    void set_is_system_etc_init_loaded(bool loaded) { is_system_etc_init_loaded_ = loaded; }
-    void set_is_vendor_etc_init_loaded(bool loaded) { is_vendor_etc_init_loaded_ = loaded; }
-    void set_is_odm_etc_init_loaded(bool loaded) { is_odm_etc_init_loaded_ = loaded; }
-    bool is_system_etc_init_loaded() { return is_system_etc_init_loaded_; }
-    bool is_vendor_etc_init_loaded() { return is_vendor_etc_init_loaded_; }
-    bool is_odm_etc_init_loaded() { return is_odm_etc_init_loaded_; }
-
-  private:
-    void ParseData(const std::string& filename, const std::string& data);
-    bool ParseConfigFile(const std::string& path);
-    bool ParseConfigDir(const std::string& path);
-
-    std::map<std::string, std::unique_ptr<SectionParser>> section_parsers_;
-    std::vector<std::pair<std::string, LineCallback>> line_callbacks_;
-    bool is_system_etc_init_loaded_ = false;
-    bool is_vendor_etc_init_loaded_ = false;
-    bool is_odm_etc_init_loaded_ = false;
-};
-
-}  // namespace init
-}  // namespace android
-
-#endif
diff --git a/init/init_parser_test.cpp b/init/init_parser_test.cpp
deleted file mode 100644
index 95f269a..0000000
--- a/init/init_parser_test.cpp
+++ /dev/null
@@ -1,150 +0,0 @@
-/*
- * Copyright (C) 2015 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 "init_parser.h"
-
-#include <string>
-#include <vector>
-
-#include <gtest/gtest.h>
-
-#include "init.h"
-#include "service.h"
-#include "util.h"
-
-namespace android {
-namespace init {
-
-TEST(init_parser, make_exec_oneshot_service_invalid_syntax) {
-    ServiceManager& sm = ServiceManager::GetInstance();
-    std::vector<std::string> args;
-    // Nothing.
-    ASSERT_EQ(nullptr, sm.MakeExecOneshotService(args));
-
-    // No arguments to 'exec'.
-    args.push_back("exec");
-    ASSERT_EQ(nullptr, sm.MakeExecOneshotService(args));
-
-    // No command in "exec --".
-    args.push_back("--");
-    ASSERT_EQ(nullptr, sm.MakeExecOneshotService(args));
-}
-
-TEST(init_parser, make_exec_oneshot_service_too_many_supplementary_gids) {
-    ServiceManager& sm = ServiceManager::GetInstance();
-    std::vector<std::string> args;
-    args.push_back("exec");
-    args.push_back("seclabel");
-    args.push_back("root"); // uid.
-    args.push_back("root"); // gid.
-    for (int i = 0; i < NR_SVC_SUPP_GIDS; ++i) {
-        args.push_back("root"); // Supplementary gid.
-    }
-    args.push_back("--");
-    args.push_back("/system/bin/id");
-    ASSERT_EQ(nullptr, sm.MakeExecOneshotService(args));
-}
-
-static void Test_make_exec_oneshot_service(bool dash_dash, bool seclabel, bool uid,
-                                           bool gid, bool supplementary_gids) {
-    ServiceManager& sm = ServiceManager::GetInstance();
-    std::vector<std::string> args;
-    args.push_back("exec");
-    if (seclabel) {
-        args.push_back("u:r:su:s0"); // seclabel
-        if (uid) {
-            args.push_back("log");      // uid
-            if (gid) {
-                args.push_back("shell");     // gid
-                if (supplementary_gids) {
-                    args.push_back("system");    // supplementary gid 0
-                    args.push_back("adb");       // supplementary gid 1
-                }
-            }
-        }
-    }
-    if (dash_dash) {
-        args.push_back("--");
-    }
-    args.push_back("/system/bin/toybox");
-    args.push_back("id");
-    Service* svc = sm.MakeExecOneshotService(args);
-    ASSERT_NE(nullptr, svc);
-
-    if (seclabel) {
-        ASSERT_EQ("u:r:su:s0", svc->seclabel());
-    } else {
-        ASSERT_EQ("", svc->seclabel());
-    }
-    if (uid) {
-        uid_t decoded_uid;
-        std::string err;
-        ASSERT_TRUE(DecodeUid("log", &decoded_uid, &err));
-        ASSERT_EQ(decoded_uid, svc->uid());
-    } else {
-        ASSERT_EQ(0U, svc->uid());
-    }
-    if (gid) {
-        uid_t decoded_uid;
-        std::string err;
-        ASSERT_TRUE(DecodeUid("shell", &decoded_uid, &err));
-        ASSERT_EQ(decoded_uid, svc->gid());
-    } else {
-        ASSERT_EQ(0U, svc->gid());
-    }
-    if (supplementary_gids) {
-        ASSERT_EQ(2U, svc->supp_gids().size());
-        uid_t decoded_uid;
-        std::string err;
-        ASSERT_TRUE(DecodeUid("system", &decoded_uid, &err));
-        ASSERT_EQ(decoded_uid, svc->supp_gids()[0]);
-        ASSERT_TRUE(DecodeUid("adb", &decoded_uid, &err));
-        ASSERT_EQ(decoded_uid, svc->supp_gids()[1]);
-    } else {
-        ASSERT_EQ(0U, svc->supp_gids().size());
-    }
-
-    ASSERT_EQ(static_cast<std::size_t>(2), svc->args().size());
-    ASSERT_EQ("/system/bin/toybox", svc->args()[0]);
-    ASSERT_EQ("id", svc->args()[1]);
-}
-
-TEST(init_parser, make_exec_oneshot_service_with_everything) {
-    Test_make_exec_oneshot_service(true, true, true, true, true);
-}
-
-TEST(init_parser, make_exec_oneshot_service_with_seclabel_uid_gid) {
-    Test_make_exec_oneshot_service(true, true, true, true, false);
-}
-
-TEST(init_parser, make_exec_oneshot_service_with_seclabel_uid) {
-    Test_make_exec_oneshot_service(true, true, true, false, false);
-}
-
-TEST(init_parser, make_exec_oneshot_service_with_seclabel) {
-    Test_make_exec_oneshot_service(true, true, false, false, false);
-}
-
-TEST(init_parser, make_exec_oneshot_service_with_just_command) {
-    Test_make_exec_oneshot_service(true, false, false, false, false);
-}
-
-TEST(init_parser, make_exec_oneshot_service_with_just_command_no_dash) {
-    Test_make_exec_oneshot_service(false, false, false, false, false);
-}
-
-}  // namespace init
-}  // namespace android
diff --git a/init/init_test.cpp b/init/init_test.cpp
index 0a4071b..0f9635f 100644
--- a/init/init_test.cpp
+++ b/init/init_test.cpp
@@ -21,47 +21,30 @@
 #include <gtest/gtest.h>
 
 #include "action.h"
+#include "action_manager.h"
+#include "action_parser.h"
 #include "builtins.h"
 #include "import_parser.h"
-#include "init_parser.h"
 #include "keyword_map.h"
+#include "parser.h"
+#include "service.h"
+#include "test_function_map.h"
 #include "util.h"
 
 namespace android {
 namespace init {
 
-class TestFunctionMap : public KeywordMap<BuiltinFunction> {
-  public:
-    // Helper for argument-less functions
-    using BuiltinFunctionNoArgs = std::function<void(void)>;
-    void Add(const std::string& name, const BuiltinFunctionNoArgs function) {
-        Add(name, 0, 0, [function](const std::vector<std::string>&) {
-            function();
-            return 0;
-        });
-    }
-
-    void Add(const std::string& name, std::size_t min_parameters, std::size_t max_parameters,
-             const BuiltinFunction function) {
-        builtin_functions_[name] = make_tuple(min_parameters, max_parameters, function);
-    }
-
-  private:
-    Map builtin_functions_ = {};
-
-    const Map& map() const override { return builtin_functions_; }
-};
-
 using ActionManagerCommand = std::function<void(ActionManager&)>;
 
 void TestInit(const std::string& init_script_file, const TestFunctionMap& test_function_map,
-              const std::vector<ActionManagerCommand>& commands) {
+              const std::vector<ActionManagerCommand>& commands, ServiceList* service_list) {
     ActionManager am;
 
     Action::set_function_map(&test_function_map);
 
     Parser parser;
-    parser.AddSectionParser("on", std::make_unique<ActionParser>(&am));
+    parser.AddSectionParser("service", std::make_unique<ServiceParser>(service_list, nullptr));
+    parser.AddSectionParser("on", std::make_unique<ActionParser>(&am, nullptr));
     parser.AddSectionParser("import", std::make_unique<ImportParser>(&parser));
 
     ASSERT_TRUE(parser.ParseConfig(init_script_file));
@@ -76,11 +59,11 @@
 }
 
 void TestInitText(const std::string& init_script, const TestFunctionMap& test_function_map,
-                  const std::vector<ActionManagerCommand>& commands) {
+                  const std::vector<ActionManagerCommand>& commands, ServiceList* service_list) {
     TemporaryFile tf;
     ASSERT_TRUE(tf.fd != -1);
     ASSERT_TRUE(android::base::WriteStringToFd(init_script, tf.fd));
-    TestInit(tf.path, test_function_map, commands);
+    TestInit(tf.path, test_function_map, commands, service_list);
 }
 
 TEST(init, SimpleEventTrigger) {
@@ -97,7 +80,8 @@
     ActionManagerCommand trigger_boot = [](ActionManager& am) { am.QueueEventTrigger("boot"); };
     std::vector<ActionManagerCommand> commands{trigger_boot};
 
-    TestInitText(init_script, test_function_map, commands);
+    ServiceList service_list;
+    TestInitText(init_script, test_function_map, commands, &service_list);
 
     EXPECT_TRUE(expect_true);
 }
@@ -125,7 +109,30 @@
     ActionManagerCommand trigger_boot = [](ActionManager& am) { am.QueueEventTrigger("boot"); };
     std::vector<ActionManagerCommand> commands{trigger_boot};
 
-    TestInitText(init_script, test_function_map, commands);
+    ServiceList service_list;
+    TestInitText(init_script, test_function_map, commands, &service_list);
+}
+
+TEST(init, OverrideService) {
+    std::string init_script = R"init(
+service A something
+    class first
+
+service A something
+    class second
+    override
+
+)init";
+
+    ServiceList service_list;
+    TestInitText(init_script, TestFunctionMap(), {}, &service_list);
+    ASSERT_EQ(1, std::distance(service_list.begin(), service_list.end()));
+
+    auto service = service_list.begin()->get();
+    ASSERT_NE(nullptr, service);
+    EXPECT_EQ(std::set<std::string>({"second"}), service->classnames());
+    EXPECT_EQ("A", service->name());
+    EXPECT_TRUE(service->is_override());
 }
 
 TEST(init, EventTriggerOrderMultipleFiles) {
@@ -156,10 +163,9 @@
                                "execute 3";
     // clang-format on
     // WriteFile() ensures the right mode is set
-    std::string err;
-    ASSERT_TRUE(WriteFile(std::string(dir.path) + "/a.rc", dir_a_script, &err));
+    ASSERT_TRUE(WriteFile(std::string(dir.path) + "/a.rc", dir_a_script));
 
-    ASSERT_TRUE(WriteFile(std::string(dir.path) + "/b.rc", "on boot\nexecute 5", &err));
+    ASSERT_TRUE(WriteFile(std::string(dir.path) + "/b.rc", "on boot\nexecute 5"));
 
     // clang-format off
     std::string start_script = "import " + std::string(first_import.path) + "\n"
@@ -172,19 +178,21 @@
     ASSERT_TRUE(android::base::WriteStringToFd(start_script, start.fd));
 
     int num_executed = 0;
-    auto execute_command = [&num_executed](const std::vector<std::string>& args) {
+    auto execute_command = [&num_executed](const BuiltinArguments& args) {
         EXPECT_EQ(2U, args.size());
         EXPECT_EQ(++num_executed, std::stoi(args[1]));
-        return 0;
+        return Success();
     };
 
     TestFunctionMap test_function_map;
-    test_function_map.Add("execute", 1, 1, execute_command);
+    test_function_map.Add("execute", 1, 1, false, execute_command);
 
     ActionManagerCommand trigger_boot = [](ActionManager& am) { am.QueueEventTrigger("boot"); };
     std::vector<ActionManagerCommand> commands{trigger_boot};
 
-    TestInit(start.path, test_function_map, commands);
+    ServiceList service_list;
+
+    TestInit(start.path, test_function_map, commands, &service_list);
 
     EXPECT_EQ(6, num_executed);
 }
diff --git a/init/keychords.cpp b/init/keychords.cpp
index a0d7cc5..1af06dd 100644
--- a/init/keychords.cpp
+++ b/init/keychords.cpp
@@ -16,105 +16,269 @@
 
 #include "keychords.h"
 
+#include <dirent.h>
 #include <fcntl.h>
-#include <stdlib.h>
-#include <sys/stat.h>
+#include <linux/input.h>
+#include <sys/cdefs.h>
+#include <sys/inotify.h>
+#include <sys/ioctl.h>
 #include <sys/types.h>
-#include <linux/keychord.h>
 #include <unistd.h>
 
-#include <android-base/logging.h>
-#include <android-base/properties.h>
+#include <algorithm>
+#include <functional>
+#include <map>
+#include <memory>
+#include <string>
+#include <vector>
 
-#include "init.h"
+#include <android-base/logging.h>
 
 namespace android {
 namespace init {
 
-static struct input_keychord *keychords = 0;
-static int keychords_count = 0;
-static int keychords_length = 0;
-static int keychord_fd = -1;
+Keychords::Keychords() : epoll_(nullptr), inotify_fd_(-1) {}
 
-void add_service_keycodes(Service* svc)
-{
-    struct input_keychord *keychord;
-    size_t i, size;
-
-    if (!svc->keycodes().empty()) {
-        /* add a new keychord to the list */
-        size = sizeof(*keychord) + svc->keycodes().size() * sizeof(keychord->keycodes[0]);
-        keychords = (input_keychord*) realloc(keychords, keychords_length + size);
-        if (!keychords) {
-            PLOG(ERROR) << "could not allocate keychords";
-            keychords_length = 0;
-            keychords_count = 0;
-            return;
-        }
-
-        keychord = (struct input_keychord *)((char *)keychords + keychords_length);
-        keychord->version = KEYCHORD_VERSION;
-        keychord->id = keychords_count + 1;
-        keychord->count = svc->keycodes().size();
-        svc->set_keychord_id(keychord->id);
-
-        for (i = 0; i < svc->keycodes().size(); i++) {
-            keychord->keycodes[i] = svc->keycodes()[i];
-        }
-        keychords_count++;
-        keychords_length += size;
+Keychords::~Keychords() noexcept {
+    if (inotify_fd_ >= 0) {
+        epoll_->UnregisterHandler(inotify_fd_);
+        ::close(inotify_fd_);
     }
+    while (!registration_.empty()) GeteventCloseDevice(registration_.begin()->first);
 }
 
-static void handle_keychord() {
-    int ret;
-    __u16 id;
+Keychords::Mask::Mask(size_t bit) : bits_((bit + sizeof(mask_t) - 1) / sizeof(mask_t), 0) {}
 
-    ret = read(keychord_fd, &id, sizeof(id));
-    if (ret != sizeof(id)) {
-        PLOG(ERROR) << "could not read keychord id";
-        return;
-    }
-
-    // Only handle keychords if adb is enabled.
-    std::string adb_enabled = android::base::GetProperty("init.svc.adbd", "");
-    if (adb_enabled == "running") {
-        Service* svc = ServiceManager::GetInstance().FindServiceByKeychord(id);
-        if (svc) {
-            LOG(INFO) << "Starting service " << svc->name() << " from keychord " << id;
-            svc->Start();
-        } else {
-            LOG(ERROR) << "Service for keychord " << id << " not found";
-        }
+void Keychords::Mask::SetBit(size_t bit, bool value) {
+    auto idx = bit / (kBitsPerByte * sizeof(mask_t));
+    if (idx >= bits_.size()) return;
+    if (value) {
+        bits_[idx] |= mask_t(1) << (bit % (kBitsPerByte * sizeof(mask_t)));
     } else {
-        LOG(WARNING) << "Not starting service for keychord " << id << " because ADB is disabled";
+        bits_[idx] &= ~(mask_t(1) << (bit % (kBitsPerByte * sizeof(mask_t))));
     }
 }
 
-void keychord_init() {
-    ServiceManager::GetInstance().ForEachService(add_service_keycodes);
+bool Keychords::Mask::GetBit(size_t bit) const {
+    auto idx = bit / (kBitsPerByte * sizeof(mask_t));
+    return bits_[idx] & (mask_t(1) << (bit % (kBitsPerByte * sizeof(mask_t))));
+}
 
-    // Nothing to do if no services require keychords.
-    if (!keychords) {
+size_t Keychords::Mask::bytesize() const {
+    return bits_.size() * sizeof(mask_t);
+}
+
+void* Keychords::Mask::data() {
+    return bits_.data();
+}
+
+size_t Keychords::Mask::size() const {
+    return bits_.size() * sizeof(mask_t) * kBitsPerByte;
+}
+
+void Keychords::Mask::resize(size_t bit) {
+    auto idx = bit / (kBitsPerByte * sizeof(mask_t));
+    if (idx >= bits_.size()) {
+        bits_.resize(idx + 1, 0);
+    }
+}
+
+Keychords::Mask::operator bool() const {
+    for (size_t i = 0; i < bits_.size(); ++i) {
+        if (bits_[i]) return true;
+    }
+    return false;
+}
+
+Keychords::Mask Keychords::Mask::operator&(const Keychords::Mask& rval) const {
+    auto len = std::min(bits_.size(), rval.bits_.size());
+    Keychords::Mask ret;
+    ret.bits_.resize(len);
+    for (size_t i = 0; i < len; ++i) {
+        ret.bits_[i] = bits_[i] & rval.bits_[i];
+    }
+    return ret;
+}
+
+void Keychords::Mask::operator|=(const Keychords::Mask& rval) {
+    auto len = rval.bits_.size();
+    bits_.resize(len);
+    for (size_t i = 0; i < len; ++i) {
+        bits_[i] |= rval.bits_[i];
+    }
+}
+
+Keychords::Entry::Entry() : notified(false) {}
+
+void Keychords::LambdaCheck() {
+    for (auto& [keycodes, entry] : entries_) {
+        auto found = true;
+        for (auto& code : keycodes) {
+            if (!current_.GetBit(code)) {
+                entry.notified = false;
+                found = false;
+                break;
+            }
+        }
+        if (!found) continue;
+        if (entry.notified) continue;
+        entry.notified = true;
+        handler_(keycodes);
+    }
+}
+
+void Keychords::LambdaHandler(int fd) {
+    input_event event;
+    auto res = TEMP_FAILURE_RETRY(::read(fd, &event, sizeof(event)));
+    if ((res != sizeof(event)) || (event.type != EV_KEY)) return;
+    current_.SetBit(event.code, event.value);
+    LambdaCheck();
+}
+
+bool Keychords::GeteventEnable(int fd) {
+    // Make sure it is an event channel, should pass this ioctl call
+    int version;
+    if (::ioctl(fd, EVIOCGVERSION, &version)) return false;
+
+#ifdef EVIOCSMASK
+    static auto EviocsmaskSupported = true;
+    if (EviocsmaskSupported) {
+        Keychords::Mask mask(EV_KEY);
+        mask.SetBit(EV_KEY);
+        input_mask msg = {};
+        msg.type = EV_SYN;
+        msg.codes_size = mask.bytesize();
+        msg.codes_ptr = reinterpret_cast<uintptr_t>(mask.data());
+        if (::ioctl(fd, EVIOCSMASK, &msg) == -1) {
+            PLOG(WARNING) << "EVIOCSMASK not supported";
+            EviocsmaskSupported = false;
+        }
+    }
+#endif
+
+    Keychords::Mask mask;
+    for (auto& [keycodes, entry] : entries_) {
+        for (auto& code : keycodes) {
+            mask.resize(code);
+            mask.SetBit(code);
+        }
+    }
+
+    current_.resize(mask.size());
+    Keychords::Mask available(mask.size());
+    auto res = ::ioctl(fd, EVIOCGBIT(EV_KEY, available.bytesize()), available.data());
+    if (res == -1) return false;
+    if (!(available & mask)) return false;
+
+#ifdef EVIOCSMASK
+    if (EviocsmaskSupported) {
+        input_mask msg = {};
+        msg.type = EV_KEY;
+        msg.codes_size = mask.bytesize();
+        msg.codes_ptr = reinterpret_cast<uintptr_t>(mask.data());
+        ::ioctl(fd, EVIOCSMASK, &msg);
+    }
+#endif
+
+    Keychords::Mask set(mask.size());
+    res = ::ioctl(fd, EVIOCGKEY(res), set.data());
+    if (res > 0) {
+        current_ |= mask & available & set;
+        LambdaCheck();
+    }
+    epoll_->RegisterHandler(fd, [this, fd]() { this->LambdaHandler(fd); });
+    return true;
+}
+
+void Keychords::GeteventOpenDevice(const std::string& device) {
+    if (registration_.count(device)) return;
+    auto fd = TEMP_FAILURE_RETRY(::open(device.c_str(), O_RDWR | O_CLOEXEC));
+    if (fd == -1) {
+        PLOG(ERROR) << "Can not open " << device;
+        return;
+    }
+    if (!GeteventEnable(fd)) {
+        ::close(fd);
+    } else {
+        registration_.emplace(device, fd);
+    }
+}
+
+void Keychords::GeteventCloseDevice(const std::string& device) {
+    auto it = registration_.find(device);
+    if (it == registration_.end()) return;
+    auto fd = (*it).second;
+    epoll_->UnregisterHandler(fd);
+    registration_.erase(it);
+    ::close(fd);
+}
+
+void Keychords::InotifyHandler() {
+    unsigned char buf[512];  // History shows 32-64 bytes typical
+
+    auto res = TEMP_FAILURE_RETRY(::read(inotify_fd_, buf, sizeof(buf)));
+    if (res < 0) {
+        PLOG(WARNING) << "could not get event";
         return;
     }
 
-    keychord_fd = TEMP_FAILURE_RETRY(open("/dev/keychord", O_RDWR | O_CLOEXEC));
-    if (keychord_fd == -1) {
-        PLOG(ERROR) << "could not open /dev/keychord";
-        return;
+    auto event_buf = buf;
+    while (static_cast<size_t>(res) >= sizeof(inotify_event)) {
+        auto event = reinterpret_cast<inotify_event*>(event_buf);
+        auto event_size = sizeof(inotify_event) + event->len;
+        if (static_cast<size_t>(res) < event_size) break;
+        if (event->len) {
+            std::string devname(kDevicePath);
+            devname += '/';
+            devname += event->name;
+            if (event->mask & IN_CREATE) {
+                GeteventOpenDevice(devname);
+            } else {
+                GeteventCloseDevice(devname);
+            }
+        }
+        res -= event_size;
+        event_buf += event_size;
+    }
+}
+
+void Keychords::GeteventOpenDevice() {
+    inotify_fd_ = ::inotify_init1(IN_NONBLOCK | IN_CLOEXEC);
+    if (inotify_fd_ < 0) {
+        PLOG(WARNING) << "Could not instantiate inotify for " << kDevicePath;
+    } else if (::inotify_add_watch(inotify_fd_, kDevicePath, IN_DELETE | IN_CREATE | IN_ONLYDIR) <
+               0) {
+        PLOG(WARNING) << "Could not add watch for " << kDevicePath;
+        ::close(inotify_fd_);
+        inotify_fd_ = -1;
     }
 
-    int ret = write(keychord_fd, keychords, keychords_length);
-    if (ret != keychords_length) {
-        PLOG(ERROR) << "could not configure /dev/keychord " << ret;
-        close(keychord_fd);
+    std::unique_ptr<DIR, decltype(&closedir)> device(opendir(kDevicePath), closedir);
+    if (device) {
+        dirent* entry;
+        while ((entry = readdir(device.get()))) {
+            if (entry->d_name[0] == '.') continue;
+            std::string devname(kDevicePath);
+            devname += '/';
+            devname += entry->d_name;
+            GeteventOpenDevice(devname);
+        }
     }
 
-    free(keychords);
-    keychords = nullptr;
+    if (inotify_fd_ >= 0) {
+        epoll_->RegisterHandler(inotify_fd_, [this]() { this->InotifyHandler(); });
+    }
+}
 
-    register_epoll_handler(keychord_fd, handle_keychord);
+void Keychords::Register(const std::vector<int>& keycodes) {
+    if (keycodes.empty()) return;
+    entries_.try_emplace(keycodes, Entry());
+}
+
+void Keychords::Start(Epoll* epoll, std::function<void(const std::vector<int>&)> handler) {
+    epoll_ = epoll;
+    handler_ = handler;
+    if (entries_.size()) GeteventOpenDevice();
 }
 
 }  // namespace init
diff --git a/init/keychords.h b/init/keychords.h
index 1c34098..00ed205 100644
--- a/init/keychords.h
+++ b/init/keychords.h
@@ -17,13 +17,81 @@
 #ifndef _INIT_KEYCHORDS_H_
 #define _INIT_KEYCHORDS_H_
 
-#include "service.h"
+#include <functional>
+#include <map>
+#include <string>
+#include <vector>
+
+#include "epoll.h"
 
 namespace android {
 namespace init {
 
-void add_service_keycodes(Service* svc);
-void keychord_init();
+class Keychords {
+  public:
+    Keychords();
+    Keychords(const Keychords&) = delete;
+    Keychords(Keychords&&) = delete;
+    Keychords& operator=(const Keychords&) = delete;
+    Keychords& operator=(Keychords&&) = delete;
+    ~Keychords() noexcept;
+
+    void Register(const std::vector<int>& keycodes);
+    void Start(Epoll* epoll, std::function<void(const std::vector<int>&)> handler);
+
+  private:
+    // Bit management
+    class Mask {
+      public:
+        explicit Mask(size_t bit = 0);
+
+        void SetBit(size_t bit, bool value = true);
+        bool GetBit(size_t bit) const;
+
+        size_t bytesize() const;
+        void* data();
+        size_t size() const;
+        void resize(size_t bit);
+
+        operator bool() const;
+        Mask operator&(const Mask& rval) const;
+        void operator|=(const Mask& rval);
+
+      private:
+        typedef unsigned int mask_t;
+        static constexpr size_t kBitsPerByte = 8;
+
+        std::vector<mask_t> bits_;
+    };
+
+    struct Entry {
+        Entry();
+
+        bool notified;
+    };
+
+    static constexpr char kDevicePath[] = "/dev/input";
+
+    void LambdaCheck();
+    void LambdaHandler(int fd);
+    void InotifyHandler();
+
+    bool GeteventEnable(int fd);
+    void GeteventOpenDevice(const std::string& device);
+    void GeteventOpenDevice();
+    void GeteventCloseDevice(const std::string& device);
+
+    Epoll* epoll_;
+    std::function<void(const std::vector<int>&)> handler_;
+
+    std::map<std::string, int> registration_;
+
+    std::map<const std::vector<int>, Entry> entries_;
+
+    Mask current_;
+
+    int inotify_fd_;
+};
 
 }  // namespace init
 }  // namespace android
diff --git a/init/keychords_test.cpp b/init/keychords_test.cpp
new file mode 100644
index 0000000..c8c47a8
--- /dev/null
+++ b/init/keychords_test.cpp
@@ -0,0 +1,348 @@
+/*
+ * 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 "keychords.h"
+
+#include <dirent.h>
+#include <fcntl.h>
+#include <linux/input.h>
+#include <linux/uinput.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <chrono>
+#include <set>
+#include <string>
+#include <vector>
+
+#include <android-base/properties.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+#include <gtest/gtest.h>
+
+#include "epoll.h"
+
+using namespace std::chrono_literals;
+
+namespace android {
+namespace init {
+
+namespace {
+
+// This class is used to inject keys.
+class EventHandler {
+  public:
+    EventHandler();
+    EventHandler(const EventHandler&) = delete;
+    EventHandler(EventHandler&&);
+    EventHandler& operator=(const EventHandler&) = delete;
+    EventHandler& operator=(EventHandler&&);
+    ~EventHandler() noexcept;
+
+    bool init();
+
+    bool send(struct input_event& e);
+    bool send(uint16_t type, uint16_t code, uint16_t value);
+    bool send(uint16_t code, bool value);
+
+  private:
+    int fd_;
+};
+
+EventHandler::EventHandler() : fd_(-1) {}
+
+EventHandler::EventHandler(EventHandler&& rval) : fd_(rval.fd_) {
+    rval.fd_ = -1;
+}
+
+EventHandler& EventHandler::operator=(EventHandler&& rval) {
+    fd_ = rval.fd_;
+    rval.fd_ = -1;
+    return *this;
+}
+
+EventHandler::~EventHandler() {
+    if (fd_ == -1) return;
+    ::ioctl(fd_, UI_DEV_DESTROY);
+    ::close(fd_);
+}
+
+bool EventHandler::init() {
+    if (fd_ != -1) return true;
+    auto fd = TEMP_FAILURE_RETRY(::open("/dev/uinput", O_WRONLY | O_NONBLOCK | O_CLOEXEC));
+    if (fd == -1) return false;
+    if (::ioctl(fd, UI_SET_EVBIT, EV_KEY) == -1) {
+        ::close(fd);
+        return false;
+    }
+
+    static const struct uinput_user_dev u = {
+        .name = "com.google.android.init.test",
+        .id.bustype = BUS_VIRTUAL,
+        .id.vendor = 0x1AE0,   // Google
+        .id.product = 0x494E,  // IN
+        .id.version = 1,
+    };
+    if (TEMP_FAILURE_RETRY(::write(fd, &u, sizeof(u))) != sizeof(u)) {
+        ::close(fd);
+        return false;
+    }
+
+    // all keys
+    for (uint16_t i = 0; i < KEY_MAX; ++i) {
+        if (::ioctl(fd, UI_SET_KEYBIT, i) == -1) {
+            ::close(fd);
+            return false;
+        }
+    }
+    if (::ioctl(fd, UI_DEV_CREATE) == -1) {
+        ::close(fd);
+        return false;
+    }
+    fd_ = fd;
+    return true;
+}
+
+bool EventHandler::send(struct input_event& e) {
+    gettimeofday(&e.time, nullptr);
+    return TEMP_FAILURE_RETRY(::write(fd_, &e, sizeof(e))) == sizeof(e);
+}
+
+bool EventHandler::send(uint16_t type, uint16_t code, uint16_t value) {
+    struct input_event e = {.type = type, .code = code, .value = value};
+    return send(e);
+}
+
+bool EventHandler::send(uint16_t code, bool value) {
+    return (code < KEY_MAX) && init() && send(EV_KEY, code, value) && send(EV_SYN, SYN_REPORT, 0);
+}
+
+std::string InitFds(const char* prefix, pid_t pid = getpid()) {
+    std::string ret;
+
+    std::string init_fds("/proc/");
+    init_fds += std::to_string(pid) + "/fd";
+    std::unique_ptr<DIR, decltype(&closedir)> fds(opendir(init_fds.c_str()), closedir);
+    if (!fds) return ret;
+
+    dirent* entry;
+    while ((entry = readdir(fds.get()))) {
+        if (entry->d_name[0] == '.') continue;
+        std::string devname = init_fds + '/' + entry->d_name;
+        char buf[256];
+        auto retval = readlink(devname.c_str(), buf, sizeof(buf) - 1);
+        if ((retval < 0) || (size_t(retval) >= (sizeof(buf) - 1))) continue;
+        buf[retval] = '\0';
+        if (!android::base::StartsWith(buf, prefix)) continue;
+        if (ret.size() != 0) ret += ",";
+        ret += buf;
+    }
+    return ret;
+}
+
+std::string InitInputFds() {
+    return InitFds("/dev/input/");
+}
+
+std::string InitInotifyFds() {
+    return InitFds("anon_inode:inotify");
+}
+
+// NB: caller (this series of tests, or conversely the service parser in init)
+// is responsible for validation, sorting and uniqueness of the chords, so no
+// fuzzing is advised.
+
+const std::vector<int> escape_chord = {KEY_ESC};
+const std::vector<int> triple1_chord = {KEY_BACKSPACE, KEY_VOLUMEDOWN, KEY_VOLUMEUP};
+const std::vector<int> triple2_chord = {KEY_VOLUMEDOWN, KEY_VOLUMEUP, KEY_BACK};
+
+const std::vector<const std::vector<int>> empty_chords;
+const std::vector<const std::vector<int>> chords = {
+    escape_chord,
+    triple1_chord,
+    triple2_chord,
+};
+
+class TestFrame {
+  public:
+    TestFrame(const std::vector<const std::vector<int>>& chords, EventHandler* ev = nullptr);
+
+    void RelaxForMs(std::chrono::milliseconds wait = 1ms);
+
+    void SetChord(int key, bool value = true);
+    void SetChords(const std::vector<int>& chord, bool value = true);
+    void ClrChord(int key);
+    void ClrChords(const std::vector<int>& chord);
+
+    bool IsOnlyChord(const std::vector<int>& chord) const;
+    bool IsNoChord() const;
+    bool IsChord(const std::vector<int>& chord) const;
+    void WaitForChord(const std::vector<int>& chord);
+
+    std::string Format() const;
+
+  private:
+    static std::string Format(const std::vector<const std::vector<int>>& chords);
+
+    Epoll epoll_;
+    Keychords keychords_;
+    std::vector<const std::vector<int>> keycodes_;
+    EventHandler* ev_;
+};
+
+TestFrame::TestFrame(const std::vector<const std::vector<int>>& chords, EventHandler* ev)
+    : ev_(ev) {
+    if (!epoll_.Open()) return;
+    for (const auto& keycodes : chords) keychords_.Register(keycodes);
+    keychords_.Start(&epoll_, [this](const std::vector<int>& keycodes) {
+        this->keycodes_.emplace_back(keycodes);
+    });
+}
+
+void TestFrame::RelaxForMs(std::chrono::milliseconds wait) {
+    epoll_.Wait(wait);
+}
+
+void TestFrame::SetChord(int key, bool value) {
+    ASSERT_TRUE(!!ev_);
+    RelaxForMs();
+    EXPECT_TRUE(ev_->send(key, value));
+}
+
+void TestFrame::SetChords(const std::vector<int>& chord, bool value) {
+    ASSERT_TRUE(!!ev_);
+    for (auto& key : chord) SetChord(key, value);
+    RelaxForMs();
+}
+
+void TestFrame::ClrChord(int key) {
+    ASSERT_TRUE(!!ev_);
+    SetChord(key, false);
+}
+
+void TestFrame::ClrChords(const std::vector<int>& chord) {
+    ASSERT_TRUE(!!ev_);
+    SetChords(chord, false);
+}
+
+bool TestFrame::IsOnlyChord(const std::vector<int>& chord) const {
+    auto ret = false;
+    for (const auto& keycode : keycodes_) {
+        if (keycode != chord) return false;
+        ret = true;
+    }
+    return ret;
+}
+
+bool TestFrame::IsNoChord() const {
+    return keycodes_.empty();
+}
+
+bool TestFrame::IsChord(const std::vector<int>& chord) const {
+    for (const auto& keycode : keycodes_) {
+        if (keycode == chord) return true;
+    }
+    return false;
+}
+
+void TestFrame::WaitForChord(const std::vector<int>& chord) {
+    for (int retry = 1000; retry && !IsChord(chord); --retry) RelaxForMs();
+}
+
+std::string TestFrame::Format(const std::vector<const std::vector<int>>& chords) {
+    std::string ret("{");
+    if (!chords.empty()) {
+        ret += android::base::Join(chords.front(), ' ');
+        for (auto it = std::next(chords.begin()); it != chords.end(); ++it) {
+            ret += ',';
+            ret += android::base::Join(*it, ' ');
+        }
+    }
+    return ret + '}';
+}
+
+std::string TestFrame::Format() const {
+    return Format(keycodes_);
+}
+
+}  // namespace
+
+TEST(keychords, not_instantiated) {
+    TestFrame test_frame(empty_chords);
+    EXPECT_TRUE(InitInotifyFds().size() == 0);
+}
+
+TEST(keychords, instantiated) {
+    // Test if a valid set of chords results in proper instantiation of the
+    // underlying mechanisms for /dev/input/ attachment.
+    TestFrame test_frame(chords);
+    EXPECT_TRUE(InitInotifyFds().size() != 0);
+}
+
+TEST(keychords, init_inotify) {
+    std::string before(InitInputFds());
+
+    TestFrame test_frame(chords);
+
+    EventHandler ev;
+    EXPECT_TRUE(ev.init());
+
+    for (int retry = 1000; retry && before == InitInputFds(); --retry) test_frame.RelaxForMs();
+    std::string after(InitInputFds());
+    EXPECT_NE(before, after);
+}
+
+TEST(keychords, key) {
+    EventHandler ev;
+    EXPECT_TRUE(ev.init());
+    TestFrame test_frame(chords, &ev);
+
+    test_frame.SetChords(escape_chord);
+    test_frame.WaitForChord(escape_chord);
+    test_frame.ClrChords(escape_chord);
+    EXPECT_TRUE(test_frame.IsOnlyChord(escape_chord))
+        << "expected only " << android::base::Join(escape_chord, ' ') << " got "
+        << test_frame.Format();
+}
+
+TEST(keychords, keys_in_series) {
+    EventHandler ev;
+    EXPECT_TRUE(ev.init());
+    TestFrame test_frame(chords, &ev);
+
+    for (auto& key : triple1_chord) {
+        test_frame.SetChord(key);
+        test_frame.ClrChord(key);
+    }
+    test_frame.WaitForChord(triple1_chord);
+    EXPECT_TRUE(test_frame.IsNoChord()) << "expected nothing got " << test_frame.Format();
+}
+
+TEST(keychords, keys_in_parallel) {
+    EventHandler ev;
+    EXPECT_TRUE(ev.init());
+    TestFrame test_frame(chords, &ev);
+
+    test_frame.SetChords(triple2_chord);
+    test_frame.WaitForChord(triple2_chord);
+    test_frame.ClrChords(triple2_chord);
+    EXPECT_TRUE(test_frame.IsOnlyChord(triple2_chord))
+        << "expected only " << android::base::Join(triple2_chord, ' ') << " got "
+        << test_frame.Format();
+}
+
+}  // namespace init
+}  // namespace android
diff --git a/init/keyword_map.h b/init/keyword_map.h
index 481d637..c95fc73 100644
--- a/init/keyword_map.h
+++ b/init/keyword_map.h
@@ -22,6 +22,8 @@
 
 #include <android-base/stringprintf.h>
 
+#include "result.h"
+
 namespace android {
 namespace init {
 
@@ -34,20 +36,17 @@
     virtual ~KeywordMap() {
     }
 
-    const Function FindFunction(const std::vector<std::string>& args, std::string* err) const {
+    const Result<Function> FindFunction(const std::vector<std::string>& args) const {
         using android::base::StringPrintf;
 
-        if (args.empty()) {
-            *err = "keyword needed, but not provided";
-            return nullptr;
-        }
+        if (args.empty()) return Error() << "Keyword needed, but not provided";
+
         auto& keyword = args[0];
         auto num_args = args.size() - 1;
 
         auto function_info_it = map().find(keyword);
         if (function_info_it == map().end()) {
-            *err = StringPrintf("invalid keyword '%s'", keyword.c_str());
-            return nullptr;
+            return Error() << StringPrintf("Invalid keyword '%s'", keyword.c_str());
         }
 
         auto function_info = function_info_it->second;
@@ -55,22 +54,18 @@
         auto min_args = std::get<0>(function_info);
         auto max_args = std::get<1>(function_info);
         if (min_args == max_args && num_args != min_args) {
-            *err = StringPrintf("%s requires %zu argument%s",
-                                keyword.c_str(), min_args,
-                                (min_args > 1 || min_args == 0) ? "s" : "");
-            return nullptr;
+            return Error() << StringPrintf("%s requires %zu argument%s", keyword.c_str(), min_args,
+                                           (min_args > 1 || min_args == 0) ? "s" : "");
         }
 
         if (num_args < min_args || num_args > max_args) {
             if (max_args == std::numeric_limits<decltype(max_args)>::max()) {
-                *err = StringPrintf("%s requires at least %zu argument%s",
-                                    keyword.c_str(), min_args,
-                                    min_args > 1 ? "s" : "");
+                return Error() << StringPrintf("%s requires at least %zu argument%s",
+                                               keyword.c_str(), min_args, min_args > 1 ? "s" : "");
             } else {
-                *err = StringPrintf("%s requires between %zu and %zu arguments",
-                                    keyword.c_str(), min_args, max_args);
+                return Error() << StringPrintf("%s requires between %zu and %zu arguments",
+                                               keyword.c_str(), min_args, max_args);
             }
-            return nullptr;
         }
 
         return std::get<Function>(function_info);
diff --git a/init/log.cpp b/init/log.cpp
deleted file mode 100644
index 1830077..0000000
--- a/init/log.cpp
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright (C) 2015 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 "log.h"
-
-#include <fcntl.h>
-#include <linux/audit.h>
-#include <string.h>
-
-#include <android-base/logging.h>
-#include <selinux/selinux.h>
-
-namespace android {
-namespace init {
-
-void InitKernelLogging(char* argv[]) {
-    // Make stdin/stdout/stderr all point to /dev/null.
-    int fd = open("/sys/fs/selinux/null", O_RDWR);
-    if (fd == -1) {
-        int saved_errno = errno;
-        android::base::InitLogging(argv, &android::base::KernelLogger);
-        errno = saved_errno;
-        PLOG(FATAL) << "Couldn't open /sys/fs/selinux/null";
-    }
-    dup2(fd, 0);
-    dup2(fd, 1);
-    dup2(fd, 2);
-    if (fd > 2) close(fd);
-
-    android::base::InitLogging(argv, &android::base::KernelLogger);
-}
-
-int selinux_klog_callback(int type, const char *fmt, ...) {
-    android::base::LogSeverity severity = android::base::ERROR;
-    if (type == SELINUX_WARNING) {
-        severity = android::base::WARNING;
-    } else if (type == SELINUX_INFO) {
-        severity = android::base::INFO;
-    }
-    char buf[1024];
-    va_list ap;
-    va_start(ap, fmt);
-    vsnprintf(buf, sizeof(buf), fmt, ap);
-    va_end(ap);
-    android::base::KernelLogger(android::base::MAIN, severity, "selinux", nullptr, 0, buf);
-    return 0;
-}
-
-}  // namespace init
-}  // namespace android
diff --git a/init/log.h b/init/log.h
deleted file mode 100644
index 5a4eba6..0000000
--- a/init/log.h
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright (C) 2010 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_LOG_H_
-#define _INIT_LOG_H_
-
-#include <sys/cdefs.h>
-
-namespace android {
-namespace init {
-
-void InitKernelLogging(char* argv[]);
-
-int selinux_klog_callback(int level, const char* fmt, ...) __printflike(2, 3);
-
-}  // namespace init
-}  // namespace android
-
-#endif
diff --git a/init/main.cpp b/init/main.cpp
new file mode 100644
index 0000000..9ed451b
--- /dev/null
+++ b/init/main.cpp
@@ -0,0 +1,21 @@
+/*
+ * 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 "init.h"
+
+int main(int argc, char** argv) {
+    android::init::main(argc, argv);
+}
diff --git a/init/modalias_handler.cpp b/init/modalias_handler.cpp
new file mode 100644
index 0000000..1734a7e
--- /dev/null
+++ b/init/modalias_handler.cpp
@@ -0,0 +1,163 @@
+/*
+ * 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 "modalias_handler.h"
+
+#include <fnmatch.h>
+#include <sys/syscall.h>
+
+#include <algorithm>
+#include <functional>
+#include <string>
+#include <vector>
+
+#include <android-base/chrono_utils.h>
+#include <android-base/logging.h>
+#include <android-base/unique_fd.h>
+
+#include "parser.h"
+
+namespace android {
+namespace init {
+
+Result<Success> ModaliasHandler::ParseDepCallback(std::vector<std::string>&& args) {
+    std::vector<std::string> deps;
+
+    // Set first item as our modules path
+    std::string::size_type pos = args[0].find(':');
+    if (pos != std::string::npos) {
+        deps.emplace_back(args[0].substr(0, pos));
+    } else {
+        return Error() << "dependency lines must start with name followed by ':'";
+    }
+
+    // Remaining items are dependencies of our module
+    for (auto arg = args.begin() + 1; arg != args.end(); ++arg) {
+        deps.push_back(*arg);
+    }
+
+    // Key is striped module name to match names in alias file
+    std::size_t start = args[0].find_last_of("/");
+    std::size_t end = args[0].find(".ko:");
+    if ((end - start) <= 1) return Error() << "malformed dependency line";
+    auto mod_name = args[0].substr(start + 1, (end - start) - 1);
+    // module names can have '-', but their file names will have '_'
+    std::replace(mod_name.begin(), mod_name.end(), '-', '_');
+    this->module_deps_[mod_name] = deps;
+
+    return Success();
+}
+
+Result<Success> ModaliasHandler::ParseAliasCallback(std::vector<std::string>&& args) {
+    auto it = args.begin();
+    const std::string& type = *it++;
+
+    if (type != "alias") {
+        return Error() << "we only handle alias lines, got: " << type;
+    }
+
+    if (args.size() != 3) {
+        return Error() << "alias lines must have 3 entries";
+    }
+
+    std::string& alias = *it++;
+    std::string& module_name = *it++;
+    this->module_aliases_.emplace_back(alias, module_name);
+
+    return Success();
+}
+
+ModaliasHandler::ModaliasHandler() {
+    using namespace std::placeholders;
+
+    static const std::string base_paths[] = {
+            "/vendor/lib/modules/",
+            "/lib/modules/",
+            "/odm/lib/modules/",
+    };
+
+    Parser alias_parser;
+    auto alias_callback = std::bind(&ModaliasHandler::ParseAliasCallback, this, _1);
+    alias_parser.AddSingleLineParser("alias", alias_callback);
+    for (const auto& base_path : base_paths) alias_parser.ParseConfig(base_path + "modules.alias");
+
+    Parser dep_parser;
+    auto dep_callback = std::bind(&ModaliasHandler::ParseDepCallback, this, _1);
+    dep_parser.AddSingleLineParser("", dep_callback);
+    for (const auto& base_path : base_paths) dep_parser.ParseConfig(base_path + "modules.dep");
+}
+
+Result<Success> ModaliasHandler::Insmod(const std::string& path_name, const std::string& args) {
+    base::unique_fd fd(
+            TEMP_FAILURE_RETRY(open(path_name.c_str(), O_RDONLY | O_NOFOLLOW | O_CLOEXEC)));
+    if (fd == -1) return ErrnoError() << "Could not open module '" << path_name << "'";
+
+    int ret = syscall(__NR_finit_module, fd.get(), args.c_str(), 0);
+    if (ret != 0) {
+        if (errno == EEXIST) {
+            // Module already loaded
+            return Success();
+        }
+        return ErrnoError() << "Failed to insmod '" << path_name << "' with args '" << args << "'";
+    }
+
+    LOG(INFO) << "Loaded kernel module " << path_name;
+    return Success();
+}
+
+Result<Success> ModaliasHandler::InsmodWithDeps(const std::string& module_name,
+                                                const std::string& args) {
+    if (module_name.empty()) {
+        return Error() << "Need valid module name";
+    }
+
+    auto it = module_deps_.find(module_name);
+    if (it == module_deps_.end()) {
+        return Error() << "Module '" << module_name << "' not in dependency file";
+    }
+    auto& dependencies = it->second;
+
+    // load module dependencies in reverse order
+    for (auto dep = dependencies.rbegin(); dep != dependencies.rend() - 1; ++dep) {
+        if (auto result = Insmod(*dep, ""); !result) return result;
+    }
+
+    // load target module itself with args
+    return Insmod(dependencies[0], args);
+}
+
+void ModaliasHandler::HandleModaliasEvent(const Uevent& uevent) {
+    if (uevent.modalias.empty()) return;
+
+    for (const auto& [alias, module] : module_aliases_) {
+        if (fnmatch(alias.c_str(), uevent.modalias.c_str(), 0) != 0) continue;  // Keep looking
+
+        LOG(DEBUG) << "Loading kernel module '" << module << "' for alias '" << uevent.modalias
+                   << "'";
+
+        if (auto result = InsmodWithDeps(module, ""); !result) {
+            LOG(ERROR) << "Cannot load module: " << result.error();
+            // try another one since there may be another match
+            continue;
+        }
+
+        // loading was successful
+        return;
+    }
+}
+
+}  // namespace init
+}  // namespace android
diff --git a/init/modalias_handler.h b/init/modalias_handler.h
new file mode 100644
index 0000000..e79da32
--- /dev/null
+++ b/init/modalias_handler.h
@@ -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.
+ */
+
+#pragma once
+
+#include "result.h"
+#include "uevent.h"
+
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+namespace android {
+namespace init {
+
+class ModaliasHandler {
+  public:
+    ModaliasHandler();
+    ~ModaliasHandler(){};
+
+    void HandleModaliasEvent(const Uevent& uevent);
+
+  private:
+    Result<Success> InsmodWithDeps(const std::string& module_name, const std::string& args);
+    Result<Success> Insmod(const std::string& path_name, const std::string& args);
+
+    Result<Success> ParseDepCallback(std::vector<std::string>&& args);
+    Result<Success> ParseAliasCallback(std::vector<std::string>&& args);
+
+    std::vector<std::pair<std::string, std::string>> module_aliases_;
+    std::unordered_map<std::string, std::vector<std::string>> module_deps_;
+};
+
+}  // namespace init
+}  // namespace android
diff --git a/init/parser.cpp b/init/parser.cpp
index c0fa6d9..fa0fd11 100644
--- a/init/parser.cpp
+++ b/init/parser.cpp
@@ -1,123 +1,185 @@
+/*
+ * Copyright (C) 2010 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 "parser.h"
 
+#include <dirent.h>
+
+#include <android-base/chrono_utils.h>
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+
+#include "tokenizer.h"
+#include "util.h"
+
 namespace android {
 namespace init {
 
-int next_token(struct parse_state *state)
-{
-    char *x = state->ptr;
-    char *s;
+Parser::Parser() {}
 
-    if (state->nexttoken) {
-        int t = state->nexttoken;
-        state->nexttoken = 0;
-        return t;
-    }
+void Parser::AddSectionParser(const std::string& name, std::unique_ptr<SectionParser> parser) {
+    section_parsers_[name] = std::move(parser);
+}
+
+void Parser::AddSingleLineParser(const std::string& prefix, LineCallback callback) {
+    line_callbacks_.emplace_back(prefix, callback);
+}
+
+void Parser::ParseData(const std::string& filename, std::string* data) {
+    data->push_back('\n');  // TODO: fix tokenizer
+    data->push_back('\0');
+
+    parse_state state;
+    state.line = 0;
+    state.ptr = data->data();
+    state.nexttoken = 0;
+
+    SectionParser* section_parser = nullptr;
+    int section_start_line = -1;
+    std::vector<std::string> args;
+
+    auto end_section = [&] {
+        if (section_parser == nullptr) return;
+
+        if (auto result = section_parser->EndSection(); !result) {
+            parse_error_count_++;
+            LOG(ERROR) << filename << ": " << section_start_line << ": " << result.error();
+        }
+
+        section_parser = nullptr;
+        section_start_line = -1;
+    };
 
     for (;;) {
-        switch (*x) {
-        case 0:
-            state->ptr = x;
-            return T_EOF;
-        case '\n':
-            x++;
-            state->ptr = x;
-            return T_NEWLINE;
-        case ' ':
-        case '\t':
-        case '\r':
-            x++;
-            continue;
-        case '#':
-            while (*x && (*x != '\n')) x++;
-            if (*x == '\n') {
-                state->ptr = x+1;
-                return T_NEWLINE;
-            } else {
-                state->ptr = x;
-                return T_EOF;
+        switch (next_token(&state)) {
+            case T_EOF:
+                end_section();
+
+                for (const auto& [section_name, section_parser] : section_parsers_) {
+                    section_parser->EndFile();
+                }
+
+                return;
+            case T_NEWLINE: {
+                state.line++;
+                if (args.empty()) break;
+                // If we have a line matching a prefix we recognize, call its callback and unset any
+                // current section parsers.  This is meant for /sys/ and /dev/ line entries for
+                // uevent.
+                auto line_callback = std::find_if(
+                    line_callbacks_.begin(), line_callbacks_.end(),
+                    [&args](const auto& c) { return android::base::StartsWith(args[0], c.first); });
+                if (line_callback != line_callbacks_.end()) {
+                    end_section();
+
+                    if (auto result = line_callback->second(std::move(args)); !result) {
+                        parse_error_count_++;
+                        LOG(ERROR) << filename << ": " << state.line << ": " << result.error();
+                    }
+                } else if (section_parsers_.count(args[0])) {
+                    end_section();
+                    section_parser = section_parsers_[args[0]].get();
+                    section_start_line = state.line;
+                    if (auto result =
+                            section_parser->ParseSection(std::move(args), filename, state.line);
+                        !result) {
+                        parse_error_count_++;
+                        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_error_count_++;
+                        LOG(ERROR) << filename << ": " << state.line << ": " << result.error();
+                    }
+                } else {
+                    parse_error_count_++;
+                    LOG(ERROR) << filename << ": " << state.line
+                               << ": Invalid section keyword found";
+                }
+                args.clear();
+                break;
             }
-        default:
-            goto text;
+            case T_TEXT:
+                args.emplace_back(state.text);
+                break;
         }
     }
+}
+
+bool Parser::ParseConfigFileInsecure(const std::string& path) {
+    std::string config_contents;
+    if (!android::base::ReadFileToString(path, &config_contents)) {
+        return false;
+    }
 
-textdone:
-    state->ptr = x;
-    *s = 0;
-    return T_TEXT;
-text:
-    state->text = s = x;
-textresume:
-    for (;;) {
-        switch (*x) {
-        case 0:
-            goto textdone;
-        case ' ':
-        case '\t':
-        case '\r':
-            x++;
-            goto textdone;
-        case '\n':
-            state->nexttoken = T_NEWLINE;
-            x++;
-            goto textdone;
-        case '"':
-            x++;
-            for (;;) {
-                switch (*x) {
-                case 0:
-                        /* unterminated quoted thing */
-                    state->ptr = x;
-                    return T_EOF;
-                case '"':
-                    x++;
-                    goto textresume;
-                default:
-                    *s++ = *x++;
-                }
-            }
-            break;
-        case '\\':
-            x++;
-            switch (*x) {
-            case 0:
-                goto textdone;
-            case 'n':
-                *s++ = '\n';
-                break;
-            case 'r':
-                *s++ = '\r';
-                break;
-            case 't':
-                *s++ = '\t';
-                break;
-            case '\\':
-                *s++ = '\\';
-                break;
-            case '\r':
-                    /* \ <cr> <lf> -> line continuation */
-                if (x[1] != '\n') {
-                    x++;
-                    continue;
-                }
-            case '\n':
-                    /* \ <lf> -> line continuation */
-                state->line++;
-                x++;
-                    /* eat any extra whitespace */
-                while((*x == ' ') || (*x == '\t')) x++;
-                continue;
-            default:
-                    /* unknown escape -- just copy */
-                *s++ = *x++;
-            }
-            continue;
-        default:
-            *s++ = *x++;
+    ParseData(path, &config_contents);
+    return true;
+}
+
+bool Parser::ParseConfigFile(const std::string& path) {
+    LOG(INFO) << "Parsing file " << path << "...";
+    android::base::Timer t;
+    auto config_contents = ReadFile(path);
+    if (!config_contents) {
+        LOG(INFO) << "Unable to read config file '" << path << "': " << config_contents.error();
+        return false;
+    }
+
+    ParseData(path, &config_contents.value());
+
+    LOG(VERBOSE) << "(Parsing " << path << " took " << t << ".)";
+    return true;
+}
+
+bool Parser::ParseConfigDir(const std::string& path) {
+    LOG(INFO) << "Parsing directory " << path << "...";
+    std::unique_ptr<DIR, decltype(&closedir)> config_dir(opendir(path.c_str()), closedir);
+    if (!config_dir) {
+        PLOG(INFO) << "Could not import directory '" << path << "'";
+        return false;
+    }
+    dirent* current_file;
+    std::vector<std::string> files;
+    while ((current_file = readdir(config_dir.get()))) {
+        // Ignore directories and only process regular files.
+        if (current_file->d_type == DT_REG) {
+            std::string current_path =
+                android::base::StringPrintf("%s/%s", path.c_str(), current_file->d_name);
+            files.emplace_back(current_path);
         }
     }
-    return T_EOF;
+    // 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)) {
+            LOG(ERROR) << "could not import file '" << file << "'";
+        }
+    }
+    return true;
+}
+
+bool Parser::ParseConfig(const std::string& path) {
+    if (is_dir(path.c_str())) {
+        return ParseConfigDir(path);
+    }
+    return ParseConfigFile(path);
 }
 
 }  // namespace init
diff --git a/init/parser.h b/init/parser.h
index 86e4c57..2454b6a 100644
--- a/init/parser.h
+++ b/init/parser.h
@@ -14,27 +14,83 @@
  * limitations under the License.
  */
 
-#ifndef PARSER_H_
-#define PARSER_H_
+#ifndef _INIT_PARSER_H_
+#define _INIT_PARSER_H_
 
-#define T_EOF 0
-#define T_TEXT 1
-#define T_NEWLINE 2
+#include <map>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "result.h"
+
+//  SectionParser is an interface that can parse a given 'section' in init.
+//
+//  You can implement up to 4 functions below, with ParseSection being mandatory. The first two
+//  functions return Result<Success> indicating if they have an error. It will be reported along
+//  with the filename and line number of where the error occurred.
+//
+//  1) ParseSection
+//    This function is called when a section is first encountered.
+//
+//  2) ParseLineSection
+//    This function is called on each subsequent line until the next section is encountered.
+//
+//  3) EndSection
+//    This function is called either when a new section is found or at the end of the file.
+//    It indicates that parsing of the current section is complete and any relevant objects should
+//    be committed.
+//
+//  4) EndFile
+//    This function is called at the end of the file.
+//    It indicates that the parsing has completed and any relevant objects should be committed.
 
 namespace android {
 namespace init {
 
-struct parse_state
-{
-    char *ptr;
-    char *text;
-    int line;
-    int nexttoken;
+class SectionParser {
+  public:
+    virtual ~SectionParser() {}
+    virtual Result<Success> ParseSection(std::vector<std::string>&& args,
+                                         const std::string& filename, int line) = 0;
+    virtual Result<Success> ParseLineSection(std::vector<std::string>&&, int) { return Success(); };
+    virtual Result<Success> EndSection() { return Success(); };
+    virtual void EndFile(){};
 };
 
-int next_token(struct parse_state *state);
+class Parser {
+  public:
+    //  LineCallback is the type for callbacks that can parse a line starting with a given prefix.
+    //
+    //  They take the form of bool Callback(std::vector<std::string>&& args, std::string* err)
+    //
+    //  Similar to ParseSection() and ParseLineSection(), this function returns bool with false
+    //  indicating a failure and has an std::string* err parameter into which an error string can
+    //  be written.
+    using LineCallback = std::function<Result<Success>(std::vector<std::string>&&)>;
+
+    Parser();
+
+    bool ParseConfig(const std::string& path);
+    void AddSectionParser(const std::string& name, std::unique_ptr<SectionParser> parser);
+    void AddSingleLineParser(const std::string& prefix, LineCallback callback);
+
+    // Host init verifier check file permissions.
+    bool ParseConfigFileInsecure(const std::string& path);
+
+    size_t parse_error_count() const { return parse_error_count_; }
+
+  private:
+    void ParseData(const std::string& filename, std::string* data);
+    bool ParseConfigFile(const std::string& path);
+    bool ParseConfigDir(const std::string& path);
+
+    std::map<std::string, std::unique_ptr<SectionParser>> section_parsers_;
+    std::vector<std::pair<std::string, LineCallback>> line_callbacks_;
+    size_t parse_error_count_ = 0;
+};
 
 }  // namespace init
 }  // namespace android
 
-#endif /* PARSER_H_ */
+#endif
diff --git a/init/persistent_properties.cpp b/init/persistent_properties.cpp
new file mode 100644
index 0000000..21adce9
--- /dev/null
+++ b/init/persistent_properties.cpp
@@ -0,0 +1,246 @@
+/*
+ * Copyright (C) 2017 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 "persistent_properties.h"
+
+#include <dirent.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/system_properties.h>
+#include <sys/types.h>
+
+#include <memory>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/strings.h>
+#include <android-base/unique_fd.h>
+
+#include "util.h"
+
+using android::base::ReadFdToString;
+using android::base::StartsWith;
+using android::base::WriteStringToFd;
+using android::base::unique_fd;
+
+namespace android {
+namespace init {
+
+std::string persistent_property_filename = "/data/property/persistent_properties";
+
+namespace {
+
+constexpr const char kLegacyPersistentPropertyDir[] = "/data/property";
+
+void AddPersistentProperty(const std::string& name, const std::string& value,
+                           PersistentProperties* persistent_properties) {
+    auto persistent_property_record = persistent_properties->add_properties();
+    persistent_property_record->set_name(name);
+    persistent_property_record->set_value(value);
+}
+
+Result<PersistentProperties> LoadLegacyPersistentProperties() {
+    std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(kLegacyPersistentPropertyDir), closedir);
+    if (!dir) {
+        return ErrnoError() << "Unable to open persistent property directory \""
+                            << kLegacyPersistentPropertyDir << "\"";
+    }
+
+    PersistentProperties persistent_properties;
+    dirent* entry;
+    while ((entry = readdir(dir.get())) != nullptr) {
+        if (!StartsWith(entry->d_name, "persist.")) {
+            continue;
+        }
+        if (entry->d_type != DT_REG) {
+            continue;
+        }
+
+        unique_fd fd(openat(dirfd(dir.get()), entry->d_name, O_RDONLY | O_NOFOLLOW));
+        if (fd == -1) {
+            PLOG(ERROR) << "Unable to open persistent property file \"" << entry->d_name << "\"";
+            continue;
+        }
+
+        struct stat sb;
+        if (fstat(fd, &sb) == -1) {
+            PLOG(ERROR) << "fstat on property file \"" << entry->d_name << "\" failed";
+            continue;
+        }
+
+        // File must not be accessible to others, be owned by root/root, and
+        // not be a hard link to any other file.
+        if (((sb.st_mode & (S_IRWXG | S_IRWXO)) != 0) || sb.st_uid != 0 || sb.st_gid != 0 ||
+            sb.st_nlink != 1) {
+            PLOG(ERROR) << "skipping insecure property file " << entry->d_name
+                        << " (uid=" << sb.st_uid << " gid=" << sb.st_gid << " nlink=" << sb.st_nlink
+                        << " mode=" << std::oct << sb.st_mode << ")";
+            continue;
+        }
+
+        std::string value;
+        if (ReadFdToString(fd, &value)) {
+            AddPersistentProperty(entry->d_name, value, &persistent_properties);
+        } else {
+            PLOG(ERROR) << "Unable to read persistent property file " << entry->d_name;
+        }
+    }
+    return persistent_properties;
+}
+
+void RemoveLegacyPersistentPropertyFiles() {
+    std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(kLegacyPersistentPropertyDir), closedir);
+    if (!dir) {
+        PLOG(ERROR) << "Unable to open persistent property directory \""
+                    << kLegacyPersistentPropertyDir << "\"";
+        return;
+    }
+
+    dirent* entry;
+    while ((entry = readdir(dir.get())) != nullptr) {
+        if (!StartsWith(entry->d_name, "persist.")) {
+            continue;
+        }
+        if (entry->d_type != DT_REG) {
+            continue;
+        }
+        unlinkat(dirfd(dir.get()), entry->d_name, 0);
+    }
+}
+
+PersistentProperties LoadPersistentPropertiesFromMemory() {
+    PersistentProperties persistent_properties;
+    __system_property_foreach(
+        [](const prop_info* pi, void* cookie) {
+            __system_property_read_callback(
+                pi,
+                [](void* cookie, const char* name, const char* value, unsigned serial) {
+                    if (StartsWith(name, "persist.")) {
+                        auto properties = reinterpret_cast<PersistentProperties*>(cookie);
+                        AddPersistentProperty(name, value, properties);
+                    }
+                },
+                cookie);
+        },
+        &persistent_properties);
+    return persistent_properties;
+}
+
+Result<std::string> ReadPersistentPropertyFile() {
+    const std::string temp_filename = persistent_property_filename + ".tmp";
+    if (access(temp_filename.c_str(), F_OK) == 0) {
+        LOG(INFO)
+            << "Found temporary property file while attempting to persistent system properties"
+               " a previous persistent property write may have failed";
+        unlink(temp_filename.c_str());
+    }
+    auto file_contents = ReadFile(persistent_property_filename);
+    if (!file_contents) {
+        return Error() << "Unable to read persistent property file: " << file_contents.error();
+    }
+    return *file_contents;
+}
+
+}  // namespace
+
+Result<PersistentProperties> LoadPersistentPropertyFile() {
+    auto file_contents = ReadPersistentPropertyFile();
+    if (!file_contents) return file_contents.error();
+
+    PersistentProperties persistent_properties;
+    if (persistent_properties.ParseFromString(*file_contents)) return persistent_properties;
+
+    // If the file cannot be parsed in either format, then we don't have any recovery
+    // mechanisms, so we delete it to allow for future writes to take place successfully.
+    unlink(persistent_property_filename.c_str());
+    return Error() << "Unable to parse persistent property file: Could not parse protobuf";
+}
+
+Result<Success> WritePersistentPropertyFile(const PersistentProperties& persistent_properties) {
+    const std::string temp_filename = persistent_property_filename + ".tmp";
+    unique_fd fd(TEMP_FAILURE_RETRY(
+        open(temp_filename.c_str(), O_WRONLY | O_CREAT | O_NOFOLLOW | O_TRUNC | O_CLOEXEC, 0600)));
+    if (fd == -1) {
+        return ErrnoError() << "Could not open temporary properties file";
+    }
+    std::string serialized_string;
+    if (!persistent_properties.SerializeToString(&serialized_string)) {
+        return Error() << "Unable to serialize properties";
+    }
+    if (!WriteStringToFd(serialized_string, fd)) {
+        return ErrnoError() << "Unable to write file contents";
+    }
+    fsync(fd);
+    fd.reset();
+
+    if (rename(temp_filename.c_str(), persistent_property_filename.c_str())) {
+        int saved_errno = errno;
+        unlink(temp_filename.c_str());
+        return Error(saved_errno) << "Unable to rename persistent property file";
+    }
+    return Success();
+}
+
+// Persistent properties are not written often, so we rather not keep any data in memory and read
+// then rewrite the persistent property file for each update.
+void WritePersistentProperty(const std::string& name, const std::string& value) {
+    auto persistent_properties = LoadPersistentPropertyFile();
+
+    if (!persistent_properties) {
+        LOG(ERROR) << "Recovering persistent properties from memory: "
+                   << persistent_properties.error();
+        persistent_properties = LoadPersistentPropertiesFromMemory();
+    }
+    auto it = std::find_if(persistent_properties->mutable_properties()->begin(),
+                           persistent_properties->mutable_properties()->end(),
+                           [&name](const auto& record) { return record.name() == name; });
+    if (it != persistent_properties->mutable_properties()->end()) {
+        it->set_name(name);
+        it->set_value(value);
+    } else {
+        AddPersistentProperty(name, value, &persistent_properties.value());
+    }
+
+    if (auto result = WritePersistentPropertyFile(*persistent_properties); !result) {
+        LOG(ERROR) << "Could not store persistent property: " << result.error();
+    }
+}
+
+PersistentProperties LoadPersistentProperties() {
+    auto persistent_properties = LoadPersistentPropertyFile();
+
+    if (!persistent_properties) {
+        LOG(ERROR) << "Could not load single persistent property file, trying legacy directory: "
+                   << persistent_properties.error();
+        persistent_properties = LoadLegacyPersistentProperties();
+        if (!persistent_properties) {
+            LOG(ERROR) << "Unable to load legacy persistent properties: "
+                       << persistent_properties.error();
+            return {};
+        }
+        if (auto result = WritePersistentPropertyFile(*persistent_properties); result) {
+            RemoveLegacyPersistentPropertyFiles();
+        } else {
+            LOG(ERROR) << "Unable to write single persistent property file: " << result.error();
+            // Fall through so that we still set the properties that we've read.
+        }
+    }
+
+    return *persistent_properties;
+}
+
+}  // namespace init
+}  // namespace android
diff --git a/init/persistent_properties.h b/init/persistent_properties.h
new file mode 100644
index 0000000..5f4df85
--- /dev/null
+++ b/init/persistent_properties.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2017 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_PERSISTENT_PROPERTIES_H
+#define _INIT_PERSISTENT_PROPERTIES_H
+
+#include <string>
+
+#include "result.h"
+#include "system/core/init/persistent_properties.pb.h"
+
+namespace android {
+namespace init {
+
+PersistentProperties LoadPersistentProperties();
+void WritePersistentProperty(const std::string& name, const std::string& value);
+
+// Exposed only for testing
+Result<PersistentProperties> LoadPersistentPropertyFile();
+Result<Success> WritePersistentPropertyFile(const PersistentProperties& persistent_properties);
+extern std::string persistent_property_filename;
+
+}  // namespace init
+}  // namespace android
+
+#endif
diff --git a/init/persistent_properties.proto b/init/persistent_properties.proto
new file mode 100644
index 0000000..c8d2e3a
--- /dev/null
+++ b/init/persistent_properties.proto
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+syntax = "proto2";
+option optimize_for = LITE_RUNTIME;
+
+message PersistentProperties {
+    message PersistentPropertyRecord {
+        optional string name = 1;
+        optional string value = 2;
+    }
+
+    repeated PersistentPropertyRecord properties = 1;
+}
diff --git a/init/persistent_properties_test.cpp b/init/persistent_properties_test.cpp
new file mode 100644
index 0000000..872e9a1
--- /dev/null
+++ b/init/persistent_properties_test.cpp
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2017 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 "persistent_properties.h"
+
+#include <errno.h>
+
+#include <vector>
+
+#include <android-base/test_utils.h>
+#include <gtest/gtest.h>
+
+#include "util.h"
+
+using namespace std::string_literals;
+
+namespace android {
+namespace init {
+
+PersistentProperties VectorToPersistentProperties(
+    const std::vector<std::pair<std::string, std::string>>& input_properties) {
+    PersistentProperties persistent_properties;
+
+    for (const auto& [name, value] : input_properties) {
+        auto persistent_property_record = persistent_properties.add_properties();
+        persistent_property_record->set_name(name);
+        persistent_property_record->set_value(value);
+    }
+
+    return persistent_properties;
+}
+
+void CheckPropertiesEqual(std::vector<std::pair<std::string, std::string>> expected,
+                          const PersistentProperties& persistent_properties) {
+    for (const auto& persistent_property_record : persistent_properties.properties()) {
+        auto it = std::find_if(expected.begin(), expected.end(),
+                               [persistent_property_record](const auto& entry) {
+                                   return entry.first == persistent_property_record.name() &&
+                                          entry.second == persistent_property_record.value();
+                               });
+        ASSERT_TRUE(it != expected.end())
+            << "Found unexpected property (" << persistent_property_record.name() << ", "
+            << persistent_property_record.value() << ")";
+        expected.erase(it);
+    }
+    auto joiner = [](const std::vector<std::pair<std::string, std::string>>& vector) {
+        std::string result;
+        for (const auto& [name, value] : vector) {
+            result += " (" + name + ", " + value + ")";
+        }
+        return result;
+    };
+    EXPECT_TRUE(expected.empty()) << "Did not find expected properties:" << joiner(expected);
+}
+
+TEST(persistent_properties, EndToEnd) {
+    TemporaryFile tf;
+    ASSERT_TRUE(tf.fd != -1);
+    persistent_property_filename = tf.path;
+
+    std::vector<std::pair<std::string, std::string>> persistent_properties = {
+        {"persist.sys.locale", "en-US"},
+        {"persist.sys.timezone", "America/Los_Angeles"},
+        {"persist.test.empty.value", ""},
+        {"persist.test.new.line", "abc\n\n\nabc"},
+        {"persist.test.numbers", "1234567890"},
+        {"persist.test.non.ascii", "\x00\x01\x02\xFF\xFE\xFD\x7F\x8F\x9F"},
+        // We don't currently allow for non-ascii names for system properties, but this is a policy
+        // decision, not a technical limitation.
+        {"persist.\x00\x01\x02\xFF\xFE\xFD\x7F\x8F\x9F", "non-ascii-name"},
+    };
+
+    ASSERT_TRUE(WritePersistentPropertyFile(VectorToPersistentProperties(persistent_properties)));
+
+    auto read_back_properties = LoadPersistentProperties();
+    CheckPropertiesEqual(persistent_properties, read_back_properties);
+}
+
+TEST(persistent_properties, AddProperty) {
+    TemporaryFile tf;
+    ASSERT_TRUE(tf.fd != -1);
+    persistent_property_filename = tf.path;
+
+    std::vector<std::pair<std::string, std::string>> persistent_properties = {
+        {"persist.sys.timezone", "America/Los_Angeles"},
+    };
+    ASSERT_TRUE(WritePersistentPropertyFile(VectorToPersistentProperties(persistent_properties)));
+
+    WritePersistentProperty("persist.sys.locale", "pt-BR");
+
+    std::vector<std::pair<std::string, std::string>> persistent_properties_expected = {
+        {"persist.sys.timezone", "America/Los_Angeles"},
+        {"persist.sys.locale", "pt-BR"},
+    };
+
+    auto read_back_properties = LoadPersistentProperties();
+    CheckPropertiesEqual(persistent_properties_expected, read_back_properties);
+}
+
+TEST(persistent_properties, UpdateProperty) {
+    TemporaryFile tf;
+    ASSERT_TRUE(tf.fd != -1);
+    persistent_property_filename = tf.path;
+
+    std::vector<std::pair<std::string, std::string>> persistent_properties = {
+        {"persist.sys.locale", "en-US"},
+        {"persist.sys.timezone", "America/Los_Angeles"},
+    };
+    ASSERT_TRUE(WritePersistentPropertyFile(VectorToPersistentProperties(persistent_properties)));
+
+    WritePersistentProperty("persist.sys.locale", "pt-BR");
+
+    std::vector<std::pair<std::string, std::string>> persistent_properties_expected = {
+        {"persist.sys.locale", "pt-BR"},
+        {"persist.sys.timezone", "America/Los_Angeles"},
+    };
+
+    auto read_back_properties = LoadPersistentProperties();
+    CheckPropertiesEqual(persistent_properties_expected, read_back_properties);
+}
+
+TEST(persistent_properties, UpdatePropertyBadParse) {
+    TemporaryFile tf;
+    ASSERT_TRUE(tf.fd != -1);
+    persistent_property_filename = tf.path;
+
+    ASSERT_TRUE(WriteFile(tf.path, "ab"));
+
+    WritePersistentProperty("persist.sys.locale", "pt-BR");
+
+    auto read_back_properties = LoadPersistentProperties();
+    EXPECT_GT(read_back_properties.properties().size(), 0);
+
+    auto it =
+        std::find_if(read_back_properties.properties().begin(),
+                     read_back_properties.properties().end(), [](const auto& entry) {
+                         return entry.name() == "persist.sys.locale" && entry.value() == "pt-BR";
+                     });
+    EXPECT_FALSE(it == read_back_properties.properties().end());
+}
+
+}  // namespace init
+}  // namespace android
diff --git a/init/property_service.cpp b/init/property_service.cpp
index 925cc9b..c0d811f 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -17,7 +17,6 @@
 #include "property_service.h"
 
 #include <ctype.h>
-#include <dirent.h>
 #include <errno.h>
 #include <fcntl.h>
 #include <inttypes.h>
@@ -34,6 +33,7 @@
 #include <sys/types.h>
 #include <sys/un.h>
 #include <unistd.h>
+#include <wchar.h>
 
 #define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
 #include <sys/_system_properties.h>
@@ -46,146 +46,110 @@
 #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 <bootimg.h>
 #include <fs_mgr.h>
+#include <property_info_parser/property_info_parser.h>
+#include <property_info_serializer/property_info_serializer.h>
 #include <selinux/android.h>
 #include <selinux/label.h>
 #include <selinux/selinux.h>
 
+#include "epoll.h"
 #include "init.h"
+#include "persistent_properties.h"
+#include "property_type.h"
+#include "selinux.h"
+#include "subcontext.h"
 #include "util.h"
 
-using android::base::Timer;
+using namespace std::literals;
 
-#define PERSISTENT_PROPERTY_DIR  "/data/property"
+using android::base::ReadFileToString;
+using android::base::Split;
+using android::base::StartsWith;
+using android::base::StringPrintf;
+using android::base::Timer;
+using android::base::Trim;
+using android::base::WriteStringToFile;
+using android::properties::BuildTrie;
+using android::properties::ParsePropertyInfoFile;
+using android::properties::PropertyInfoAreaFile;
+using android::properties::PropertyInfoEntry;
+
 #define RECOVERY_MOUNT_POINT "/recovery"
 
 namespace android {
 namespace init {
 
-static int persistent_properties_loaded = 0;
+static bool persistent_properties_loaded = false;
 
 static int property_set_fd = -1;
 
+static PropertyInfoAreaFile property_info_area;
+
+uint32_t InitPropertySet(const std::string& name, const std::string& value);
+
+uint32_t (*property_set)(const std::string& name, const std::string& value) = InitPropertySet;
+
+void CreateSerializedPropertyInfo();
+
+struct PropertyAuditData {
+    const ucred* cr;
+    const char* name;
+};
+
 void property_init() {
+    mkdir("/dev/__properties__", S_IRWXU | S_IXGRP | S_IXOTH);
+    CreateSerializedPropertyInfo();
     if (__system_property_area_init()) {
-        LOG(ERROR) << "Failed to initialize property area";
-        exit(1);
+        LOG(FATAL) << "Failed to initialize property area";
+    }
+    if (!property_info_area.LoadDefaultPath()) {
+        LOG(FATAL) << "Failed to load serialized property info file";
     }
 }
-
-static bool check_mac_perms(const std::string& name, char* sctx, struct ucred* cr) {
-
-    if (!sctx) {
-      return false;
-    }
-
-    if (!sehandle_prop) {
-      return false;
-    }
-
-    char* tctx = nullptr;
-    if (selabel_lookup(sehandle_prop, &tctx, name.c_str(), 1) != 0) {
-      return false;
-    }
-
-    property_audit_data audit_data;
-
-    audit_data.name = name.c_str();
-    audit_data.cr = cr;
-
-    bool has_access = (selinux_check_access(sctx, tctx, "property_service", "set", &audit_data) == 0);
-
-    freecon(tctx);
-    return has_access;
-}
-
-static int check_control_mac_perms(const char *name, char *sctx, struct ucred *cr)
-{
-    /*
-     *  Create a name prefix out of ctl.<service name>
-     *  The new prefix allows the use of the existing
-     *  property service backend labeling while avoiding
-     *  mislabels based on true property prefixes.
-     */
-    char ctl_name[PROP_VALUE_MAX+4];
-    int ret = snprintf(ctl_name, sizeof(ctl_name), "ctl.%s", name);
-
-    if (ret < 0 || (size_t) ret >= sizeof(ctl_name))
-        return 0;
-
-    return check_mac_perms(ctl_name, sctx, cr);
-}
-
-static void write_persistent_property(const char *name, const char *value)
-{
-    char tempPath[PATH_MAX];
-    char path[PATH_MAX];
-    int fd;
-
-    snprintf(tempPath, sizeof(tempPath), "%s/.temp.XXXXXX", PERSISTENT_PROPERTY_DIR);
-    fd = mkstemp(tempPath);
-    if (fd < 0) {
-        PLOG(ERROR) << "Unable to write persistent property to temp file " << tempPath;
-        return;
-    }
-    write(fd, value, strlen(value));
-    fsync(fd);
-    close(fd);
-
-    snprintf(path, sizeof(path), "%s/%s", PERSISTENT_PROPERTY_DIR, name);
-    if (rename(tempPath, path)) {
-        PLOG(ERROR) << "Unable to rename persistent property file " << tempPath << " to " << path;
-        unlink(tempPath);
-    }
-}
-
-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;
+static bool CheckMacPerms(const std::string& name, const char* target_context,
+                          const char* source_context, const ucred& cr) {
+    if (!target_context || !source_context) {
         return false;
     }
 
-    return true;
+    PropertyAuditData audit_data;
+
+    audit_data.name = name.c_str();
+    audit_data.cr = &cr;
+
+    bool has_access = (selinux_check_access(source_context, target_context, "property_service",
+                                            "set", &audit_data) == 0);
+
+    return has_access;
 }
 
-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) {
-        LOG(ERROR) << "property_set(\"" << name << "\", \"" << value << "\") failed: "
-                   << "value too long";
+    if (valuelen >= PROP_VALUE_MAX && !StartsWith(name, "ro.")) {
+        *error = "Property value too long";
+        return PROP_ERROR_INVALID_VALUE;
+    }
+
+    if (mbstowcs(nullptr, value.data(), 0) == static_cast<std::size_t>(-1)) {
+        *error = "Value is not a UTF8 encoded string";
         return PROP_ERROR_INVALID_VALUE;
     }
 
     prop_info* pi = (prop_info*) __system_property_find(name.c_str());
     if (pi != nullptr) {
         // ro.* properties are actually "write-once".
-        if (android::base::StartsWith(name, "ro.")) {
-            LOG(ERROR) << "property_set(\"" << name << "\", \"" << value << "\") failed: "
-                       << "property already set";
+        if (StartsWith(name, "ro.")) {
+            *error = "Read-only property was already set";
             return PROP_ERROR_READ_ONLY_PROPERTY;
         }
 
@@ -193,16 +157,15 @@
     } 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;
         }
     }
 
     // Don't write properties to disk until after we have read all default
     // properties to prevent them from being overwritten by default values.
-    if (persistent_properties_loaded && android::base::StartsWith(name, "persist.")) {
-        write_persistent_property(name.c_str(), value.c_str());
+    if (persistent_properties_loaded && StartsWith(name, "persist.")) {
+        WritePersistentProperty(name, value);
     }
     property_changed(name, value);
     return PROP_SUCCESS;
@@ -236,7 +199,7 @@
             LOG(ERROR) << "property_set_async(\"" << info.name << "\", \"" << info.value
                        << "\") failed";
         }
-        exit(0);
+        _exit(0);
     }
 }
 
@@ -248,8 +211,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()) {
@@ -259,9 +224,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;
@@ -279,180 +244,243 @@
     return selinux_android_restorecon(value.c_str(), SELINUX_ANDROID_RESTORECON_RECURSE);
 }
 
-uint32_t property_set(const std::string& name, const std::string& value) {
+uint32_t InitPropertySet(const std::string& name, const std::string& value) {
+    if (StartsWith(name, "ctl.")) {
+        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") {
-        return PropertySetAsync(name, value, RestoreconRecursiveAsync);
+        LOG(ERROR) << "InitPropertySet: Do not set selinux.restorecon_recursive from init; use the "
+                      "restorecon builtin directly";
+        return PROP_ERROR_INVALID_NAME;
     }
 
-    return PropertySetImpl(name, 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 result;
 }
 
 class SocketConnection {
- public:
-  SocketConnection(int socket, const struct ucred& cred)
-      : socket_(socket), cred_(cred) {}
+  public:
+    SocketConnection(int socket, const ucred& cred) : socket_(socket), cred_(cred) {}
 
-  ~SocketConnection() {
-    close(socket_);
-  }
+    ~SocketConnection() { close(socket_); }
 
-  bool RecvUint32(uint32_t* value, uint32_t* timeout_ms) {
-    return RecvFully(value, sizeof(*value), timeout_ms);
-  }
-
-  bool RecvChars(char* chars, size_t size, uint32_t* timeout_ms) {
-    return RecvFully(chars, size, timeout_ms);
-  }
-
-  bool RecvString(std::string* value, uint32_t* timeout_ms) {
-    uint32_t len = 0;
-    if (!RecvUint32(&len, timeout_ms)) {
-      return false;
+    bool RecvUint32(uint32_t* value, uint32_t* timeout_ms) {
+        return RecvFully(value, sizeof(*value), timeout_ms);
     }
 
-    if (len == 0) {
-      *value = "";
-      return true;
+    bool RecvChars(char* chars, size_t size, uint32_t* timeout_ms) {
+        return RecvFully(chars, size, timeout_ms);
     }
 
-    // http://b/35166374: don't allow init to make arbitrarily large allocations.
-    if (len > 0xffff) {
-      LOG(ERROR) << "sys_prop: RecvString asked to read huge string: " << len;
-      errno = ENOMEM;
-      return false;
-    }
-
-    std::vector<char> chars(len);
-    if (!RecvChars(&chars[0], len, timeout_ms)) {
-      return false;
-    }
-
-    *value = std::string(&chars[0], len);
-    return true;
-  }
-
-  bool SendUint32(uint32_t value) {
-    int result = TEMP_FAILURE_RETRY(send(socket_, &value, sizeof(value), 0));
-    return result == sizeof(value);
-  }
-
-  int socket() {
-    return socket_;
-  }
-
-  const struct ucred& cred() {
-    return cred_;
-  }
-
- private:
-  bool PollIn(uint32_t* timeout_ms) {
-    struct pollfd ufds[1];
-    ufds[0].fd = socket_;
-    ufds[0].events = POLLIN;
-    ufds[0].revents = 0;
-    while (*timeout_ms > 0) {
-      Timer timer;
-      int nr = poll(ufds, 1, *timeout_ms);
-      uint64_t millis = timer.duration().count();
-      *timeout_ms = (millis > *timeout_ms) ? 0 : *timeout_ms - millis;
-
-      if (nr > 0) {
-        return true;
-      }
-
-      if (nr == 0) {
-        // Timeout
-        break;
-      }
-
-      if (nr < 0 && errno != EINTR) {
-        PLOG(ERROR) << "sys_prop: error waiting for uid " << cred_.uid << " to send property message";
-        return false;
-      } else { // errno == EINTR
-        // Timer rounds milliseconds down in case of EINTR we want it to be rounded up
-        // to avoid slowing init down by causing EINTR with under millisecond timeout.
-        if (*timeout_ms > 0) {
-          --(*timeout_ms);
+    bool RecvString(std::string* value, uint32_t* timeout_ms) {
+        uint32_t len = 0;
+        if (!RecvUint32(&len, timeout_ms)) {
+            return false;
         }
-      }
+
+        if (len == 0) {
+            *value = "";
+            return true;
+        }
+
+        // http://b/35166374: don't allow init to make arbitrarily large allocations.
+        if (len > 0xffff) {
+            LOG(ERROR) << "sys_prop: RecvString asked to read huge string: " << len;
+            errno = ENOMEM;
+            return false;
+        }
+
+        std::vector<char> chars(len);
+        if (!RecvChars(&chars[0], len, timeout_ms)) {
+            return false;
+        }
+
+        *value = std::string(&chars[0], len);
+        return true;
     }
 
-    LOG(ERROR) << "sys_prop: timeout waiting for uid " << cred_.uid << " to send property message.";
-    return false;
-  }
-
-  bool RecvFully(void* data_ptr, size_t size, uint32_t* timeout_ms) {
-    size_t bytes_left = size;
-    char* data = static_cast<char*>(data_ptr);
-    while (*timeout_ms > 0 && bytes_left > 0) {
-      if (!PollIn(timeout_ms)) {
-        return false;
-      }
-
-      int result = TEMP_FAILURE_RETRY(recv(socket_, data, bytes_left, MSG_DONTWAIT));
-      if (result <= 0) {
-        return false;
-      }
-
-      bytes_left -= result;
-      data += result;
+    bool SendUint32(uint32_t value) {
+        int result = TEMP_FAILURE_RETRY(send(socket_, &value, sizeof(value), 0));
+        return result == sizeof(value);
     }
 
-    return bytes_left == 0;
-  }
+    int socket() { return socket_; }
 
-  int socket_;
-  struct ucred cred_;
+    const ucred& cred() { return cred_; }
 
-  DISALLOW_IMPLICIT_CONSTRUCTORS(SocketConnection);
+    std::string source_context() const {
+        char* source_context = nullptr;
+        getpeercon(socket_, &source_context);
+        std::string result = source_context;
+        freecon(source_context);
+        return result;
+    }
+
+  private:
+    bool PollIn(uint32_t* timeout_ms) {
+        struct pollfd ufds[1];
+        ufds[0].fd = socket_;
+        ufds[0].events = POLLIN;
+        ufds[0].revents = 0;
+        while (*timeout_ms > 0) {
+            auto start_time = std::chrono::steady_clock::now();
+            int nr = poll(ufds, 1, *timeout_ms);
+            auto now = std::chrono::steady_clock::now();
+            auto time_elapsed =
+                std::chrono::duration_cast<std::chrono::milliseconds>(now - start_time);
+            uint64_t millis = time_elapsed.count();
+            *timeout_ms = (millis > *timeout_ms) ? 0 : *timeout_ms - millis;
+
+            if (nr > 0) {
+                return true;
+            }
+
+            if (nr == 0) {
+                // Timeout
+                break;
+            }
+
+            if (nr < 0 && errno != EINTR) {
+                PLOG(ERROR) << "sys_prop: error waiting for uid " << cred_.uid
+                            << " to send property message";
+                return false;
+            } else {  // errno == EINTR
+                // Timer rounds milliseconds down in case of EINTR we want it to be rounded up
+                // to avoid slowing init down by causing EINTR with under millisecond timeout.
+                if (*timeout_ms > 0) {
+                    --(*timeout_ms);
+                }
+            }
+        }
+
+        LOG(ERROR) << "sys_prop: timeout waiting for uid " << cred_.uid
+                   << " to send property message.";
+        return false;
+    }
+
+    bool RecvFully(void* data_ptr, size_t size, uint32_t* timeout_ms) {
+        size_t bytes_left = size;
+        char* data = static_cast<char*>(data_ptr);
+        while (*timeout_ms > 0 && bytes_left > 0) {
+            if (!PollIn(timeout_ms)) {
+                return false;
+            }
+
+            int result = TEMP_FAILURE_RETRY(recv(socket_, data, bytes_left, MSG_DONTWAIT));
+            if (result <= 0) {
+                PLOG(ERROR) << "sys_prop: recv error";
+                return false;
+            }
+
+            bytes_left -= result;
+            data += result;
+        }
+
+        if (bytes_left != 0) {
+            LOG(ERROR) << "sys_prop: recv data is not properly obtained.";
+        }
+
+        return bytes_left == 0;
+    }
+
+    int socket_;
+    ucred cred_;
+
+    DISALLOW_IMPLICIT_CONSTRUCTORS(SocketConnection);
 };
 
-static void handle_property_set(SocketConnection& socket,
-                                const std::string& name,
-                                const std::string& value,
-                                bool legacy_protocol) {
-  const char* cmd_name = legacy_protocol ? "PROP_MSG_SETPROP" : "PROP_MSG_SETPROP2";
-  if (!is_legal_property_name(name)) {
-    LOG(ERROR) << "sys_prop(" << cmd_name << "): illegal property name \"" << name << "\"";
-    socket.SendUint32(PROP_ERROR_INVALID_NAME);
-    return;
-  }
+bool CheckControlPropertyPerms(const std::string& name, const std::string& value,
+                               const std::string& source_context, const ucred& cr) {
+    // We check the legacy method first but these properties are dontaudit, so we only log an audit
+    // if the newer method fails as well.  We only do this with the legacy ctl. properties.
+    if (name == "ctl.start" || name == "ctl.stop" || name == "ctl.restart") {
+        // The legacy permissions model is that ctl. properties have their name ctl.<action> and
+        // their value is the name of the service to apply that action to.  Permissions for these
+        // actions are based on the service, so we must create a fake name of ctl.<service> to
+        // check permissions.
+        auto control_string_legacy = "ctl." + value;
+        const char* target_context_legacy = nullptr;
+        const char* type_legacy = nullptr;
+        property_info_area->GetPropertyInfo(control_string_legacy.c_str(), &target_context_legacy,
+                                            &type_legacy);
 
-  struct ucred cr = socket.cred();
-  char* source_ctx = nullptr;
-  getpeercon(socket.socket(), &source_ctx);
-
-  if (android::base::StartsWith(name, "ctl.")) {
-    if (check_control_mac_perms(value.c_str(), source_ctx, &cr)) {
-      handle_control_message(name.c_str() + 4, value.c_str());
-      if (!legacy_protocol) {
-        socket.SendUint32(PROP_SUCCESS);
-      }
-    } else {
-      LOG(ERROR) << "sys_prop(" << cmd_name << "): Unable to " << (name.c_str() + 4)
-                 << " service ctl [" << value << "]"
-                 << " uid:" << cr.uid
-                 << " gid:" << cr.gid
-                 << " pid:" << cr.pid;
-      if (!legacy_protocol) {
-        socket.SendUint32(PROP_ERROR_HANDLE_CONTROL_MESSAGE);
-      }
+        if (CheckMacPerms(control_string_legacy, target_context_legacy, source_context.c_str(), cr)) {
+            return true;
+        }
     }
-  } else {
-    if (check_mac_perms(name, source_ctx, &cr)) {
-      uint32_t result = property_set(name, value);
-      if (!legacy_protocol) {
-        socket.SendUint32(result);
-      }
-    } else {
-      LOG(ERROR) << "sys_prop(" << cmd_name << "): permission denied uid:" << cr.uid << " name:" << name;
-      if (!legacy_protocol) {
-        socket.SendUint32(PROP_ERROR_PERMISSION_DENIED);
-      }
-    }
-  }
 
-  freecon(source_ctx);
+    auto control_string_full = name + "$" + value;
+    const char* target_context_full = nullptr;
+    const char* type_full = nullptr;
+    property_info_area->GetPropertyInfo(control_string_full.c_str(), &target_context_full,
+                                        &type_full);
+
+    return CheckMacPerms(control_string_full, target_context_full, source_context.c_str(), cr);
+}
+
+// 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, std::string* error) {
+    if (!IsLegalPropertyName(name)) {
+        *error = "Illegal property name";
+        return PROP_ERROR_INVALID_NAME;
+    }
+
+    if (StartsWith(name, "ctl.")) {
+        if (!CheckControlPropertyPerms(name, value, source_context, cr)) {
+            *error = StringPrintf("Invalid permissions to perform '%s' on '%s'", name.c_str() + 4,
+                                  value.c_str());
+            return PROP_ERROR_HANDLE_CONTROL_MESSAGE;
+        }
+
+        HandleControlMessage(name.c_str() + 4, value, cr.pid);
+        return PROP_SUCCESS;
+    }
+
+    const char* target_context = nullptr;
+    const char* type = nullptr;
+    property_info_area->GetPropertyInfo(name.c_str(), &target_context, &type);
+
+    if (!CheckMacPerms(name, target_context, source_context.c_str(), cr)) {
+        *error = "SELinux permission check failed";
+        return PROP_ERROR_PERMISSION_DENIED;
+    }
+
+    if (type == nullptr || !CheckType(type, value)) {
+        *error = StringPrintf("Property type check failed, value doesn't match expected type '%s'",
+                              (type ?: "(null)"));
+        return PROP_ERROR_INVALID_VALUE;
+    }
+
+    // sys.powerctl is a special property that is used to make the device reboot.  We want to log
+    // any process that sets this property to be able to accurately blame the cause of a shutdown.
+    if (name == "sys.powerctl") {
+        std::string cmdline_path = StringPrintf("proc/%d/cmdline", cr.pid);
+        std::string process_cmdline;
+        std::string process_log_string;
+        if (ReadFileToString(cmdline_path, &process_cmdline)) {
+            // Since cmdline is null deliminated, .c_str() conveniently gives us just the process
+            // path.
+            process_log_string = StringPrintf(" (%s)", process_cmdline.c_str());
+        }
+        LOG(INFO) << "Received sys.powerctl='" << value << "' from pid: " << cr.pid
+                  << process_log_string;
+    }
+
+    if (name == "selinux.restorecon_recursive") {
+        return PropertySetAsync(name, value, RestoreconRecursiveAsync, error);
+    }
+
+    return PropertySet(name, value, error);
 }
 
 static void handle_property_set_fd() {
@@ -463,7 +491,7 @@
         return;
     }
 
-    struct ucred cr;
+    ucred cr;
     socklen_t cr_size = sizeof(cr);
     if (getsockopt(s, SOL_SOCKET, SO_PEERCRED, &cr, &cr_size) < 0) {
         close(s);
@@ -495,7 +523,16 @@
         prop_name[PROP_NAME_MAX-1] = 0;
         prop_value[PROP_VALUE_MAX-1] = 0;
 
-        handle_property_set(socket, prop_value, prop_value, true);
+        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;
       }
 
@@ -509,7 +546,15 @@
           return;
         }
 
-        handle_property_set(socket, name, value, false);
+        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;
       }
 
@@ -520,17 +565,25 @@
     }
 }
 
-static void load_properties_from_file(const char *, const char *);
+static bool load_properties_from_file(const char *, const char *);
 
 /*
  * 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();
+    if (SelinuxHasVendorInit()) {
+        for (const auto& [path_prefix, secontext] : paths_and_secontexts) {
+            if (StartsWith(filename, path_prefix)) {
+                context = secontext;
+            }
+        }
+    }
+
     if (filter) {
         flen = strlen(filter);
     }
@@ -577,79 +630,40 @@
                 }
             }
 
-            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;
+            }
         }
     }
 }
 
 // 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_from_file(const char* filename, const char* filter) {
+static bool load_properties_from_file(const char* filename, const char* filter) {
     Timer t;
-    std::string data;
-    std::string err;
-    if (!ReadFile(filename, &data, &err)) {
-        PLOG(WARNING) << "Couldn't load property file: " << err;
-        return;
+    auto file_contents = ReadFile(filename);
+    if (!file_contents) {
+        PLOG(WARNING) << "Couldn't load property file '" << filename
+                      << "': " << file_contents.error();
+        return false;
     }
-    data.push_back('\n');
-    load_properties(&data[0], filter);
+    file_contents->push_back('\n');
+
+    LoadProperties(file_contents->data(), filter, filename);
     LOG(VERBOSE) << "(Loading properties from " << filename << " took " << t << ".)";
-}
-
-static void load_persistent_properties() {
-    persistent_properties_loaded = 1;
-
-    std::unique_ptr<DIR, int(*)(DIR*)> dir(opendir(PERSISTENT_PROPERTY_DIR), closedir);
-    if (!dir) {
-        PLOG(ERROR) << "Unable to open persistent property directory \""
-                    << PERSISTENT_PROPERTY_DIR << "\"";
-        return;
-    }
-
-    struct dirent* entry;
-    while ((entry = readdir(dir.get())) != NULL) {
-        if (strncmp("persist.", entry->d_name, strlen("persist."))) {
-            continue;
-        }
-        if (entry->d_type != DT_REG) {
-            continue;
-        }
-
-        // Open the file and read the property value.
-        int fd = openat(dirfd(dir.get()), entry->d_name, O_RDONLY | O_NOFOLLOW);
-        if (fd == -1) {
-            PLOG(ERROR) << "Unable to open persistent property file \"" << entry->d_name << "\"";
-            continue;
-        }
-
-        struct stat sb;
-        if (fstat(fd, &sb) == -1) {
-            PLOG(ERROR) << "fstat on property file \"" << entry->d_name << "\" failed";
-            close(fd);
-            continue;
-        }
-
-        // File must not be accessible to others, be owned by root/root, and
-        // not be a hard link to any other file.
-        if (((sb.st_mode & (S_IRWXG | S_IRWXO)) != 0) || sb.st_uid != 0 || sb.st_gid != 0 || sb.st_nlink != 1) {
-            PLOG(ERROR) << "skipping insecure property file " << entry->d_name
-                        << " (uid=" << sb.st_uid << " gid=" << sb.st_gid
-                        << " nlink=" << sb.st_nlink << " mode=" << std::oct << sb.st_mode << ")";
-            close(fd);
-            continue;
-        }
-
-        char value[PROP_VALUE_MAX];
-        int length = read(fd, value, sizeof(value) - 1);
-        if (length >= 0) {
-            value[length] = 0;
-            property_set(entry->d_name, value);
-        } else {
-            PLOG(ERROR) << "Unable to read persistent property file " << entry->d_name;
-        }
-        close(fd);
-    }
+    return true;
 }
 
 // persist.sys.usb.config values can't be combined on build-time when property
@@ -669,7 +683,15 @@
 }
 
 void property_load_boot_defaults() {
-    load_properties_from_file("/default.prop", NULL);
+    if (!load_properties_from_file("/system/etc/prop.default", NULL)) {
+        // Try recovery path
+        if (!load_properties_from_file("/prop.default", NULL)) {
+            // Try legacy path
+            load_properties_from_file("/default.prop", NULL);
+        }
+    }
+    load_properties_from_file("/product/build.prop", NULL);
+    load_properties_from_file("/product-services/build.prop", NULL);
     load_properties_from_file("/odm/default.prop", NULL);
     load_properties_from_file("/vendor/default.prop", NULL);
 
@@ -688,9 +710,24 @@
  * has mounted /data.
  */
 void load_persist_props(void) {
+    // Devices with FDE have load_persist_props called twice; the first time when the temporary
+    // /data partition is mounted and then again once /data is truly mounted.  We do not want to
+    // read persistent properties from the temporary /data partition or mark persistent properties
+    // as having been loaded during the first call, so we return in that case.
+    std::string crypto_state = android::base::GetProperty("ro.crypto.state", "");
+    std::string crypto_type = android::base::GetProperty("ro.crypto.type", "");
+    if (crypto_state == "encrypted" && crypto_type == "block") {
+        static size_t num_calls = 0;
+        if (++num_calls == 1) return;
+    }
+
     load_override_properties();
     /* Read persistent properties after all default values have been loaded. */
-    load_persistent_properties();
+    auto persistent_properties = LoadPersistentProperties();
+    for (const auto& persistent_property_record : persistent_properties.properties()) {
+        property_set(persistent_property_record.name(), persistent_property_record.value());
+    }
+    persistent_properties_loaded = true;
     property_set("ro.persistent_properties.ready", "true");
 }
 
@@ -733,19 +770,97 @@
     load_recovery_id_prop();
 }
 
-void start_property_service() {
+static int SelinuxAuditCallback(void* data, security_class_t /*cls*/, char* buf, size_t len) {
+    auto* d = reinterpret_cast<PropertyAuditData*>(data);
+
+    if (!d || !d->name || !d->cr) {
+        LOG(ERROR) << "AuditCallback invoked with null data arguments!";
+        return 0;
+    }
+
+    snprintf(buf, len, "property=%s pid=%d uid=%d gid=%d", d->name, d->cr->pid, d->cr->uid,
+             d->cr->gid);
+    return 0;
+}
+
+bool LoadPropertyInfoFromFile(const std::string& filename,
+                              std::vector<PropertyInfoEntry>* property_infos) {
+    auto file_contents = std::string();
+    if (!ReadFileToString(filename, &file_contents)) {
+        PLOG(ERROR) << "Could not read properties from '" << filename << "'";
+        return false;
+    }
+
+    auto errors = std::vector<std::string>{};
+    ParsePropertyInfoFile(file_contents, property_infos, &errors);
+    // Individual parsing errors are reported but do not cause a failed boot, which is what
+    // returning false would do here.
+    for (const auto& error : errors) {
+        LOG(ERROR) << "Could not read line from '" << filename << "': " << error;
+    }
+
+    return true;
+}
+
+void CreateSerializedPropertyInfo() {
+    auto property_infos = std::vector<PropertyInfoEntry>();
+    if (access("/system/etc/selinux/plat_property_contexts", R_OK) != -1) {
+        if (!LoadPropertyInfoFromFile("/system/etc/selinux/plat_property_contexts",
+                                      &property_infos)) {
+            return;
+        }
+        // Don't check for failure here, so we always have a sane list of properties.
+        // E.g. In case of recovery, the vendor partition will not have mounted and we
+        // still need the system / platform properties to function.
+        if (!LoadPropertyInfoFromFile("/vendor/etc/selinux/vendor_property_contexts",
+                                      &property_infos)) {
+            // Fallback to nonplat_* if vendor_* doesn't exist.
+            LoadPropertyInfoFromFile("/vendor/etc/selinux/nonplat_property_contexts",
+                                     &property_infos);
+        }
+    } else {
+        if (!LoadPropertyInfoFromFile("/plat_property_contexts", &property_infos)) {
+            return;
+        }
+        if (!LoadPropertyInfoFromFile("/vendor_property_contexts", &property_infos)) {
+            // Fallback to nonplat_* if vendor_* doesn't exist.
+            LoadPropertyInfoFromFile("/nonplat_property_contexts", &property_infos);
+        }
+    }
+
+    auto serialized_contexts = std::string();
+    auto error = std::string();
+    if (!BuildTrie(property_infos, "u:object_r:default_prop:s0", "string", &serialized_contexts,
+                   &error)) {
+        LOG(ERROR) << "Unable to serialize property contexts: " << error;
+        return;
+    }
+
+    constexpr static const char kPropertyInfosPath[] = "/dev/__properties__/property_info";
+    if (!WriteStringToFile(serialized_contexts, kPropertyInfosPath, 0444, 0, 0, false)) {
+        PLOG(ERROR) << "Unable to write serialized property infos to file";
+    }
+    selinux_android_restorecon(kPropertyInfosPath, 0);
+}
+
+void StartPropertyService(Epoll* epoll) {
+    selinux_callback cb;
+    cb.func_audit = SelinuxAuditCallback;
+    selinux_set_callback(SELINUX_CB_AUDIT, cb);
+
     property_set("ro.property_service.version", "2");
 
     property_set_fd = CreateSocket(PROP_SERVICE_NAME, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
-                                   false, 0666, 0, 0, nullptr, sehandle);
+                                   false, 0666, 0, 0, nullptr);
     if (property_set_fd == -1) {
-        PLOG(ERROR) << "start_property_service socket creation failed";
-        exit(1);
+        PLOG(FATAL) << "start_property_service socket creation failed";
     }
 
     listen(property_set_fd, 8);
 
-    register_epoll_handler(property_set_fd, handle_property_set_fd);
+    if (auto result = epoll->RegisterHandler(property_set_fd, handle_property_set_fd); !result) {
+        PLOG(FATAL) << result.error();
+    }
 }
 
 }  // namespace init
diff --git a/init/property_service.h b/init/property_service.h
index a55e79c..cacd987 100644
--- a/init/property_service.h
+++ b/init/property_service.h
@@ -21,13 +21,15 @@
 
 #include <string>
 
+#include "epoll.h"
+
 namespace android {
 namespace init {
 
-struct property_audit_data {
-    ucred *cr;
-    const char* name;
-};
+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);
 
 extern bool PropertyChildReap(pid_t pid);
 
@@ -35,9 +37,7 @@
 void property_load_boot_defaults(void);
 void load_persist_props(void);
 void load_system_props(void);
-void start_property_service(void);
-uint32_t property_set(const std::string& name, const std::string& value);
-bool is_legal_property_name(const std::string& name);
+void StartPropertyService(Epoll* epoll);
 
 }  // namespace init
 }  // namespace android
diff --git a/init/property_service_test.cpp b/init/property_service_test.cpp
index 3a64e02..c038aff 100644
--- a/init/property_service_test.cpp
+++ b/init/property_service_test.cpp
@@ -21,8 +21,11 @@
 #define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
 #include <sys/_system_properties.h>
 
+#include <android-base/properties.h>
 #include <gtest/gtest.h>
 
+using android::base::SetProperty;
+
 namespace android {
 namespace init {
 
@@ -42,13 +45,29 @@
   // ...so we can send it a malformed request.
   uint32_t msg = PROP_MSG_SETPROP2;
   uint32_t size = 0xffffffff;
-  uint32_t data = 0xdeadbeef;
 
   ASSERT_EQ(static_cast<ssize_t>(sizeof(msg)), send(fd, &msg, sizeof(msg), 0));
   ASSERT_EQ(static_cast<ssize_t>(sizeof(size)), send(fd, &size, sizeof(size), 0));
-  ASSERT_EQ(static_cast<ssize_t>(sizeof(data)), send(fd, &data, sizeof(data), 0));
+  uint32_t result = 0;
+  ASSERT_EQ(static_cast<ssize_t>(sizeof(result)),
+            TEMP_FAILURE_RETRY(recv(fd, &result, sizeof(result), MSG_WAITALL)));
+  EXPECT_EQ(static_cast<uint32_t>(PROP_ERROR_READ_DATA), result);
   ASSERT_EQ(0, close(fd));
 }
 
+TEST(property_service, non_utf8_value) {
+    ASSERT_TRUE(SetProperty("property_service_utf8_test", "base_success"));
+    EXPECT_FALSE(SetProperty("property_service_utf8_test", "\x80"));
+    EXPECT_FALSE(SetProperty("property_service_utf8_test", "\xC2\x01"));
+    EXPECT_FALSE(SetProperty("property_service_utf8_test", "\xE0\xFF"));
+    EXPECT_FALSE(SetProperty("property_service_utf8_test", "\xE0\xA0\xFF"));
+    EXPECT_FALSE(SetProperty("property_service_utf8_test", "\xF0\x01\xFF"));
+    EXPECT_FALSE(SetProperty("property_service_utf8_test", "\xF0\x90\xFF"));
+    EXPECT_FALSE(SetProperty("property_service_utf8_test", "\xF0\x90\x80\xFF"));
+    EXPECT_FALSE(SetProperty("property_service_utf8_test", "\xF0\x90\x80"));
+    EXPECT_FALSE(SetProperty("property_service_utf8_test", "ab\xF0\x90\x80\x80qe\xF0\x90\x80"));
+    EXPECT_TRUE(SetProperty("property_service_utf8_test", "\xF0\x90\x80\x80"));
+}
+
 }  // namespace init
 }  // namespace android
diff --git a/init/property_type.cpp b/init/property_type.cpp
new file mode 100644
index 0000000..249b12b
--- /dev/null
+++ b/init/property_type.cpp
@@ -0,0 +1,81 @@
+//
+// Copyright (C) 2017 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 "property_type.h"
+
+#include <android-base/parsedouble.h>
+#include <android-base/parseint.h>
+#include <android-base/strings.h>
+
+using android::base::ParseDouble;
+using android::base::ParseInt;
+using android::base::ParseUint;
+using android::base::Split;
+
+namespace android {
+namespace init {
+
+bool CheckType(const std::string& type_string, const std::string& value) {
+    auto type_strings = Split(type_string, " ");
+    if (type_strings.empty()) {
+        return false;
+    }
+    auto type = type_strings[0];
+
+    if (type == "string") {
+        return true;
+    }
+    if (type == "bool") {
+        return value == "true" || value == "false" || value == "1" || value == "0";
+    }
+    if (type == "int") {
+        int64_t parsed;
+        return ParseInt(value, &parsed);
+    }
+    if (type == "uint") {
+        uint64_t parsed;
+        if (value.empty() || value.front() == '-') {
+            return false;
+        }
+        return ParseUint(value, &parsed);
+    }
+    if (type == "double") {
+        double parsed;
+        return ParseDouble(value.c_str(), &parsed);
+    }
+    if (type == "size") {
+        auto it = value.begin();
+        while (it != value.end() && isdigit(*it)) {
+            it++;
+        }
+        if (it == value.begin() || it == value.end() || (*it != 'g' && *it != 'k' && *it != 'm')) {
+            return false;
+        }
+        it++;
+        return it == value.end();
+    }
+    if (type == "enum") {
+        for (auto it = std::next(type_strings.begin()); it != type_strings.end(); ++it) {
+            if (*it == value) {
+                return true;
+            }
+        }
+    }
+    return false;
+}
+
+}  // namespace init
+}  // namespace android
diff --git a/init/property_type.h b/init/property_type.h
new file mode 100644
index 0000000..c889e16
--- /dev/null
+++ b/init/property_type.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2017 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_PROPERTY_TYPE_H
+#define _INIT_PROPERTY_TYPE_H
+
+#include <string>
+
+namespace android {
+namespace init {
+
+bool CheckType(const std::string& type_string, const std::string& value);
+
+}  // namespace init
+}  // namespace android
+
+#endif
diff --git a/init/property_type_test.cpp b/init/property_type_test.cpp
new file mode 100644
index 0000000..068bccc
--- /dev/null
+++ b/init/property_type_test.cpp
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2017 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 "property_type.h"
+
+#include <gtest/gtest.h>
+
+namespace android {
+namespace init {
+
+TEST(property_type, CheckType_string) {
+    EXPECT_TRUE(CheckType("string", ""));
+    EXPECT_TRUE(CheckType("string", "-234"));
+    EXPECT_TRUE(CheckType("string", "234"));
+    EXPECT_TRUE(CheckType("string", "true"));
+    EXPECT_TRUE(CheckType("string", "false"));
+    EXPECT_TRUE(CheckType("string", "45645634563456345634563456"));
+    EXPECT_TRUE(CheckType("string", "some other string"));
+}
+
+TEST(property_type, CheckType_int) {
+    EXPECT_FALSE(CheckType("int", ""));
+    EXPECT_FALSE(CheckType("int", "abc"));
+    EXPECT_FALSE(CheckType("int", "-abc"));
+    EXPECT_TRUE(CheckType("int", "0"));
+    EXPECT_TRUE(CheckType("int", std::to_string(std::numeric_limits<int64_t>::min())));
+    EXPECT_TRUE(CheckType("int", std::to_string(std::numeric_limits<int64_t>::max())));
+    EXPECT_TRUE(CheckType("int", "123"));
+    EXPECT_TRUE(CheckType("int", "-123"));
+}
+
+TEST(property_type, CheckType_uint) {
+    EXPECT_FALSE(CheckType("uint", ""));
+    EXPECT_FALSE(CheckType("uint", "abc"));
+    EXPECT_FALSE(CheckType("uint", "-abc"));
+    EXPECT_TRUE(CheckType("uint", "0"));
+    EXPECT_TRUE(CheckType("uint", std::to_string(std::numeric_limits<uint64_t>::max())));
+    EXPECT_TRUE(CheckType("uint", "123"));
+    EXPECT_FALSE(CheckType("uint", "-123"));
+}
+
+TEST(property_type, CheckType_double) {
+    EXPECT_FALSE(CheckType("double", ""));
+    EXPECT_FALSE(CheckType("double", "abc"));
+    EXPECT_FALSE(CheckType("double", "-abc"));
+    EXPECT_TRUE(CheckType("double", "0.0"));
+    EXPECT_TRUE(CheckType("double", std::to_string(std::numeric_limits<double>::min())));
+    EXPECT_TRUE(CheckType("double", std::to_string(std::numeric_limits<double>::max())));
+    EXPECT_TRUE(CheckType("double", "123.1"));
+    EXPECT_TRUE(CheckType("double", "-123.1"));
+}
+
+TEST(property_type, CheckType_size) {
+    EXPECT_FALSE(CheckType("size", ""));
+    EXPECT_FALSE(CheckType("size", "ab"));
+    EXPECT_FALSE(CheckType("size", "abcd"));
+    EXPECT_FALSE(CheckType("size", "0"));
+
+    EXPECT_TRUE(CheckType("size", "512g"));
+    EXPECT_TRUE(CheckType("size", "512k"));
+    EXPECT_TRUE(CheckType("size", "512m"));
+
+    EXPECT_FALSE(CheckType("size", "512gggg"));
+    EXPECT_FALSE(CheckType("size", "512mgk"));
+    EXPECT_FALSE(CheckType("size", "g"));
+    EXPECT_FALSE(CheckType("size", "m"));
+}
+
+TEST(property_type, CheckType_enum) {
+    EXPECT_FALSE(CheckType("enum abc", ""));
+    EXPECT_FALSE(CheckType("enum abc", "ab"));
+    EXPECT_FALSE(CheckType("enum abc", "abcd"));
+    EXPECT_FALSE(CheckType("enum 123 456 789", "0"));
+
+    EXPECT_TRUE(CheckType("enum abc", "abc"));
+    EXPECT_TRUE(CheckType("enum 123 456 789", "123"));
+    EXPECT_TRUE(CheckType("enum 123 456 789", "456"));
+    EXPECT_TRUE(CheckType("enum 123 456 789", "789"));
+}
+
+}  // namespace init
+}  // namespace android
diff --git a/init/reboot.cpp b/init/reboot.cpp
index 969caec..11507f4 100644
--- a/init/reboot.cpp
+++ b/init/reboot.cpp
@@ -48,12 +48,16 @@
 #include <fs_mgr.h>
 #include <logwrap/logwrap.h>
 #include <private/android_filesystem_config.h>
+#include <selinux/selinux.h>
 
+#include "action_manager.h"
 #include "capabilities.h"
 #include "init.h"
 #include "property_service.h"
 #include "service.h"
+#include "sigchld_handler.h"
 
+using android::base::Split;
 using android::base::StringPrintf;
 using android::base::Timer;
 
@@ -84,13 +88,14 @@
           mnt_type_(entry.mnt_type),
           mnt_opts_(entry.mnt_opts) {}
 
-    bool Umount() {
-        int r = umount2(mnt_dir_.c_str(), 0);
+    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;
         }
@@ -134,25 +139,12 @@
 
 // Turn off backlight while we are performing power down cleanup activities.
 static void TurnOffBacklight() {
-    static constexpr char OFF[] = "0";
-
-    android::base::WriteStringToFile(OFF, "/sys/class/leds/lcd-backlight/brightness");
-
-    static const char backlightDir[] = "/sys/class/backlight";
-    std::unique_ptr<DIR, int (*)(DIR*)> dir(opendir(backlightDir), closedir);
-    if (!dir) {
+    Service* service = ServiceList::GetInstance().FindService("blank_screen");
+    if (service == nullptr) {
+        LOG(WARNING) << "cannot find blank_screen in TurnOffBacklight";
         return;
     }
-
-    struct dirent* dp;
-    while ((dp = readdir(dir.get())) != nullptr) {
-        if (((dp->d_type != DT_DIR) && (dp->d_type != DT_LNK)) || (dp->d_name[0] == '.')) {
-            continue;
-        }
-
-        std::string fileName = StringPrintf("%s/%s/brightness", backlightDir, dp->d_name);
-        android::base::WriteStringToFile(OFF, fileName);
-    }
+    service->Start();
 }
 
 static void ShutdownVold() {
@@ -167,9 +159,7 @@
                  << stat;
 }
 
-// Determines whether the system is capable of rebooting. This is conservative,
-// so if any of the attempts to determine this fail, it will still return true.
-static bool IsRebootCapable() {
+bool IsRebootCapable() {
     if (!CAP_IS_SUPPORTED(CAP_SYS_BOOT)) {
         PLOG(WARNING) << "CAP_SYS_BOOT is not supported";
         return true;
@@ -189,8 +179,7 @@
     return value == CAP_SET;
 }
 
-static void __attribute__((noreturn))
-RebootSystem(unsigned int cmd, const std::string& rebootTarget) {
+void __attribute__((noreturn)) RebootSystem(unsigned int cmd, const std::string& rebootTarget) {
     LOG(INFO) << "Reboot ending, jumping to kernel";
 
     if (!IsRebootCapable()) {
@@ -214,7 +203,7 @@
             break;
     }
     // In normal case, reboot should not return.
-    PLOG(FATAL) << "reboot call returned";
+    PLOG(ERROR) << "reboot call returned";
     abort();
 }
 
@@ -234,7 +223,13 @@
             LOG(INFO) << "mount entry " << mentry->mnt_fsname << ":" << mentry->mnt_dir << " opts "
                       << mentry->mnt_opts << " type " << mentry->mnt_type;
         } else if (MountEntry::IsBlockDevice(*mentry) && hasmntopt(mentry, "rw")) {
-            blockDevPartitions->emplace(blockDevPartitions->begin(), *mentry);
+            std::string mount_dir(mentry->mnt_dir);
+            // These are R/O partitions changed to R/W after adb remount.
+            // Do not umount them as shutdown critical services may rely on them.
+            if (mount_dir != "/" && mount_dir != "/system" && mount_dir != "/vendor" &&
+                mount_dir != "/oem") {
+                blockDevPartitions->emplace(blockDevPartitions->begin(), *mentry);
+            }
         } else if (MountEntry::IsEmulatedDevice(*mentry)) {
             emulatedPartitions->emplace(emulatedPartitions->begin(), *mentry);
         }
@@ -259,8 +254,6 @@
 
 static UmountStat UmountPartitions(std::chrono::milliseconds timeout) {
     Timer t;
-    UmountStat stat = UMOUNT_STAT_TIMEOUT;
-    int retry = 0;
     /* data partition needs all pending writes to be completed and all emulated partitions
      * umounted.If the current waiting is not good enough, give
      * up and leave it to e2fsck after reboot to fix it.
@@ -272,25 +265,28 @@
             return UMOUNT_STAT_ERROR;
         }
         if (block_devices.size() == 0) {
-            stat = UMOUNT_STAT_SUCCESS;
-            break;
+            return UMOUNT_STAT_SUCCESS;
         }
-        if ((timeout < t.duration()) && retry > 0) {  // try umount at least once
-            stat = UMOUNT_STAT_TIMEOUT;
-            break;
-        }
-        if (emulated_devices.size() > 0 &&
-            std::all_of(emulated_devices.begin(), emulated_devices.end(),
-                        [](auto& entry) { return entry.Umount(); })) {
-            sync();
+        bool unmount_done = true;
+        if (emulated_devices.size() > 0) {
+            for (auto& entry : emulated_devices) {
+                if (!entry.Umount(false)) unmount_done = false;
+            }
+            if (unmount_done) {
+                sync();
+            }
         }
         for (auto& entry : block_devices) {
-            entry.Umount();
+            if (!entry.Umount(timeout == 0ms)) unmount_done = false;
         }
-        retry++;
+        if (unmount_done) {
+            return UMOUNT_STAT_SUCCESS;
+        }
+        if ((timeout < t.duration())) {  // try umount at least once
+            return UMOUNT_STAT_TIMEOUT;
+        }
         std::this_thread::sleep_for(100ms);
     }
-    return stat;
 }
 
 static void KillAllProcesses() { android::base::WriteStringToFile("i", "/proc/sysrq-trigger"); }
@@ -309,8 +305,6 @@
     std::vector<MountEntry> block_devices;
     std::vector<MountEntry> emulated_devices;
 
-    TurnOffBacklight();  // this part can take time. save power.
-
     if (runFsck && !FindPartitionsToUmount(&block_devices, &emulated_devices, false)) {
         return UMOUNT_STAT_ERROR;
     }
@@ -318,11 +312,11 @@
     UmountStat stat = UmountPartitions(timeout - t.duration());
     if (stat != UMOUNT_STAT_SUCCESS) {
         LOG(INFO) << "umount timeout, last resort, kill all and try";
-        if (DUMP_ON_UMOUNT_FAILURE) DumpUmountDebuggingInfo(false);
+        if (DUMP_ON_UMOUNT_FAILURE) DumpUmountDebuggingInfo(true);
         KillAllProcesses();
         // even if it succeeds, still it is timeout and do not run fsck with all processes killed
-        UmountPartitions(0ms);
-        if (DUMP_ON_UMOUNT_FAILURE) DumpUmountDebuggingInfo(true);
+        UmountStat st = UmountPartitions(0ms);
+        if ((st != UMOUNT_STAT_SUCCESS) && DUMP_ON_UMOUNT_FAILURE) DumpUmountDebuggingInfo(false);
     }
 
     if (stat == UMOUNT_STAT_SUCCESS && runFsck) {
@@ -335,76 +329,93 @@
     return stat;
 }
 
-static void __attribute__((noreturn)) DoThermalOff() {
-    LOG(WARNING) << "Thermal system shutdown";
-    sync();
-    RebootSystem(ANDROID_RB_THERMOFF, "");
-    abort();
-}
-
 void DoReboot(unsigned int cmd, const std::string& reason, const std::string& rebootTarget,
               bool runFsck) {
     Timer t;
     LOG(INFO) << "Reboot start, reason: " << reason << ", rebootTarget: " << rebootTarget;
 
-    android::base::WriteStringToFile(StringPrintf("%s\n", reason.c_str()), LAST_REBOOT_REASON_FILE,
-                                     S_IRUSR | S_IWUSR, AID_SYSTEM, AID_SYSTEM);
-
-    if (cmd == ANDROID_RB_THERMOFF) {  // do not wait if it is thermal
-        DoThermalOff();
-        abort();
+    // Ensure last reboot reason is reduced to canonical
+    // alias reported in bootloader or system boot reason.
+    size_t skip = 0;
+    std::vector<std::string> reasons = Split(reason, ",");
+    if (reasons.size() >= 2 && reasons[0] == "reboot" &&
+        (reasons[1] == "recovery" || reasons[1] == "bootloader" || reasons[1] == "cold" ||
+         reasons[1] == "hard" || reasons[1] == "warm")) {
+        skip = strlen("reboot,");
     }
+    property_set(LAST_REBOOT_REASON_PROPERTY, reason.c_str() + skip);
+    sync();
 
-    auto shutdown_timeout = 0s;
+    bool is_thermal_shutdown = cmd == ANDROID_RB_THERMOFF;
+
+    auto shutdown_timeout = 0ms;
     if (!SHUTDOWN_ZERO_TIMEOUT) {
-        constexpr unsigned int shutdown_timeout_default = 6;
-        auto shutdown_timeout_property =
-            android::base::GetUintProperty("ro.build.shutdown_timeout", shutdown_timeout_default);
-        shutdown_timeout = std::chrono::seconds(shutdown_timeout_property);
+        if (is_thermal_shutdown) {
+            constexpr unsigned int thermal_shutdown_timeout = 1;
+            shutdown_timeout = std::chrono::seconds(thermal_shutdown_timeout);
+        } else {
+            constexpr unsigned int shutdown_timeout_default = 6;
+            auto shutdown_timeout_property = android::base::GetUintProperty(
+                "ro.build.shutdown_timeout", shutdown_timeout_default);
+            shutdown_timeout = std::chrono::seconds(shutdown_timeout_property);
+        }
     }
-    LOG(INFO) << "Shutdown timeout: " << shutdown_timeout.count() << " seconds";
+    LOG(INFO) << "Shutdown timeout: " << shutdown_timeout.count() << " ms";
 
     // keep debugging tools until non critical ones are all gone.
     const std::set<std::string> kill_after_apps{"tombstoned", "logd", "adbd"};
     // watchdogd is a vendor specific component but should be alive to complete shutdown safely.
     const std::set<std::string> to_starts{"watchdogd"};
-    ServiceManager::GetInstance().ForEachService([&kill_after_apps, &to_starts](Service* s) {
+    for (const auto& s : ServiceList::GetInstance()) {
         if (kill_after_apps.count(s->name())) {
             s->SetShutdownCritical();
         } else if (to_starts.count(s->name())) {
-            s->Start();
+            if (auto result = s->Start(); !result) {
+                LOG(ERROR) << "Could not start shutdown 'to_start' service '" << s->name()
+                           << "': " << result.error();
+            }
             s->SetShutdownCritical();
         } else if (s->IsShutdownCritical()) {
-            s->Start();  // start shutdown critical service if not started
+            // Start shutdown critical service if not started.
+            if (auto result = s->Start(); !result) {
+                LOG(ERROR) << "Could not start shutdown critical service '" << s->name()
+                           << "': " << result.error();
+            }
         }
-    });
+    }
 
-    Service* bootAnim = ServiceManager::GetInstance().FindServiceByName("bootanim");
-    Service* surfaceFlinger = ServiceManager::GetInstance().FindServiceByName("surfaceflinger");
+    // remaining operations (specifically fsck) may take a substantial duration
+    if (cmd == ANDROID_RB_POWEROFF || is_thermal_shutdown) {
+        TurnOffBacklight();
+    }
+
+    Service* bootAnim = ServiceList::GetInstance().FindService("bootanim");
+    Service* surfaceFlinger = ServiceList::GetInstance().FindService("surfaceflinger");
     if (bootAnim != nullptr && surfaceFlinger != nullptr && surfaceFlinger->IsRunning()) {
-        ServiceManager::GetInstance().ForEachServiceInClass("animation", [](Service* s) {
-            s->SetShutdownCritical();  // will not check animation class separately
-        });
+        // will not check animation class separately
+        for (const auto& service : ServiceList::GetInstance()) {
+            if (service->classnames().count("animation")) service->SetShutdownCritical();
+        }
     }
 
     // optional shutdown step
     // 1. terminate all services except shutdown critical ones. wait for delay to finish
-    if (shutdown_timeout > 0s) {
+    if (shutdown_timeout > 0ms) {
         LOG(INFO) << "terminating init services";
 
         // Ask all services to terminate except shutdown critical ones.
-        ServiceManager::GetInstance().ForEachService([](Service* s) {
+        for (const auto& s : ServiceList::GetInstance().services_in_shutdown_order()) {
             if (!s->IsShutdownCritical()) s->Terminate();
-        });
+        }
 
         int service_count = 0;
-        // Up to half as long as shutdown_timeout or 3 seconds, whichever is lower.
-        auto termination_wait_timeout = std::min((shutdown_timeout + 1s) / 2, 3s);
+        // Only wait up to half of timeout here
+        auto termination_wait_timeout = shutdown_timeout / 2;
         while (t.duration() < termination_wait_timeout) {
-            ServiceManager::GetInstance().ReapAnyOutstandingChildren();
+            ReapAnyOutstandingChildren();
 
             service_count = 0;
-            ServiceManager::GetInstance().ForEachService([&service_count](Service* s) {
+            for (const auto& s : ServiceList::GetInstance()) {
                 // Count the number of services running except shutdown critical.
                 // Exclude the console as it will ignore the SIGTERM signal
                 // and not exit.
@@ -413,7 +424,7 @@
                 if (!s->IsShutdownCritical() && s->pid() != 0 && (s->flags() & SVC_CONSOLE) == 0) {
                     service_count++;
                 }
-            });
+            }
 
             if (service_count == 0) {
                 // All terminable services terminated. We can exit early.
@@ -429,13 +440,14 @@
 
     // minimum safety steps before restarting
     // 2. kill all services except ones that are necessary for the shutdown sequence.
-    ServiceManager::GetInstance().ForEachService([](Service* s) {
+    for (const auto& s : ServiceList::GetInstance().services_in_shutdown_order()) {
         if (!s->IsShutdownCritical()) s->Stop();
-    });
-    ServiceManager::GetInstance().ReapAnyOutstandingChildren();
+    }
+    SubcontextTerminate();
+    ReapAnyOutstandingChildren();
 
     // 3. send volume shutdown to vold
-    Service* voldService = ServiceManager::GetInstance().FindServiceByName("vold");
+    Service* voldService = ServiceList::GetInstance().FindService("vold");
     if (voldService != nullptr && voldService->IsRunning()) {
         ShutdownVold();
         voldService->Stop();
@@ -443,15 +455,25 @@
         LOG(INFO) << "vold not running, skipping vold shutdown";
     }
     // logcat stopped here
-    ServiceManager::GetInstance().ForEachService([&kill_after_apps](Service* s) {
+    for (const auto& s : ServiceList::GetInstance().services_in_shutdown_order()) {
         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();
-    std::this_thread::sleep_for(100ms);
+    {
+        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.
     RebootSystem(cmd, rebootTarget);
@@ -460,7 +482,7 @@
 
 bool HandlePowerctlMessage(const std::string& command) {
     unsigned int cmd = 0;
-    std::vector<std::string> cmd_params = android::base::Split(command, ",");
+    std::vector<std::string> cmd_params = Split(command, ",");
     std::string reboot_target = "";
     bool run_fsck = false;
     bool command_invalid = false;
@@ -469,10 +491,17 @@
         command_invalid = true;
     } else if (cmd_params[0] == "shutdown") {
         cmd = ANDROID_RB_POWEROFF;
-        if (cmd_params.size() == 2 && cmd_params[1] == "userrequested") {
-            // The shutdown reason is PowerManager.SHUTDOWN_USER_REQUESTED.
-            // Run fsck once the file system is remounted in read-only mode.
-            run_fsck = true;
+        if (cmd_params.size() == 2) {
+            if (cmd_params[1] == "userrequested") {
+                // The shutdown reason is PowerManager.SHUTDOWN_USER_REQUESTED.
+                // Run fsck once the file system is remounted in read-only mode.
+                run_fsck = true;
+            } else if (cmd_params[1] == "thermal") {
+                // Turn off sources of heat immediately.
+                TurnOffBacklight();
+                // run_fsck is false to avoid delay
+                cmd = ANDROID_RB_THERMOFF;
+            }
         }
     } else if (cmd_params[0] == "reboot") {
         cmd = ANDROID_RB_RESTART2;
@@ -488,16 +517,11 @@
                                << err;
                 }
             }
-            // If there is an additional bootloader parameter, pass it along
-            if (cmd_params.size() == 3) {
+            // If there is an additional parameter, pass it along
+            if ((cmd_params.size() == 3) && cmd_params[2].size()) {
                 reboot_target += "," + cmd_params[2];
             }
         }
-    } else if (command == "thermal-shutdown") {  // no additional parameter allowed
-        cmd = ANDROID_RB_THERMOFF;
-        // Do not queue "shutdown" trigger since we want to shutdown immediately
-        DoReboot(cmd, command, reboot_target, run_fsck);
-        return true;
     } else {
         command_invalid = true;
     }
@@ -511,19 +535,18 @@
     // Queue shutdown trigger first
     ActionManager::GetInstance().QueueEventTrigger("shutdown");
     // Queue built-in shutdown_done
-    auto shutdown_handler = [cmd, command, reboot_target,
-                             run_fsck](const std::vector<std::string>&) {
+    auto shutdown_handler = [cmd, command, reboot_target, run_fsck](const BuiltinArguments&) {
         DoReboot(cmd, command, reboot_target, run_fsck);
-        return 0;
+        return Success();
     };
     ActionManager::GetInstance().QueueBuiltinAction(shutdown_handler, "shutdown_done");
 
     // Skip wait for prop if it is in progress
     ResetWaitForProp();
 
-    // Skip wait for exec if it is in progress
-    if (ServiceManager::GetInstance().IsWaitingForExec()) {
-        ServiceManager::GetInstance().ClearExecWait();
+    // Clear EXEC flag if there is one pending
+    for (const auto& s : ServiceList::GetInstance()) {
+        s->UnSetExec();
     }
 
     return true;
diff --git a/init/reboot.h b/init/reboot.h
index e559540..1c58bd1 100644
--- a/init/reboot.h
+++ b/init/reboot.h
@@ -22,9 +22,12 @@
 namespace android {
 namespace init {
 
+// This is a wrapper around the actual reboot calls.  DoReboot() should be preferred in most cases.
+void __attribute__((noreturn)) RebootSystem(unsigned int cmd, const std::string& rebootTarget);
+
 /* Reboot / shutdown the system.
  * cmd ANDROID_RB_* as defined in android_reboot.h
- * reason Reason string like "reboot", "userrequested"
+ * reason Reason string like "reboot", "shutdown,userrequested"
  * rebootTarget Reboot target string like "bootloader". Otherwise, it should be an
  *              empty string.
  * runFsck Whether to run fsck after umount is done.
@@ -35,6 +38,10 @@
 // Parses and handles a setprop sys.powerctl message.
 bool HandlePowerctlMessage(const std::string& command);
 
+// Determines whether the system is capable of rebooting. This is conservative,
+// so if any of the attempts to determine this fail, it will still return true.
+bool IsRebootCapable();
+
 }  // namespace init
 }  // namespace android
 
diff --git a/init/result.h b/init/result.h
new file mode 100644
index 0000000..fc03962
--- /dev/null
+++ b/init/result.h
@@ -0,0 +1,211 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+// This file contains classes for returning a successful result along with an optional
+// arbitrarily typed return value or for returning a failure result along with an optional string
+// indicating why the function failed.
+
+// There are 3 classes that implement this functionality and one additional helper type.
+//
+// Result<T> either contains a member of type T that can be accessed using similar semantics as
+// std::optional<T> or it contains a ResultError describing an error, which can be accessed via
+// Result<T>::error().
+//
+// ResultError is a type that contains both a std::string describing the error and a copy of errno
+// from when the error occurred.  ResultError can be used in an ostream directly to print its
+// string value.
+//
+// Success is a typedef that aids in creating Result<T> that do not contain a return value.
+// Result<Success> is the correct return type for a function that either returns successfully or
+// returns an error value.  Returning Success() from a function that returns Result<Success> is the
+// correct way to indicate that a function without a return type has completed successfully.
+//
+// A successful Result<T> is constructed implicitly from any type that can be implicitly converted
+// to T or from the constructor arguments for T.  This allows you to return a type T directly from
+// a function that returns Result<T>.
+//
+// Error and ErrnoError are used to construct a Result<T> that has failed.  The Error class takes
+// an ostream as an input and are implicitly cast to a Result<T> containing that failure.
+// ErrnoError() is a helper function to create an Error class that appends ": " + strerror(errno)
+// to the end of the failure string to aid in interacting with C APIs.  Alternatively, an errno
+// value can be directly specified via the Error() constructor.
+//
+// ResultError can be used in the ostream when using Error to construct a Result<T>.  In this case,
+// the string that the ResultError takes is passed through the stream normally, but the errno is
+// passed to the Result<T>.  This can be used to pass errno from a failing C function up multiple
+// callers.
+//
+// ResultError can also directly construct a Result<T>.  This is particularly useful if you have a
+// function that return Result<T> but you have a Result<U> and want to return its error.  In this
+// case, you can return the .error() from the Result<U> to construct the Result<T>.
+
+// An example of how to use these is below:
+// Result<U> CalculateResult(const T& input) {
+//   U output;
+//   if (!SomeOtherCppFunction(input, &output)) {
+//     return Error() << "SomeOtherCppFunction(" << input << ") failed";
+//   }
+//   if (!c_api_function(output)) {
+//     return ErrnoError() << "c_api_function(" << output << ") failed";
+//   }
+//   return output;
+// }
+//
+// auto output = CalculateResult(input);
+// if (!output) return Error() << "CalculateResult failed: " << output.error();
+// UseOutput(*output);
+
+#ifndef _INIT_RESULT_H
+#define _INIT_RESULT_H
+
+#include <errno.h>
+
+#include <sstream>
+#include <string>
+#include <variant>
+
+namespace android {
+namespace init {
+
+struct ResultError {
+    template <typename T>
+    ResultError(T&& error_string, int error_errno)
+        : error_string(std::forward<T>(error_string)), error_errno(error_errno) {}
+
+    std::string error_string;
+    int error_errno;
+};
+
+inline std::ostream& operator<<(std::ostream& os, const ResultError& t) {
+    os << t.error_string;
+    return os;
+}
+
+inline std::ostream& operator<<(std::ostream& os, ResultError&& t) {
+    os << std::move(t.error_string);
+    return os;
+}
+
+class Error {
+  public:
+    Error() : errno_(0), append_errno_(false) {}
+    Error(int errno_to_append) : errno_(errno_to_append), append_errno_(true) {}
+
+    template <typename T>
+    Error&& operator<<(T&& t) {
+        ss_ << std::forward<T>(t);
+        return std::move(*this);
+    }
+
+    Error&& operator<<(const ResultError& result_error) {
+        ss_ << result_error.error_string;
+        errno_ = result_error.error_errno;
+        return std::move(*this);
+    }
+
+    Error&& operator<<(ResultError&& result_error) {
+        ss_ << std::move(result_error.error_string);
+        errno_ = result_error.error_errno;
+        return std::move(*this);
+    }
+
+    const std::string str() const {
+        std::string str = ss_.str();
+        if (append_errno_) {
+            if (str.empty()) {
+                return strerror(errno_);
+            }
+            return str + ": " + strerror(errno_);
+        }
+        return str;
+    }
+
+    int get_errno() const { return errno_; }
+
+    Error(const Error&) = delete;
+    Error(Error&&) = delete;
+    Error& operator=(const Error&) = delete;
+    Error& operator=(Error&&) = delete;
+
+  private:
+    std::stringstream ss_;
+    int errno_;
+    bool append_errno_;
+};
+
+inline Error ErrnoError() {
+    return Error(errno);
+}
+
+template <typename T>
+class Result {
+  public:
+    Result() {}
+
+    template <typename U, typename... V,
+              typename = std::enable_if_t<!(std::is_same_v<std::decay_t<U>, Result<T>> &&
+                                            sizeof...(V) == 0)>>
+    Result(U&& result, V&&... results)
+        : contents_(std::in_place_index_t<0>(), std::forward<U>(result),
+                    std::forward<V>(results)...) {}
+
+    Result(Error&& error) : contents_(std::in_place_index_t<1>(), error.str(), error.get_errno()) {}
+    Result(const ResultError& result_error)
+        : contents_(std::in_place_index_t<1>(), result_error.error_string,
+                    result_error.error_errno) {}
+    Result(ResultError&& result_error)
+        : contents_(std::in_place_index_t<1>(), std::move(result_error.error_string),
+                    result_error.error_errno) {}
+
+    bool has_value() const { return contents_.index() == 0; }
+
+    T& value() & { return std::get<0>(contents_); }
+    const T& value() const & { return std::get<0>(contents_); }
+    T&& value() && { return std::get<0>(std::move(contents_)); }
+    const T&& value() const && { return std::get<0>(std::move(contents_)); }
+
+    const ResultError& error() const & { return std::get<1>(contents_); }
+    ResultError&& error() && { return std::get<1>(std::move(contents_)); }
+    const ResultError&& error() const && { return std::get<1>(std::move(contents_)); }
+
+    const std::string& error_string() const & { return std::get<1>(contents_).error_string; }
+    std::string&& error_string() && { return std::get<1>(std::move(contents_)).error_string; }
+    const std::string&& error_string() const && {
+        return std::get<1>(std::move(contents_)).error_string;
+    }
+
+    int error_errno() const { return std::get<1>(contents_).error_errno; }
+
+    explicit operator bool() const { return has_value(); }
+
+    T& operator*() & { return value(); }
+    const T& operator*() const & { return value(); }
+    T&& operator*() && { return std::move(value()); }
+    const T&& operator*() const && { return std::move(value()); }
+
+    T* operator->() { return &value(); }
+    const T* operator->() const { return &value(); }
+
+  private:
+    std::variant<T, ResultError> contents_;
+};
+
+using Success = std::monostate;
+
+}  // namespace init
+}  // namespace android
+
+#endif
diff --git a/init/result_test.cpp b/init/result_test.cpp
new file mode 100644
index 0000000..327b444
--- /dev/null
+++ b/init/result_test.cpp
@@ -0,0 +1,333 @@
+/*
+ * Copyright (C) 2017 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 "result.h"
+
+#include "errno.h"
+
+#include <string>
+
+#include <gtest/gtest.h>
+
+using namespace std::string_literals;
+
+namespace android {
+namespace init {
+
+TEST(result, result_accessors) {
+    Result<std::string> result = "success";
+    ASSERT_TRUE(result);
+    ASSERT_TRUE(result.has_value());
+
+    EXPECT_EQ("success", *result);
+    EXPECT_EQ("success", result.value());
+
+    EXPECT_EQ('s', result->data()[0]);
+}
+
+TEST(result, result_accessors_rvalue) {
+    ASSERT_TRUE(Result<std::string>("success"));
+    ASSERT_TRUE(Result<std::string>("success").has_value());
+
+    EXPECT_EQ("success", *Result<std::string>("success"));
+    EXPECT_EQ("success", Result<std::string>("success").value());
+
+    EXPECT_EQ('s', Result<std::string>("success")->data()[0]);
+}
+
+TEST(result, result_success) {
+    Result<Success> result = Success();
+    ASSERT_TRUE(result);
+    ASSERT_TRUE(result.has_value());
+
+    EXPECT_EQ(Success(), *result);
+    EXPECT_EQ(Success(), result.value());
+}
+
+TEST(result, result_success_rvalue) {
+    // Success() doesn't actually create a Result<Success> object, but rather an object that can be
+    // implicitly constructed into a Result<Success> object.
+
+    auto MakeRvalueSuccessResult = []() -> Result<Success> { return Success(); };
+    ASSERT_TRUE(MakeRvalueSuccessResult());
+    ASSERT_TRUE(MakeRvalueSuccessResult().has_value());
+
+    EXPECT_EQ(Success(), *MakeRvalueSuccessResult());
+    EXPECT_EQ(Success(), MakeRvalueSuccessResult().value());
+}
+
+TEST(result, result_error) {
+    Result<Success> result = Error() << "failure" << 1;
+    ASSERT_FALSE(result);
+    ASSERT_FALSE(result.has_value());
+
+    EXPECT_EQ(0, result.error_errno());
+    EXPECT_EQ("failure1", result.error_string());
+}
+
+TEST(result, result_error_empty) {
+    Result<Success> result = Error();
+    ASSERT_FALSE(result);
+    ASSERT_FALSE(result.has_value());
+
+    EXPECT_EQ(0, result.error_errno());
+    EXPECT_EQ("", result.error_string());
+}
+
+TEST(result, result_error_rvalue) {
+    // Error() and ErrnoError() aren't actually used to create a Result<T> object.
+    // Under the hood, they are an intermediate class that can be implicitly constructed into a
+    // Result<T>.  This is needed both to create the ostream and because Error() itself, by
+    // definition will not know what the type, T, of the underlying Result<T> object that it would
+    // create is.
+
+    auto MakeRvalueErrorResult = []() -> Result<Success> { return Error() << "failure" << 1; };
+    ASSERT_FALSE(MakeRvalueErrorResult());
+    ASSERT_FALSE(MakeRvalueErrorResult().has_value());
+
+    EXPECT_EQ(0, MakeRvalueErrorResult().error_errno());
+    EXPECT_EQ("failure1", MakeRvalueErrorResult().error_string());
+}
+
+TEST(result, result_errno_error) {
+    constexpr int test_errno = 6;
+    errno = test_errno;
+    Result<Success> result = ErrnoError() << "failure" << 1;
+
+    ASSERT_FALSE(result);
+    ASSERT_FALSE(result.has_value());
+
+    EXPECT_EQ(test_errno, result.error_errno());
+    EXPECT_EQ("failure1: "s + strerror(test_errno), result.error_string());
+}
+
+TEST(result, result_errno_error_no_text) {
+    constexpr int test_errno = 6;
+    errno = test_errno;
+    Result<Success> result = ErrnoError();
+
+    ASSERT_FALSE(result);
+    ASSERT_FALSE(result.has_value());
+
+    EXPECT_EQ(test_errno, result.error_errno());
+    EXPECT_EQ(strerror(test_errno), result.error_string());
+}
+
+TEST(result, result_error_from_other_result) {
+    auto error_text = "test error"s;
+    Result<Success> result = Error() << error_text;
+
+    ASSERT_FALSE(result);
+    ASSERT_FALSE(result.has_value());
+
+    Result<std::string> result2 = result.error();
+
+    ASSERT_FALSE(result2);
+    ASSERT_FALSE(result2.has_value());
+
+    EXPECT_EQ(0, result.error_errno());
+    EXPECT_EQ(error_text, result.error_string());
+}
+
+TEST(result, result_error_through_ostream) {
+    auto error_text = "test error"s;
+    Result<Success> result = Error() << error_text;
+
+    ASSERT_FALSE(result);
+    ASSERT_FALSE(result.has_value());
+
+    Result<std::string> result2 = Error() << result.error();
+
+    ASSERT_FALSE(result2);
+    ASSERT_FALSE(result2.has_value());
+
+    EXPECT_EQ(0, result.error_errno());
+    EXPECT_EQ(error_text, result.error_string());
+}
+
+TEST(result, result_errno_error_through_ostream) {
+    auto error_text = "test error"s;
+    constexpr int test_errno = 6;
+    errno = 6;
+    Result<Success> result = ErrnoError() << error_text;
+
+    errno = 0;
+
+    ASSERT_FALSE(result);
+    ASSERT_FALSE(result.has_value());
+
+    Result<std::string> result2 = Error() << result.error();
+
+    ASSERT_FALSE(result2);
+    ASSERT_FALSE(result2.has_value());
+
+    EXPECT_EQ(test_errno, result.error_errno());
+    EXPECT_EQ(error_text + ": " + strerror(test_errno), result.error_string());
+}
+
+TEST(result, constructor_forwarding) {
+    auto result = Result<std::string>(5, 'a');
+
+    ASSERT_TRUE(result);
+    ASSERT_TRUE(result.has_value());
+
+    EXPECT_EQ("aaaaa", *result);
+}
+
+struct ConstructorTracker {
+    static size_t constructor_called;
+    static size_t copy_constructor_called;
+    static size_t move_constructor_called;
+    static size_t copy_assignment_called;
+    static size_t move_assignment_called;
+
+    template <typename T>
+    ConstructorTracker(T&& string) : string(string) {
+        ++constructor_called;
+    }
+
+    ConstructorTracker(const ConstructorTracker& ct) {
+        ++copy_constructor_called;
+        string = ct.string;
+    }
+    ConstructorTracker(ConstructorTracker&& ct) noexcept {
+        ++move_constructor_called;
+        string = std::move(ct.string);
+    }
+    ConstructorTracker& operator=(const ConstructorTracker& ct) {
+        ++copy_assignment_called;
+        string = ct.string;
+        return *this;
+    }
+    ConstructorTracker& operator=(ConstructorTracker&& ct) noexcept {
+        ++move_assignment_called;
+        string = std::move(ct.string);
+        return *this;
+    }
+
+    std::string string;
+};
+
+size_t ConstructorTracker::constructor_called = 0;
+size_t ConstructorTracker::copy_constructor_called = 0;
+size_t ConstructorTracker::move_constructor_called = 0;
+size_t ConstructorTracker::copy_assignment_called = 0;
+size_t ConstructorTracker::move_assignment_called = 0;
+
+Result<ConstructorTracker> ReturnConstructorTracker(const std::string& in) {
+    if (in.empty()) {
+        return "literal string";
+    }
+    if (in == "test2") {
+        return ConstructorTracker(in + in + "2");
+    }
+    ConstructorTracker result(in + " " + in);
+    return result;
+};
+
+TEST(result, no_copy_on_return) {
+    // If returning parameters that may be used to implicitly construct the type T of Result<T>,
+    // then those parameters are forwarded to the construction of Result<T>.
+
+    // If returning an prvalue or xvalue, it will be move constructed during the construction of
+    // Result<T>.
+
+    // This check ensures that that is the case, and particularly that no copy constructors
+    // are called.
+
+    auto result1 = ReturnConstructorTracker("");
+    ASSERT_TRUE(result1);
+    EXPECT_EQ("literal string", result1->string);
+    EXPECT_EQ(1U, ConstructorTracker::constructor_called);
+    EXPECT_EQ(0U, ConstructorTracker::copy_constructor_called);
+    EXPECT_EQ(0U, ConstructorTracker::move_constructor_called);
+    EXPECT_EQ(0U, ConstructorTracker::copy_assignment_called);
+    EXPECT_EQ(0U, ConstructorTracker::move_assignment_called);
+
+    auto result2 = ReturnConstructorTracker("test2");
+    ASSERT_TRUE(result2);
+    EXPECT_EQ("test2test22", result2->string);
+    EXPECT_EQ(2U, ConstructorTracker::constructor_called);
+    EXPECT_EQ(0U, ConstructorTracker::copy_constructor_called);
+    EXPECT_EQ(1U, ConstructorTracker::move_constructor_called);
+    EXPECT_EQ(0U, ConstructorTracker::copy_assignment_called);
+    EXPECT_EQ(0U, ConstructorTracker::move_assignment_called);
+
+    auto result3 = ReturnConstructorTracker("test3");
+    ASSERT_TRUE(result3);
+    EXPECT_EQ("test3 test3", result3->string);
+    EXPECT_EQ(3U, ConstructorTracker::constructor_called);
+    EXPECT_EQ(0U, ConstructorTracker::copy_constructor_called);
+    EXPECT_EQ(2U, ConstructorTracker::move_constructor_called);
+    EXPECT_EQ(0U, ConstructorTracker::copy_assignment_called);
+    EXPECT_EQ(0U, ConstructorTracker::move_assignment_called);
+}
+
+// Below two tests require that we do not hide the move constructor with our forwarding reference
+// constructor.  This is done with by disabling the forwarding reference constructor if its first
+// and only type is Result<T>.
+TEST(result, result_result_with_success) {
+    auto return_result_result_with_success = []() -> Result<Result<Success>> {
+        return Result<Success>();
+    };
+    auto result = return_result_result_with_success();
+    ASSERT_TRUE(result);
+    ASSERT_TRUE(*result);
+
+    auto inner_result = result.value();
+    ASSERT_TRUE(inner_result);
+}
+
+TEST(result, result_result_with_failure) {
+    auto return_result_result_with_error = []() -> Result<Result<Success>> {
+        return Result<Success>(ResultError("failure string", 6));
+    };
+    auto result = return_result_result_with_error();
+    ASSERT_TRUE(result);
+    ASSERT_FALSE(*result);
+    EXPECT_EQ("failure string", result->error_string());
+    EXPECT_EQ(6, result->error_errno());
+}
+
+// This test requires that we disable the forwarding reference constructor if Result<T> is the
+// *only* type that we are forwarding.  In otherwords, if we are forwarding Result<T>, int to
+// construct a Result<T>, then we still need the constructor.
+TEST(result, result_two_parameter_constructor_same_type) {
+    struct TestStruct {
+        TestStruct(int value) : value_(value) {}
+        TestStruct(Result<TestStruct> result, int value) : value_(result->value_ * value) {}
+        int value_;
+    };
+
+    auto return_test_struct = []() -> Result<TestStruct> { return {Result<TestStruct>(6), 6}; };
+
+    auto result = return_test_struct();
+    ASSERT_TRUE(result);
+    EXPECT_EQ(36, result->value_);
+}
+
+TEST(result, die_on_access_failed_result) {
+    Result<std::string> result = Error();
+    ASSERT_DEATH(*result, "");
+}
+
+TEST(result, die_on_get_error_succesful_result) {
+    Result<std::string> result = "success";
+    ASSERT_DEATH(result.error_string(), "");
+}
+
+}  // namespace init
+}  // namespace android
diff --git a/init/rlimit_parser.cpp b/init/rlimit_parser.cpp
new file mode 100644
index 0000000..fe1d6a7
--- /dev/null
+++ b/init/rlimit_parser.cpp
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2017 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 "rlimit_parser.h"
+
+#include <android-base/parseint.h>
+#include <android-base/strings.h>
+
+using android::base::EqualsIgnoreCase;
+using android::base::ParseInt;
+using android::base::ParseUint;
+using android::base::StartsWith;
+
+namespace android {
+namespace init {
+
+// Builtins and service definitions both have their arguments start at 1 and finish at 3.
+Result<std::pair<int, rlimit>> ParseRlimit(const std::vector<std::string>& args) {
+    static const std::vector<std::pair<const char*, int>> text_to_resources = {
+        {"cpu", 0},       {"fsize", 1}, {"data", 2},    {"stack", 3},
+        {"core", 4},      {"rss", 5},   {"nproc", 6},   {"nofile", 7},
+        {"memlock", 8},   {"as", 9},    {"locks", 10},  {"sigpending", 11},
+        {"msgqueue", 12}, {"nice", 13}, {"rtprio", 14}, {"rttime", 15},
+    };
+
+    int resource;
+
+    if (ParseInt(args[1], &resource)) {
+        if (resource >= RLIM_NLIMITS) {
+            return Error() << "Resource '" << args[1] << "' over the maximum resource value '"
+                           << RLIM_NLIMITS << "'";
+        } else if (resource < 0) {
+            return Error() << "Resource '" << args[1] << "' below the minimum resource value '0'";
+        }
+    } else {
+        std::string resource_string;
+        if (StartsWith(args[1], "RLIM_")) {
+            resource_string = args[1].substr(5);
+        } else {
+            resource_string = args[1];
+        }
+
+        auto it = std::find_if(text_to_resources.begin(), text_to_resources.end(),
+                               [&resource_string](const auto& entry) {
+                                   return EqualsIgnoreCase(resource_string, entry.first);
+                               });
+        if (it == text_to_resources.end()) {
+            return Error() << "Could not parse resource '" << args[1] << "'";
+        }
+
+        resource = it->second;
+    }
+
+    rlimit limit;
+    if (!ParseUint(args[2], &limit.rlim_cur)) {
+        return Error() << "Could not parse soft limit '" << args[2] << "'";
+    }
+    if (!ParseUint(args[3], &limit.rlim_max)) {
+        return Error() << "Could not parse hard limit '" << args[3] << "'";
+    }
+    return {resource, limit};
+}
+
+}  // namespace init
+}  // namespace android
diff --git a/init/rlimit_parser.h b/init/rlimit_parser.h
new file mode 100644
index 0000000..0396463
--- /dev/null
+++ b/init/rlimit_parser.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2017 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_RLIMIT_PARSER_H
+#define _INIT_RLIMIT_PARSER_H
+
+#include <sys/resource.h>
+
+#include <string>
+#include <vector>
+
+#include "result.h"
+
+namespace android {
+namespace init {
+
+Result<std::pair<int, rlimit>> ParseRlimit(const std::vector<std::string>& args);
+
+}  // namespace init
+}  // namespace android
+
+#endif
diff --git a/init/rlimit_parser_test.cpp b/init/rlimit_parser_test.cpp
new file mode 100644
index 0000000..f3f9eb4
--- /dev/null
+++ b/init/rlimit_parser_test.cpp
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2017 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 "rlimit_parser.h"
+
+#include <iostream>
+
+#include <gtest/gtest.h>
+
+namespace android {
+namespace init {
+
+void TestRlimitSuccess(std::vector<std::string> input,
+                       const std::pair<int, rlimit>& expected_result) {
+    input.emplace(input.begin(), "");
+    ASSERT_EQ(4U, input.size());
+    auto result = ParseRlimit(input);
+
+    ASSERT_TRUE(result) << "input: " << input[1];
+    const auto& [resource, rlimit] = *result;
+    const auto& [expected_resource, expected_rlimit] = expected_result;
+    EXPECT_EQ(expected_resource, resource);
+    EXPECT_EQ(expected_rlimit.rlim_cur, rlimit.rlim_cur);
+    EXPECT_EQ(expected_rlimit.rlim_max, rlimit.rlim_max);
+}
+
+void TestRlimitFailure(std::vector<std::string> input, const std::string& expected_result) {
+    input.emplace(input.begin(), "");
+    ASSERT_EQ(4U, input.size());
+    auto result = ParseRlimit(input);
+
+    ASSERT_FALSE(result) << "input: " << input[1];
+    EXPECT_EQ(expected_result, result.error_string());
+    EXPECT_EQ(0, result.error_errno());
+}
+
+TEST(rlimit, RlimitSuccess) {
+    const std::vector<std::pair<std::vector<std::string>, std::pair<int, rlimit>>>
+        inputs_and_results = {
+            {{"cpu", "10", "10"}, {0, {10, 10}}},
+            {{"fsize", "10", "10"}, {1, {10, 10}}},
+            {{"data", "10", "10"}, {2, {10, 10}}},
+            {{"stack", "10", "10"}, {3, {10, 10}}},
+            {{"core", "10", "10"}, {4, {10, 10}}},
+            {{"rss", "10", "10"}, {5, {10, 10}}},
+            {{"nproc", "10", "10"}, {6, {10, 10}}},
+            {{"nofile", "10", "10"}, {7, {10, 10}}},
+            {{"memlock", "10", "10"}, {8, {10, 10}}},
+            {{"as", "10", "10"}, {9, {10, 10}}},
+            {{"locks", "10", "10"}, {10, {10, 10}}},
+            {{"sigpending", "10", "10"}, {11, {10, 10}}},
+            {{"msgqueue", "10", "10"}, {12, {10, 10}}},
+            {{"nice", "10", "10"}, {13, {10, 10}}},
+            {{"rtprio", "10", "10"}, {14, {10, 10}}},
+            {{"rttime", "10", "10"}, {15, {10, 10}}},
+
+            {{"RLIM_CPU", "10", "10"}, {0, {10, 10}}},
+            {{"RLIM_FSIZE", "10", "10"}, {1, {10, 10}}},
+            {{"RLIM_DATA", "10", "10"}, {2, {10, 10}}},
+            {{"RLIM_STACK", "10", "10"}, {3, {10, 10}}},
+            {{"RLIM_CORE", "10", "10"}, {4, {10, 10}}},
+            {{"RLIM_RSS", "10", "10"}, {5, {10, 10}}},
+            {{"RLIM_NPROC", "10", "10"}, {6, {10, 10}}},
+            {{"RLIM_NOFILE", "10", "10"}, {7, {10, 10}}},
+            {{"RLIM_MEMLOCK", "10", "10"}, {8, {10, 10}}},
+            {{"RLIM_AS", "10", "10"}, {9, {10, 10}}},
+            {{"RLIM_LOCKS", "10", "10"}, {10, {10, 10}}},
+            {{"RLIM_SIGPENDING", "10", "10"}, {11, {10, 10}}},
+            {{"RLIM_MSGQUEUE", "10", "10"}, {12, {10, 10}}},
+            {{"RLIM_NICE", "10", "10"}, {13, {10, 10}}},
+            {{"RLIM_RTPRIO", "10", "10"}, {14, {10, 10}}},
+            {{"RLIM_RTTIME", "10", "10"}, {15, {10, 10}}},
+
+            {{"0", "10", "10"}, {0, {10, 10}}},
+            {{"1", "10", "10"}, {1, {10, 10}}},
+            {{"2", "10", "10"}, {2, {10, 10}}},
+            {{"3", "10", "10"}, {3, {10, 10}}},
+            {{"4", "10", "10"}, {4, {10, 10}}},
+            {{"5", "10", "10"}, {5, {10, 10}}},
+            {{"6", "10", "10"}, {6, {10, 10}}},
+            {{"7", "10", "10"}, {7, {10, 10}}},
+            {{"8", "10", "10"}, {8, {10, 10}}},
+            {{"9", "10", "10"}, {9, {10, 10}}},
+            {{"10", "10", "10"}, {10, {10, 10}}},
+            {{"11", "10", "10"}, {11, {10, 10}}},
+            {{"12", "10", "10"}, {12, {10, 10}}},
+            {{"13", "10", "10"}, {13, {10, 10}}},
+            {{"14", "10", "10"}, {14, {10, 10}}},
+            {{"15", "10", "10"}, {15, {10, 10}}},
+        };
+
+    for (const auto& [input, expected_result] : inputs_and_results) {
+        TestRlimitSuccess(input, expected_result);
+    }
+}
+
+TEST(rlimit, RlimitFailure) {
+    const std::vector<std::pair<std::vector<std::string>, std::string>> inputs_and_results = {
+        {{"-4", "10", "10"}, "Resource '-4' below the minimum resource value '0'"},
+        {{"100", "10", "10"}, "Resource '100' over the maximum resource value '16'"},
+        {{"bad_string", "10", "10"}, "Could not parse resource 'bad_string'"},
+        {{"RLIM_", "10", "10"}, "Could not parse resource 'RLIM_'"},
+        {{"cpu", "abc", "10"}, "Could not parse soft limit 'abc'"},
+        {{"cpu", "10", "abc"}, "Could not parse hard limit 'abc'"},
+    };
+
+    for (const auto& [input, expected_result] : inputs_and_results) {
+        TestRlimitFailure(input, expected_result);
+    }
+}
+
+}  // namespace init
+}  // namespace android
diff --git a/init/security.cpp b/init/security.cpp
new file mode 100644
index 0000000..a3494a2
--- /dev/null
+++ b/init/security.cpp
@@ -0,0 +1,201 @@
+/*
+ * Copyright (C) 2017 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 "security.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include <fstream>
+
+#include <android-base/logging.h>
+#include <android-base/unique_fd.h>
+
+using android::base::unique_fd;
+
+namespace android {
+namespace init {
+
+// Writes 512 bytes of output from Hardware RNG (/dev/hw_random, backed
+// by Linux kernel's hw_random framework) into Linux RNG's via /dev/urandom.
+// Does nothing if Hardware RNG is not present.
+//
+// Since we don't yet trust the quality of Hardware RNG, these bytes are not
+// mixed into the primary pool of Linux RNG and the entropy estimate is left
+// unmodified.
+//
+// If the HW RNG device /dev/hw_random is present, we require that at least
+// 512 bytes read from it are written into Linux RNG. QA is expected to catch
+// devices/configurations where these I/O operations are blocking for a long
+// time. We do not reboot or halt on failures, as this is a best-effort
+// attempt.
+Result<Success> MixHwrngIntoLinuxRngAction(const BuiltinArguments&) {
+    unique_fd hwrandom_fd(
+        TEMP_FAILURE_RETRY(open("/dev/hw_random", O_RDONLY | O_NOFOLLOW | O_CLOEXEC)));
+    if (hwrandom_fd == -1) {
+        if (errno == ENOENT) {
+            LOG(INFO) << "/dev/hw_random not found";
+            // It's not an error to not have a Hardware RNG.
+            return Success();
+        }
+        return ErrnoError() << "Failed to open /dev/hw_random";
+    }
+
+    unique_fd urandom_fd(
+        TEMP_FAILURE_RETRY(open("/dev/urandom", O_WRONLY | O_NOFOLLOW | O_CLOEXEC)));
+    if (urandom_fd == -1) {
+        return ErrnoError() << "Failed to open /dev/urandom";
+    }
+
+    char buf[512];
+    size_t total_bytes_written = 0;
+    while (total_bytes_written < sizeof(buf)) {
+        ssize_t chunk_size =
+            TEMP_FAILURE_RETRY(read(hwrandom_fd, buf, sizeof(buf) - total_bytes_written));
+        if (chunk_size == -1) {
+            return ErrnoError() << "Failed to read from /dev/hw_random";
+        } else if (chunk_size == 0) {
+            return Error() << "Failed to read from /dev/hw_random: EOF";
+        }
+
+        chunk_size = TEMP_FAILURE_RETRY(write(urandom_fd, buf, chunk_size));
+        if (chunk_size == -1) {
+            return ErrnoError() << "Failed to write to /dev/urandom";
+        }
+        total_bytes_written += chunk_size;
+    }
+
+    LOG(INFO) << "Mixed " << total_bytes_written << " bytes from /dev/hw_random into /dev/urandom";
+    return Success();
+}
+
+static bool SetHighestAvailableOptionValue(std::string path, int min, int max) {
+    std::ifstream inf(path, std::fstream::in);
+    if (!inf) {
+        LOG(ERROR) << "Cannot open for reading: " << path;
+        return false;
+    }
+
+    int current = max;
+    while (current >= min) {
+        // try to write out new value
+        std::string str_val = std::to_string(current);
+        std::ofstream of(path, std::fstream::out);
+        if (!of) {
+            LOG(ERROR) << "Cannot open for writing: " << path;
+            return false;
+        }
+        of << str_val << std::endl;
+        of.close();
+
+        // check to make sure it was recorded
+        inf.seekg(0);
+        std::string str_rec;
+        inf >> str_rec;
+        if (str_val.compare(str_rec) == 0) {
+            break;
+        }
+        current--;
+    }
+    inf.close();
+
+    if (current < min) {
+        LOG(ERROR) << "Unable to set minimum option value " << min << " in " << path;
+        return false;
+    }
+    return true;
+}
+
+#define MMAP_RND_PATH "/proc/sys/vm/mmap_rnd_bits"
+#define MMAP_RND_COMPAT_PATH "/proc/sys/vm/mmap_rnd_compat_bits"
+
+// __attribute__((unused)) due to lack of mips support: see mips block in SetMmapRndBitsAction
+static bool __attribute__((unused)) SetMmapRndBitsMin(int start, int min, bool compat) {
+    std::string path;
+    if (compat) {
+        path = MMAP_RND_COMPAT_PATH;
+    } else {
+        path = MMAP_RND_PATH;
+    }
+
+    return SetHighestAvailableOptionValue(path, min, start);
+}
+
+// Set /proc/sys/vm/mmap_rnd_bits and potentially
+// /proc/sys/vm/mmap_rnd_compat_bits to the maximum supported values.
+// Returns -1 if unable to set these to an acceptable value.
+//
+// To support this sysctl, the following upstream commits are needed:
+//
+// d07e22597d1d mm: mmap: add new /proc tunable for mmap_base ASLR
+// e0c25d958f78 arm: mm: support ARCH_MMAP_RND_BITS
+// 8f0d3aa9de57 arm64: mm: support ARCH_MMAP_RND_BITS
+// 9e08f57d684a x86: mm: support ARCH_MMAP_RND_BITS
+// ec9ee4acd97c drivers: char: random: add get_random_long()
+// 5ef11c35ce86 mm: ASLR: use get_random_long()
+Result<Success> SetMmapRndBitsAction(const BuiltinArguments&) {
+// values are arch-dependent
+#if defined(USER_MODE_LINUX)
+    // uml does not support mmap_rnd_bits
+    return Success();
+#elif defined(__aarch64__)
+    // arm64 supports 18 - 33 bits depending on pagesize and VA_SIZE
+    if (SetMmapRndBitsMin(33, 24, false) && SetMmapRndBitsMin(16, 16, true)) {
+        return Success();
+    }
+#elif defined(__x86_64__)
+    // x86_64 supports 28 - 32 bits
+    if (SetMmapRndBitsMin(32, 32, false) && SetMmapRndBitsMin(16, 16, true)) {
+        return Success();
+    }
+#elif defined(__arm__) || defined(__i386__)
+    // check to see if we're running on 64-bit kernel
+    bool h64 = !access(MMAP_RND_COMPAT_PATH, F_OK);
+    // supported 32-bit architecture must have 16 bits set
+    if (SetMmapRndBitsMin(16, 16, h64)) {
+        return Success();
+    }
+#elif defined(__mips__) || defined(__mips64__)
+    // TODO: add mips support b/27788820
+    return Success();
+#else
+    LOG(ERROR) << "Unknown architecture";
+#endif
+
+    LOG(FATAL) << "Unable to set adequate mmap entropy value!";
+    return Error();
+}
+
+#define KPTR_RESTRICT_PATH "/proc/sys/kernel/kptr_restrict"
+#define KPTR_RESTRICT_MINVALUE 2
+#define KPTR_RESTRICT_MAXVALUE 4
+
+// Set kptr_restrict to the highest available level.
+//
+// Aborts if unable to set this to an acceptable value.
+Result<Success> SetKptrRestrictAction(const BuiltinArguments&) {
+    std::string path = KPTR_RESTRICT_PATH;
+
+    if (!SetHighestAvailableOptionValue(path, KPTR_RESTRICT_MINVALUE, KPTR_RESTRICT_MAXVALUE)) {
+        LOG(FATAL) << "Unable to set adequate kptr_restrict value!";
+        return Error();
+    }
+    return Success();
+}
+
+}  // namespace init
+}  // namespace android
diff --git a/init/security.h b/init/security.h
new file mode 100644
index 0000000..6f6b944
--- /dev/null
+++ b/init/security.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2017 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_SECURITY_H
+#define _INIT_SECURITY_H
+
+#include <string>
+#include <vector>
+
+#include "builtin_arguments.h"
+#include "result.h"
+
+namespace android {
+namespace init {
+
+Result<Success> MixHwrngIntoLinuxRngAction(const BuiltinArguments&);
+Result<Success> SetMmapRndBitsAction(const BuiltinArguments&);
+Result<Success> SetKptrRestrictAction(const BuiltinArguments&);
+
+}  // namespace init
+}  // namespace android
+
+#endif
diff --git a/init/selinux.cpp b/init/selinux.cpp
new file mode 100644
index 0000000..b788be9
--- /dev/null
+++ b/init/selinux.cpp
@@ -0,0 +1,550 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+// This file contains the functions that initialize SELinux during boot as well as helper functions
+// for SELinux operation for init.
+
+// When the system boots, there is no SEPolicy present and init is running in the kernel domain.
+// Init loads the SEPolicy from the file system, restores the context of /init based on this
+// SEPolicy, and finally exec()'s itself to run in the proper domain.
+
+// The SEPolicy on Android comes in two variants: monolithic and split.
+
+// The monolithic policy variant is for legacy non-treble devices that contain a single SEPolicy
+// file located at /sepolicy and is directly loaded into the kernel SELinux subsystem.
+
+// The split policy is for supporting treble devices.  It splits the SEPolicy across files on
+// /system/etc/selinux (the 'plat' portion of the policy) and /vendor/etc/selinux (the 'nonplat'
+// portion of the policy).  This is necessary to allow the system image to be updated independently
+// of the vendor image, while maintaining contributions from both partitions in the SEPolicy.  This
+// is especially important for VTS testing, where the SEPolicy on the Google System Image may not be
+// identical to the system image shipped on a vendor's device.
+
+// The split SEPolicy is loaded as described below:
+// 1) There is a precompiled SEPolicy located at /vendor/etc/selinux/precompiled_sepolicy.
+//    Stored along with this file is the sha256 hash of the parts of the SEPolicy on /system that
+//    were used to compile this precompiled policy.  The system partition contains a similar sha256
+//    of the parts of the SEPolicy that it currently contains.  If these two hashes match, then the
+//    system loads this precompiled_sepolicy directly.
+// 2) If these hashes do not match, then /system has been updated out of sync with /vendor and the
+//    init needs to compile the SEPolicy.  /system contains the SEPolicy compiler, secilc, and it
+//    is used by the LoadSplitPolicy() function below to compile the SEPolicy to a temp directory
+//    and load it.  That function contains even more documentation with the specific implementation
+//    details of how the SEPolicy is compiled if needed.
+
+#include "selinux.h"
+
+#include <fcntl.h>
+#include <stdlib.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <android-base/chrono_utils.h>
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/parseint.h>
+#include <android-base/unique_fd.h>
+#include <selinux/android.h>
+
+#include "util.h"
+
+using android::base::ParseInt;
+using android::base::Timer;
+using android::base::unique_fd;
+
+namespace android {
+namespace init {
+
+namespace {
+
+selabel_handle* sehandle = nullptr;
+
+enum EnforcingStatus { SELINUX_PERMISSIVE, SELINUX_ENFORCING };
+
+EnforcingStatus StatusFromCmdline() {
+    EnforcingStatus status = SELINUX_ENFORCING;
+
+    import_kernel_cmdline(false,
+                          [&](const std::string& key, const std::string& value, bool in_qemu) {
+                              if (key == "androidboot.selinux" && value == "permissive") {
+                                  status = SELINUX_PERMISSIVE;
+                              }
+                          });
+
+    return status;
+}
+
+bool IsEnforcing() {
+    if (ALLOW_PERMISSIVE_SELINUX) {
+        return StatusFromCmdline() == SELINUX_ENFORCING;
+    }
+    return true;
+}
+
+// Forks, executes the provided program in the child, and waits for the completion in the parent.
+// Child's stderr is captured and logged using LOG(ERROR).
+bool ForkExecveAndWaitForCompletion(const char* filename, char* const argv[]) {
+    // Create a pipe used for redirecting child process's output.
+    // * pipe_fds[0] is the FD the parent will use for reading.
+    // * pipe_fds[1] is the FD the child will use for writing.
+    int pipe_fds[2];
+    if (pipe(pipe_fds) == -1) {
+        PLOG(ERROR) << "Failed to create pipe";
+        return false;
+    }
+
+    pid_t child_pid = fork();
+    if (child_pid == -1) {
+        PLOG(ERROR) << "Failed to fork for " << filename;
+        return false;
+    }
+
+    if (child_pid == 0) {
+        // fork succeeded -- this is executing in the child process
+
+        // Close the pipe FD not used by this process
+        close(pipe_fds[0]);
+
+        // Redirect stderr to the pipe FD provided by the parent
+        if (TEMP_FAILURE_RETRY(dup2(pipe_fds[1], STDERR_FILENO)) == -1) {
+            PLOG(ERROR) << "Failed to redirect stderr of " << filename;
+            _exit(127);
+            return false;
+        }
+        close(pipe_fds[1]);
+
+        if (execv(filename, argv) == -1) {
+            PLOG(ERROR) << "Failed to execve " << filename;
+            return false;
+        }
+        // Unreachable because execve will have succeeded and replaced this code
+        // with child process's code.
+        _exit(127);
+        return false;
+    } else {
+        // fork succeeded -- this is executing in the original/parent process
+
+        // Close the pipe FD not used by this process
+        close(pipe_fds[1]);
+
+        // Log the redirected output of the child process.
+        // It's unfortunate that there's no standard way to obtain an istream for a file descriptor.
+        // As a result, we're buffering all output and logging it in one go at the end of the
+        // invocation, instead of logging it as it comes in.
+        const int child_out_fd = pipe_fds[0];
+        std::string child_output;
+        if (!android::base::ReadFdToString(child_out_fd, &child_output)) {
+            PLOG(ERROR) << "Failed to capture full output of " << filename;
+        }
+        close(child_out_fd);
+        if (!child_output.empty()) {
+            // Log captured output, line by line, because LOG expects to be invoked for each line
+            std::istringstream in(child_output);
+            std::string line;
+            while (std::getline(in, line)) {
+                LOG(ERROR) << filename << ": " << line;
+            }
+        }
+
+        // Wait for child to terminate
+        int status;
+        if (TEMP_FAILURE_RETRY(waitpid(child_pid, &status, 0)) != child_pid) {
+            PLOG(ERROR) << "Failed to wait for " << filename;
+            return false;
+        }
+
+        if (WIFEXITED(status)) {
+            int status_code = WEXITSTATUS(status);
+            if (status_code == 0) {
+                return true;
+            } else {
+                LOG(ERROR) << filename << " exited with status " << status_code;
+            }
+        } else if (WIFSIGNALED(status)) {
+            LOG(ERROR) << filename << " killed by signal " << WTERMSIG(status);
+        } else if (WIFSTOPPED(status)) {
+            LOG(ERROR) << filename << " stopped by signal " << WSTOPSIG(status);
+        } else {
+            LOG(ERROR) << "waitpid for " << filename << " returned unexpected status: " << status;
+        }
+
+        return false;
+    }
+}
+
+bool ReadFirstLine(const char* file, std::string* line) {
+    line->clear();
+
+    std::string contents;
+    if (!android::base::ReadFileToString(file, &contents, true /* follow symlinks */)) {
+        return false;
+    }
+    std::istringstream in(contents);
+    std::getline(in, *line);
+    return true;
+}
+
+bool FindPrecompiledSplitPolicy(std::string* file) {
+    file->clear();
+    // If there is an odm partition, precompiled_sepolicy will be in
+    // odm/etc/selinux. Otherwise it will be in vendor/etc/selinux.
+    static constexpr const char vendor_precompiled_sepolicy[] =
+        "/vendor/etc/selinux/precompiled_sepolicy";
+    static constexpr const char odm_precompiled_sepolicy[] =
+        "/odm/etc/selinux/precompiled_sepolicy";
+    if (access(odm_precompiled_sepolicy, R_OK) == 0) {
+        *file = odm_precompiled_sepolicy;
+    } else if (access(vendor_precompiled_sepolicy, R_OK) == 0) {
+        *file = vendor_precompiled_sepolicy;
+    } else {
+        PLOG(INFO) << "No precompiled sepolicy";
+        return false;
+    }
+    std::string actual_plat_id;
+    if (!ReadFirstLine("/system/etc/selinux/plat_and_mapping_sepolicy.cil.sha256", &actual_plat_id)) {
+        PLOG(INFO) << "Failed to read "
+                      "/system/etc/selinux/plat_and_mapping_sepolicy.cil.sha256";
+        return false;
+    }
+
+    std::string precompiled_plat_id;
+    std::string precompiled_sha256 = *file + ".plat_and_mapping.sha256";
+    if (!ReadFirstLine(precompiled_sha256.c_str(), &precompiled_plat_id)) {
+        PLOG(INFO) << "Failed to read " << precompiled_sha256;
+        file->clear();
+        return false;
+    }
+    if ((actual_plat_id.empty()) || (actual_plat_id != precompiled_plat_id)) {
+        file->clear();
+        return false;
+    }
+    return true;
+}
+
+bool GetVendorMappingVersion(std::string* plat_vers) {
+    if (!ReadFirstLine("/vendor/etc/selinux/plat_sepolicy_vers.txt", plat_vers)) {
+        PLOG(ERROR) << "Failed to read /vendor/etc/selinux/plat_sepolicy_vers.txt";
+        return false;
+    }
+    if (plat_vers->empty()) {
+        LOG(ERROR) << "No version present in plat_sepolicy_vers.txt";
+        return false;
+    }
+    return true;
+}
+
+constexpr const char plat_policy_cil_file[] = "/system/etc/selinux/plat_sepolicy.cil";
+
+bool IsSplitPolicyDevice() {
+    return access(plat_policy_cil_file, R_OK) != -1;
+}
+
+bool LoadSplitPolicy() {
+    // IMPLEMENTATION NOTE: Split policy consists of three CIL files:
+    // * platform -- policy needed due to logic contained in the system image,
+    // * non-platform -- policy needed due to logic contained in the vendor image,
+    // * mapping -- mapping policy which helps preserve forward-compatibility of non-platform policy
+    //   with newer versions of platform policy.
+    //
+    // secilc is invoked to compile the above three policy files into a single monolithic policy
+    // file. This file is then loaded into the kernel.
+
+    // Load precompiled policy from vendor image, if a matching policy is found there. The policy
+    // must match the platform policy on the system image.
+    std::string precompiled_sepolicy_file;
+    if (FindPrecompiledSplitPolicy(&precompiled_sepolicy_file)) {
+        unique_fd fd(open(precompiled_sepolicy_file.c_str(), O_RDONLY | O_CLOEXEC | O_BINARY));
+        if (fd != -1) {
+            if (selinux_android_load_policy_from_fd(fd, precompiled_sepolicy_file.c_str()) < 0) {
+                LOG(ERROR) << "Failed to load SELinux policy from " << precompiled_sepolicy_file;
+                return false;
+            }
+            return true;
+        }
+    }
+    // No suitable precompiled policy could be loaded
+
+    LOG(INFO) << "Compiling SELinux policy";
+
+    // Determine the highest policy language version supported by the kernel
+    set_selinuxmnt("/sys/fs/selinux");
+    int max_policy_version = security_policyvers();
+    if (max_policy_version == -1) {
+        PLOG(ERROR) << "Failed to determine highest policy version supported by kernel";
+        return false;
+    }
+
+    // We store the output of the compilation on /dev because this is the most convenient tmpfs
+    // storage mount available this early in the boot sequence.
+    char compiled_sepolicy[] = "/dev/sepolicy.XXXXXX";
+    unique_fd compiled_sepolicy_fd(mkostemp(compiled_sepolicy, O_CLOEXEC));
+    if (compiled_sepolicy_fd < 0) {
+        PLOG(ERROR) << "Failed to create temporary file " << compiled_sepolicy;
+        return false;
+    }
+
+    // Determine which mapping file to include
+    std::string vend_plat_vers;
+    if (!GetVendorMappingVersion(&vend_plat_vers)) {
+        return false;
+    }
+    std::string mapping_file("/system/etc/selinux/mapping/" + vend_plat_vers + ".cil");
+
+    // vendor_sepolicy.cil and plat_pub_versioned.cil are the new design to replace
+    // nonplat_sepolicy.cil.
+    std::string plat_pub_versioned_cil_file("/vendor/etc/selinux/plat_pub_versioned.cil");
+    std::string vendor_policy_cil_file("/vendor/etc/selinux/vendor_sepolicy.cil");
+
+    if (access(vendor_policy_cil_file.c_str(), F_OK) == -1) {
+        // For backward compatibility.
+        // TODO: remove this after no device is using nonplat_sepolicy.cil.
+        vendor_policy_cil_file = "/vendor/etc/selinux/nonplat_sepolicy.cil";
+        plat_pub_versioned_cil_file.clear();
+    } else if (access(plat_pub_versioned_cil_file.c_str(), F_OK) == -1) {
+        LOG(ERROR) << "Missing " << plat_pub_versioned_cil_file;
+        return false;
+    }
+
+    // odm_sepolicy.cil is default but optional.
+    std::string odm_policy_cil_file("/odm/etc/selinux/odm_sepolicy.cil");
+    if (access(odm_policy_cil_file.c_str(), F_OK) == -1) {
+        odm_policy_cil_file.clear();
+    }
+    const std::string version_as_string = std::to_string(max_policy_version);
+
+    // clang-format off
+    std::vector<const char*> compile_args {
+        "/system/bin/secilc",
+        plat_policy_cil_file,
+        "-m", "-M", "true", "-G", "-N",
+        // Target the highest policy language version supported by the kernel
+        "-c", version_as_string.c_str(),
+        mapping_file.c_str(),
+        "-o", compiled_sepolicy,
+        // We don't care about file_contexts output by the compiler
+        "-f", "/sys/fs/selinux/null",  // /dev/null is not yet available
+    };
+    // clang-format on
+
+    if (!plat_pub_versioned_cil_file.empty()) {
+        compile_args.push_back(plat_pub_versioned_cil_file.c_str());
+    }
+    if (!vendor_policy_cil_file.empty()) {
+        compile_args.push_back(vendor_policy_cil_file.c_str());
+    }
+    if (!odm_policy_cil_file.empty()) {
+        compile_args.push_back(odm_policy_cil_file.c_str());
+    }
+    compile_args.push_back(nullptr);
+
+    if (!ForkExecveAndWaitForCompletion(compile_args[0], (char**)compile_args.data())) {
+        unlink(compiled_sepolicy);
+        return false;
+    }
+    unlink(compiled_sepolicy);
+
+    LOG(INFO) << "Loading compiled SELinux policy";
+    if (selinux_android_load_policy_from_fd(compiled_sepolicy_fd, compiled_sepolicy) < 0) {
+        LOG(ERROR) << "Failed to load SELinux policy from " << compiled_sepolicy;
+        return false;
+    }
+
+    return true;
+}
+
+bool LoadMonolithicPolicy() {
+    LOG(VERBOSE) << "Loading SELinux policy from monolithic file";
+    if (selinux_android_load_policy() < 0) {
+        PLOG(ERROR) << "Failed to load monolithic SELinux policy";
+        return false;
+    }
+    return true;
+}
+
+bool LoadPolicy() {
+    return IsSplitPolicyDevice() ? LoadSplitPolicy() : LoadMonolithicPolicy();
+}
+
+}  // namespace
+
+void SelinuxInitialize() {
+    Timer t;
+
+    LOG(INFO) << "Loading SELinux policy";
+    if (!LoadPolicy()) {
+        LOG(FATAL) << "Unable to load SELinux policy";
+    }
+
+    bool kernel_enforcing = (security_getenforce() == 1);
+    bool is_enforcing = IsEnforcing();
+    if (kernel_enforcing != is_enforcing) {
+        if (security_setenforce(is_enforcing)) {
+            PLOG(FATAL) << "security_setenforce(%s) failed" << (is_enforcing ? "true" : "false");
+        }
+    }
+
+    if (auto result = WriteFile("/sys/fs/selinux/checkreqprot", "0"); !result) {
+        LOG(FATAL) << "Unable to write to /sys/fs/selinux/checkreqprot: " << result.error();
+    }
+
+    // init's first stage can't set properties, so pass the time to the second stage.
+    setenv("INIT_SELINUX_TOOK", std::to_string(t.duration().count()).c_str(), 1);
+}
+
+// The files and directories that were created before initial sepolicy load or
+// files on ramdisk need to have their security context restored to the proper
+// value. This must happen before /dev is populated by ueventd.
+void SelinuxRestoreContext() {
+    LOG(INFO) << "Running restorecon...";
+    selinux_android_restorecon("/dev", 0);
+    selinux_android_restorecon("/dev/kmsg", 0);
+    if constexpr (WORLD_WRITABLE_KMSG) {
+        selinux_android_restorecon("/dev/kmsg_debug", 0);
+    }
+    selinux_android_restorecon("/dev/null", 0);
+    selinux_android_restorecon("/dev/ptmx", 0);
+    selinux_android_restorecon("/dev/socket", 0);
+    selinux_android_restorecon("/dev/random", 0);
+    selinux_android_restorecon("/dev/urandom", 0);
+    selinux_android_restorecon("/dev/__properties__", 0);
+
+    selinux_android_restorecon("/plat_file_contexts", 0);
+    selinux_android_restorecon("/nonplat_file_contexts", 0);
+    selinux_android_restorecon("/vendor_file_contexts", 0);
+    selinux_android_restorecon("/plat_property_contexts", 0);
+    selinux_android_restorecon("/nonplat_property_contexts", 0);
+    selinux_android_restorecon("/vendor_property_contexts", 0);
+    selinux_android_restorecon("/plat_seapp_contexts", 0);
+    selinux_android_restorecon("/nonplat_seapp_contexts", 0);
+    selinux_android_restorecon("/vendor_seapp_contexts", 0);
+    selinux_android_restorecon("/plat_service_contexts", 0);
+    selinux_android_restorecon("/nonplat_service_contexts", 0);
+    selinux_android_restorecon("/vendor_service_contexts", 0);
+    selinux_android_restorecon("/plat_hwservice_contexts", 0);
+    selinux_android_restorecon("/nonplat_hwservice_contexts", 0);
+    selinux_android_restorecon("/vendor_hwservice_contexts", 0);
+    selinux_android_restorecon("/sepolicy", 0);
+    selinux_android_restorecon("/vndservice_contexts", 0);
+
+    selinux_android_restorecon("/dev/block", SELINUX_ANDROID_RESTORECON_RECURSE);
+    selinux_android_restorecon("/dev/device-mapper", 0);
+
+    selinux_android_restorecon("/sbin/mke2fs_static", 0);
+    selinux_android_restorecon("/sbin/e2fsdroid_static", 0);
+
+    selinux_android_restorecon("/sbin/mkfs.f2fs", 0);
+    selinux_android_restorecon("/sbin/sload.f2fs", 0);
+}
+
+int SelinuxKlogCallback(int type, const char* fmt, ...) {
+    android::base::LogSeverity severity = android::base::ERROR;
+    if (type == SELINUX_WARNING) {
+        severity = android::base::WARNING;
+    } else if (type == SELINUX_INFO) {
+        severity = android::base::INFO;
+    }
+    char buf[1024];
+    va_list ap;
+    va_start(ap, fmt);
+    vsnprintf(buf, sizeof(buf), fmt, ap);
+    va_end(ap);
+    android::base::KernelLogger(android::base::MAIN, severity, "selinux", nullptr, 0, buf);
+    return 0;
+}
+
+// This function sets up SELinux logging to be written to kmsg, to match init's logging.
+void SelinuxSetupKernelLogging() {
+    selinux_callback cb;
+    cb.func_log = SelinuxKlogCallback;
+    selinux_set_callback(SELINUX_CB_LOG, cb);
+}
+
+// This function checks whether the sepolicy supports vendor init.
+bool SelinuxHasVendorInit() {
+    if (!IsSplitPolicyDevice()) {
+        // If this device does not split sepolicy files, vendor_init will be available in the latest
+        // monolithic sepolicy file.
+        return true;
+    }
+
+    std::string version;
+    if (!GetVendorMappingVersion(&version)) {
+        // Return true as the default if we failed to load the vendor sepolicy version.
+        return true;
+    }
+
+    int major_version;
+    std::string major_version_str(version, 0, version.find('.'));
+    if (!ParseInt(major_version_str, &major_version)) {
+        PLOG(ERROR) << "Failed to parse the vendor sepolicy major version " << major_version_str;
+        // Return true as the default if we failed to parse the major version.
+        return true;
+    }
+
+    return major_version >= 28;
+}
+
+// selinux_android_file_context_handle() takes on the order of 10+ms to run, so we want to cache
+// its value.  selinux_android_restorecon() also needs an sehandle for file context look up.  It
+// will create and store its own copy, but selinux_android_set_sehandle() can be used to provide
+// one, thus eliminating an extra call to selinux_android_file_context_handle().
+void SelabelInitialize() {
+    sehandle = selinux_android_file_context_handle();
+    selinux_android_set_sehandle(sehandle);
+}
+
+// A C++ wrapper around selabel_lookup() using the cached sehandle.
+// If sehandle is null, this returns success with an empty context.
+bool SelabelLookupFileContext(const std::string& key, int type, std::string* result) {
+    result->clear();
+
+    if (!sehandle) return true;
+
+    char* context;
+    if (selabel_lookup(sehandle, &context, key.c_str(), type) != 0) {
+        return false;
+    }
+    *result = context;
+    free(context);
+    return true;
+}
+
+// A C++ wrapper around selabel_lookup_best_match() using the cached sehandle.
+// If sehandle is null, this returns success with an empty context.
+bool SelabelLookupFileContextBestMatch(const std::string& key,
+                                       const std::vector<std::string>& aliases, int type,
+                                       std::string* result) {
+    result->clear();
+
+    if (!sehandle) return true;
+
+    std::vector<const char*> c_aliases;
+    for (const auto& alias : aliases) {
+        c_aliases.emplace_back(alias.c_str());
+    }
+    c_aliases.emplace_back(nullptr);
+
+    char* context;
+    if (selabel_lookup_best_match(sehandle, &context, key.c_str(), &c_aliases[0], type) != 0) {
+        return false;
+    }
+    *result = context;
+    free(context);
+    return true;
+}
+
+}  // namespace init
+}  // namespace android
diff --git a/init/selinux.h b/init/selinux.h
new file mode 100644
index 0000000..30069b5
--- /dev/null
+++ b/init/selinux.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2017 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_SELINUX_H
+#define _INIT_SELINUX_H
+
+#include <string>
+#include <vector>
+
+namespace android {
+namespace init {
+
+void SelinuxInitialize();
+void SelinuxRestoreContext();
+
+void SelinuxSetupKernelLogging();
+bool SelinuxHasVendorInit();
+
+void SelabelInitialize();
+bool SelabelLookupFileContext(const std::string& key, int type, std::string* result);
+bool SelabelLookupFileContextBestMatch(const std::string& key,
+                                       const std::vector<std::string>& aliases, int type,
+                                       std::string* result);
+
+}  // namespace init
+}  // namespace android
+
+#endif
diff --git a/init/service.cpp b/init/service.cpp
index 7a657c8..4c2747e 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -18,13 +18,13 @@
 
 #include <fcntl.h>
 #include <inttypes.h>
+#include <linux/input.h>
 #include <linux/securebits.h>
 #include <sched.h>
 #include <sys/mount.h>
 #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>
@@ -34,45 +34,51 @@
 #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 <android-base/unique_fd.h>
+#include <hidl-util/FQName.h>
 #include <processgroup/processgroup.h>
 #include <selinux/selinux.h>
 #include <system/thread_defs.h>
 
+#include "rlimit_parser.h"
+#include "util.h"
+
+#if defined(__ANDROID__)
+#include <sys/system_properties.h>
+
 #include "init.h"
 #include "property_service.h"
-#include "util.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;
+using android::base::unique_fd;
 using android::base::WriteStringToFile;
 
 namespace android {
 namespace init {
 
-static std::string ComputeContextFromExecutable(std::string& service_name,
-                                                const std::string& service_path) {
+static Result<std::string> ComputeContextFromExecutable(const std::string& service_path) {
     std::string computed_context;
 
     char* raw_con = nullptr;
     char* raw_filecon = nullptr;
 
     if (getcon(&raw_con) == -1) {
-        LOG(ERROR) << "could not get context while starting '" << service_name << "'";
-        return "";
+        return Error() << "Could not get security context";
     }
     std::unique_ptr<char> mycon(raw_con);
 
     if (getfilecon(service_path.c_str(), &raw_filecon) == -1) {
-        LOG(ERROR) << "could not get file context while starting '" << service_name << "'";
-        return "";
+        return Error() << "Could not get file context";
     }
     std::unique_ptr<char> filecon(raw_filecon);
 
@@ -84,32 +90,61 @@
         free(new_con);
     }
     if (rc == 0 && computed_context == mycon.get()) {
-        LOG(ERROR) << "service " << service_name << " does not have a SELinux domain defined";
-        return "";
+        return Error() << "File " << service_path << "(labeled \"" << filecon.get()
+                       << "\") has incorrect label or no domain transition from " << mycon.get()
+                       << " to another SELinux domain defined. Have you configured your "
+                          "service correctly? https://source.android.com/security/selinux/"
+                          "device-policy#label_new_services_and_address_denials";
     }
     if (rc < 0) {
-        LOG(ERROR) << "could not get context while starting '" << service_name << "'";
-        return "";
+        return Error() << "Could not get process context";
     }
     return computed_context;
 }
 
-static void SetUpPidNamespace(const std::string& service_name) {
+Result<Success> Service::SetUpMountNamespace() const {
     constexpr unsigned int kSafeFlags = MS_NODEV | MS_NOEXEC | MS_NOSUID;
 
-    // It's OK to LOG(FATAL) in this function since it's running in the first
-    // child process.
-    if (mount("", "/proc", "proc", kSafeFlags | MS_REMOUNT, "") == -1) {
-        PLOG(FATAL) << "couldn't remount(/proc) for " << service_name;
+    // Recursively remount / as slave like zygote does so unmounting and mounting /proc
+    // doesn't interfere with the parent namespace's /proc mount. This will also
+    // prevent any other mounts/unmounts initiated by the service from interfering
+    // with the parent namespace but will still allow mount events from the parent
+    // namespace to propagate to the child.
+    if (mount("rootfs", "/", nullptr, (MS_SLAVE | MS_REC), nullptr) == -1) {
+        return ErrnoError() << "Could not remount(/) recursively as slave";
     }
 
-    if (prctl(PR_SET_NAME, service_name.c_str()) == -1) {
-        PLOG(FATAL) << "couldn't set name for " << service_name;
+    // umount() then mount() /proc and/or /sys
+    // Note that it is not sufficient to mount with MS_REMOUNT.
+    if (namespace_flags_ & CLONE_NEWPID) {
+        if (umount("/proc") == -1) {
+            return ErrnoError() << "Could not umount(/proc)";
+        }
+        if (mount("", "/proc", "proc", kSafeFlags, "") == -1) {
+            return ErrnoError() << "Could not mount(/proc)";
+        }
+    }
+    bool remount_sys = std::any_of(namespaces_to_enter_.begin(), namespaces_to_enter_.end(),
+                                   [](const auto& entry) { return entry.first == CLONE_NEWNET; });
+    if (remount_sys) {
+        if (umount2("/sys", MNT_DETACH) == -1) {
+            return ErrnoError() << "Could not umount(/sys)";
+        }
+        if (mount("", "/sys", "sysfs", kSafeFlags, "") == -1) {
+            return ErrnoError() << "Could not mount(/sys)";
+        }
+    }
+    return Success();
+}
+
+Result<Success> Service::SetUpPidNamespace() const {
+    if (prctl(PR_SET_NAME, name_.c_str()) == -1) {
+        return ErrnoError() << "Could not set name";
     }
 
     pid_t child_pid = fork();
     if (child_pid == -1) {
-        PLOG(FATAL) << "couldn't fork init inside the PID namespace for " << service_name;
+        return ErrnoError() << "Could not fork init inside the PID namespace";
     }
 
     if (child_pid > 0) {
@@ -132,53 +167,54 @@
         }
         _exit(WEXITSTATUS(init_exitstatus));
     }
+    return Success();
 }
 
-static void ExpandArgs(const std::vector<std::string>& args, std::vector<char*>* strs) {
+Result<Success> Service::EnterNamespaces() const {
+    for (const auto& [nstype, path] : namespaces_to_enter_) {
+        auto fd = unique_fd{open(path.c_str(), O_RDONLY | O_CLOEXEC)};
+        if (!fd) {
+            return ErrnoError() << "Could not open namespace at " << path;
+        }
+        if (setns(fd, nstype) == -1) {
+            return ErrnoError() << "Could not setns() namespace at " << path;
+        }
+    }
+    return Success();
+}
+
+static bool ExpandArgsAndExecv(const std::vector<std::string>& args, bool sigstop) {
     std::vector<std::string> expanded_args;
+    std::vector<char*> c_strings;
+
     expanded_args.resize(args.size());
-    strs->push_back(const_cast<char*>(args[0].c_str()));
+    c_strings.push_back(const_cast<char*>(args[0].data()));
     for (std::size_t i = 1; i < args.size(); ++i) {
         if (!expand_props(args[i], &expanded_args[i])) {
             LOG(FATAL) << args[0] << ": cannot expand '" << args[i] << "'";
         }
-        strs->push_back(const_cast<char*>(expanded_args[i].c_str()));
+        c_strings.push_back(expanded_args[i].data());
     }
-    strs->push_back(nullptr);
+    c_strings.push_back(nullptr);
+
+    if (sigstop) {
+        kill(getpid(), SIGSTOP);
+    }
+
+    return execv(c_strings[0], c_strings.data()) == 0;
 }
 
-ServiceEnvironmentInfo::ServiceEnvironmentInfo() {
-}
+unsigned long Service::next_start_order_ = 1;
+bool Service::is_exec_service_running_ = false;
 
-ServiceEnvironmentInfo::ServiceEnvironmentInfo(const std::string& name,
-                                               const std::string& value)
-    : name(name), value(value) {
-}
-
-Service::Service(const std::string& name, const std::vector<std::string>& args)
-    : name_(name),
-      classnames_({"default"}),
-      flags_(0),
-      pid_(0),
-      crash_count_(0),
-      uid_(0),
-      gid_(0),
-      namespace_flags_(0),
-      seclabel_(""),
-      onrestart_(false, "<Service '" + name + "' onrestart>", 0),
-      keychord_id_(0),
-      ioprio_class_(IoSchedClass_NONE),
-      ioprio_pri_(0),
-      priority_(0),
-      oom_score_adjust_(-1000),
-      args_(args) {
-    onrestart_.InitSingleTrigger("onrestart");
-}
+Service::Service(const std::string& name, Subcontext* subcontext_for_restart_commands,
+                 const std::vector<std::string>& args)
+    : Service(name, 0, 0, 0, {}, 0, 0, "", subcontext_for_restart_commands, args) {}
 
 Service::Service(const std::string& name, unsigned flags, uid_t uid, gid_t gid,
                  const std::vector<gid_t>& supp_gids, const CapSet& capabilities,
                  unsigned namespace_flags, const std::string& seclabel,
-                 const std::vector<std::string>& args)
+                 Subcontext* subcontext_for_restart_commands, const std::vector<std::string>& args)
     : name_(name),
       classnames_({"default"}),
       flags_(flags),
@@ -190,15 +226,17 @@
       capabilities_(capabilities),
       namespace_flags_(namespace_flags),
       seclabel_(seclabel),
-      onrestart_(false, "<Service '" + name + "' onrestart>", 0),
-      keychord_id_(0),
+      onrestart_(false, subcontext_for_restart_commands, "<Service '" + name + "' onrestart>", 0,
+                 "onrestart", {}),
       ioprio_class_(IoSchedClass_NONE),
       ioprio_pri_(0),
       priority_(0),
       oom_score_adjust_(-1000),
-      args_(args) {
-    onrestart_.InitSingleTrigger("onrestart");
-}
+      swappiness_(-1),
+      soft_limit_in_bytes_(-1),
+      limit_in_bytes_(-1),
+      start_order_(0),
+      args_(args) {}
 
 void Service::NotifyStateChange(const std::string& new_state) const {
     if ((flags_ & SVC_TEMPORARY) != 0) {
@@ -211,7 +249,10 @@
 
     if (new_state == "running") {
         uint64_t start_ns = time_started_.time_since_epoch().count();
-        property_set("ro.boottime." + name_, std::to_string(start_ns));
+        std::string boottime_property = "ro.boottime." + name_;
+        if (GetProperty(boottime_property, "").empty()) {
+            property_set(boottime_property, std::to_string(start_ns));
+        }
     }
 }
 
@@ -235,12 +276,18 @@
 }
 
 void Service::SetProcessAttributes() {
+    for (const auto& rlimit : rlimits_) {
+        if (setrlimit(rlimit.first, &rlimit.second) == -1) {
+            LOG(FATAL) << StringPrintf("setrlimit(%d, {rlim_cur=%ld, rlim_max=%ld}) failed",
+                                       rlimit.first, rlimit.second.rlim_cur, rlimit.second.rlim_max);
+        }
+    }
     // Keep capabilites on uid change.
     if (capabilities_.any() && uid_) {
         // If Android is running in a container, some securebits might already
         // be locked, so don't change those.
-        int64_t securebits = prctl(PR_GET_SECUREBITS);
-        if (securebits == -1) {
+        unsigned long securebits = prctl(PR_GET_SECUREBITS);
+        if (securebits == -1UL) {
             PLOG(FATAL) << "prctl(PR_GET_SECUREBITS) failed for " << name_;
         }
         securebits |= SECBIT_KEEP_CAPS | SECBIT_KEEP_CAPS_LOCKED;
@@ -279,10 +326,15 @@
         if (!SetCapsForExec(capabilities_)) {
             LOG(FATAL) << "cannot set capabilities for " << name_;
         }
+    } else if (uid_) {
+        // Inheritable caps can be non-zero when running in a container.
+        if (!DropInheritableCaps()) {
+            LOG(FATAL) << "cannot drop inheritable caps for " << name_;
+        }
     }
 }
 
-void Service::Reap() {
+void Service::Reap(const siginfo_t& siginfo) {
     if (!(flags_ & SVC_ONESHOT) || (flags_ & SVC_RESTART)) {
         KillProcessGroup(SIGKILL);
     }
@@ -291,12 +343,17 @@
     std::for_each(descriptors_.begin(), descriptors_.end(),
                   std::bind(&DescriptorInfo::Clean, std::placeholders::_1));
 
-    if (flags_ & SVC_TEMPORARY) {
-        return;
+    for (const auto& f : reap_callbacks_) {
+        f(siginfo);
     }
 
+    if (flags_ & SVC_EXEC) UnSetExec();
+
+    if (flags_ & SVC_TEMPORARY) return;
+
     pid_ = 0;
     flags_ &= (~SVC_RUNNING);
+    start_order_ = 0;
 
     // Oneshot processes go into the disabled state on exit,
     // except when manually restarted.
@@ -315,8 +372,7 @@
     if ((flags_ & SVC_CRITICAL) && !(flags_ & SVC_RESTART)) {
         if (now < time_crashed_ + 4min) {
             if (++crash_count_ > 4) {
-                LOG(ERROR) << "critical process '" << name_ << "' exited 4 times in 4 minutes";
-                panic();
+                LOG(FATAL) << "critical process '" << name_ << "' exited 4 times in 4 minutes";
             }
         } else {
             time_crashed_ = now;
@@ -342,12 +398,12 @@
                   [] (const auto& info) { LOG(INFO) << *info; });
 }
 
-bool Service::ParseCapabilities(const std::vector<std::string>& args, std::string* err) {
+Result<Success> Service::ParseCapabilities(const std::vector<std::string>& args) {
     capabilities_ = 0;
 
     if (!CapAmbientSupported()) {
-        *err = "capabilities requested but the kernel does not support ambient capabilities";
-        return false;
+        return Error()
+               << "capabilities requested but the kernel does not support ambient capabilities";
     }
 
     unsigned int last_valid_cap = GetLastValidCap();
@@ -359,74 +415,116 @@
         const std::string& arg = args[i];
         int res = LookupCap(arg);
         if (res < 0) {
-            *err = StringPrintf("invalid capability '%s'", arg.c_str());
-            return false;
+            return Error() << StringPrintf("invalid capability '%s'", arg.c_str());
         }
         unsigned int cap = static_cast<unsigned int>(res);  // |res| is >= 0.
         if (cap > last_valid_cap) {
-            *err = StringPrintf("capability '%s' not supported by the kernel", arg.c_str());
-            return false;
+            return Error() << StringPrintf("capability '%s' not supported by the kernel",
+                                           arg.c_str());
         }
         capabilities_[cap] = true;
     }
-    return true;
+    return Success();
 }
 
-bool Service::ParseClass(const std::vector<std::string>& args, std::string* err) {
+Result<Success> Service::ParseClass(const std::vector<std::string>& args) {
     classnames_ = std::set<std::string>(args.begin() + 1, args.end());
-    return true;
+    return Success();
 }
 
-bool Service::ParseConsole(const std::vector<std::string>& args, std::string* err) {
+Result<Success> Service::ParseConsole(const std::vector<std::string>& args) {
     flags_ |= SVC_CONSOLE;
     console_ = args.size() > 1 ? "/dev/" + args[1] : "";
-    return true;
+    return Success();
 }
 
-bool Service::ParseCritical(const std::vector<std::string>& args, std::string* err) {
+Result<Success> Service::ParseCritical(const std::vector<std::string>& args) {
     flags_ |= SVC_CRITICAL;
-    return true;
+    return Success();
 }
 
-bool Service::ParseDisabled(const std::vector<std::string>& args, std::string* err) {
+Result<Success> Service::ParseDisabled(const std::vector<std::string>& args) {
     flags_ |= SVC_DISABLED;
     flags_ |= SVC_RC_DISABLED;
-    return true;
+    return Success();
 }
 
-bool Service::ParseGroup(const std::vector<std::string>& args, std::string* err) {
-    std::string decode_uid_err;
-    if (!DecodeUid(args[1], &gid_, &decode_uid_err)) {
-        *err = "Unable to find GID for '" + args[1] + "': " + decode_uid_err;
-        return false;
+Result<Success> Service::ParseEnterNamespace(const std::vector<std::string>& args) {
+    if (args[1] != "net") {
+        return Error() << "Init only supports entering network namespaces";
     }
+    if (!namespaces_to_enter_.empty()) {
+        return Error() << "Only one network namespace may be entered";
+    }
+    // Network namespaces require that /sys is remounted, otherwise the old adapters will still be
+    // present. Therefore, they also require mount namespaces.
+    namespace_flags_ |= CLONE_NEWNS;
+    namespaces_to_enter_.emplace_back(CLONE_NEWNET, args[2]);
+    return Success();
+}
+
+Result<Success> Service::ParseGroup(const std::vector<std::string>& args) {
+    auto gid = DecodeUid(args[1]);
+    if (!gid) {
+        return Error() << "Unable to decode GID for '" << args[1] << "': " << gid.error();
+    }
+    gid_ = *gid;
+
     for (std::size_t n = 2; n < args.size(); n++) {
-        gid_t gid;
-        if (!DecodeUid(args[n], &gid, &decode_uid_err)) {
-            *err = "Unable to find GID for '" + args[n] + "': " + decode_uid_err;
-            return false;
+        gid = DecodeUid(args[n]);
+        if (!gid) {
+            return Error() << "Unable to decode GID for '" << args[n] << "': " << gid.error();
         }
-        supp_gids_.emplace_back(gid);
+        supp_gids_.emplace_back(*gid);
     }
-    return true;
+    return Success();
 }
 
-bool Service::ParsePriority(const std::vector<std::string>& args, std::string* err) {
+Result<Success> Service::ParsePriority(const std::vector<std::string>& args) {
     priority_ = 0;
     if (!ParseInt(args[1], &priority_,
                   static_cast<int>(ANDROID_PRIORITY_HIGHEST), // highest is negative
                   static_cast<int>(ANDROID_PRIORITY_LOWEST))) {
-        *err = StringPrintf("process priority value must be range %d - %d",
-                ANDROID_PRIORITY_HIGHEST, ANDROID_PRIORITY_LOWEST);
-        return false;
+        return Error() << StringPrintf("process priority value must be range %d - %d",
+                                       ANDROID_PRIORITY_HIGHEST, ANDROID_PRIORITY_LOWEST);
     }
-    return true;
+    return Success();
 }
 
-bool Service::ParseIoprio(const std::vector<std::string>& args, std::string* err) {
+Result<Success> Service::ParseInterface(const std::vector<std::string>& args) {
+    const std::string& interface_name = args[1];
+    const std::string& instance_name = args[2];
+
+    FQName fq_name;
+    if (!FQName::parse(interface_name, &fq_name)) {
+        return Error() << "Invalid fully-qualified name for interface '" << interface_name << "'";
+    }
+
+    if (!fq_name.isFullyQualified()) {
+        return Error() << "Interface name not fully-qualified '" << interface_name << "'";
+    }
+
+    if (fq_name.isValidValueName()) {
+        return Error() << "Interface name must not be a value name '" << interface_name << "'";
+    }
+
+    const std::string fullname = interface_name + "/" + instance_name;
+
+    for (const auto& svc : ServiceList::GetInstance()) {
+        if (svc->interfaces().count(fullname) > 0) {
+            return Error() << "Interface '" << fullname << "' redefined in " << name()
+                           << " but is already defined by " << svc->name();
+        }
+    }
+
+    interfaces_.insert(fullname);
+
+    return Success();
+}
+
+Result<Success> Service::ParseIoprio(const std::vector<std::string>& args) {
     if (!ParseInt(args[2], &ioprio_pri_, 0, 7)) {
-        *err = "priority value must be range 0 - 7";
-        return false;
+        return Error() << "priority value must be range 0 - 7";
     }
 
     if (args[1] == "rt") {
@@ -436,38 +534,42 @@
     } else if (args[1] == "idle") {
         ioprio_class_ = IoSchedClass_IDLE;
     } else {
-        *err = "ioprio option usage: ioprio <rt|be|idle> <0-7>";
-        return false;
+        return Error() << "ioprio option usage: ioprio <rt|be|idle> <0-7>";
     }
 
-    return true;
+    return Success();
 }
 
-bool Service::ParseKeycodes(const std::vector<std::string>& args, std::string* err) {
+Result<Success> Service::ParseKeycodes(const std::vector<std::string>& args) {
     for (std::size_t i = 1; i < args.size(); i++) {
         int code;
-        if (ParseInt(args[i], &code)) {
-            keycodes_.emplace_back(code);
+        if (ParseInt(args[i], &code, 0, KEY_MAX)) {
+            for (auto& key : keycodes_) {
+                if (key == code) return Error() << "duplicate keycode: " << args[i];
+            }
+            keycodes_.insert(std::upper_bound(keycodes_.begin(), keycodes_.end(), code), code);
         } else {
-            LOG(WARNING) << "ignoring invalid keycode: " << args[i];
+            return Error() << "invalid keycode: " << args[i];
         }
     }
-    return true;
+    return Success();
 }
 
-bool Service::ParseOneshot(const std::vector<std::string>& args, std::string* err) {
+Result<Success> Service::ParseOneshot(const std::vector<std::string>& args) {
     flags_ |= SVC_ONESHOT;
-    return true;
+    return Success();
 }
 
-bool Service::ParseOnrestart(const std::vector<std::string>& args, std::string* err) {
+Result<Success> Service::ParseOnrestart(const std::vector<std::string>& args) {
     std::vector<std::string> str_args(args.begin() + 1, args.end());
     int line = onrestart_.NumCommands() + 1;
-    onrestart_.AddCommand(str_args, line, err);
-    return true;
+    if (auto result = onrestart_.AddCommand(str_args, line); !result) {
+        return Error() << "cannot add Onrestart command: " << result.error();
+    }
+    return Success();
 }
 
-bool Service::ParseNamespace(const std::vector<std::string>& args, std::string* err) {
+Result<Success> Service::ParseNamespace(const std::vector<std::string>& args) {
     for (size_t i = 1; i < args.size(); i++) {
         if (args[i] == "pid") {
             namespace_flags_ |= CLONE_NEWPID;
@@ -476,111 +578,143 @@
         } else if (args[i] == "mnt") {
             namespace_flags_ |= CLONE_NEWNS;
         } else {
-            *err = "namespace must be 'pid' or 'mnt'";
-            return false;
+            return Error() << "namespace must be 'pid' or 'mnt'";
         }
     }
-    return true;
+    return Success();
 }
 
-bool Service::ParseOomScoreAdjust(const std::vector<std::string>& args, std::string* err) {
+Result<Success> Service::ParseOomScoreAdjust(const std::vector<std::string>& args) {
     if (!ParseInt(args[1], &oom_score_adjust_, -1000, 1000)) {
-        *err = "oom_score_adjust value must be in range -1000 - +1000";
-        return false;
+        return Error() << "oom_score_adjust value must be in range -1000 - +1000";
     }
-    return true;
+    return Success();
 }
 
-bool Service::ParseSeclabel(const std::vector<std::string>& args, std::string* err) {
+Result<Success> Service::ParseOverride(const std::vector<std::string>& args) {
+    override_ = true;
+    return Success();
+}
+
+Result<Success> Service::ParseMemcgSwappiness(const std::vector<std::string>& args) {
+    if (!ParseInt(args[1], &swappiness_, 0)) {
+        return Error() << "swappiness value must be equal or greater than 0";
+    }
+    return Success();
+}
+
+Result<Success> Service::ParseMemcgLimitInBytes(const std::vector<std::string>& args) {
+    if (!ParseInt(args[1], &limit_in_bytes_, 0)) {
+        return Error() << "limit_in_bytes value must be equal or greater than 0";
+    }
+    return Success();
+}
+
+Result<Success> Service::ParseMemcgSoftLimitInBytes(const std::vector<std::string>& args) {
+    if (!ParseInt(args[1], &soft_limit_in_bytes_, 0)) {
+        return Error() << "soft_limit_in_bytes value must be equal or greater than 0";
+    }
+    return Success();
+}
+
+Result<Success> Service::ParseProcessRlimit(const std::vector<std::string>& args) {
+    auto rlimit = ParseRlimit(args);
+    if (!rlimit) return rlimit.error();
+
+    rlimits_.emplace_back(*rlimit);
+    return Success();
+}
+
+Result<Success> Service::ParseSeclabel(const std::vector<std::string>& args) {
     seclabel_ = args[1];
-    return true;
+    return Success();
 }
 
-bool Service::ParseSetenv(const std::vector<std::string>& args, std::string* err) {
-    envvars_.emplace_back(args[1], args[2]);
-    return true;
+Result<Success> Service::ParseSigstop(const std::vector<std::string>& args) {
+    sigstop_ = true;
+    return Success();
 }
 
-bool Service::ParseShutdown(const std::vector<std::string>& args, std::string* err) {
+Result<Success> Service::ParseSetenv(const std::vector<std::string>& args) {
+    environment_vars_.emplace_back(args[1], args[2]);
+    return Success();
+}
+
+Result<Success> Service::ParseShutdown(const std::vector<std::string>& args) {
     if (args[1] == "critical") {
         flags_ |= SVC_SHUTDOWN_CRITICAL;
-        return true;
+        return Success();
     }
-    return false;
+    return Error() << "Invalid shutdown option";
 }
 
 template <typename T>
-bool Service::AddDescriptor(const std::vector<std::string>& args, std::string* err) {
+Result<Success> Service::AddDescriptor(const std::vector<std::string>& args) {
     int perm = args.size() > 3 ? std::strtoul(args[3].c_str(), 0, 8) : -1;
-    uid_t uid = 0;
-    gid_t gid = 0;
+    Result<uid_t> uid = 0;
+    Result<gid_t> gid = 0;
     std::string context = args.size() > 6 ? args[6] : "";
 
-    std::string decode_uid_err;
     if (args.size() > 4) {
-        if (!DecodeUid(args[4], &uid, &decode_uid_err)) {
-            *err = "Unable to find UID for '" + args[4] + "': " + decode_uid_err;
-            return false;
+        uid = DecodeUid(args[4]);
+        if (!uid) {
+            return Error() << "Unable to find UID for '" << args[4] << "': " << uid.error();
         }
     }
 
     if (args.size() > 5) {
-        if (!DecodeUid(args[5], &gid, &decode_uid_err)) {
-            *err = "Unable to find GID for '" + args[5] + "': " + decode_uid_err;
-            return false;
+        gid = DecodeUid(args[5]);
+        if (!gid) {
+            return Error() << "Unable to find GID for '" << args[5] << "': " << gid.error();
         }
     }
 
-    auto descriptor = std::make_unique<T>(args[1], args[2], uid, gid, perm, context);
+    auto descriptor = std::make_unique<T>(args[1], args[2], *uid, *gid, perm, context);
 
     auto old =
         std::find_if(descriptors_.begin(), descriptors_.end(),
                      [&descriptor] (const auto& other) { return descriptor.get() == other.get(); });
 
     if (old != descriptors_.end()) {
-        *err = "duplicate descriptor " + args[1] + " " + args[2];
-        return false;
+        return Error() << "duplicate descriptor " << args[1] << " " << args[2];
     }
 
     descriptors_.emplace_back(std::move(descriptor));
-    return true;
+    return Success();
 }
 
 // name type perm [ uid gid context ]
-bool Service::ParseSocket(const std::vector<std::string>& args, std::string* err) {
+Result<Success> Service::ParseSocket(const std::vector<std::string>& args) {
     if (!StartsWith(args[2], "dgram") && !StartsWith(args[2], "stream") &&
         !StartsWith(args[2], "seqpacket")) {
-        *err = "socket type must be 'dgram', 'stream' or 'seqpacket'";
-        return false;
+        return Error() << "socket type must be 'dgram', 'stream' or 'seqpacket'";
     }
-    return AddDescriptor<SocketInfo>(args, err);
+    return AddDescriptor<SocketInfo>(args);
 }
 
 // name type perm [ uid gid context ]
-bool Service::ParseFile(const std::vector<std::string>& args, std::string* err) {
+Result<Success> Service::ParseFile(const std::vector<std::string>& args) {
     if (args[2] != "r" && args[2] != "w" && args[2] != "rw") {
-        *err = "file type must be 'r', 'w' or 'rw'";
-        return false;
+        return Error() << "file type must be 'r', 'w' or 'rw'";
     }
     if ((args[1][0] != '/') || (args[1].find("../") != std::string::npos)) {
-        *err = "file name must not be relative";
-        return false;
+        return Error() << "file name must not be relative";
     }
-    return AddDescriptor<FileInfo>(args, err);
+    return AddDescriptor<FileInfo>(args);
 }
 
-bool Service::ParseUser(const std::vector<std::string>& args, std::string* err) {
-    std::string decode_uid_err;
-    if (!DecodeUid(args[1], &uid_, &decode_uid_err)) {
-        *err = "Unable to find UID for '" + args[1] + "': " + decode_uid_err;
-        return false;
+Result<Success> Service::ParseUser(const std::vector<std::string>& args) {
+    auto uid = DecodeUid(args[1]);
+    if (!uid) {
+        return Error() << "Unable to find UID for '" << args[1] << "': " << uid.error();
     }
-    return true;
+    uid_ = *uid;
+    return Success();
 }
 
-bool Service::ParseWritepid(const std::vector<std::string>& args, std::string* err) {
+Result<Success> Service::ParseWritepid(const std::vector<std::string>& args) {
     writepid_files_.assign(args.begin() + 1, args.end());
-    return true;
+    return Success();
 }
 
 class Service::OptionParserMap : public KeywordMap<OptionParser> {
@@ -601,20 +735,32 @@
         {"console",     {0,     1,    &Service::ParseConsole}},
         {"critical",    {0,     0,    &Service::ParseCritical}},
         {"disabled",    {0,     0,    &Service::ParseDisabled}},
+        {"enter_namespace",
+                        {2,     2,    &Service::ParseEnterNamespace}},
+        {"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}},
+        {"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}},
-        {"namespace",   {1,     2,    &Service::ParseNamespace}},
+        {"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}},
+        {"sigstop",     {0,     0,    &Service::ParseSigstop}},
         {"socket",      {3,     6,    &Service::ParseSocket}},
-        {"file",        {2,     2,    &Service::ParseFile}},
         {"user",        {1,     1,    &Service::ParseUser}},
         {"writepid",    {1,     kMax, &Service::ParseWritepid}},
     };
@@ -622,39 +768,49 @@
     return option_parsers;
 }
 
-bool Service::ParseLine(const std::vector<std::string>& args, std::string* err) {
+Result<Success> Service::ParseLine(const std::vector<std::string>& args) {
     static const OptionParserMap parser_map;
-    auto parser = parser_map.FindFunction(args, err);
+    auto parser = parser_map.FindFunction(args);
 
-    if (!parser) {
-        return false;
-    }
+    if (!parser) return parser.error();
 
-    return (this->*parser)(args, err);
+    return std::invoke(*parser, this, args);
 }
 
-bool Service::ExecStart(std::unique_ptr<android::base::Timer>* exec_waiter) {
-    flags_ |= SVC_EXEC | SVC_ONESHOT;
+Result<Success> Service::ExecStart() {
+    flags_ |= SVC_ONESHOT;
 
-    exec_waiter->reset(new android::base::Timer);
-
-    if (!Start()) {
-        exec_waiter->reset();
-        return false;
+    if (auto result = Start(); !result) {
+        return result;
     }
-    return true;
+
+    flags_ |= SVC_EXEC;
+    is_exec_service_running_ = true;
+
+    LOG(INFO) << "SVC_EXEC service '" << name_ << "' pid " << pid_ << " (uid " << uid_ << " gid "
+              << gid_ << "+" << supp_gids_.size() << " context "
+              << (!seclabel_.empty() ? seclabel_ : "default") << ") started; waiting...";
+
+    return Success();
 }
 
-bool Service::Start() {
+Result<Success> Service::Start() {
+    bool disabled = (flags_ & (SVC_DISABLED | SVC_RESET));
     // Starting a service removes it from the disabled or reset state and
     // immediately takes it out of the restarting state if it was in there.
     flags_ &= (~(SVC_DISABLED|SVC_RESTARTING|SVC_RESET|SVC_RESTART|SVC_DISABLED_START));
 
     // Running processes require no additional work --- if they're in the
     // process of exiting, we've ensured that they will immediately restart
-    // on exit, unless they are ONESHOT.
+    // on exit, unless they are ONESHOT. For ONESHOT service, if it's in
+    // stopping status, we just set SVC_RESTART flag so it will get restarted
+    // in Reap().
     if (flags_ & SVC_RUNNING) {
-        return false;
+        if ((flags_ & SVC_ONESHOT) && disabled) {
+            flags_ |= SVC_RESTART;
+        }
+        // It is not an error to try to start a service that is already running.
+        return Success();
     }
 
     bool needs_console = (flags_ & SVC_CONSOLE);
@@ -667,28 +823,27 @@
         // properly registered for the device node
         int console_fd = open(console_.c_str(), O_RDWR | O_CLOEXEC);
         if (console_fd < 0) {
-            PLOG(ERROR) << "service '" << name_ << "' couldn't open console '" << console_ << "'";
             flags_ |= SVC_DISABLED;
-            return false;
+            return ErrnoError() << "Couldn't open console '" << console_ << "'";
         }
         close(console_fd);
     }
 
     struct stat sb;
     if (stat(args_[0].c_str(), &sb) == -1) {
-        PLOG(ERROR) << "cannot find '" << args_[0] << "', disabling '" << name_ << "'";
         flags_ |= SVC_DISABLED;
-        return false;
+        return ErrnoError() << "Cannot find '" << args_[0] << "'";
     }
 
     std::string scon;
     if (!seclabel_.empty()) {
         scon = seclabel_;
     } else {
-        scon = ComputeContextFromExecutable(name_, args_[0]);
-        if (scon == "") {
-            return false;
+        auto result = ComputeContextFromExecutable(args_[0]);
+        if (!result) {
+            return result.error();
         }
+        scon = *result;
     }
 
     LOG(INFO) << "starting service '" << name_ << "'...";
@@ -703,14 +858,28 @@
     if (pid == 0) {
         umask(077);
 
+        if (auto result = EnterNamespaces(); !result) {
+            LOG(FATAL) << "Service '" << name_ << "' could not enter namespaces: " << result.error();
+        }
+
+        if (namespace_flags_ & CLONE_NEWNS) {
+            if (auto result = SetUpMountNamespace(); !result) {
+                LOG(FATAL) << "Service '" << name_
+                           << "' could not set up mount namespace: " << result.error();
+            }
+        }
+
         if (namespace_flags_ & CLONE_NEWPID) {
             // This will fork again to run an init process inside the PID
             // namespace.
-            SetUpPidNamespace(name_);
+            if (auto result = SetUpPidNamespace(); !result) {
+                LOG(FATAL) << "Service '" << name_
+                           << "' could not set up PID namespace: " << result.error();
+            }
         }
 
-        for (const auto& ei : envvars_) {
-            add_environment(ei.name.c_str(), ei.value.c_str());
+        for (const auto& [key, value] : environment_vars_) {
+            setenv(key.c_str(), value.c_str(), 1);
         }
 
         std::for_each(descriptors_.begin(), descriptors_.end(),
@@ -763,19 +932,16 @@
         // priority. Aborts on failure.
         SetProcessAttributes();
 
-        std::vector<char*> strs;
-        ExpandArgs(args_, &strs);
-        if (execve(strs[0], (char**) &strs[0], (char**) ENV) < 0) {
-            PLOG(ERROR) << "cannot execve('" << strs[0] << "')";
+        if (!ExpandArgsAndExecv(args_, sigstop_)) {
+            PLOG(ERROR) << "cannot execve('" << args_[0] << "')";
         }
 
         _exit(127);
     }
 
     if (pid < 0) {
-        PLOG(ERROR) << "failed to fork for '" << name_ << "'";
         pid_ = 0;
-        return false;
+        return ErrnoError() << "Failed to fork";
     }
 
     if (oom_score_adjust_ != -1000) {
@@ -789,39 +955,52 @@
     time_started_ = boot_clock::now();
     pid_ = pid;
     flags_ |= SVC_RUNNING;
+    start_order_ = next_start_order_++;
     process_cgroup_empty_ = false;
 
     errno = -createProcessGroup(uid_, pid_);
     if (errno != 0) {
         PLOG(ERROR) << "createProcessGroup(" << uid_ << ", " << pid_ << ") failed for service '"
                     << name_ << "'";
-    }
+    } else {
+        if (swappiness_ != -1) {
+            if (!setProcessGroupSwappiness(uid_, pid_, swappiness_)) {
+                PLOG(ERROR) << "setProcessGroupSwappiness failed";
+            }
+        }
 
-    if ((flags_ & SVC_EXEC) != 0) {
-        LOG(INFO) << "SVC_EXEC pid " << pid_ << " (uid " << uid_ << " gid " << gid_ << "+"
-                  << supp_gids_.size() << " context "
-                  << (!seclabel_.empty() ? seclabel_ : "default") << ") started; waiting...";
+        if (soft_limit_in_bytes_ != -1) {
+            if (!setProcessGroupSoftLimit(uid_, pid_, soft_limit_in_bytes_)) {
+                PLOG(ERROR) << "setProcessGroupSoftLimit failed";
+            }
+        }
+
+        if (limit_in_bytes_ != -1) {
+            if (!setProcessGroupLimit(uid_, pid_, limit_in_bytes_)) {
+                PLOG(ERROR) << "setProcessGroupLimit failed";
+            }
+        }
     }
 
     NotifyStateChange("running");
-    return true;
+    return Success();
 }
 
-bool Service::StartIfNotDisabled() {
+Result<Success> Service::StartIfNotDisabled() {
     if (!(flags_ & SVC_DISABLED)) {
         return Start();
     } else {
         flags_ |= SVC_DISABLED_START;
     }
-    return true;
+    return Success();
 }
 
-bool Service::Enable() {
+Result<Success> Service::Enable() {
     flags_ &= ~(SVC_DISABLED | SVC_RC_DISABLED);
     if (flags_ & SVC_DISABLED_START) {
         return Start();
     }
-    return true;
+    return Success();
 }
 
 void Service::Reset() {
@@ -847,26 +1026,12 @@
         StopOrReset(SVC_RESTART);
     } else if (!(flags_ & SVC_RESTARTING)) {
         /* Just start the service since it's not running. */
-        Start();
+        if (auto result = Start(); !result) {
+            LOG(ERROR) << "Could not restart '" << name_ << "': " << result.error();
+        }
     } /* else: Service is restarting anyways. */
 }
 
-void Service::RestartIfNeeded(time_t* process_needs_restart_at) {
-    boot_clock::time_point now = boot_clock::now();
-    boot_clock::time_point next_start = time_started_ + 5s;
-    if (now > next_start) {
-        flags_ &= (~SVC_RESTARTING);
-        Start();
-        return;
-    }
-
-    time_t next_start_time_t = time(nullptr) +
-        time_t(std::chrono::duration_cast<std::chrono::seconds>(next_start - now).count());
-    if (next_start_time_t < *process_needs_restart_at || *process_needs_restart_at == 0) {
-        *process_needs_restart_at = next_start_time_t;
-    }
-}
-
 // The how field should be either SVC_DISABLED, SVC_RESET, or SVC_RESTART.
 void Service::StopOrReset(int how) {
     // The service is still SVC_RUNNING until its process exits, but if it has
@@ -884,6 +1049,13 @@
     } else {
         flags_ |= how;
     }
+    // Make sure it's in right status when a restart immediately follow a
+    // stop/reset or vice versa.
+    if (how == SVC_RESTART) {
+        flags_ &= (~(SVC_DISABLED | SVC_RESET));
+    } else {
+        flags_ &= (~SVC_RESTART);
+    }
 
     if (pid_) {
         KillProcessGroup(SIGKILL);
@@ -912,50 +1084,18 @@
     close(fd);
 }
 
-int ServiceManager::exec_count_ = 0;
+ServiceList::ServiceList() {}
 
-ServiceManager::ServiceManager() {
-}
-
-ServiceManager& ServiceManager::GetInstance() {
-    static ServiceManager instance;
+ServiceList& ServiceList::GetInstance() {
+    static ServiceList instance;
     return instance;
 }
 
-void ServiceManager::AddService(std::unique_ptr<Service> service) {
+void ServiceList::AddService(std::unique_ptr<Service> service) {
     services_.emplace_back(std::move(service));
 }
 
-bool ServiceManager::Exec(const std::vector<std::string>& args) {
-    Service* svc = MakeExecOneshotService(args);
-    if (!svc) {
-        LOG(ERROR) << "Could not create exec service";
-        return false;
-    }
-    if (!svc->ExecStart(&exec_waiter_)) {
-        LOG(ERROR) << "Could not start exec service";
-        ServiceManager::GetInstance().RemoveService(*svc);
-        return false;
-    }
-    return true;
-}
-
-bool ServiceManager::ExecStart(const std::string& name) {
-    Service* svc = FindServiceByName(name);
-    if (!svc) {
-        LOG(ERROR) << "ExecStart(" << name << "): Service not found";
-        return false;
-    }
-    if (!svc->ExecStart(&exec_waiter_)) {
-        LOG(ERROR) << "ExecStart(" << name << "): Could not start Service";
-        return false;
-    }
-    return true;
-}
-
-bool ServiceManager::IsWaitingForExec() const { return exec_waiter_ != nullptr; }
-
-Service* ServiceManager::MakeExecOneshotService(const std::vector<std::string>& args) {
+std::unique_ptr<Service> Service::MakeTemporaryOneshotService(const std::vector<std::string>& args) {
     // Parse the arguments: exec [SECLABEL [UID [GID]*] --] COMMAND ARGS...
     // SECLABEL can be a - to denote default
     std::size_t command_arg = 1;
@@ -976,10 +1116,11 @@
     }
     std::vector<std::string> str_args(args.begin() + command_arg, args.end());
 
-    exec_count_++;
-    std::string name = "exec " + std::to_string(exec_count_) + " (" + Join(str_args, " ") + ")";
+    static size_t exec_count = 0;
+    exec_count++;
+    std::string name = "exec " + std::to_string(exec_count) + " (" + Join(str_args, " ") + ")";
 
-    unsigned flags = SVC_EXEC | SVC_ONESHOT | SVC_TEMPORARY;
+    unsigned flags = SVC_ONESHOT | SVC_TEMPORARY;
     CapSet no_capabilities;
     unsigned namespace_flags = 0;
 
@@ -987,100 +1128,50 @@
     if (command_arg > 2 && args[1] != "-") {
         seclabel = args[1];
     }
-    uid_t uid = 0;
+    Result<uid_t> uid = 0;
     if (command_arg > 3) {
-        std::string decode_uid_err;
-        if (!DecodeUid(args[2], &uid, &decode_uid_err)) {
-            LOG(ERROR) << "Unable to find UID for '" << args[2] << "': " << decode_uid_err;
+        uid = DecodeUid(args[2]);
+        if (!uid) {
+            LOG(ERROR) << "Unable to decode UID for '" << args[2] << "': " << uid.error();
             return nullptr;
         }
     }
-    gid_t gid = 0;
+    Result<gid_t> gid = 0;
     std::vector<gid_t> supp_gids;
     if (command_arg > 4) {
-        std::string decode_uid_err;
-        if (!DecodeUid(args[3], &gid, &decode_uid_err)) {
-            LOG(ERROR) << "Unable to find GID for '" << args[3] << "': " << decode_uid_err;
+        gid = DecodeUid(args[3]);
+        if (!gid) {
+            LOG(ERROR) << "Unable to decode GID for '" << args[3] << "': " << gid.error();
             return nullptr;
         }
         std::size_t nr_supp_gids = command_arg - 1 /* -- */ - 4 /* exec SECLABEL UID GID */;
         for (size_t i = 0; i < nr_supp_gids; ++i) {
-            gid_t supp_gid;
-            if (!DecodeUid(args[4 + i], &supp_gid, &decode_uid_err)) {
-                LOG(ERROR) << "Unable to find UID for '" << args[4 + i] << "': " << decode_uid_err;
+            auto supp_gid = DecodeUid(args[4 + i]);
+            if (!supp_gid) {
+                LOG(ERROR) << "Unable to decode GID for '" << args[4 + i]
+                           << "': " << supp_gid.error();
                 return nullptr;
             }
-            supp_gids.push_back(supp_gid);
+            supp_gids.push_back(*supp_gid);
         }
     }
 
-    auto svc_p = std::make_unique<Service>(name, flags, uid, gid, supp_gids, no_capabilities,
-                                           namespace_flags, seclabel, str_args);
-    Service* svc = svc_p.get();
-    services_.emplace_back(std::move(svc_p));
-
-    return svc;
+    return std::make_unique<Service>(name, flags, *uid, *gid, supp_gids, no_capabilities,
+                                     namespace_flags, seclabel, nullptr, str_args);
 }
 
-Service* ServiceManager::FindServiceByName(const std::string& name) const {
-    auto svc = std::find_if(services_.begin(), services_.end(),
-                            [&name] (const std::unique_ptr<Service>& s) {
-                                return name == s->name();
-                            });
-    if (svc != services_.end()) {
-        return svc->get();
+// Shutdown services in the opposite order that they were started.
+const std::vector<Service*> ServiceList::services_in_shutdown_order() const {
+    std::vector<Service*> shutdown_services;
+    for (const auto& service : services_) {
+        if (service->start_order() > 0) shutdown_services.emplace_back(service.get());
     }
-    return nullptr;
+    std::sort(shutdown_services.begin(), shutdown_services.end(),
+              [](const auto& a, const auto& b) { return a->start_order() > b->start_order(); });
+    return shutdown_services;
 }
 
-Service* ServiceManager::FindServiceByPid(pid_t pid) const {
-    auto svc = std::find_if(services_.begin(), services_.end(),
-                            [&pid] (const std::unique_ptr<Service>& s) {
-                                return s->pid() == pid;
-                            });
-    if (svc != services_.end()) {
-        return svc->get();
-    }
-    return nullptr;
-}
-
-Service* ServiceManager::FindServiceByKeychord(int keychord_id) const {
-    auto svc = std::find_if(services_.begin(), services_.end(),
-                            [&keychord_id] (const std::unique_ptr<Service>& s) {
-                                return s->keychord_id() == keychord_id;
-                            });
-
-    if (svc != services_.end()) {
-        return svc->get();
-    }
-    return nullptr;
-}
-
-void ServiceManager::ForEachService(const std::function<void(Service*)>& callback) const {
-    for (const auto& s : services_) {
-        callback(s.get());
-    }
-}
-
-void ServiceManager::ForEachServiceInClass(const std::string& classname,
-                                           void (*func)(Service* svc)) const {
-    for (const auto& s : services_) {
-        if (s->classnames().find(classname) != s->classnames().end()) {
-            func(s.get());
-        }
-    }
-}
-
-void ServiceManager::ForEachServiceWithFlags(unsigned matchflags,
-                                             void (*func)(Service* svc)) const {
-    for (const auto& s : services_) {
-        if (s->flags() & matchflags) {
-            func(s.get());
-        }
-    }
-}
-
-void ServiceManager::RemoveService(const Service& svc) {
+void ServiceList::RemoveService(const Service& svc) {
     auto svc_it = std::find_if(services_.begin(), services_.end(),
                                [&svc] (const std::unique_ptr<Service>& s) {
                                    return svc.name() == s->name();
@@ -1092,117 +1183,59 @@
     services_.erase(svc_it);
 }
 
-void ServiceManager::DumpState() const {
+void ServiceList::DumpState() const {
     for (const auto& s : services_) {
         s->DumpState();
     }
 }
 
-bool ServiceManager::ReapOneProcess() {
-    siginfo_t siginfo = {};
-    // This returns a zombie pid or informs us that there are no zombies left to be reaped.
-    // It does NOT reap the pid; that is done below.
-    if (TEMP_FAILURE_RETRY(waitid(P_ALL, 0, &siginfo, WEXITED | WNOHANG | WNOWAIT)) != 0) {
-        PLOG(ERROR) << "waitid failed";
-        return false;
-    }
-
-    auto pid = siginfo.si_pid;
-    if (pid == 0) return false;
-
-    // At this point we know we have a zombie pid, so we use this scopeguard to reap the pid
-    // whenever the function returns from this point forward.
-    // We do NOT want to reap the zombie earlier as in Service::Reap(), we kill(-pid, ...) and we
-    // want the pid to remain valid throughout that (and potentially future) usages.
-    auto reaper = make_scope_guard([pid] { TEMP_FAILURE_RETRY(waitpid(pid, nullptr, WNOHANG)); });
-
-    if (PropertyChildReap(pid)) {
-        return true;
-    }
-
-    Service* svc = FindServiceByPid(pid);
-
-    std::string name;
-    std::string wait_string;
-    if (svc) {
-        name = StringPrintf("Service '%s' (pid %d)", svc->name().c_str(), pid);
-        if (svc->flags() & SVC_EXEC) {
-            wait_string = StringPrintf(" waiting took %f seconds",
-                                       exec_waiter_->duration().count() / 1000.0f);
-        }
-    } else {
-        name = StringPrintf("Untracked pid %d", pid);
-    }
-
-    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 (!svc) {
-        return true;
-    }
-
-    svc->Reap();
-
-    if (svc->flags() & SVC_EXEC) {
-        exec_waiter_.reset();
-    }
-    if (svc->flags() & SVC_TEMPORARY) {
-        RemoveService(*svc);
-    }
-
-    return true;
-}
-
-void ServiceManager::ReapAnyOutstandingChildren() {
-    while (ReapOneProcess()) {
-    }
-}
-
-void ServiceManager::ClearExecWait() {
-    // Clear EXEC flag if there is one pending
-    // And clear the wait flag
-    for (const auto& s : services_) {
-        s->UnSetExec();
-    }
-    exec_waiter_.reset();
-}
-
-bool ServiceParser::ParseSection(std::vector<std::string>&& args, const std::string& filename,
-                                 int line, std::string* err) {
+Result<Success> ServiceParser::ParseSection(std::vector<std::string>&& args,
+                                            const std::string& filename, int line) {
     if (args.size() < 3) {
-        *err = "services must have a name and a program";
-        return false;
+        return Error() << "services must have a name and a program";
     }
 
     const std::string& name = args[1];
     if (!IsValidName(name)) {
-        *err = StringPrintf("invalid service name '%s'", name.c_str());
-        return false;
+        return Error() << "invalid service name '" << name << "'";
     }
 
-    Service* old_service = service_manager_->FindServiceByName(name);
-    if (old_service) {
-        *err = "ignored duplicate definition of service '" + name + "'";
-        return false;
+    Subcontext* restart_action_subcontext = nullptr;
+    if (subcontexts_) {
+        for (auto& subcontext : *subcontexts_) {
+            if (StartsWith(filename, subcontext.path_prefix())) {
+                restart_action_subcontext = &subcontext;
+                break;
+            }
+        }
     }
 
     std::vector<std::string> str_args(args.begin() + 2, args.end());
-    service_ = std::make_unique<Service>(name, str_args);
-    return true;
+    service_ = std::make_unique<Service>(name, restart_action_subcontext, str_args);
+    return Success();
 }
 
-bool ServiceParser::ParseLineSection(std::vector<std::string>&& args, int line, std::string* err) {
-    return service_ ? service_->ParseLine(std::move(args), err) : false;
+Result<Success> ServiceParser::ParseLineSection(std::vector<std::string>&& args, int line) {
+    return service_ ? service_->ParseLine(std::move(args)) : Success();
 }
 
-void ServiceParser::EndSection() {
+Result<Success> ServiceParser::EndSection() {
     if (service_) {
-        service_manager_->AddService(std::move(service_));
+        Service* old_service = service_list_->FindService(service_->name());
+        if (old_service) {
+            if (!service_->is_override()) {
+                return Error() << "ignored duplicate definition of service '" << service_->name()
+                               << "'";
+            }
+
+            service_list_->RemoveService(*old_service);
+            old_service = nullptr;
+        }
+
+        service_list_->AddService(std::move(service_));
     }
+
+    return Success();
 }
 
 bool ServiceParser::IsValidName(const std::string& name) const {
@@ -1210,7 +1243,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 f682abd..ea79a07 100644
--- a/init/service.h
+++ b/init/service.h
@@ -17,6 +17,8 @@
 #ifndef _INIT_SERVICE_H
 #define _INIT_SERVICE_H
 
+#include <signal.h>
+#include <sys/resource.h>
 #include <sys/types.h>
 
 #include <memory>
@@ -30,8 +32,9 @@
 #include "action.h"
 #include "capabilities.h"
 #include "descriptors.h"
-#include "init_parser.h"
 #include "keyword_map.h"
+#include "parser.h"
+#include "subcontext.h"
 
 #define SVC_DISABLED 0x001        // do not autostart with class
 #define SVC_ONESHOT 0x002         // do not restart on exit
@@ -57,43 +60,47 @@
 namespace android {
 namespace init {
 
-struct ServiceEnvironmentInfo {
-    ServiceEnvironmentInfo();
-    ServiceEnvironmentInfo(const std::string& name, const std::string& value);
-    std::string name;
-    std::string value;
-};
-
 class Service {
   public:
-    Service(const std::string& name, const std::vector<std::string>& args);
+    Service(const std::string& name, Subcontext* subcontext_for_restart_commands,
+            const std::vector<std::string>& args);
 
     Service(const std::string& name, unsigned flags, uid_t uid, gid_t gid,
             const std::vector<gid_t>& supp_gids, const CapSet& capabilities,
             unsigned namespace_flags, const std::string& seclabel,
-            const std::vector<std::string>& args);
+            Subcontext* subcontext_for_restart_commands, const std::vector<std::string>& args);
+
+    static std::unique_ptr<Service> MakeTemporaryOneshotService(const std::vector<std::string>& args);
 
     bool IsRunning() { return (flags_ & SVC_RUNNING) != 0; }
-    bool ParseLine(const std::vector<std::string>& args, std::string* err);
-    bool ExecStart(std::unique_ptr<android::base::Timer>* exec_waiter);
-    bool Start();
-    bool StartIfNotDisabled();
-    bool Enable();
+    Result<Success> ParseLine(const std::vector<std::string>& args);
+    Result<Success> ExecStart();
+    Result<Success> Start();
+    Result<Success> StartIfNotDisabled();
+    Result<Success> Enable();
     void Reset();
     void Stop();
     void Terminate();
     void Restart();
-    void RestartIfNeeded(time_t* process_needs_restart_at);
-    void Reap();
+    void Reap(const siginfo_t& siginfo);
     void DumpState() const;
     void SetShutdownCritical() { flags_ |= SVC_SHUTDOWN_CRITICAL; }
     bool IsShutdownCritical() const { return (flags_ & SVC_SHUTDOWN_CRITICAL) != 0; }
-    void UnSetExec() { flags_ &= ~SVC_EXEC; }
+    void UnSetExec() {
+        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_; }
 
     const std::string& name() const { return name_; }
     const std::set<std::string>& classnames() const { return classnames_; }
     unsigned flags() const { return flags_; }
     pid_t pid() const { return pid_; }
+    android::base::boot_clock::time_point time_started() const { return time_started_; }
     int crash_count() const { return crash_count_; }
     uid_t uid() const { return uid_; }
     gid_t gid() const { return gid_; }
@@ -101,20 +108,24 @@
     const std::vector<gid_t>& supp_gids() const { return supp_gids_; }
     const std::string& seclabel() const { return seclabel_; }
     const std::vector<int>& keycodes() const { return keycodes_; }
-    int keychord_id() const { return keychord_id_; }
-    void set_keychord_id(int keychord_id) { keychord_id_ = keychord_id; }
     IoSchedClass ioprio_class() const { return ioprio_class_; }
     int ioprio_pri() const { return ioprio_pri_; }
+    const std::set<std::string>& interfaces() const { return interfaces_; }
     int priority() const { return priority_; }
     int oom_score_adjust() const { return oom_score_adjust_; }
+    bool is_override() const { return override_; }
     bool process_cgroup_empty() const { return process_cgroup_empty_; }
+    unsigned long start_order() const { return start_order_; }
+    void set_sigstop(bool value) { sigstop_ = value; }
     const std::vector<std::string>& args() const { return args_; }
 
   private:
-    using OptionParser = bool (Service::*) (const std::vector<std::string>& args,
-                                            std::string* err);
+    using OptionParser = Result<Success> (Service::*)(const std::vector<std::string>& args);
     class OptionParserMap;
 
+    Result<Success> SetUpMountNamespace() const;
+    Result<Success> SetUpPidNamespace() const;
+    Result<Success> EnterNamespaces() const;
     void NotifyStateChange(const std::string& new_state) const;
     void StopOrReset(int how);
     void ZapStdio() const;
@@ -122,29 +133,40 @@
     void KillProcessGroup(int signal);
     void SetProcessAttributes();
 
-    bool ParseCapabilities(const std::vector<std::string>& args, std::string *err);
-    bool ParseClass(const std::vector<std::string>& args, std::string* err);
-    bool ParseConsole(const std::vector<std::string>& args, std::string* err);
-    bool ParseCritical(const std::vector<std::string>& args, std::string* err);
-    bool ParseDisabled(const std::vector<std::string>& args, std::string* err);
-    bool ParseGroup(const std::vector<std::string>& args, std::string* err);
-    bool ParsePriority(const std::vector<std::string>& args, std::string* err);
-    bool ParseIoprio(const std::vector<std::string>& args, std::string* err);
-    bool ParseKeycodes(const std::vector<std::string>& args, std::string* err);
-    bool ParseOneshot(const std::vector<std::string>& args, std::string* err);
-    bool ParseOnrestart(const std::vector<std::string>& args, std::string* err);
-    bool ParseOomScoreAdjust(const std::vector<std::string>& args, std::string* err);
-    bool ParseNamespace(const std::vector<std::string>& args, std::string* err);
-    bool ParseSeclabel(const std::vector<std::string>& args, std::string* err);
-    bool ParseSetenv(const std::vector<std::string>& args, std::string* err);
-    bool ParseShutdown(const std::vector<std::string>& args, std::string* err);
-    bool ParseSocket(const std::vector<std::string>& args, std::string* err);
-    bool ParseFile(const std::vector<std::string>& args, std::string* err);
-    bool ParseUser(const std::vector<std::string>& args, std::string* err);
-    bool ParseWritepid(const std::vector<std::string>& args, std::string* err);
+    Result<Success> ParseCapabilities(const std::vector<std::string>& args);
+    Result<Success> ParseClass(const std::vector<std::string>& args);
+    Result<Success> ParseConsole(const std::vector<std::string>& args);
+    Result<Success> ParseCritical(const std::vector<std::string>& args);
+    Result<Success> ParseDisabled(const std::vector<std::string>& args);
+    Result<Success> ParseEnterNamespace(const std::vector<std::string>& args);
+    Result<Success> ParseGroup(const std::vector<std::string>& args);
+    Result<Success> ParsePriority(const std::vector<std::string>& args);
+    Result<Success> ParseInterface(const std::vector<std::string>& args);
+    Result<Success> ParseIoprio(const std::vector<std::string>& args);
+    Result<Success> ParseKeycodes(const std::vector<std::string>& args);
+    Result<Success> ParseOneshot(const std::vector<std::string>& args);
+    Result<Success> ParseOnrestart(const std::vector<std::string>& args);
+    Result<Success> ParseOomScoreAdjust(const std::vector<std::string>& args);
+    Result<Success> ParseOverride(const std::vector<std::string>& args);
+    Result<Success> ParseMemcgLimitInBytes(const std::vector<std::string>& args);
+    Result<Success> ParseMemcgSoftLimitInBytes(const std::vector<std::string>& args);
+    Result<Success> ParseMemcgSwappiness(const std::vector<std::string>& args);
+    Result<Success> ParseNamespace(const std::vector<std::string>& args);
+    Result<Success> ParseProcessRlimit(const std::vector<std::string>& args);
+    Result<Success> ParseSeclabel(const std::vector<std::string>& args);
+    Result<Success> ParseSetenv(const std::vector<std::string>& args);
+    Result<Success> ParseShutdown(const std::vector<std::string>& args);
+    Result<Success> ParseSigstop(const std::vector<std::string>& args);
+    Result<Success> ParseSocket(const std::vector<std::string>& args);
+    Result<Success> ParseFile(const std::vector<std::string>& args);
+    Result<Success> ParseUser(const std::vector<std::string>& args);
+    Result<Success> ParseWritepid(const std::vector<std::string>& args);
 
     template <typename T>
-    bool AddDescriptor(const std::vector<std::string>& args, std::string* err);
+    Result<Success> AddDescriptor(const std::vector<std::string>& args);
+
+    static unsigned long next_start_order_;
+    static bool is_exec_service_running_;
 
     std::string name_;
     std::set<std::string> classnames_;
@@ -161,19 +183,22 @@
     std::vector<gid_t> supp_gids_;
     CapSet capabilities_;
     unsigned namespace_flags_;
+    // Pair of namespace type, path to namespace.
+    std::vector<std::pair<int, std::string>> namespaces_to_enter_;
 
     std::string seclabel_;
 
     std::vector<std::unique_ptr<DescriptorInfo>> descriptors_;
-    std::vector<ServiceEnvironmentInfo> envvars_;
+    std::vector<std::pair<std::string, std::string>> environment_vars_;
 
     Action onrestart_;  // Commands to execute on restart.
 
     std::vector<std::string> writepid_files_;
 
-    // keycodes for triggering this service via /dev/keychord
+    std::set<std::string> interfaces_;  // e.g. some.package.foo@1.0::IBaz/instance-name
+
+    // keycodes for triggering this service via /dev/input/input*
     std::vector<int> keycodes_;
-    int keychord_id_;
 
     IoSchedClass ioprio_class_;
     int ioprio_pri_;
@@ -181,60 +206,82 @@
 
     int oom_score_adjust_;
 
+    int swappiness_;
+    int soft_limit_in_bytes_;
+    int limit_in_bytes_;
+
     bool process_cgroup_empty_ = false;
 
+    bool override_ = false;
+
+    unsigned long start_order_;
+
+    std::vector<std::pair<int, rlimit>> rlimits_;
+
+    bool sigstop_ = false;
+
     std::vector<std::string> args_;
+
+    std::vector<std::function<void(const siginfo_t& siginfo)>> reap_callbacks_;
 };
 
-class ServiceManager {
+class ServiceList {
   public:
-    static ServiceManager& GetInstance();
+    static ServiceList& GetInstance();
 
     // Exposed for testing
-    ServiceManager();
+    ServiceList();
 
     void AddService(std::unique_ptr<Service> service);
-    Service* MakeExecOneshotService(const std::vector<std::string>& args);
-    bool Exec(const std::vector<std::string>& args);
-    bool ExecStart(const std::string& name);
-    bool IsWaitingForExec() const;
-    Service* FindServiceByName(const std::string& name) const;
-    Service* FindServiceByPid(pid_t pid) const;
-    Service* FindServiceByKeychord(int keychord_id) const;
-    void ForEachService(const std::function<void(Service*)>& callback) const;
-    void ForEachServiceInClass(const std::string& classname,
-                               void (*func)(Service* svc)) const;
-    void ForEachServiceWithFlags(unsigned matchflags,
-                             void (*func)(Service* svc)) const;
-    void ReapAnyOutstandingChildren();
     void RemoveService(const Service& svc);
+
+    template <typename T, typename F = decltype(&Service::name)>
+    Service* FindService(T value, F function = &Service::name) const {
+        auto svc = std::find_if(services_.begin(), services_.end(),
+                                [&function, &value](const std::unique_ptr<Service>& s) {
+                                    return std::invoke(function, s) == value;
+                                });
+        if (svc != services_.end()) {
+            return svc->get();
+        }
+        return nullptr;
+    }
+
+    Service* FindInterface(const std::string& interface_name) {
+        for (const auto& svc : services_) {
+            if (svc->interfaces().count(interface_name) > 0) {
+                return svc.get();
+            }
+        }
+
+        return nullptr;
+    }
+
     void DumpState() const;
-    void ClearExecWait();
+
+    auto begin() const { return services_.begin(); }
+    auto end() const { return services_.end(); }
+    const std::vector<std::unique_ptr<Service>>& services() const { return services_; }
+    const std::vector<Service*> services_in_shutdown_order() const;
 
   private:
-    // Cleans up a child process that exited.
-    // Returns true iff a children was cleaned up.
-    bool ReapOneProcess();
-
-    static int exec_count_; // Every service needs a unique name.
-    std::unique_ptr<android::base::Timer> exec_waiter_;
-
     std::vector<std::unique_ptr<Service>> services_;
 };
 
 class ServiceParser : public SectionParser {
   public:
-    ServiceParser(ServiceManager* service_manager)
-        : service_manager_(service_manager), service_(nullptr) {}
-    bool ParseSection(std::vector<std::string>&& args, const std::string& filename, int line,
-                      std::string* err) override;
-    bool ParseLineSection(std::vector<std::string>&& args, int line, std::string* err) override;
-    void EndSection() override;
+    ServiceParser(ServiceList* service_list, std::vector<Subcontext>* subcontexts)
+        : service_list_(service_list), subcontexts_(subcontexts), service_(nullptr) {}
+    Result<Success> ParseSection(std::vector<std::string>&& args, const std::string& filename,
+                                 int line) override;
+    Result<Success> ParseLineSection(std::vector<std::string>&& args, int line) override;
+    Result<Success> EndSection() override;
 
   private:
     bool IsValidName(const std::string& name) const;
 
-    ServiceManager* service_manager_;
+    ServiceList* service_list_;
+    std::vector<Subcontext>* subcontexts_;
     std::unique_ptr<Service> service_;
 };
 
diff --git a/init/service_test.cpp b/init/service_test.cpp
index 44f28a3..194aa2b 100644
--- a/init/service_test.cpp
+++ b/init/service_test.cpp
@@ -23,6 +23,8 @@
 
 #include <gtest/gtest.h>
 
+#include "util.h"
+
 namespace android {
 namespace init {
 
@@ -35,7 +37,8 @@
     }
 
     std::vector<std::string> dummy_args{"/bin/test"};
-    Service* service_in_old_memory = new (old_memory) Service("test_old_memory", dummy_args);
+    Service* service_in_old_memory =
+        new (old_memory) Service("test_old_memory", nullptr, dummy_args);
 
     EXPECT_EQ(0U, service_in_old_memory->flags());
     EXPECT_EQ(0, service_in_old_memory->pid());
@@ -43,7 +46,6 @@
     EXPECT_EQ(0U, service_in_old_memory->uid());
     EXPECT_EQ(0U, service_in_old_memory->gid());
     EXPECT_EQ(0U, service_in_old_memory->namespace_flags());
-    EXPECT_EQ(0, service_in_old_memory->keychord_id());
     EXPECT_EQ(IoSchedClass_NONE, service_in_old_memory->ioprio_class());
     EXPECT_EQ(0, service_in_old_memory->ioprio_pri());
     EXPECT_EQ(0, service_in_old_memory->priority());
@@ -54,8 +56,8 @@
         old_memory[i] = 0xFF;
     }
 
-    Service* service_in_old_memory2 = new (old_memory)
-        Service("test_old_memory", 0U, 0U, 0U, std::vector<gid_t>(), CapSet(), 0U, "", dummy_args);
+    Service* service_in_old_memory2 = new (old_memory) Service(
+        "test_old_memory", 0U, 0U, 0U, std::vector<gid_t>(), CapSet(), 0U, "", nullptr, dummy_args);
 
     EXPECT_EQ(0U, service_in_old_memory2->flags());
     EXPECT_EQ(0, service_in_old_memory2->pid());
@@ -63,7 +65,6 @@
     EXPECT_EQ(0U, service_in_old_memory2->uid());
     EXPECT_EQ(0U, service_in_old_memory2->gid());
     EXPECT_EQ(0U, service_in_old_memory2->namespace_flags());
-    EXPECT_EQ(0, service_in_old_memory2->keychord_id());
     EXPECT_EQ(IoSchedClass_NONE, service_in_old_memory2->ioprio_class());
     EXPECT_EQ(0, service_in_old_memory2->ioprio_pri());
     EXPECT_EQ(0, service_in_old_memory2->priority());
@@ -71,5 +72,120 @@
     EXPECT_FALSE(service_in_old_memory->process_cgroup_empty());
 }
 
+TEST(service, make_temporary_oneshot_service_invalid_syntax) {
+    std::vector<std::string> args;
+    // Nothing.
+    ASSERT_EQ(nullptr, Service::MakeTemporaryOneshotService(args));
+
+    // No arguments to 'exec'.
+    args.push_back("exec");
+    ASSERT_EQ(nullptr, Service::MakeTemporaryOneshotService(args));
+
+    // No command in "exec --".
+    args.push_back("--");
+    ASSERT_EQ(nullptr, Service::MakeTemporaryOneshotService(args));
+}
+
+TEST(service, make_temporary_oneshot_service_too_many_supplementary_gids) {
+    std::vector<std::string> args;
+    args.push_back("exec");
+    args.push_back("seclabel");
+    args.push_back("root");  // uid.
+    args.push_back("root");  // gid.
+    for (int i = 0; i < NR_SVC_SUPP_GIDS; ++i) {
+        args.push_back("root");  // Supplementary gid.
+    }
+    args.push_back("--");
+    args.push_back("/system/bin/id");
+    ASSERT_EQ(nullptr, Service::MakeTemporaryOneshotService(args));
+}
+
+static void Test_make_temporary_oneshot_service(bool dash_dash, bool seclabel, bool uid, bool gid,
+                                                bool supplementary_gids) {
+    std::vector<std::string> args;
+    args.push_back("exec");
+    if (seclabel) {
+        args.push_back("u:r:su:s0");  // seclabel
+        if (uid) {
+            args.push_back("log");  // uid
+            if (gid) {
+                args.push_back("shell");  // gid
+                if (supplementary_gids) {
+                    args.push_back("system");  // supplementary gid 0
+                    args.push_back("adb");     // supplementary gid 1
+                }
+            }
+        }
+    }
+    if (dash_dash) {
+        args.push_back("--");
+    }
+    args.push_back("/system/bin/toybox");
+    args.push_back("id");
+    auto svc = Service::MakeTemporaryOneshotService(args);
+    ASSERT_NE(nullptr, svc);
+
+    if (seclabel) {
+        ASSERT_EQ("u:r:su:s0", svc->seclabel());
+    } else {
+        ASSERT_EQ("", svc->seclabel());
+    }
+    if (uid) {
+        auto decoded_uid = DecodeUid("log");
+        ASSERT_TRUE(decoded_uid);
+        ASSERT_EQ(*decoded_uid, svc->uid());
+    } else {
+        ASSERT_EQ(0U, svc->uid());
+    }
+    if (gid) {
+        auto decoded_uid = DecodeUid("shell");
+        ASSERT_TRUE(decoded_uid);
+        ASSERT_EQ(*decoded_uid, svc->gid());
+    } else {
+        ASSERT_EQ(0U, svc->gid());
+    }
+    if (supplementary_gids) {
+        ASSERT_EQ(2U, svc->supp_gids().size());
+
+        auto decoded_uid = DecodeUid("system");
+        ASSERT_TRUE(decoded_uid);
+        ASSERT_EQ(*decoded_uid, svc->supp_gids()[0]);
+
+        decoded_uid = DecodeUid("adb");
+        ASSERT_TRUE(decoded_uid);
+        ASSERT_EQ(*decoded_uid, svc->supp_gids()[1]);
+    } else {
+        ASSERT_EQ(0U, svc->supp_gids().size());
+    }
+
+    ASSERT_EQ(static_cast<std::size_t>(2), svc->args().size());
+    ASSERT_EQ("/system/bin/toybox", svc->args()[0]);
+    ASSERT_EQ("id", svc->args()[1]);
+}
+
+TEST(service, make_temporary_oneshot_service_with_everything) {
+    Test_make_temporary_oneshot_service(true, true, true, true, true);
+}
+
+TEST(service, make_temporary_oneshot_service_with_seclabel_uid_gid) {
+    Test_make_temporary_oneshot_service(true, true, true, true, false);
+}
+
+TEST(service, make_temporary_oneshot_service_with_seclabel_uid) {
+    Test_make_temporary_oneshot_service(true, true, true, false, false);
+}
+
+TEST(service, make_temporary_oneshot_service_with_seclabel) {
+    Test_make_temporary_oneshot_service(true, true, false, false, false);
+}
+
+TEST(service, make_temporary_oneshot_service_with_just_command) {
+    Test_make_temporary_oneshot_service(true, false, false, false, false);
+}
+
+TEST(service, make_temporary_oneshot_service_with_just_command_no_dash) {
+    Test_make_temporary_oneshot_service(false, false, false, false, false);
+}
+
 }  // namespace init
 }  // namespace android
diff --git a/init/sigchld_handler.cpp b/init/sigchld_handler.cpp
new file mode 100644
index 0000000..0b03324
--- /dev/null
+++ b/init/sigchld_handler.cpp
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2010 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 "sigchld_handler.h"
+
+#include <signal.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <android-base/chrono_utils.h>
+#include <android-base/logging.h>
+#include <android-base/scopeguard.h>
+#include <android-base/stringprintf.h>
+
+#include "init.h"
+#include "property_service.h"
+#include "service.h"
+
+using android::base::StringPrintf;
+using android::base::boot_clock;
+using android::base::make_scope_guard;
+
+namespace android {
+namespace init {
+
+static bool ReapOneProcess() {
+    siginfo_t siginfo = {};
+    // This returns a zombie pid or informs us that there are no zombies left to be reaped.
+    // It does NOT reap the pid; that is done below.
+    if (TEMP_FAILURE_RETRY(waitid(P_ALL, 0, &siginfo, WEXITED | WNOHANG | WNOWAIT)) != 0) {
+        PLOG(ERROR) << "waitid failed";
+        return false;
+    }
+
+    auto pid = siginfo.si_pid;
+    if (pid == 0) return false;
+
+    // At this point we know we have a zombie pid, so we use this scopeguard to reap the pid
+    // whenever the function returns from this point forward.
+    // We do NOT want to reap the zombie earlier as in Service::Reap(), we kill(-pid, ...) and we
+    // want the pid to remain valid throughout that (and potentially future) usages.
+    auto reaper = make_scope_guard([pid] { TEMP_FAILURE_RETRY(waitpid(pid, nullptr, WNOHANG)); });
+
+    std::string name;
+    std::string wait_string;
+    Service* service = nullptr;
+
+    if (PropertyChildReap(pid)) {
+        name = "Async property child";
+    } else if (SubcontextChildReap(pid)) {
+        name = "Subcontext";
+    } else {
+        service = ServiceList::GetInstance().FindService(pid, &Service::pid);
+
+        if (service) {
+            name = StringPrintf("Service '%s' (pid %d)", service->name().c_str(), pid);
+            if (service->flags() & SVC_EXEC) {
+                auto exec_duration = boot_clock::now() - service->time_started();
+                auto exec_duration_ms =
+                    std::chrono::duration_cast<std::chrono::milliseconds>(exec_duration).count();
+                wait_string = StringPrintf(" waiting took %f seconds", exec_duration_ms / 1000.0f);
+            }
+        } else {
+            name = StringPrintf("Untracked pid %d", pid);
+        }
+    }
+
+    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;
+    }
+
+    if (!service) return true;
+
+    service->Reap(siginfo);
+
+    if (service->flags() & SVC_TEMPORARY) {
+        ServiceList::GetInstance().RemoveService(*service);
+    }
+
+    return true;
+}
+
+void ReapAnyOutstandingChildren() {
+    while (ReapOneProcess()) {
+    }
+}
+
+}  // namespace init
+}  // namespace android
diff --git a/init/sigchld_handler.h b/init/sigchld_handler.h
new file mode 100644
index 0000000..30063f2
--- /dev/null
+++ b/init/sigchld_handler.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2010 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_SIGCHLD_HANDLER_H_
+#define _INIT_SIGCHLD_HANDLER_H_
+
+namespace android {
+namespace init {
+
+void ReapAnyOutstandingChildren();
+
+}  // namespace init
+}  // namespace android
+
+#endif
diff --git a/init/signal_handler.cpp b/init/signal_handler.cpp
deleted file mode 100644
index db1bfcf..0000000
--- a/init/signal_handler.cpp
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Copyright (C) 2010 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 <signal.h>
-#include <string.h>
-#include <sys/socket.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include <android-base/logging.h>
-
-#include "init.h"
-#include "service.h"
-
-namespace android {
-namespace init {
-
-static int signal_write_fd = -1;
-static int signal_read_fd = -1;
-
-static void handle_signal() {
-    // Clear outstanding requests.
-    char buf[32];
-    read(signal_read_fd, buf, sizeof(buf));
-
-    ServiceManager::GetInstance().ReapAnyOutstandingChildren();
-}
-
-static void SIGCHLD_handler(int) {
-    if (TEMP_FAILURE_RETRY(write(signal_write_fd, "1", 1)) == -1) {
-        PLOG(ERROR) << "write(signal_write_fd) failed";
-    }
-}
-
-void signal_handler_init() {
-    // Create a signalling mechanism for SIGCHLD.
-    int s[2];
-    if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0, s) == -1) {
-        PLOG(ERROR) << "socketpair failed";
-        exit(1);
-    }
-
-    signal_write_fd = s[0];
-    signal_read_fd = s[1];
-
-    // Write to signal_write_fd if we catch SIGCHLD.
-    struct sigaction act;
-    memset(&act, 0, sizeof(act));
-    act.sa_handler = SIGCHLD_handler;
-    act.sa_flags = SA_NOCLDSTOP;
-    sigaction(SIGCHLD, &act, 0);
-
-    ServiceManager::GetInstance().ReapAnyOutstandingChildren();
-
-    register_epoll_handler(signal_read_fd, handle_signal);
-}
-
-}  // namespace init
-}  // namespace android
diff --git a/init/signal_handler.h b/init/signal_handler.h
deleted file mode 100644
index f7881ab..0000000
--- a/init/signal_handler.h
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Copyright (C) 2010 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_SIGNAL_HANDLER_H_
-#define _INIT_SIGNAL_HANDLER_H_
-
-namespace android {
-namespace init {
-
-void signal_handler_init(void);
-
-}  // namespace init
-}  // namespace android
-
-#endif
diff --git a/init/stable_properties.h b/init/stable_properties.h
new file mode 100644
index 0000000..05b2acb
--- /dev/null
+++ b/init/stable_properties.h
@@ -0,0 +1,65 @@
+/*
+ * 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_STABLE_PROPERTIES_H
+#define _INIT_STABLE_PROPERTIES_H
+
+#include <set>
+#include <string>
+
+namespace android {
+namespace init {
+
+static constexpr const char* kPartnerPrefixes[] = {
+    "init.svc.vendor.", "ro.vendor.", "persist.vendor.", "vendor.", "init.svc.odm.", "ro.odm.",
+    "persist.odm.",     "odm.",       "ro.boot.",
+};
+
+static const std::set<std::string> kExportedActionableProperties = {
+    "dev.bootcomplete",
+    "init.svc.console",
+    "init.svc.mediadrm",
+    "init.svc.surfaceflinger",
+    "init.svc.zygote",
+    "persist.bluetooth.btsnoopenable",
+    "persist.sys.crash_rcu",
+    "persist.sys.zram_enabled",
+    "ro.board.platform",
+    "ro.bootmode",
+    "ro.build.type",
+    "ro.crypto.state",
+    "ro.crypto.type",
+    "ro.debuggable",
+    "sys.boot_completed",
+    "sys.boot_from_charger_mode",
+    "sys.retaildemo.enabled",
+    "sys.shutdown.requested",
+    "sys.usb.config",
+    "sys.usb.configfs",
+    "sys.usb.ffs.mtp.ready",
+    "sys.usb.ffs.ready",
+    "sys.user.0.ce_available",
+    "sys.vdso",
+    "vold.decrypt",
+    "vold.post_fs_data_done",
+    "vts.native_server.on",
+    "wlan.driver.status",
+};
+
+}  // namespace init
+}  // namespace android
+
+#endif
diff --git a/init/subcontext.cpp b/init/subcontext.cpp
new file mode 100644
index 0000000..ee72513
--- /dev/null
+++ b/init/subcontext.cpp
@@ -0,0 +1,386 @@
+/*
+ * Copyright (C) 2017 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 "subcontext.h"
+
+#include <fcntl.h>
+#include <poll.h>
+#include <sys/socket.h>
+#include <unistd.h>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/strings.h>
+#include <selinux/android.h>
+
+#include "action.h"
+#include "util.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;
+using android::base::Socketpair;
+using android::base::Split;
+using android::base::StartsWith;
+using android::base::unique_fd;
+
+namespace android {
+namespace init {
+
+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;
+
+Result<std::string> ReadMessage(int socket) {
+    char buffer[kBufferSize] = {};
+    auto result = TEMP_FAILURE_RETRY(recv(socket, buffer, sizeof(buffer), 0));
+    if (result <= 0) {
+        return ErrnoError();
+    }
+    return std::string(buffer, result);
+}
+
+template <typename T>
+Result<Success> SendMessage(int socket, const T& message) {
+    std::string message_string;
+    if (!message.SerializeToString(&message_string)) {
+        return Error() << "Unable to serialize message";
+    }
+
+    if (message_string.size() > kBufferSize) {
+        return Error() << "Serialized message too long to send";
+    }
+
+    if (auto result =
+            TEMP_FAILURE_RETRY(send(socket, message_string.c_str(), message_string.size(), 0));
+        result != static_cast<long>(message_string.size())) {
+        return ErrnoError() << "send() failed to send message contents";
+    }
+    return Success();
+}
+
+std::vector<std::pair<std::string, std::string>> properties_to_set;
+
+uint32_t SubcontextPropertySet(const std::string& name, const std::string& value) {
+    properties_to_set.emplace_back(name, value);
+    return 0;
+}
+
+class SubcontextProcess {
+  public:
+    SubcontextProcess(const KeywordFunctionMap* function_map, std::string context, int init_fd)
+        : function_map_(function_map), context_(std::move(context)), init_fd_(init_fd){};
+    void MainLoop();
+
+  private:
+    void RunCommand(const SubcontextCommand::ExecuteCommand& execute_command,
+                    SubcontextReply* reply) const;
+    void ExpandArgs(const SubcontextCommand::ExpandArgsCommand& expand_args_command,
+                    SubcontextReply* reply) const;
+
+    const KeywordFunctionMap* function_map_;
+    const std::string context_;
+    const int init_fd_;
+};
+
+void SubcontextProcess::RunCommand(const SubcontextCommand::ExecuteCommand& execute_command,
+                                   SubcontextReply* reply) const {
+    // Need to use ArraySplice instead of this code.
+    auto args = std::vector<std::string>();
+    for (const auto& string : execute_command.args()) {
+        args.emplace_back(string);
+    }
+
+    auto map_result = function_map_->FindFunction(args);
+    Result<Success> result;
+    if (!map_result) {
+        result = Error() << "Cannot find command: " << map_result.error();
+    } else {
+        result = RunBuiltinFunction(map_result->second, args, context_);
+    }
+
+    for (const auto& [name, value] : properties_to_set) {
+        auto property = reply->add_properties_to_set();
+        property->set_name(name);
+        property->set_value(value);
+    }
+
+    properties_to_set.clear();
+
+    if (result) {
+        reply->set_success(true);
+    } else {
+        auto* failure = reply->mutable_failure();
+        failure->set_error_string(result.error_string());
+        failure->set_error_errno(result.error_errno());
+    }
+}
+
+void SubcontextProcess::ExpandArgs(const SubcontextCommand::ExpandArgsCommand& expand_args_command,
+                                   SubcontextReply* reply) const {
+    for (const auto& arg : expand_args_command.args()) {
+        auto expanded_prop = std::string{};
+        if (!expand_props(arg, &expanded_prop)) {
+            auto* failure = reply->mutable_failure();
+            failure->set_error_string("Failed to expand '" + arg + "'");
+            failure->set_error_errno(0);
+            return;
+        } else {
+            auto* expand_args_reply = reply->mutable_expand_args_reply();
+            expand_args_reply->add_expanded_args(expanded_prop);
+        }
+    }
+}
+
+void SubcontextProcess::MainLoop() {
+    pollfd ufd[1];
+    ufd[0].events = POLLIN;
+    ufd[0].fd = init_fd_;
+
+    while (true) {
+        ufd[0].revents = 0;
+        int nr = TEMP_FAILURE_RETRY(poll(ufd, arraysize(ufd), -1));
+        if (nr == 0) continue;
+        if (nr < 0) {
+            PLOG(FATAL) << "poll() of subcontext socket failed, continuing";
+        }
+
+        auto init_message = ReadMessage(init_fd_);
+        if (!init_message) {
+            LOG(FATAL) << "Could not read message from init: " << init_message.error();
+        }
+
+        auto subcontext_command = SubcontextCommand();
+        if (!subcontext_command.ParseFromString(*init_message)) {
+            LOG(FATAL) << "Unable to parse message from init";
+        }
+
+        auto reply = SubcontextReply();
+        switch (subcontext_command.command_case()) {
+            case SubcontextCommand::kExecuteCommand: {
+                RunCommand(subcontext_command.execute_command(), &reply);
+                break;
+            }
+            case SubcontextCommand::kExpandArgsCommand: {
+                ExpandArgs(subcontext_command.expand_args_command(), &reply);
+                break;
+            }
+            default:
+                LOG(FATAL) << "Unknown message type from init: "
+                           << subcontext_command.command_case();
+        }
+
+        if (auto result = SendMessage(init_fd_, reply); !result) {
+            LOG(FATAL) << "Failed to send message to init: " << result.error();
+        }
+    }
+}
+
+}  // namespace
+
+int SubcontextMain(int argc, char** argv, const KeywordFunctionMap* function_map) {
+    if (argc < 4) LOG(FATAL) << "Fewer than 4 args specified to subcontext (" << argc << ")";
+
+    auto context = std::string(argv[2]);
+    auto init_fd = std::atoi(argv[3]);
+
+    SelabelInitialize();
+
+    property_set = SubcontextPropertySet;
+
+    auto subcontext_process = SubcontextProcess(function_map, context, init_fd);
+    subcontext_process.MainLoop();
+    return 0;
+}
+
+void Subcontext::Fork() {
+    unique_fd subcontext_socket;
+    if (!Socketpair(AF_UNIX, SOCK_SEQPACKET | SOCK_CLOEXEC, 0, &socket_, &subcontext_socket)) {
+        LOG(FATAL) << "Could not create socket pair to communicate to subcontext";
+        return;
+    }
+
+    auto result = fork();
+
+    if (result == -1) {
+        LOG(FATAL) << "Could not fork subcontext";
+    } else if (result == 0) {
+        socket_.reset();
+
+        // We explicitly do not use O_CLOEXEC here, such that we can reference this FD by number
+        // in the subcontext process after we exec.
+        int child_fd = dup(subcontext_socket);
+        if (child_fd < 0) {
+            PLOG(FATAL) << "Could not dup child_fd";
+        }
+
+        if (setexeccon(context_.c_str()) < 0) {
+            PLOG(FATAL) << "Could not set execcon for '" << context_ << "'";
+        }
+
+        auto init_path = GetExecutablePath();
+        auto child_fd_string = std::to_string(child_fd);
+        const char* args[] = {init_path.c_str(), "subcontext", context_.c_str(),
+                              child_fd_string.c_str(), nullptr};
+        execv(init_path.data(), const_cast<char**>(args));
+
+        PLOG(FATAL) << "Could not execv subcontext init";
+    } else {
+        subcontext_socket.reset();
+        pid_ = result;
+        LOG(INFO) << "Forked subcontext for '" << context_ << "' with pid " << pid_;
+    }
+}
+
+void Subcontext::Restart() {
+    LOG(ERROR) << "Restarting subcontext '" << context_ << "'";
+    if (pid_) {
+        kill(pid_, SIGKILL);
+    }
+    pid_ = 0;
+    socket_.reset();
+    Fork();
+}
+
+Result<SubcontextReply> Subcontext::TransmitMessage(const SubcontextCommand& subcontext_command) {
+    if (auto result = SendMessage(socket_, subcontext_command); !result) {
+        Restart();
+        return ErrnoError() << "Failed to send message to subcontext";
+    }
+
+    auto subcontext_message = ReadMessage(socket_);
+    if (!subcontext_message) {
+        Restart();
+        return Error() << "Failed to receive result from subcontext: " << subcontext_message.error();
+    }
+
+    auto subcontext_reply = SubcontextReply{};
+    if (!subcontext_reply.ParseFromString(*subcontext_message)) {
+        Restart();
+        return Error() << "Unable to parse message from subcontext";
+    }
+    return subcontext_reply;
+}
+
+Result<Success> Subcontext::Execute(const std::vector<std::string>& args) {
+    auto subcontext_command = SubcontextCommand();
+    std::copy(
+        args.begin(), args.end(),
+        RepeatedPtrFieldBackInserter(subcontext_command.mutable_execute_command()->mutable_args()));
+
+    auto subcontext_reply = TransmitMessage(subcontext_command);
+    if (!subcontext_reply) {
+        return subcontext_reply.error();
+    }
+
+    for (const auto& property : subcontext_reply->properties_to_set()) {
+        ucred cr = {.pid = pid_, .uid = 0, .gid = 0};
+        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) {
+        auto& failure = subcontext_reply->failure();
+        return ResultError(failure.error_string(), failure.error_errno());
+    }
+
+    if (subcontext_reply->reply_case() != SubcontextReply::kSuccess) {
+        return Error() << "Unexpected message type from subcontext: "
+                       << subcontext_reply->reply_case();
+    }
+
+    return Success();
+}
+
+Result<std::vector<std::string>> Subcontext::ExpandArgs(const std::vector<std::string>& args) {
+    auto subcontext_command = SubcontextCommand{};
+    std::copy(args.begin(), args.end(),
+              RepeatedPtrFieldBackInserter(
+                  subcontext_command.mutable_expand_args_command()->mutable_args()));
+
+    auto subcontext_reply = TransmitMessage(subcontext_command);
+    if (!subcontext_reply) {
+        return subcontext_reply.error();
+    }
+
+    if (subcontext_reply->reply_case() == SubcontextReply::kFailure) {
+        auto& failure = subcontext_reply->failure();
+        return ResultError(failure.error_string(), failure.error_errno());
+    }
+
+    if (subcontext_reply->reply_case() != SubcontextReply::kExpandArgsReply) {
+        return Error() << "Unexpected message type from subcontext: "
+                       << subcontext_reply->reply_case();
+    }
+
+    auto& reply = subcontext_reply->expand_args_reply();
+    auto expanded_args = std::vector<std::string>{};
+    for (const auto& string : reply.expanded_args()) {
+        expanded_args.emplace_back(string);
+    }
+    return expanded_args;
+}
+
+static std::vector<Subcontext> subcontexts;
+static bool shutting_down;
+
+std::vector<Subcontext>* InitializeSubcontexts() {
+    if (SelinuxHasVendorInit()) {
+        for (const auto& [path_prefix, secontext] : paths_and_secontexts) {
+            subcontexts.emplace_back(path_prefix, secontext);
+        }
+    }
+    return &subcontexts;
+}
+
+bool SubcontextChildReap(pid_t pid) {
+    for (auto& subcontext : subcontexts) {
+        if (subcontext.pid() == pid) {
+            if (!shutting_down) {
+                subcontext.Restart();
+            }
+            return true;
+        }
+    }
+    return false;
+}
+
+void SubcontextTerminate() {
+    shutting_down = true;
+    for (auto& subcontext : subcontexts) {
+        kill(subcontext.pid(), SIGTERM);
+    }
+}
+
+}  // namespace init
+}  // namespace android
diff --git a/init/subcontext.h b/init/subcontext.h
new file mode 100644
index 0000000..628fd50
--- /dev/null
+++ b/init/subcontext.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2017 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_SUBCONTEXT_H
+#define _INIT_SUBCONTEXT_H
+
+#include <signal.h>
+
+#include <string>
+#include <vector>
+
+#include <android-base/unique_fd.h>
+
+#include "builtins.h"
+#include "result.h"
+#include "system/core/init/subcontext.pb.h"
+
+namespace android {
+namespace init {
+
+extern const std::string kInitContext;
+extern const std::string kVendorContext;
+extern const char* const paths_and_secontexts[2][2];
+
+class Subcontext {
+  public:
+    Subcontext(std::string path_prefix, std::string context)
+        : path_prefix_(std::move(path_prefix)), context_(std::move(context)), pid_(0) {
+        Fork();
+    }
+
+    Result<Success> Execute(const std::vector<std::string>& args);
+    Result<std::vector<std::string>> ExpandArgs(const std::vector<std::string>& args);
+    void Restart();
+
+    const std::string& path_prefix() const { return path_prefix_; }
+    const std::string& context() const { return context_; }
+    pid_t pid() const { return pid_; }
+
+  private:
+    void Fork();
+    Result<SubcontextReply> TransmitMessage(const SubcontextCommand& subcontext_command);
+
+    std::string path_prefix_;
+    std::string context_;
+    pid_t pid_;
+    android::base::unique_fd socket_;
+};
+
+int SubcontextMain(int argc, char** argv, const KeywordFunctionMap* function_map);
+std::vector<Subcontext>* InitializeSubcontexts();
+bool SubcontextChildReap(pid_t pid);
+void SubcontextTerminate();
+
+}  // namespace init
+}  // namespace android
+
+#endif
diff --git a/init/subcontext.proto b/init/subcontext.proto
new file mode 100644
index 0000000..c31f4fb
--- /dev/null
+++ b/init/subcontext.proto
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+syntax = "proto2";
+option optimize_for = LITE_RUNTIME;
+
+message SubcontextCommand {
+    message ExecuteCommand { repeated string args = 1; }
+    message ExpandArgsCommand { repeated string args = 1; }
+    oneof command {
+        ExecuteCommand execute_command = 1;
+        ExpandArgsCommand expand_args_command = 2;
+    }
+}
+
+message SubcontextReply {
+    message Failure {
+        optional string error_string = 1;
+        optional int32 error_errno = 2;
+    }
+    message ExpandArgsReply { repeated string expanded_args = 1; }
+
+    oneof reply {
+        bool success = 1;
+        Failure failure = 2;
+        ExpandArgsReply expand_args_reply = 3;
+    }
+
+    message PropertyToSet {
+        optional string name = 1;
+        optional string value = 2;
+    }
+    repeated PropertyToSet properties_to_set = 4;
+}
\ No newline at end of file
diff --git a/init/subcontext_benchmark.cpp b/init/subcontext_benchmark.cpp
new file mode 100644
index 0000000..6307993
--- /dev/null
+++ b/init/subcontext_benchmark.cpp
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2017 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 "subcontext.h"
+
+#include <benchmark/benchmark.h>
+#include <selinux/selinux.h>
+
+#include "test_function_map.h"
+
+namespace android {
+namespace init {
+
+static void BenchmarkSuccess(benchmark::State& state) {
+    if (getuid() != 0) {
+        state.SkipWithError("Skipping benchmark, must be run as root.");
+        return;
+    }
+    char* context;
+    if (getcon(&context) != 0) {
+        state.SkipWithError("getcon() failed");
+        return;
+    }
+
+    auto subcontext = Subcontext("path", context);
+    free(context);
+
+    while (state.KeepRunning()) {
+        subcontext.Execute(std::vector<std::string>{"return_success"});
+    }
+
+    if (subcontext.pid() > 0) {
+        kill(subcontext.pid(), SIGTERM);
+        kill(subcontext.pid(), SIGKILL);
+    }
+}
+
+BENCHMARK(BenchmarkSuccess);
+
+TestFunctionMap BuildTestFunctionMap() {
+    TestFunctionMap test_function_map;
+    test_function_map.Add("return_success", 0, 0, true,
+                          [](const BuiltinArguments& args) { return Success(); });
+
+    return test_function_map;
+}
+
+}  // namespace init
+}  // namespace android
+
+int main(int argc, char** argv) {
+    if (argc > 1 && !strcmp(basename(argv[1]), "subcontext")) {
+        auto test_function_map = android::init::BuildTestFunctionMap();
+        return android::init::SubcontextMain(argc, argv, &test_function_map);
+    }
+
+    ::benchmark::Initialize(&argc, argv);
+    if (::benchmark::ReportUnrecognizedArguments(argc, argv)) return 1;
+    ::benchmark::RunSpecifiedBenchmarks();
+}
diff --git a/init/subcontext_test.cpp b/init/subcontext_test.cpp
new file mode 100644
index 0000000..230203a
--- /dev/null
+++ b/init/subcontext_test.cpp
@@ -0,0 +1,228 @@
+/*
+ * Copyright (C) 2017 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 "subcontext.h"
+
+#include <unistd.h>
+
+#include <chrono>
+
+#include <android-base/properties.h>
+#include <android-base/strings.h>
+#include <gtest/gtest.h>
+#include <selinux/selinux.h>
+
+#include "builtin_arguments.h"
+#include "test_function_map.h"
+
+using namespace std::literals;
+
+using android::base::GetProperty;
+using android::base::Join;
+using android::base::SetProperty;
+using android::base::Split;
+using android::base::WaitForProperty;
+
+namespace android {
+namespace init {
+
+// I would use test fixtures, but I cannot skip the test if not root with them, so instead we have
+// this test runner.
+template <typename F>
+void RunTest(F&& test_function) {
+    if (getuid() != 0) {
+        GTEST_LOG_(INFO) << "Skipping test, must be run as root.";
+        return;
+    }
+
+    char* context;
+    ASSERT_EQ(0, getcon(&context));
+    auto context_string = std::string(context);
+    free(context);
+
+    auto subcontext = Subcontext("dummy_path", context_string);
+    ASSERT_NE(0, subcontext.pid());
+
+    test_function(subcontext, context_string);
+
+    if (subcontext.pid() > 0) {
+        kill(subcontext.pid(), SIGTERM);
+        kill(subcontext.pid(), SIGKILL);
+    }
+}
+
+TEST(subcontext, CheckDifferentPid) {
+    RunTest([](auto& subcontext, auto& context_string) {
+        auto result = subcontext.Execute(std::vector<std::string>{"return_pids_as_error"});
+        ASSERT_FALSE(result);
+
+        auto pids = Split(result.error_string(), " ");
+        ASSERT_EQ(2U, pids.size());
+        auto our_pid = std::to_string(getpid());
+        EXPECT_NE(our_pid, pids[0]);
+        EXPECT_EQ(our_pid, pids[1]);
+    });
+}
+
+TEST(subcontext, SetProp) {
+    RunTest([](auto& subcontext, auto& context_string) {
+        SetProperty("init.test.subcontext", "fail");
+        WaitForProperty("init.test.subcontext", "fail");
+
+        auto args = std::vector<std::string>{
+            "setprop",
+            "init.test.subcontext",
+            "success",
+        };
+        auto result = subcontext.Execute(args);
+        ASSERT_TRUE(result) << result.error();
+
+        EXPECT_TRUE(WaitForProperty("init.test.subcontext", "success", 10s));
+    });
+}
+
+TEST(subcontext, MultipleCommands) {
+    RunTest([](auto& subcontext, auto& context_string) {
+        auto first_pid = subcontext.pid();
+
+        auto expected_words = std::vector<std::string>{
+            "this",
+            "is",
+            "a",
+            "test",
+        };
+
+        for (const auto& word : expected_words) {
+            auto args = std::vector<std::string>{
+                "add_word",
+                word,
+            };
+            auto result = subcontext.Execute(args);
+            ASSERT_TRUE(result) << result.error();
+        }
+
+        auto result = subcontext.Execute(std::vector<std::string>{"return_words_as_error"});
+        ASSERT_FALSE(result);
+        EXPECT_EQ(Join(expected_words, " "), result.error_string());
+        EXPECT_EQ(first_pid, subcontext.pid());
+    });
+}
+
+TEST(subcontext, RecoverAfterAbort) {
+    RunTest([](auto& subcontext, auto& context_string) {
+        auto first_pid = subcontext.pid();
+
+        auto result = subcontext.Execute(std::vector<std::string>{"cause_log_fatal"});
+        ASSERT_FALSE(result);
+
+        auto result2 = subcontext.Execute(std::vector<std::string>{"generate_sane_error"});
+        ASSERT_FALSE(result2);
+        EXPECT_EQ("Sane error!", result2.error_string());
+        EXPECT_NE(subcontext.pid(), first_pid);
+    });
+}
+
+TEST(subcontext, ContextString) {
+    RunTest([](auto& subcontext, auto& context_string) {
+        auto result = subcontext.Execute(std::vector<std::string>{"return_context_as_error"});
+        ASSERT_FALSE(result);
+        ASSERT_EQ(context_string, result.error_string());
+    });
+}
+
+TEST(subcontext, ExpandArgs) {
+    RunTest([](auto& subcontext, auto& context_string) {
+        auto args = std::vector<std::string>{
+            "first",
+            "${ro.hardware}",
+            "$$third",
+        };
+        auto result = subcontext.ExpandArgs(args);
+        ASSERT_TRUE(result) << result.error();
+        ASSERT_EQ(3U, result->size());
+        EXPECT_EQ(args[0], result->at(0));
+        EXPECT_EQ(GetProperty("ro.hardware", ""), result->at(1));
+        EXPECT_EQ("$third", result->at(2));
+    });
+}
+
+TEST(subcontext, ExpandArgsFailure) {
+    RunTest([](auto& subcontext, auto& context_string) {
+        auto args = std::vector<std::string>{
+            "first",
+            "${",
+        };
+        auto result = subcontext.ExpandArgs(args);
+        ASSERT_FALSE(result);
+        EXPECT_EQ("Failed to expand '" + args[1] + "'", result.error_string());
+    });
+}
+
+TestFunctionMap BuildTestFunctionMap() {
+    TestFunctionMap test_function_map;
+    // For CheckDifferentPid
+    test_function_map.Add("return_pids_as_error", 0, 0, true,
+                          [](const BuiltinArguments& args) -> Result<Success> {
+                              return Error() << getpid() << " " << getppid();
+                          });
+
+    // For SetProp
+    test_function_map.Add("setprop", 2, 2, true, [](const BuiltinArguments& args) {
+        android::base::SetProperty(args[1], args[2]);
+        return Success();
+    });
+
+    // For MultipleCommands
+    // Using a shared_ptr to extend lifetime of words to both lambdas
+    auto words = std::make_shared<std::vector<std::string>>();
+    test_function_map.Add("add_word", 1, 1, true, [words](const BuiltinArguments& args) {
+        words->emplace_back(args[1]);
+        return Success();
+    });
+    test_function_map.Add("return_words_as_error", 0, 0, true,
+                          [words](const BuiltinArguments& args) -> Result<Success> {
+                              return Error() << Join(*words, " ");
+                          });
+
+    // For RecoverAfterAbort
+    test_function_map.Add("cause_log_fatal", 0, 0, true,
+                          [](const BuiltinArguments& args) -> Result<Success> {
+                              return Error() << std::string(4097, 'f');
+                          });
+    test_function_map.Add(
+        "generate_sane_error", 0, 0, true,
+        [](const BuiltinArguments& args) -> Result<Success> { return Error() << "Sane error!"; });
+
+    // For ContextString
+    test_function_map.Add(
+        "return_context_as_error", 0, 0, true,
+        [](const BuiltinArguments& args) -> Result<Success> { return Error() << args.context; });
+
+    return test_function_map;
+}
+
+}  // namespace init
+}  // namespace android
+
+int main(int argc, char** argv) {
+    if (argc > 1 && !strcmp(basename(argv[1]), "subcontext")) {
+        auto test_function_map = android::init::BuildTestFunctionMap();
+        return android::init::SubcontextMain(argc, argv, &test_function_map);
+    }
+
+    testing::InitGoogleTest(&argc, argv);
+    return RUN_ALL_TESTS();
+}
diff --git a/init/test_function_map.h b/init/test_function_map.h
new file mode 100644
index 0000000..583df1a
--- /dev/null
+++ b/init/test_function_map.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2017 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_TEST_FUNCTION_MAP_H
+#define _INIT_TEST_FUNCTION_MAP_H
+
+#include <string>
+#include <vector>
+
+#include "builtin_arguments.h"
+#include "keyword_map.h"
+
+namespace android {
+namespace init {
+
+class TestFunctionMap : public KeywordFunctionMap {
+  public:
+    // Helper for argument-less functions
+    using BuiltinFunctionNoArgs = std::function<void(void)>;
+    void Add(const std::string& name, const BuiltinFunctionNoArgs function) {
+        Add(name, 0, 0, false, [function](const BuiltinArguments&) {
+            function();
+            return Success();
+        });
+    }
+
+    void Add(const std::string& name, std::size_t min_parameters, std::size_t max_parameters,
+             bool run_in_subcontext, const BuiltinFunction function) {
+        builtin_functions_[name] =
+            make_tuple(min_parameters, max_parameters, make_pair(run_in_subcontext, function));
+    }
+
+  private:
+    Map builtin_functions_ = {};
+
+    const Map& map() const override { return builtin_functions_; }
+};
+
+}  // namespace init
+}  // namespace android
+
+#endif
diff --git a/init/test_service/Android.bp b/init/test_service/Android.bp
index 9bd6f27..8bd16a7 100644
--- a/init/test_service/Android.bp
+++ b/init/test_service/Android.bp
@@ -17,6 +17,10 @@
 cc_binary {
     name: "test_service",
     srcs: ["test_service.cpp"],
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
     shared_libs: ["libbase"],
     init_rc: ["test_service.rc"],
 }
diff --git a/init/test_service/test_service.cpp b/init/test_service/test_service.cpp
index e7206f8..71d1ea4 100644
--- a/init/test_service/test_service.cpp
+++ b/init/test_service/test_service.cpp
@@ -59,7 +59,6 @@
     }
 
     bool test_fails = false;
-    size_t uargc = static_cast<size_t>(argc);  // |argc| >= 3.
     for (size_t i = 1; i < static_cast<size_t>(argc); i = i + 2) {
         std::string expected_value = argv[i + 1];
         auto f = fields.find(argv[i]);
diff --git a/init/tokenizer.cpp b/init/tokenizer.cpp
new file mode 100644
index 0000000..bb143f1
--- /dev/null
+++ b/init/tokenizer.cpp
@@ -0,0 +1,129 @@
+#include "tokenizer.h"
+
+namespace android {
+namespace init {
+
+int next_token(struct parse_state *state)
+{
+    char *x = state->ptr;
+    char *s;
+
+    if (state->nexttoken) {
+        int t = state->nexttoken;
+        state->nexttoken = 0;
+        return t;
+    }
+
+    for (;;) {
+        switch (*x) {
+        case 0:
+            state->ptr = x;
+            return T_EOF;
+        case '\n':
+            x++;
+            state->ptr = x;
+            return T_NEWLINE;
+        case ' ':
+        case '\t':
+        case '\r':
+            x++;
+            continue;
+        case '#':
+            while (*x && (*x != '\n')) x++;
+            if (*x == '\n') {
+                state->ptr = x+1;
+                return T_NEWLINE;
+            } else {
+                state->ptr = x;
+                return T_EOF;
+            }
+        default:
+            goto text;
+        }
+    }
+
+textdone:
+    state->ptr = x;
+    *s = 0;
+    return T_TEXT;
+text:
+    state->text = s = x;
+textresume:
+    for (;;) {
+        switch (*x) {
+        case 0:
+            goto textdone;
+        case ' ':
+        case '\t':
+        case '\r':
+            x++;
+            goto textdone;
+        case '\n':
+            state->nexttoken = T_NEWLINE;
+            x++;
+            goto textdone;
+        case '"':
+            x++;
+            for (;;) {
+                switch (*x) {
+                case 0:
+                        /* unterminated quoted thing */
+                    state->ptr = x;
+                    return T_EOF;
+                case '"':
+                    x++;
+                    goto textresume;
+                default:
+                    *s++ = *x++;
+                }
+            }
+            break;
+        case '\\':
+            x++;
+            switch (*x) {
+            case 0:
+                goto textdone;
+            case 'n':
+                *s++ = '\n';
+                x++;
+                break;
+            case 'r':
+                *s++ = '\r';
+                x++;
+                break;
+            case 't':
+                *s++ = '\t';
+                x++;
+                break;
+            case '\\':
+                *s++ = '\\';
+                x++;
+                break;
+            case '\r':
+                    /* \ <cr> <lf> -> line continuation */
+                if (x[1] != '\n') {
+                    x++;
+                    continue;
+                }
+                x++;
+            case '\n':
+                    /* \ <lf> -> line continuation */
+                state->line++;
+                x++;
+                    /* eat any extra whitespace */
+                while((*x == ' ') || (*x == '\t')) x++;
+                continue;
+            default:
+                    /* unknown escape -- just copy */
+                *s++ = *x++;
+            }
+            continue;
+        default:
+            *s++ = *x++;
+        }
+    }
+    return T_EOF;
+}
+
+}  // namespace init
+}  // namespace android
diff --git a/init/tokenizer.h b/init/tokenizer.h
new file mode 100644
index 0000000..72c08ef
--- /dev/null
+++ b/init/tokenizer.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2010 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_TOKENIZER_H_
+#define _INIT_TOKENIZER_H_
+
+#define T_EOF 0
+#define T_TEXT 1
+#define T_NEWLINE 2
+
+namespace android {
+namespace init {
+
+struct parse_state
+{
+    char *ptr;
+    char *text;
+    int line;
+    int nexttoken;
+};
+
+int next_token(struct parse_state *state);
+
+}  // namespace init
+}  // namespace android
+
+#endif
diff --git a/init/tokenizer_test.cpp b/init/tokenizer_test.cpp
new file mode 100644
index 0000000..acfc7c7
--- /dev/null
+++ b/init/tokenizer_test.cpp
@@ -0,0 +1,163 @@
+//
+// 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 "tokenizer.h"
+
+#include <string>
+#include <vector>
+
+#include <gtest/gtest.h>
+
+namespace android {
+namespace init {
+
+namespace {
+
+void RunTest(const std::string& data, const std::vector<std::vector<std::string>>& expected_tokens) {
+    auto data_copy = std::string{data};
+    data_copy.push_back('\n');  // TODO: fix tokenizer
+    data_copy.push_back('\0');
+
+    parse_state state;
+    state.line = 0;
+    state.ptr = data_copy.data();
+    state.nexttoken = 0;
+
+    std::vector<std::string> current_line;
+    std::vector<std::vector<std::string>> tokens;
+
+    while (true) {
+        switch (next_token(&state)) {
+            case T_EOF:
+                EXPECT_EQ(expected_tokens, tokens) << data;
+                return;
+            case T_NEWLINE:
+                tokens.emplace_back(std::move(current_line));
+                break;
+            case T_TEXT:
+                current_line.emplace_back(state.text);
+                break;
+        }
+    }
+}
+
+}  // namespace
+
+TEST(tokenizer, null) {
+    RunTest("", {{}});
+}
+
+TEST(tokenizer, simple_oneline) {
+    RunTest("one two\tthree\rfour", {{"one", "two", "three", "four"}});
+}
+
+TEST(tokenizer, simple_multiline) {
+    RunTest("1 2 3\n4 5 6\n7 8 9", {{"1", "2", "3"}, {"4", "5", "6"}, {"7", "8", "9"}});
+}
+
+TEST(tokenizer, preceding_space) {
+    // Preceding spaces are ignored.
+    RunTest("    1 2 3\n\t\t\t\t4 5 6\n\r\r\r\r7 8 9",
+            {{"1", "2", "3"}, {"4", "5", "6"}, {"7", "8", "9"}});
+}
+
+TEST(tokenizer, comments) {
+    // Entirely commented lines still produce a T_NEWLINE token for tracking line count.
+    RunTest("1 2 3\n#4 5 6\n7 8 9", {{"1", "2", "3"}, {}, {"7", "8", "9"}});
+
+    RunTest("#1 2 3\n4 5 6\n7 8 9", {{}, {"4", "5", "6"}, {"7", "8", "9"}});
+
+    RunTest("1 2 3\n4 5 6\n#7 8 9", {{"1", "2", "3"}, {"4", "5", "6"}, {}});
+
+    RunTest("1 2 #3\n4 #5 6\n#7 8 9", {{"1", "2"}, {"4"}, {}});
+}
+
+TEST(tokenizer, control_chars) {
+    // Literal \n, \r, \t, and \\ produce the control characters \n, \r, \t, and \\ respectively.
+    // Literal \? produces ? for all other character '?'
+
+    RunTest(R"(1 token\ntoken 2)", {{"1", "token\ntoken", "2"}});
+    RunTest(R"(1 token\rtoken 2)", {{"1", "token\rtoken", "2"}});
+    RunTest(R"(1 token\ttoken 2)", {{"1", "token\ttoken", "2"}});
+    RunTest(R"(1 token\\token 2)", {{"1", "token\\token", "2"}});
+    RunTest(R"(1 token\btoken 2)", {{"1", "tokenbtoken", "2"}});
+
+    RunTest(R"(1 token\n 2)", {{"1", "token\n", "2"}});
+    RunTest(R"(1 token\r 2)", {{"1", "token\r", "2"}});
+    RunTest(R"(1 token\t 2)", {{"1", "token\t", "2"}});
+    RunTest(R"(1 token\\ 2)", {{"1", "token\\", "2"}});
+    RunTest(R"(1 token\b 2)", {{"1", "tokenb", "2"}});
+
+    RunTest(R"(1 \ntoken 2)", {{"1", "\ntoken", "2"}});
+    RunTest(R"(1 \rtoken 2)", {{"1", "\rtoken", "2"}});
+    RunTest(R"(1 \ttoken 2)", {{"1", "\ttoken", "2"}});
+    RunTest(R"(1 \\token 2)", {{"1", "\\token", "2"}});
+    RunTest(R"(1 \btoken 2)", {{"1", "btoken", "2"}});
+
+    RunTest(R"(1 \n 2)", {{"1", "\n", "2"}});
+    RunTest(R"(1 \r 2)", {{"1", "\r", "2"}});
+    RunTest(R"(1 \t 2)", {{"1", "\t", "2"}});
+    RunTest(R"(1 \\ 2)", {{"1", "\\", "2"}});
+    RunTest(R"(1 \b 2)", {{"1", "b", "2"}});
+}
+
+TEST(tokenizer, cr_lf) {
+    // \ before \n, \r, or \r\n is interpreted as a line continuation
+    // Extra whitespace on the next line is eaten, except \r unlike in the above tests.
+
+    RunTest("lf\\\ncont", {{"lfcont"}});
+    RunTest("lf\\\n    \t\t\t\tcont", {{"lfcont"}});
+
+    RunTest("crlf\\\r\ncont", {{"crlfcont"}});
+    RunTest("crlf\\\r\n    \t\t\t\tcont", {{"crlfcont"}});
+
+    RunTest("cr\\\rcont", {{"crcont"}});
+
+    RunTest("lfspace \\\ncont", {{"lfspace", "cont"}});
+    RunTest("lfspace \\\n    \t\t\t\tcont", {{"lfspace", "cont"}});
+
+    RunTest("crlfspace \\\r\ncont", {{"crlfspace", "cont"}});
+    RunTest("crlfspace \\\r\n    \t\t\t\tcont", {{"crlfspace", "cont"}});
+
+    RunTest("crspace \\\rcont", {{"crspace", "cont"}});
+}
+
+TEST(tokenizer, quoted) {
+    RunTest("\"quoted simple string\"", {{"quoted simple string"}});
+
+    // Unterminated quotes just return T_EOF without any T_NEWLINE.
+    RunTest("\"unterminated quoted string", {});
+
+    RunTest("\"1 2 3\"\n \"unterminated quoted string", {{"1 2 3"}});
+
+    // Escaping quotes is not allowed and are treated as an unterminated quoted string.
+    RunTest("\"quoted escaped quote\\\"\"", {});
+    RunTest("\"quoted escaped\\\" quote\"", {});
+    RunTest("\"\\\"quoted escaped quote\"", {});
+
+    RunTest("\"quoted control characters \\n \\r \\t \\\\ \\b \\\r \\\n \r \n\"",
+            {{"quoted control characters \\n \\r \\t \\\\ \\b \\\r \\\n \r \n"}});
+
+    RunTest("\"quoted simple string\" \"second quoted string\"",
+            {{"quoted simple string", "second quoted string"}});
+
+    RunTest("\"# comment quoted string\"", {{"# comment quoted string"}});
+
+    RunTest("\"Adjacent \"\"quoted strings\"", {{"Adjacent quoted strings"}});
+}
+
+}  // namespace init
+}  // namespace android
diff --git a/init/uevent.h b/init/uevent.h
index c4fd945..dc35fd9 100644
--- a/init/uevent.h
+++ b/init/uevent.h
@@ -29,6 +29,7 @@
     std::string firmware;
     std::string partition_name;
     std::string device_name;
+    std::string modalias;
     int partition_num;
     int major;
     int minor;
diff --git a/init/uevent_listener.cpp b/init/uevent_listener.cpp
index ac1d7c7..8cf2128 100644
--- a/init/uevent_listener.cpp
+++ b/init/uevent_listener.cpp
@@ -39,6 +39,7 @@
     uevent->firmware.clear();
     uevent->partition_name.clear();
     uevent->device_name.clear();
+    uevent->modalias.clear();
     // currently ignoring SEQNUM
     while (*msg) {
         if (!strncmp(msg, "ACTION=", 7)) {
@@ -68,6 +69,9 @@
         } else if (!strncmp(msg, "DEVNAME=", 8)) {
             msg += 8;
             uevent->device_name = msg;
+        } else if (!strncmp(msg, "MODALIAS=", 9)) {
+            msg += 9;
+            uevent->modalias = msg;
         }
 
         // advance to after the next \0
@@ -83,8 +87,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/ueventd.cpp b/init/ueventd.cpp
index c0eae1e..e9d829b 100644
--- a/init/ueventd.cpp
+++ b/init/ueventd.cpp
@@ -30,12 +30,14 @@
 #include <android-base/chrono_utils.h>
 #include <android-base/logging.h>
 #include <android-base/properties.h>
+#include <fstab/fstab.h>
 #include <selinux/android.h>
 #include <selinux/selinux.h>
 
 #include "devices.h"
 #include "firmware_handler.h"
-#include "log.h"
+#include "modalias_handler.h"
+#include "selinux.h"
 #include "uevent_listener.h"
 #include "ueventd_parser.h"
 #include "util.h"
@@ -105,9 +107,11 @@
 
 class ColdBoot {
   public:
-    ColdBoot(UeventListener& uevent_listener, DeviceHandler& device_handler)
+    ColdBoot(UeventListener& uevent_listener, DeviceHandler& device_handler,
+             ModaliasHandler& modalias_handler)
         : uevent_listener_(uevent_listener),
           device_handler_(device_handler),
+          modalias_handler_(modalias_handler),
           num_handler_subprocesses_(std::thread::hardware_concurrency() ?: 4) {}
 
     void Run();
@@ -121,6 +125,7 @@
 
     UeventListener& uevent_listener_;
     DeviceHandler& device_handler_;
+    ModaliasHandler& modalias_handler_;
 
     unsigned int num_handler_subprocesses_;
     std::vector<Uevent> uevent_queue_;
@@ -132,6 +137,7 @@
     for (unsigned int i = process_num; i < uevent_queue_.size(); i += total_processes) {
         auto& uevent = uevent_queue_[i];
         device_handler_.HandleDeviceEvent(uevent);
+        modalias_handler_.HandleModaliasEvent(uevent);
     }
     _exit(EXIT_SUCCESS);
 }
@@ -213,38 +219,6 @@
     LOG(INFO) << "Coldboot took " << cold_boot_timer.duration().count() / 1000.0f << " seconds";
 }
 
-DeviceHandler CreateDeviceHandler() {
-    Parser parser;
-
-    std::vector<Subsystem> subsystems;
-    parser.AddSectionParser("subsystem", std::make_unique<SubsystemParser>(&subsystems));
-
-    using namespace std::placeholders;
-    std::vector<SysfsPermissions> sysfs_permissions;
-    std::vector<Permissions> dev_permissions;
-    parser.AddSingleLineParser(
-        "/sys/", std::bind(ParsePermissionsLine, _1, _2, &sysfs_permissions, nullptr));
-    parser.AddSingleLineParser("/dev/",
-                               std::bind(ParsePermissionsLine, _1, _2, nullptr, &dev_permissions));
-
-    parser.ParseConfig("/ueventd.rc");
-    parser.ParseConfig("/vendor/ueventd.rc");
-    parser.ParseConfig("/odm/ueventd.rc");
-
-    /*
-     * keep the current product name base configuration so
-     * we remain backwards compatible and allow it to override
-     * everything
-     * TODO: cleanup platform ueventd.rc to remove vendor specific
-     * device node entries (b/34968103)
-     */
-    std::string hardware = android::base::GetProperty("ro.hardware", "");
-    parser.ParseConfig("/ueventd." + hardware + ".rc");
-
-    return DeviceHandler(std::move(dev_permissions), std::move(sysfs_permissions),
-                         std::move(subsystems), true);
-}
-
 int ueventd_main(int argc, char** argv) {
     /*
      * init sets the umask to 077 for forked processes. We need to
@@ -253,19 +227,37 @@
      */
     umask(000);
 
-    InitKernelLogging(argv);
+    android::base::InitLogging(argv, &android::base::KernelLogger);
 
     LOG(INFO) << "ueventd started!";
 
-    selinux_callback cb;
-    cb.func_log = selinux_klog_callback;
-    selinux_set_callback(SELINUX_CB_LOG, cb);
+    SelinuxSetupKernelLogging();
+    SelabelInitialize();
 
-    DeviceHandler device_handler = CreateDeviceHandler();
+    DeviceHandler device_handler;
+    ModaliasHandler modalias_handler;
     UeventListener uevent_listener;
 
+    {
+        // Keep the current product name base configuration so we remain backwards compatible and
+        // allow it to override everything.
+        // TODO: cleanup platform ueventd.rc to remove vendor specific device node entries (b/34968103)
+        auto hardware = android::base::GetProperty("ro.hardware", "");
+
+        auto ueventd_configuration =
+                ParseConfig({"/ueventd.rc", "/vendor/ueventd.rc", "/odm/ueventd.rc",
+                             "/ueventd." + hardware + ".rc"});
+
+        device_handler = DeviceHandler{std::move(ueventd_configuration.dev_permissions),
+                                       std::move(ueventd_configuration.sysfs_permissions),
+                                       std::move(ueventd_configuration.subsystems),
+                                       fs_mgr_get_boot_devices(), true};
+
+        firmware_directories = ueventd_configuration.firmware_directories;
+    }
+
     if (access(COLDBOOT_DONE, F_OK) != 0) {
-        ColdBoot cold_boot(uevent_listener, device_handler);
+        ColdBoot cold_boot(uevent_listener, device_handler, modalias_handler);
         cold_boot.Run();
     }
 
@@ -276,8 +268,9 @@
     while (waitpid(-1, nullptr, WNOHANG) > 0) {
     }
 
-    uevent_listener.Poll([&device_handler](const Uevent& uevent) {
+    uevent_listener.Poll([&device_handler, &modalias_handler](const Uevent& uevent) {
         HandleFirmwareEvent(uevent);
+        modalias_handler.HandleModaliasEvent(uevent);
         device_handler.HandleDeviceEvent(uevent);
         return ListenerAction::kContinue;
     });
diff --git a/init/ueventd_parser.cpp b/init/ueventd_parser.cpp
index 02e0d42..54b0d16 100644
--- a/init/ueventd_parser.cpp
+++ b/init/ueventd_parser.cpp
@@ -20,22 +20,21 @@
 #include <pwd.h>
 
 #include "keyword_map.h"
+#include "parser.h"
 
 namespace android {
 namespace init {
 
-bool ParsePermissionsLine(std::vector<std::string>&& args, std::string* err,
-                          std::vector<SysfsPermissions>* out_sysfs_permissions,
-                          std::vector<Permissions>* out_dev_permissions) {
+Result<Success> ParsePermissionsLine(std::vector<std::string>&& args,
+                                     std::vector<SysfsPermissions>* out_sysfs_permissions,
+                                     std::vector<Permissions>* out_dev_permissions) {
     bool is_sysfs = out_sysfs_permissions != nullptr;
     if (is_sysfs && args.size() != 5) {
-        *err = "/sys/ lines must have 5 entries";
-        return false;
+        return Error() << "/sys/ lines must have 5 entries";
     }
 
     if (!is_sysfs && args.size() != 4) {
-        *err = "/dev/ lines must have 4 entries";
-        return false;
+        return Error() << "/dev/ lines must have 4 entries";
     }
 
     auto it = args.begin();
@@ -49,23 +48,20 @@
     char* end_pointer = 0;
     mode_t perm = strtol(perm_string.c_str(), &end_pointer, 8);
     if (end_pointer == nullptr || *end_pointer != '\0') {
-        *err = "invalid mode '" + perm_string + "'";
-        return false;
+        return Error() << "invalid mode '" << perm_string << "'";
     }
 
     std::string& uid_string = *it++;
     passwd* pwd = getpwnam(uid_string.c_str());
     if (!pwd) {
-        *err = "invalid uid '" + uid_string + "'";
-        return false;
+        return Error() << "invalid uid '" << uid_string << "'";
     }
     uid_t uid = pwd->pw_uid;
 
     std::string& gid_string = *it++;
     struct group* grp = getgrnam(gid_string.c_str());
     if (!grp) {
-        *err = "invalid gid '" + gid_string + "'";
-        return false;
+        return Error() << "invalid gid '" << gid_string << "'";
     }
     gid_t gid = grp->gr_gid;
 
@@ -74,53 +70,76 @@
     } else {
         out_dev_permissions->emplace_back(name, perm, uid, gid);
     }
-    return true;
+    return Success();
 }
 
-bool SubsystemParser::ParseSection(std::vector<std::string>&& args, const std::string& filename,
-                                   int line, std::string* err) {
+Result<Success> ParseFirmwareDirectoriesLine(std::vector<std::string>&& args,
+                                             std::vector<std::string>* firmware_directories) {
+    if (args.size() < 2) {
+        return Error() << "firmware_directories must have at least 1 entry";
+    }
+
+    std::move(std::next(args.begin()), args.end(), std::back_inserter(*firmware_directories));
+
+    return Success();
+}
+
+class SubsystemParser : public SectionParser {
+  public:
+    SubsystemParser(std::vector<Subsystem>* subsystems) : subsystems_(subsystems) {}
+    Result<Success> ParseSection(std::vector<std::string>&& args, const std::string& filename,
+                                 int line) override;
+    Result<Success> ParseLineSection(std::vector<std::string>&& args, int line) override;
+    Result<Success> EndSection() override;
+
+  private:
+    Result<Success> ParseDevName(std::vector<std::string>&& args);
+    Result<Success> ParseDirName(std::vector<std::string>&& args);
+
+    Subsystem subsystem_;
+    std::vector<Subsystem>* subsystems_;
+};
+
+Result<Success> SubsystemParser::ParseSection(std::vector<std::string>&& args,
+                                              const std::string& filename, int line) {
     if (args.size() != 2) {
-        *err = "subsystems must have exactly one name";
-        return false;
+        return Error() << "subsystems must have exactly one name";
     }
 
     if (std::find(subsystems_->begin(), subsystems_->end(), args[1]) != subsystems_->end()) {
-        *err = "ignoring duplicate subsystem entry";
-        return false;
+        return Error() << "ignoring duplicate subsystem entry";
     }
 
-    subsystem_.name_ = args[1];
+    subsystem_ = Subsystem(std::move(args[1]));
 
-    return true;
+    return Success();
 }
 
-bool SubsystemParser::ParseDevName(std::vector<std::string>&& args, std::string* err) {
+Result<Success> SubsystemParser::ParseDevName(std::vector<std::string>&& args) {
     if (args[1] == "uevent_devname") {
-        subsystem_.devname_source_ = Subsystem::DevnameSource::DEVNAME_UEVENT_DEVNAME;
-        return true;
+        subsystem_.devname_source_ = Subsystem::DEVNAME_UEVENT_DEVNAME;
+        return Success();
     }
     if (args[1] == "uevent_devpath") {
-        subsystem_.devname_source_ = Subsystem::DevnameSource::DEVNAME_UEVENT_DEVPATH;
-        return true;
+        subsystem_.devname_source_ = Subsystem::DEVNAME_UEVENT_DEVPATH;
+        return Success();
     }
 
-    *err = "invalid devname '" + args[1] + "'";
-    return false;
+    return Error() << "invalid devname '" << args[1] << "'";
 }
 
-bool SubsystemParser::ParseDirName(std::vector<std::string>&& args, std::string* err) {
+Result<Success> SubsystemParser::ParseDirName(std::vector<std::string>&& args) {
     if (args[1].front() != '/') {
-        *err = "dirname '" + args[1] + " ' does not start with '/'";
-        return false;
+        return Error() << "dirname '" << args[1] << " ' does not start with '/'";
     }
 
     subsystem_.dir_name_ = args[1];
-    return true;
+    return Success();
 }
 
-bool SubsystemParser::ParseLineSection(std::vector<std::string>&& args, int line, std::string* err) {
-    using OptionParser =
-        bool (SubsystemParser::*)(std::vector<std::string> && args, std::string * err);
+Result<Success> SubsystemParser::ParseLineSection(std::vector<std::string>&& args, int line) {
+    using OptionParser = Result<Success> (SubsystemParser::*)(std::vector<std::string> && args);
+
     static class OptionParserMap : public KeywordMap<OptionParser> {
       private:
         const Map& map() const override {
@@ -134,17 +153,41 @@
         }
     } parser_map;
 
-    auto parser = parser_map.FindFunction(args, err);
+    auto parser = parser_map.FindFunction(args);
 
-    if (!parser) {
-        return false;
-    }
+    if (!parser) return Error() << parser.error();
 
-    return (this->*parser)(std::move(args), err);
+    return std::invoke(*parser, this, std::move(args));
 }
 
-void SubsystemParser::EndSection() {
+Result<Success> SubsystemParser::EndSection() {
     subsystems_->emplace_back(std::move(subsystem_));
+
+    return Success();
+}
+
+UeventdConfiguration ParseConfig(const std::vector<std::string>& configs) {
+    Parser parser;
+    UeventdConfiguration ueventd_configuration;
+
+    parser.AddSectionParser("subsystem",
+                            std::make_unique<SubsystemParser>(&ueventd_configuration.subsystems));
+
+    using namespace std::placeholders;
+    parser.AddSingleLineParser(
+            "/sys/",
+            std::bind(ParsePermissionsLine, _1, &ueventd_configuration.sysfs_permissions, nullptr));
+    parser.AddSingleLineParser("/dev/", std::bind(ParsePermissionsLine, _1, nullptr,
+                                                  &ueventd_configuration.dev_permissions));
+    parser.AddSingleLineParser("firmware_directories",
+                               std::bind(ParseFirmwareDirectoriesLine, _1,
+                                         &ueventd_configuration.firmware_directories));
+
+    for (const auto& config : configs) {
+        parser.ParseConfig(config);
+    }
+
+    return ueventd_configuration;
 }
 
 }  // namespace init
diff --git a/init/ueventd_parser.h b/init/ueventd_parser.h
index 592df63..343d58b 100644
--- a/init/ueventd_parser.h
+++ b/init/ueventd_parser.h
@@ -21,30 +21,18 @@
 #include <vector>
 
 #include "devices.h"
-#include "init_parser.h"
 
 namespace android {
 namespace init {
 
-class SubsystemParser : public SectionParser {
-  public:
-    SubsystemParser(std::vector<Subsystem>* subsystems) : subsystems_(subsystems) {}
-    bool ParseSection(std::vector<std::string>&& args, const std::string& filename, int line,
-                      std::string* err) override;
-    bool ParseLineSection(std::vector<std::string>&& args, int line, std::string* err) override;
-    void EndSection() override;
-
-  private:
-    bool ParseDevName(std::vector<std::string>&& args, std::string* err);
-    bool ParseDirName(std::vector<std::string>&& args, std::string* err);
-
-    Subsystem subsystem_;
-    std::vector<Subsystem>* subsystems_;
+struct UeventdConfiguration {
+    std::vector<Subsystem> subsystems;
+    std::vector<SysfsPermissions> sysfs_permissions;
+    std::vector<Permissions> dev_permissions;
+    std::vector<std::string> firmware_directories;
 };
 
-bool ParsePermissionsLine(std::vector<std::string>&& args, std::string* err,
-                          std::vector<SysfsPermissions>* out_sysfs_permissions,
-                          std::vector<Permissions>* out_dev_permissions);
+UeventdConfiguration ParseConfig(const std::vector<std::string>& configs);
 
 }  // namespace init
 }  // namespace android
diff --git a/init/ueventd_parser_test.cpp b/init/ueventd_parser_test.cpp
new file mode 100644
index 0000000..31208b9
--- /dev/null
+++ b/init/ueventd_parser_test.cpp
@@ -0,0 +1,224 @@
+/*
+ * 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 "ueventd_parser.h"
+
+#include <android-base/test_utils.h>
+#include <gtest/gtest.h>
+#include <private/android_filesystem_config.h>
+
+namespace android {
+namespace init {
+
+void TestSubsystems(const Subsystem& expected, const Subsystem& test) {
+    EXPECT_EQ(expected.name_, test.name_);
+    EXPECT_EQ(expected.devname_source_, test.devname_source_) << expected.name_;
+    EXPECT_EQ(expected.dir_name_, test.dir_name_) << expected.name_;
+}
+
+void TestPermissions(const Permissions& expected, const Permissions& test) {
+    EXPECT_EQ(expected.name_, test.name_);
+    EXPECT_EQ(expected.perm_, test.perm_) << expected.name_;
+    EXPECT_EQ(expected.uid_, test.uid_) << expected.name_;
+    EXPECT_EQ(expected.gid_, test.gid_) << expected.name_;
+    EXPECT_EQ(expected.prefix_, test.prefix_) << expected.name_;
+    EXPECT_EQ(expected.wildcard_, test.wildcard_) << expected.name_;
+}
+
+void TestSysfsPermissions(const SysfsPermissions& expected, const SysfsPermissions& test) {
+    TestPermissions(expected, test);
+    EXPECT_EQ(expected.attribute_, test.attribute_);
+}
+
+template <typename T, typename F>
+void TestVector(const T& expected, const T& test, F function) {
+    ASSERT_EQ(expected.size(), test.size());
+    auto expected_it = expected.begin();
+    auto test_it = test.begin();
+
+    for (; expected_it != expected.end(); ++expected_it, ++test_it) {
+        function(*expected_it, *test_it);
+    }
+}
+
+void TestUeventdFile(const std::string& content, const UeventdConfiguration& expected) {
+    TemporaryFile tf;
+    ASSERT_TRUE(tf.fd != -1);
+    ASSERT_TRUE(android::base::WriteStringToFd(content, tf.fd));
+
+    auto result = ParseConfig({tf.path});
+
+    TestVector(expected.subsystems, result.subsystems, TestSubsystems);
+    TestVector(expected.sysfs_permissions, result.sysfs_permissions, TestSysfsPermissions);
+    TestVector(expected.dev_permissions, result.dev_permissions, TestPermissions);
+    EXPECT_EQ(expected.firmware_directories, result.firmware_directories);
+}
+
+TEST(ueventd_parser, EmptyFile) {
+    TestUeventdFile("", {});
+}
+
+TEST(ueventd_parser, Subsystems) {
+    auto ueventd_file = R"(
+subsystem test_devname
+    devname uevent_devname
+
+subsystem test_devpath_no_dirname
+    devname uevent_devpath
+
+subsystem test_devname2
+    devname uevent_devname
+
+subsystem test_devpath_dirname
+    devname uevent_devpath
+    dirname /dev/graphics
+)";
+
+    auto subsystems = std::vector<Subsystem>{
+            {"test_devname", Subsystem::DEVNAME_UEVENT_DEVNAME, "/dev"},
+            {"test_devpath_no_dirname", Subsystem::DEVNAME_UEVENT_DEVPATH, "/dev"},
+            {"test_devname2", Subsystem::DEVNAME_UEVENT_DEVNAME, "/dev"},
+            {"test_devpath_dirname", Subsystem::DEVNAME_UEVENT_DEVPATH, "/dev/graphics"}};
+
+    TestUeventdFile(ueventd_file, {subsystems, {}, {}, {}});
+}
+
+TEST(ueventd_parser, Permissions) {
+    auto ueventd_file = R"(
+/dev/rtc0                 0640   system     system
+/dev/graphics/*           0660   root       graphics
+/dev/*/test               0660   root       system
+
+/sys/devices/platform/trusty.*      trusty_version        0440  root   log
+/sys/devices/virtual/input/input   enable      0660  root   input
+/sys/devices/virtual/*/input   poll_delay  0660  root   input
+)";
+
+    auto permissions = std::vector<Permissions>{
+            {"/dev/rtc0", 0640, AID_SYSTEM, AID_SYSTEM},
+            {"/dev/graphics/*", 0660, AID_ROOT, AID_GRAPHICS},
+            {"/dev/*/test", 0660, AID_ROOT, AID_SYSTEM},
+    };
+
+    auto sysfs_permissions = std::vector<SysfsPermissions>{
+            {"/sys/devices/platform/trusty.*", "trusty_version", 0440, AID_ROOT, AID_LOG},
+            {"/sys/devices/virtual/input/input", "enable", 0660, AID_ROOT, AID_INPUT},
+            {"/sys/devices/virtual/*/input", "poll_delay", 0660, AID_ROOT, AID_INPUT},
+    };
+
+    TestUeventdFile(ueventd_file, {{}, sysfs_permissions, permissions, {}});
+}
+
+TEST(ueventd_parser, FirmwareDirectories) {
+    auto ueventd_file = R"(
+firmware_directories /first/ /second /third
+firmware_directories /more
+)";
+
+    auto firmware_directories = std::vector<std::string>{
+            "/first/",
+            "/second",
+            "/third",
+            "/more",
+    };
+
+    TestUeventdFile(ueventd_file, {{}, {}, {}, firmware_directories});
+}
+
+TEST(ueventd_parser, AllTogether) {
+    auto ueventd_file = R"(
+
+/dev/rtc0                 0640   system     system
+firmware_directories /first/ /second /third
+/sys/devices/platform/trusty.*      trusty_version        0440  root   log
+
+subsystem test_devname
+    devname uevent_devname
+
+/dev/graphics/*           0660   root       graphics
+
+subsystem test_devpath_no_dirname
+    devname uevent_devpath
+
+/sys/devices/virtual/input/input   enable      0660  root   input
+
+## this is a comment
+
+subsystem test_devname2
+## another comment
+    devname uevent_devname
+
+subsystem test_devpath_dirname
+    devname uevent_devpath
+    dirname /dev/graphics
+
+/dev/*/test               0660   root       system
+/sys/devices/virtual/*/input   poll_delay  0660  root   input
+firmware_directories /more
+
+#ending comment
+)";
+
+    auto subsystems = std::vector<Subsystem>{
+            {"test_devname", Subsystem::DEVNAME_UEVENT_DEVNAME, "/dev"},
+            {"test_devpath_no_dirname", Subsystem::DEVNAME_UEVENT_DEVPATH, "/dev"},
+            {"test_devname2", Subsystem::DEVNAME_UEVENT_DEVNAME, "/dev"},
+            {"test_devpath_dirname", Subsystem::DEVNAME_UEVENT_DEVPATH, "/dev/graphics"}};
+
+    auto permissions = std::vector<Permissions>{
+            {"/dev/rtc0", 0640, AID_SYSTEM, AID_SYSTEM},
+            {"/dev/graphics/*", 0660, AID_ROOT, AID_GRAPHICS},
+            {"/dev/*/test", 0660, AID_ROOT, AID_SYSTEM},
+    };
+
+    auto sysfs_permissions = std::vector<SysfsPermissions>{
+            {"/sys/devices/platform/trusty.*", "trusty_version", 0440, AID_ROOT, AID_LOG},
+            {"/sys/devices/virtual/input/input", "enable", 0660, AID_ROOT, AID_INPUT},
+            {"/sys/devices/virtual/*/input", "poll_delay", 0660, AID_ROOT, AID_INPUT},
+    };
+
+    auto firmware_directories = std::vector<std::string>{
+            "/first/",
+            "/second",
+            "/third",
+            "/more",
+    };
+
+    TestUeventdFile(ueventd_file,
+                    {subsystems, sysfs_permissions, permissions, firmware_directories});
+}
+
+// All of these lines are ill-formed, so test that there is 0 output.
+TEST(ueventd_parser, ParseErrors) {
+    auto ueventd_file = R"(
+
+/dev/rtc0                 badmode   baduidbad     system
+/dev/rtc0                 0640   baduidbad     system
+/dev/rtc0                 0640   system     baduidbad
+firmware_directories #no directory listed
+/sys/devices/platform/trusty.*      trusty_version        badmode  root   log
+/sys/devices/platform/trusty.*      trusty_version        0440  baduidbad   log
+/sys/devices/platform/trusty.*      trusty_version        0440  root   baduidbad
+
+subsystem #no name
+
+)";
+
+    TestUeventdFile(ueventd_file, {});
+}
+
+}  // namespace init
+}  // namespace android
diff --git a/init/ueventd_test.cpp b/init/ueventd_test.cpp
new file mode 100644
index 0000000..7290051
--- /dev/null
+++ b/init/ueventd_test.cpp
@@ -0,0 +1,204 @@
+/*
+ * Copyright (C) 2017 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 <linux/futex.h>
+#include <pthread.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <atomic>
+#include <chrono>
+#include <string>
+#include <thread>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/scopeguard.h>
+#include <android-base/test_utils.h>
+#include <gtest/gtest.h>
+#include <selinux/android.h>
+#include <selinux/label.h>
+#include <selinux/selinux.h>
+
+using namespace std::chrono_literals;
+using namespace std::string_literals;
+
+template <typename T, typename F>
+void WriteFromMultipleThreads(std::vector<std::pair<std::string, T>>& files_and_parameters,
+                              F function) {
+    auto num_threads = files_and_parameters.size();
+    pthread_barrier_t barrier;
+    pthread_barrier_init(&barrier, nullptr, num_threads);
+    auto barrier_destroy =
+        android::base::make_scope_guard([&barrier]() { pthread_barrier_destroy(&barrier); });
+
+    auto make_thread_function = [&function, &barrier](const auto& file, const auto& parameter) {
+        return [&]() {
+            function(parameter);
+            pthread_barrier_wait(&barrier);
+            android::base::WriteStringToFile("<empty>", file);
+        };
+    };
+
+    std::vector<std::thread> threads;
+    // TODO(b/63712782): Structured bindings + templated containers are broken in clang :(
+    // for (const auto& [file, parameter] : files_and_parameters) {
+    for (const auto& pair : files_and_parameters) {
+        const auto& file = pair.first;
+        const auto& parameter = pair.second;
+        threads.emplace_back(std::thread(make_thread_function(file, parameter)));
+    }
+
+    for (auto& thread : threads) {
+        thread.join();
+    }
+}
+
+TEST(ueventd, setegid_IsPerThread) {
+    if (getuid() != 0) {
+        GTEST_LOG_(INFO) << "Skipping test, must be run as root.";
+        return;
+    }
+
+    TemporaryDir dir;
+
+    gid_t gid = 0;
+    std::vector<std::pair<std::string, gid_t>> files_and_gids;
+    std::generate_n(std::back_inserter(files_and_gids), 100, [&gid, &dir]() {
+        gid++;
+        return std::pair(dir.path + "/gid_"s + std::to_string(gid), gid);
+    });
+
+    WriteFromMultipleThreads(files_and_gids, [](gid_t gid) { EXPECT_EQ(0, setegid(gid)); });
+
+    for (const auto& [file, expected_gid] : files_and_gids) {
+        struct stat info;
+        ASSERT_EQ(0, stat(file.c_str(), &info));
+        EXPECT_EQ(expected_gid, info.st_gid);
+    }
+}
+
+TEST(ueventd, setfscreatecon_IsPerThread) {
+    if (getuid() != 0) {
+        GTEST_LOG_(INFO) << "Skipping test, must be run as root.";
+        return;
+    }
+    if (!is_selinux_enabled() || security_getenforce() == 1) {
+        GTEST_LOG_(INFO) << "Skipping test, SELinux must be enabled and in permissive mode.";
+        return;
+    }
+
+    const char* const contexts[] = {
+        "u:object_r:audio_device:s0",
+        "u:object_r:sensors_device:s0",
+        "u:object_r:video_device:s0"
+        "u:object_r:zero_device:s0",
+    };
+
+    TemporaryDir dir;
+    std::vector<std::pair<std::string, std::string>> files_and_contexts;
+    for (const char* context : contexts) {
+        files_and_contexts.emplace_back(dir.path + "/context_"s + context, context);
+    }
+
+    WriteFromMultipleThreads(files_and_contexts, [](const std::string& context) {
+        EXPECT_EQ(0, setfscreatecon(context.c_str()));
+    });
+
+    for (const auto& [file, expected_context] : files_and_contexts) {
+        char* file_context;
+        ASSERT_GT(getfilecon(file.c_str(), &file_context), 0);
+        EXPECT_EQ(expected_context, file_context);
+        freecon(file_context);
+    }
+}
+
+TEST(ueventd, selabel_lookup_MultiThreaded) {
+    if (getuid() != 0) {
+        GTEST_LOG_(INFO) << "Skipping test, must be run as root.";
+        return;
+    }
+
+    // Test parameters
+    constexpr auto num_threads = 10;
+    constexpr auto run_time = 200ms;
+
+    std::unique_ptr<selabel_handle, decltype(&selabel_close)> sehandle(
+        selinux_android_file_context_handle(), &selabel_close);
+
+    ASSERT_TRUE(sehandle);
+
+    struct {
+        const char* file;
+        int mode;
+        std::string expected_context;
+    } files_and_modes[] = {
+        {"/dev/zero", 020666, ""},
+        {"/dev/null", 020666, ""},
+        {"/dev/random", 020666, ""},
+        {"/dev/urandom", 020666, ""},
+    };
+
+    // Precondition, ensure that we can lookup all of these from a single thread, and store the
+    // expected context for each.
+    for (size_t i = 0; i < arraysize(files_and_modes); ++i) {
+        char* secontext;
+        ASSERT_EQ(0, selabel_lookup(sehandle.get(), &secontext, files_and_modes[i].file,
+                                    files_and_modes[i].mode));
+        files_and_modes[i].expected_context = secontext;
+        freecon(secontext);
+    }
+
+    // Now that we know we can access them, and what their context should be, run in parallel.
+    std::atomic_bool stopped = false;
+    std::atomic_uint num_api_failures = 0;
+    std::atomic_uint num_context_check_failures = 0;
+    std::atomic_uint num_successes = 0;
+
+    auto thread_function = [&]() {
+        while (!stopped) {
+            for (size_t i = 0; i < arraysize(files_and_modes); ++i) {
+                char* secontext;
+                int result = selabel_lookup(sehandle.get(), &secontext, files_and_modes[i].file,
+                                            files_and_modes[i].mode);
+                if (result != 0) {
+                    num_api_failures++;
+                } else {
+                    if (files_and_modes[i].expected_context != secontext) {
+                        num_context_check_failures++;
+                    } else {
+                        num_successes++;
+                    }
+                    freecon(secontext);
+                }
+            }
+        }
+    };
+
+    std::vector<std::thread> threads;
+    std::generate_n(back_inserter(threads), num_threads,
+                    [&]() { return std::thread(thread_function); });
+
+    std::this_thread::sleep_for(run_time);
+    stopped = true;
+    for (auto& thread : threads) {
+        thread.join();
+    }
+
+    EXPECT_EQ(0U, num_api_failures);
+    EXPECT_EQ(0U, num_context_check_failures);
+    EXPECT_GT(num_successes, 0U);
+}
diff --git a/init/util.cpp b/init/util.cpp
index 2792794..7735717 100644
--- a/init/util.cpp
+++ b/init/util.cpp
@@ -37,11 +37,14 @@
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
 #include <android-base/unique_fd.h>
-#include <cutils/android_reboot.h>
 #include <cutils/sockets.h>
 #include <selinux/android.h>
 
-#include "reboot.h"
+#if defined(__ANDROID__)
+#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"
@@ -53,31 +56,23 @@
 namespace android {
 namespace init {
 
-// DecodeUid() - decodes and returns the given string, which can be either the
-// numeric or name representation, into the integer uid or gid. Returns
-// UINT_MAX on error.
-bool DecodeUid(const std::string& name, uid_t* uid, std::string* err) {
-    *uid = UINT_MAX;
-    *err = "";
+const std::string kDefaultAndroidDtDir("/proc/device-tree/firmware/android/");
 
+// DecodeUid() - decodes and returns the given string, which can be either the
+// numeric or name representation, into the integer uid or gid.
+Result<uid_t> DecodeUid(const std::string& name) {
     if (isalpha(name[0])) {
         passwd* pwd = getpwnam(name.c_str());
-        if (!pwd) {
-            *err = "getpwnam failed: "s + strerror(errno);
-            return false;
-        }
-        *uid = pwd->pw_uid;
-        return true;
+        if (!pwd) return ErrnoError() << "getpwnam failed";
+
+        return pwd->pw_uid;
     }
 
     errno = 0;
     uid_t result = static_cast<uid_t>(strtoul(name.c_str(), 0, 0));
-    if (errno) {
-        *err = "strtoul failed: "s + strerror(errno);
-        return false;
-    }
-    *uid = result;
-    return true;
+    if (errno) return ErrnoError() << "strtoul failed";
+
+    return result;
 }
 
 /*
@@ -87,7 +82,7 @@
  * variable ANDROID_SOCKET_ENV_PREFIX<name> ("ANDROID_SOCKET_foo").
  */
 int CreateSocket(const char* name, int type, bool passcred, mode_t perm, uid_t uid, gid_t gid,
-                 const char* socketcon, selabel_handle* sehandle) {
+                 const char* socketcon) {
     if (socketcon) {
         if (setsockcreatecon(socketcon) == -1) {
             PLOG(ERROR) << "setsockcreatecon(\"" << socketcon << "\") failed";
@@ -114,11 +109,9 @@
         return -1;
     }
 
-    char *filecon = NULL;
-    if (sehandle) {
-        if (selabel_lookup(sehandle, &filecon, addr.sun_path, S_IFSOCK) == 0) {
-            setfscreatecon(filecon);
-        }
+    std::string secontext;
+    if (SelabelLookupFileContext(addr.sun_path, S_IFSOCK, &secontext) && !secontext.empty()) {
+        setfscreatecon(secontext.c_str());
     }
 
     if (passcred) {
@@ -132,8 +125,9 @@
     int ret = bind(fd, (struct sockaddr *) &addr, sizeof (addr));
     int savederrno = errno;
 
-    setfscreatecon(NULL);
-    freecon(filecon);
+    if (!secontext.empty()) {
+        setfscreatecon(nullptr);
+    }
 
     if (ret) {
         errno = savederrno;
@@ -162,75 +156,85 @@
     return -1;
 }
 
-bool ReadFile(const std::string& path, std::string* content, std::string* err) {
-    content->clear();
-    *err = "";
-
+Result<std::string> ReadFile(const std::string& path) {
     android::base::unique_fd fd(
         TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY | O_NOFOLLOW | O_CLOEXEC)));
     if (fd == -1) {
-        *err = "Unable to open '" + path + "': " + strerror(errno);
-        return false;
+        return ErrnoError() << "open() failed";
     }
 
     // For security reasons, disallow world-writable
     // or group-writable files.
     struct stat sb;
     if (fstat(fd, &sb) == -1) {
-        *err = "fstat failed for '" + path + "': " + strerror(errno);
-        return false;
+        return ErrnoError() << "fstat failed()";
     }
     if ((sb.st_mode & (S_IWGRP | S_IWOTH)) != 0) {
-        *err = "Skipping insecure file '" + path + "'";
-        return false;
+        return Error() << "Skipping insecure file";
     }
 
-    if (!android::base::ReadFdToString(fd, content)) {
-        *err = "Unable to read '" + path + "': " + strerror(errno);
-        return false;
+    std::string content;
+    if (!android::base::ReadFdToString(fd, &content)) {
+        return ErrnoError() << "Unable to read file contents";
     }
-    return true;
+    return content;
 }
 
-bool WriteFile(const std::string& path, const std::string& content, std::string* err) {
-    *err = "";
+static int OpenFile(const std::string& path, int flags, mode_t mode) {
+    std::string secontext;
+    if (SelabelLookupFileContext(path, mode, &secontext) && !secontext.empty()) {
+        setfscreatecon(secontext.c_str());
+    }
 
+    int rc = open(path.c_str(), flags, mode);
+
+    if (!secontext.empty()) {
+        int save_errno = errno;
+        setfscreatecon(nullptr);
+        errno = save_errno;
+    }
+
+    return rc;
+}
+
+Result<Success> WriteFile(const std::string& path, const std::string& content) {
     android::base::unique_fd fd(TEMP_FAILURE_RETRY(
-        open(path.c_str(), O_WRONLY | O_CREAT | O_NOFOLLOW | O_TRUNC | O_CLOEXEC, 0600)));
+        OpenFile(path, O_WRONLY | O_CREAT | O_NOFOLLOW | O_TRUNC | O_CLOEXEC, 0600)));
     if (fd == -1) {
-        *err = "Unable to open '" + path + "': " + strerror(errno);
-        return false;
+        return ErrnoError() << "open() failed";
     }
     if (!android::base::WriteStringToFd(content, fd)) {
-        *err = "Unable to write to '" + path + "': " + strerror(errno);
-        return false;
+        return ErrnoError() << "Unable to write file contents";
     }
-    return true;
+    return Success();
 }
 
-int mkdir_recursive(const std::string& path, mode_t mode, selabel_handle* sehandle) {
+bool mkdir_recursive(const std::string& path, mode_t mode) {
     std::string::size_type slash = 0;
     while ((slash = path.find('/', slash + 1)) != std::string::npos) {
         auto directory = path.substr(0, slash);
         struct stat info;
         if (stat(directory.c_str(), &info) != 0) {
-            auto ret = make_dir(directory.c_str(), mode, sehandle);
-            if (ret && errno != EEXIST) return ret;
+            auto ret = make_dir(directory, mode);
+            if (!ret && errno != EEXIST) return false;
         }
     }
-    auto ret = make_dir(path.c_str(), mode, sehandle);
-    if (ret && errno != EEXIST) return ret;
-    return 0;
+    auto ret = make_dir(path, mode);
+    if (!ret && errno != EEXIST) return false;
+    return true;
 }
 
 int wait_for_file(const char* filename, std::chrono::nanoseconds timeout) {
-    boot_clock::time_point timeout_time = boot_clock::now() + timeout;
-    while (boot_clock::now() < timeout_time) {
+    android::base::Timer t;
+    while (t.duration() < timeout) {
         struct stat sb;
-        if (stat(filename, &sb) != -1) return 0;
-
+        if (stat(filename, &sb) != -1) {
+            LOG(INFO) << "wait for '" << filename << "' took " << t;
+            return 0;
+        }
         std::this_thread::sleep_for(10ms);
     }
+    LOG(WARNING) << "wait for '" << filename << "' timed out and took " << t;
     return -1;
 }
 
@@ -247,26 +251,21 @@
     }
 }
 
-int make_dir(const char* path, mode_t mode, selabel_handle* sehandle) {
-    int rc;
-
-    char *secontext = NULL;
-
-    if (sehandle) {
-        selabel_lookup(sehandle, &secontext, path, mode);
-        setfscreatecon(secontext);
+bool make_dir(const std::string& path, mode_t mode) {
+    std::string secontext;
+    if (SelabelLookupFileContext(path, mode, &secontext) && !secontext.empty()) {
+        setfscreatecon(secontext.c_str());
     }
 
-    rc = mkdir(path, mode);
+    int rc = mkdir(path.c_str(), mode);
 
-    if (secontext) {
+    if (!secontext.empty()) {
         int save_errno = errno;
-        freecon(secontext);
-        setfscreatecon(NULL);
+        setfscreatecon(nullptr);
         errno = save_errno;
     }
 
-    return rc;
+    return rc == 0;
 }
 
 /*
@@ -368,16 +367,31 @@
     return true;
 }
 
-void panic() {
-    LOG(ERROR) << "panic: rebooting to bootloader";
-    // Do not queue "shutdown" trigger since we want to shutdown immediately
-    DoReboot(ANDROID_RB_RESTART2, "reboot", "bootloader", false);
+static std::string init_android_dt_dir() {
+    // Use the standard procfs-based path by default
+    std::string android_dt_dir = kDefaultAndroidDtDir;
+    // The platform may specify a custom Android DT path in kernel cmdline
+    import_kernel_cmdline(false,
+                          [&](const std::string& key, const std::string& value, bool in_qemu) {
+                              if (key == "androidboot.android_dt_dir") {
+                                  android_dt_dir = value;
+                              }
+                          });
+    LOG(INFO) << "Using Android DT directory " << android_dt_dir;
+    return android_dt_dir;
 }
 
-// Reads the content of device tree file under kAndroidDtDir directory.
+// FIXME: The same logic is duplicated in system/core/fs_mgr/
+const std::string& get_android_dt_dir() {
+    // Set once and saves time for subsequent calls to this function
+    static const std::string kAndroidDtDir = init_android_dt_dir();
+    return kAndroidDtDir;
+}
+
+// Reads the content of device tree file under the platform's Android DT directory.
 // Returns true if the read is success, false otherwise.
 bool read_android_dt_file(const std::string& sub_path, std::string* dt_content) {
-    const std::string file_name = kAndroidDtDir + sub_path;
+    const std::string file_name = get_android_dt_dir() + sub_path;
     if (android::base::ReadFileToString(file_name, dt_content)) {
         if (!dt_content->empty()) {
             dt_content->pop_back();  // Trims the trailing '\0' out.
@@ -397,5 +411,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 452df2d..07e4864 100644
--- a/init/util.h
+++ b/init/util.h
@@ -28,9 +28,9 @@
 #include <android-base/chrono_utils.h>
 #include <selinux/label.h>
 
-#define COLDBOOT_DONE "/dev/.coldboot_done"
+#include "result.h"
 
-const std::string kAndroidDtDir("/proc/device-tree/firmware/android/");
+#define COLDBOOT_DONE "/dev/.coldboot_done"
 
 using android::base::boot_clock;
 using namespace std::chrono_literals;
@@ -39,28 +39,31 @@
 namespace init {
 
 int CreateSocket(const char* name, int type, bool passcred, mode_t perm, uid_t uid, gid_t gid,
-                 const char* socketcon, selabel_handle* sehandle);
+                 const char* socketcon);
 
-bool ReadFile(const std::string& path, std::string* content, std::string* err);
-bool WriteFile(const std::string& path, const std::string& content, std::string* err);
+Result<std::string> ReadFile(const std::string& path);
+Result<Success> WriteFile(const std::string& path, const std::string& content);
 
-bool DecodeUid(const std::string& name, uid_t* uid, std::string* err);
+Result<uid_t> DecodeUid(const std::string& name);
 
-int mkdir_recursive(const std::string& pathname, mode_t mode, selabel_handle* sehandle);
+bool mkdir_recursive(const std::string& pathname, mode_t mode);
 int wait_for_file(const char *filename, std::chrono::nanoseconds timeout);
 void import_kernel_cmdline(bool in_qemu,
                            const std::function<void(const std::string&, const std::string&, bool)>&);
-int make_dir(const char* path, mode_t mode, selabel_handle* sehandle);
+bool make_dir(const std::string& path, mode_t mode);
 std::string bytes_to_hex(const uint8_t *bytes, size_t bytes_len);
 bool is_dir(const char* pathname);
 bool expand_props(const std::string& src, std::string* dst);
 
-void panic() __attribute__((__noreturn__));
-
-// Reads or compares the content of device tree file under kAndroidDtDir directory.
+// Returns the platform's Android DT directory as specified in the kernel cmdline.
+// If the platform does not configure a custom DT path, returns the standard one (based in procfs).
+const std::string& get_android_dt_dir();
+// Reads or compares the content of device tree file under the platform's Android DT directory.
 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/init/util_test.cpp b/init/util_test.cpp
index c16ab74..3ae53a4 100644
--- a/init/util_test.cpp
+++ b/init/util_test.cpp
@@ -30,61 +30,51 @@
 namespace init {
 
 TEST(util, ReadFile_ENOENT) {
-    std::string s("hello");
-    std::string err;
     errno = 0;
-    EXPECT_FALSE(ReadFile("/proc/does-not-exist", &s, &err));
-    EXPECT_EQ("Unable to open '/proc/does-not-exist': No such file or directory", err);
+    auto file_contents = ReadFile("/proc/does-not-exist");
     EXPECT_EQ(ENOENT, errno);
-    EXPECT_EQ("", s);  // s was cleared.
+    ASSERT_FALSE(file_contents);
+    EXPECT_EQ("open() failed: No such file or directory", file_contents.error_string());
 }
 
 TEST(util, ReadFileGroupWriteable) {
     std::string s("hello");
     TemporaryFile tf;
-    std::string err;
     ASSERT_TRUE(tf.fd != -1);
-    EXPECT_TRUE(WriteFile(tf.path, s, &err)) << strerror(errno);
-    EXPECT_EQ("", err);
+    EXPECT_TRUE(WriteFile(tf.path, s)) << strerror(errno);
     EXPECT_NE(-1, fchmodat(AT_FDCWD, tf.path, 0620, AT_SYMLINK_NOFOLLOW)) << strerror(errno);
-    EXPECT_FALSE(ReadFile(tf.path, &s, &err)) << strerror(errno);
-    EXPECT_EQ("Skipping insecure file '"s + tf.path + "'", err);
-    EXPECT_EQ("", s);  // s was cleared.
+    auto file_contents = ReadFile(tf.path);
+    ASSERT_FALSE(file_contents) << strerror(errno);
+    EXPECT_EQ("Skipping insecure file", file_contents.error_string());
 }
 
 TEST(util, ReadFileWorldWiteable) {
     std::string s("hello");
     TemporaryFile tf;
-    std::string err;
     ASSERT_TRUE(tf.fd != -1);
-    EXPECT_TRUE(WriteFile(tf.path, s, &err)) << strerror(errno);
-    EXPECT_EQ("", err);
+    EXPECT_TRUE(WriteFile(tf.path, s)) << strerror(errno);
     EXPECT_NE(-1, fchmodat(AT_FDCWD, tf.path, 0602, AT_SYMLINK_NOFOLLOW)) << strerror(errno);
-    EXPECT_FALSE(ReadFile(tf.path, &s, &err)) << strerror(errno);
-    EXPECT_EQ("Skipping insecure file '"s + tf.path + "'", err);
-    EXPECT_EQ("", s);  // s was cleared.
+    auto file_contents = ReadFile(tf.path);
+    ASSERT_FALSE(file_contents) << strerror(errno);
+    EXPECT_EQ("Skipping insecure file", file_contents.error_string());
 }
 
 TEST(util, ReadFileSymbolicLink) {
-    std::string s("hello");
     errno = 0;
     // lrwxrwxrwx 1 root root 13 1970-01-01 00:00 charger -> /sbin/healthd
-    std::string err;
-    EXPECT_FALSE(ReadFile("/charger", &s, &err));
-    EXPECT_EQ("Unable to open '/charger': Too many symbolic links encountered", err);
+    auto file_contents = ReadFile("/charger");
     EXPECT_EQ(ELOOP, errno);
-    EXPECT_EQ("", s);  // s was cleared.
+    ASSERT_FALSE(file_contents);
+    EXPECT_EQ("open() failed: Too many symbolic links encountered", file_contents.error_string());
 }
 
 TEST(util, ReadFileSuccess) {
-    std::string s("hello");
-    std::string err;
-    EXPECT_TRUE(ReadFile("/proc/version", &s, &err));
-    EXPECT_EQ("", err);
-    EXPECT_GT(s.length(), 6U);
-    EXPECT_EQ('\n', s[s.length() - 1]);
-    s[5] = 0;
-    EXPECT_STREQ("Linux", s.c_str());
+    auto file_contents = ReadFile("/proc/version");
+    ASSERT_TRUE(file_contents);
+    EXPECT_GT(file_contents->length(), 6U);
+    EXPECT_EQ('\n', file_contents->at(file_contents->length() - 1));
+    (*file_contents)[5] = 0;
+    EXPECT_STREQ("Linux", file_contents->c_str());
 }
 
 TEST(util, WriteFileBinary) {
@@ -95,29 +85,23 @@
     ASSERT_EQ(10u, contents.size());
 
     TemporaryFile tf;
-    std::string err;
     ASSERT_TRUE(tf.fd != -1);
-    EXPECT_TRUE(WriteFile(tf.path, contents, &err)) << strerror(errno);
-    EXPECT_EQ("", err);
+    EXPECT_TRUE(WriteFile(tf.path, contents)) << strerror(errno);
 
-    std::string read_back_contents;
-    EXPECT_TRUE(ReadFile(tf.path, &read_back_contents, &err)) << strerror(errno);
-    EXPECT_EQ("", err);
-    EXPECT_EQ(contents, read_back_contents);
-    EXPECT_EQ(10u, read_back_contents.size());
+    auto read_back_contents = ReadFile(tf.path);
+    ASSERT_TRUE(read_back_contents) << strerror(errno);
+    EXPECT_EQ(contents, *read_back_contents);
+    EXPECT_EQ(10u, read_back_contents->size());
 }
 
 TEST(util, WriteFileNotExist) {
     std::string s("hello");
-    std::string s2("hello");
     TemporaryDir test_dir;
     std::string path = android::base::StringPrintf("%s/does-not-exist", test_dir.path);
-    std::string err;
-    EXPECT_TRUE(WriteFile(path, s, &err));
-    EXPECT_EQ("", err);
-    EXPECT_TRUE(ReadFile(path, &s2, &err));
-    EXPECT_EQ("", err);
-    EXPECT_EQ(s, s2);
+    EXPECT_TRUE(WriteFile(path, s));
+    auto file_contents = ReadFile(path);
+    ASSERT_TRUE(file_contents);
+    EXPECT_EQ(s, *file_contents);
     struct stat sb;
     int fd = open(path.c_str(), O_RDONLY | O_NOFOLLOW | O_CLOEXEC);
     EXPECT_NE(-1, fd);
@@ -127,37 +111,30 @@
 }
 
 TEST(util, WriteFileExist) {
-    std::string s2("");
     TemporaryFile tf;
     ASSERT_TRUE(tf.fd != -1);
-    std::string err;
-    EXPECT_TRUE(WriteFile(tf.path, "1hello1", &err)) << strerror(errno);
-    EXPECT_EQ("", err);
-    EXPECT_TRUE(ReadFile(tf.path, &s2, &err));
-    EXPECT_EQ("", err);
-    EXPECT_STREQ("1hello1", s2.c_str());
-    EXPECT_TRUE(WriteFile(tf.path, "2ll2", &err));
-    EXPECT_EQ("", err);
-    EXPECT_TRUE(ReadFile(tf.path, &s2, &err));
-    EXPECT_EQ("", err);
-    EXPECT_STREQ("2ll2", s2.c_str());
+    EXPECT_TRUE(WriteFile(tf.path, "1hello1")) << strerror(errno);
+    auto file_contents = ReadFile(tf.path);
+    ASSERT_TRUE(file_contents);
+    EXPECT_EQ("1hello1", *file_contents);
+    EXPECT_TRUE(WriteFile(tf.path, "2ll2"));
+    file_contents = ReadFile(tf.path);
+    ASSERT_TRUE(file_contents);
+    EXPECT_EQ("2ll2", *file_contents);
 }
 
 TEST(util, DecodeUid) {
-    uid_t decoded_uid;
-    std::string err;
+    auto decoded_uid = DecodeUid("root");
+    EXPECT_TRUE(decoded_uid);
+    EXPECT_EQ(0U, *decoded_uid);
 
-    EXPECT_TRUE(DecodeUid("root", &decoded_uid, &err));
-    EXPECT_EQ("", err);
-    EXPECT_EQ(0U, decoded_uid);
+    decoded_uid = DecodeUid("toot");
+    EXPECT_FALSE(decoded_uid);
+    EXPECT_EQ("getpwnam failed: No such file or directory", decoded_uid.error_string());
 
-    EXPECT_FALSE(DecodeUid("toot", &decoded_uid, &err));
-    EXPECT_EQ("getpwnam failed: No such file or directory", err);
-    EXPECT_EQ(UINT_MAX, decoded_uid);
-
-    EXPECT_TRUE(DecodeUid("123", &decoded_uid, &err));
-    EXPECT_EQ("", err);
-    EXPECT_EQ(123U, decoded_uid);
+    decoded_uid = DecodeUid("123");
+    EXPECT_TRUE(decoded_uid);
+    EXPECT_EQ(123U, *decoded_uid);
 }
 
 TEST(util, is_dir) {
@@ -170,7 +147,7 @@
 TEST(util, mkdir_recursive) {
     TemporaryDir test_dir;
     std::string path = android::base::StringPrintf("%s/three/directories/deep", test_dir.path);
-    EXPECT_EQ(0, mkdir_recursive(path, 0755, nullptr));
+    EXPECT_TRUE(mkdir_recursive(path, 0755));
     std::string path1 = android::base::StringPrintf("%s/three", test_dir.path);
     EXPECT_TRUE(is_dir(path1.c_str()));
     std::string path2 = android::base::StringPrintf("%s/three/directories", test_dir.path);
@@ -182,7 +159,7 @@
 TEST(util, mkdir_recursive_extra_slashes) {
     TemporaryDir test_dir;
     std::string path = android::base::StringPrintf("%s/three////directories/deep//", test_dir.path);
-    EXPECT_EQ(0, mkdir_recursive(path, 0755, nullptr));
+    EXPECT_TRUE(mkdir_recursive(path, 0755));
     std::string path1 = android::base::StringPrintf("%s/three", test_dir.path);
     EXPECT_TRUE(is_dir(path1.c_str()));
     std::string path2 = android::base::StringPrintf("%s/three/directories", test_dir.path);
diff --git a/init/watchdogd.cpp b/init/watchdogd.cpp
index e0164b4..e03a2c3 100644
--- a/init/watchdogd.cpp
+++ b/init/watchdogd.cpp
@@ -23,8 +23,6 @@
 
 #include <android-base/logging.h>
 
-#include "log.h"
-
 #ifdef _INIT_INIT_H
 #error "Do not include init.h in files used by ueventd or watchdogd; it will expose init's globals"
 #endif
@@ -35,7 +33,7 @@
 namespace init {
 
 int watchdogd_main(int argc, char **argv) {
-    InitKernelLogging(argv);
+    android::base::InitLogging(argv, &android::base::KernelLogger);
 
     int interval = 10;
     if (argc >= 2) interval = atoi(argv[1]);
diff --git a/libappfuse/Android.bp b/libappfuse/Android.bp
index e659f79..ae1481f 100644
--- a/libappfuse/Android.bp
+++ b/libappfuse/Android.bp
@@ -8,7 +8,6 @@
         "-Wall",
         "-Werror",
     ],
-    clang: true
 }
 
 cc_library_shared {
@@ -19,7 +18,8 @@
         "FuseAppLoop.cc",
         "FuseBuffer.cc",
         "FuseBridgeLoop.cc",
-    ]
+        "EpollController.cc",
+    ],
 }
 
 cc_test {
@@ -31,5 +31,5 @@
         "tests/FuseAppLoopTest.cc",
         "tests/FuseBridgeLoopTest.cc",
         "tests/FuseBufferTest.cc",
-    ]
+    ],
 }
diff --git a/libappfuse/EpollController.cc b/libappfuse/EpollController.cc
new file mode 100644
index 0000000..9daeab8
--- /dev/null
+++ b/libappfuse/EpollController.cc
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2017 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 specic language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android-base/logging.h>
+
+#include "libappfuse/EpollController.h"
+
+namespace android {
+namespace fuse {
+
+EpollController::EpollController(base::unique_fd&& poll_fd) : poll_fd_(std::move(poll_fd)) {
+}
+
+bool EpollController::Wait(size_t event_count) {
+    events_.resize(event_count);
+    const int result = TEMP_FAILURE_RETRY(epoll_wait(poll_fd_, events_.data(), event_count, -1));
+    if (result == -1) {
+        PLOG(ERROR) << "Failed to wait for epoll";
+        return false;
+    }
+    events_.resize(result);
+    return true;
+}
+
+bool EpollController::AddFd(int fd, int events, void* data) {
+    return InvokeControl(EPOLL_CTL_ADD, fd, events, data);
+}
+
+bool EpollController::UpdateFd(int fd, int events, void* data) {
+    return InvokeControl(EPOLL_CTL_MOD, fd, events, data);
+}
+
+bool EpollController::RemoveFd(int fd) {
+    return InvokeControl(EPOLL_CTL_DEL, fd, /* events */ 0, nullptr);
+}
+
+const std::vector<epoll_event>& EpollController::events() const {
+    return events_;
+}
+
+bool EpollController::InvokeControl(int op, int fd, int events, void* data) const {
+    epoll_event event;
+    memset(&event, 0, sizeof(event));
+    event.events = events;
+    event.data.ptr = data;
+    if (epoll_ctl(poll_fd_, op, fd, &event) == -1) {
+        PLOG(ERROR) << "epoll_ctl() error op=" << op;
+        return false;
+    }
+    return true;
+}
+}
+}
diff --git a/libappfuse/FuseAppLoop.cc b/libappfuse/FuseAppLoop.cc
index a31880e..b6bc191 100644
--- a/libappfuse/FuseAppLoop.cc
+++ b/libappfuse/FuseAppLoop.cc
@@ -16,205 +16,232 @@
 
 #include "libappfuse/FuseAppLoop.h"
 
+#include <sys/eventfd.h>
 #include <sys/stat.h>
 
 #include <android-base/logging.h>
 #include <android-base/unique_fd.h>
 
+#include "libappfuse/EpollController.h"
+
 namespace android {
 namespace fuse {
 
 namespace {
 
-void HandleLookUp(FuseBuffer* buffer, FuseAppLoopCallback* callback) {
-  // AppFuse does not support directory structure now.
-  // It can lookup only files under the mount point.
-  if (buffer->request.header.nodeid != FUSE_ROOT_ID) {
-    LOG(ERROR) << "Nodeid is not FUSE_ROOT_ID.";
-    buffer->response.Reset(0, -ENOENT, buffer->request.header.unique);
-    return;
-  }
-
-  // Ensure that the filename ends with 0.
-  const size_t filename_length =
-      buffer->request.header.len - sizeof(fuse_in_header);
-  if (buffer->request.lookup_name[filename_length - 1] != 0) {
-    LOG(ERROR) << "File name does not end with 0.";
-    buffer->response.Reset(0, -ENOENT, buffer->request.header.unique);
-    return;
-  }
-
-  const uint64_t inode =
-      static_cast<uint64_t>(atol(buffer->request.lookup_name));
-  if (inode == 0 || inode == LONG_MAX) {
-    LOG(ERROR) << "Invalid filename";
-    buffer->response.Reset(0, -ENOENT, buffer->request.header.unique);
-    return;
-  }
-
-  const int64_t size = callback->OnGetSize(inode);
-  if (size < 0) {
-    buffer->response.Reset(0, size, buffer->request.header.unique);
-    return;
-  }
-
-  buffer->response.Reset(sizeof(fuse_entry_out), 0,
-                         buffer->request.header.unique);
-  buffer->response.entry_out.nodeid = inode;
-  buffer->response.entry_out.attr_valid = 10;
-  buffer->response.entry_out.entry_valid = 10;
-  buffer->response.entry_out.attr.ino = inode;
-  buffer->response.entry_out.attr.mode = S_IFREG | 0777;
-  buffer->response.entry_out.attr.size = size;
-}
-
-void HandleGetAttr(FuseBuffer* buffer, FuseAppLoopCallback* callback) {
-  const uint64_t nodeid = buffer->request.header.nodeid;
-  int64_t size;
-  uint32_t mode;
-  if (nodeid == FUSE_ROOT_ID) {
-    size = 0;
-    mode = S_IFDIR | 0777;
-  } else {
-    size = callback->OnGetSize(buffer->request.header.nodeid);
-    if (size < 0) {
-      buffer->response.Reset(0, size, buffer->request.header.unique);
-      return;
+bool HandleLookUp(FuseAppLoop* loop, FuseBuffer* buffer, FuseAppLoopCallback* callback) {
+    // AppFuse does not support directory structure now.
+    // It can lookup only files under the mount point.
+    if (buffer->request.header.nodeid != FUSE_ROOT_ID) {
+        LOG(ERROR) << "Nodeid is not FUSE_ROOT_ID.";
+        return loop->ReplySimple(buffer->request.header.unique, -ENOENT);
     }
-    mode = S_IFREG | 0777;
-  }
 
-  buffer->response.Reset(sizeof(fuse_attr_out), 0,
-                         buffer->request.header.unique);
-  buffer->response.attr_out.attr_valid = 10;
-  buffer->response.attr_out.attr.ino = nodeid;
-  buffer->response.attr_out.attr.mode = mode;
-  buffer->response.attr_out.attr.size = size;
+    // Ensure that the filename ends with 0.
+    const size_t filename_length = buffer->request.header.len - sizeof(fuse_in_header);
+    if (buffer->request.lookup_name[filename_length - 1] != 0) {
+        LOG(ERROR) << "File name does not end with 0.";
+        return loop->ReplySimple(buffer->request.header.unique, -ENOENT);
+    }
+
+    const uint64_t inode = static_cast<uint64_t>(atol(buffer->request.lookup_name));
+    if (inode == 0 || inode == LONG_MAX) {
+        LOG(ERROR) << "Invalid filename";
+        return loop->ReplySimple(buffer->request.header.unique, -ENOENT);
+    }
+
+    callback->OnLookup(buffer->request.header.unique, inode);
+    return true;
 }
 
-void HandleOpen(FuseBuffer* buffer, FuseAppLoopCallback* callback) {
-  const int32_t file_handle = callback->OnOpen(buffer->request.header.nodeid);
-  if (file_handle < 0) {
-    buffer->response.Reset(0, file_handle, buffer->request.header.unique);
-    return;
-  }
-  buffer->response.Reset(sizeof(fuse_open_out), kFuseSuccess,
-                         buffer->request.header.unique);
-  buffer->response.open_out.fh = file_handle;
+bool HandleGetAttr(FuseAppLoop* loop, FuseBuffer* buffer, FuseAppLoopCallback* callback) {
+    if (buffer->request.header.nodeid == FUSE_ROOT_ID) {
+        return loop->ReplyGetAttr(buffer->request.header.unique, buffer->request.header.nodeid, 0,
+                                  S_IFDIR | 0777);
+    } else {
+        callback->OnGetAttr(buffer->request.header.unique, buffer->request.header.nodeid);
+        return true;
+    }
 }
 
-void HandleFsync(FuseBuffer* buffer, FuseAppLoopCallback* callback) {
-  buffer->response.Reset(0, callback->OnFsync(buffer->request.header.nodeid),
-                         buffer->request.header.unique);
+bool HandleRead(FuseAppLoop* loop, FuseBuffer* buffer, FuseAppLoopCallback* callback) {
+    if (buffer->request.read_in.size > kFuseMaxRead) {
+        return loop->ReplySimple(buffer->request.header.unique, -EINVAL);
+    }
+
+    callback->OnRead(buffer->request.header.unique, buffer->request.header.nodeid,
+                     buffer->request.read_in.offset, buffer->request.read_in.size);
+    return true;
 }
 
-void HandleRelease(FuseBuffer* buffer, FuseAppLoopCallback* callback) {
-  buffer->response.Reset(0, callback->OnRelease(buffer->request.header.nodeid),
-                         buffer->request.header.unique);
+bool HandleWrite(FuseAppLoop* loop, FuseBuffer* buffer, FuseAppLoopCallback* callback) {
+    if (buffer->request.write_in.size > kFuseMaxWrite) {
+        return loop->ReplySimple(buffer->request.header.unique, -EINVAL);
+    }
+
+    callback->OnWrite(buffer->request.header.unique, buffer->request.header.nodeid,
+                      buffer->request.write_in.offset, buffer->request.write_in.size,
+                      buffer->request.write_data);
+    return true;
 }
 
-void HandleRead(FuseBuffer* buffer, FuseAppLoopCallback* callback) {
-  const uint64_t unique = buffer->request.header.unique;
-  const uint64_t nodeid = buffer->request.header.nodeid;
-  const uint64_t offset = buffer->request.read_in.offset;
-  const uint32_t size = buffer->request.read_in.size;
+bool HandleMessage(FuseAppLoop* loop, FuseBuffer* buffer, int fd, FuseAppLoopCallback* callback) {
+    if (!buffer->request.Read(fd)) {
+        return false;
+    }
 
-  if (size > kFuseMaxRead) {
-    buffer->response.Reset(0, -EINVAL, buffer->request.header.unique);
-    return;
-  }
+    const uint32_t opcode = buffer->request.header.opcode;
+    LOG(VERBOSE) << "Read a fuse packet, opcode=" << opcode;
+    switch (opcode) {
+        case FUSE_FORGET:
+            // Do not reply to FUSE_FORGET.
+            return true;
 
-  const int32_t read_size = callback->OnRead(nodeid, offset, size,
-                                             buffer->response.read_data);
-  if (read_size < 0) {
-    buffer->response.Reset(0, read_size, buffer->request.header.unique);
-    return;
-  }
+        case FUSE_LOOKUP:
+            return HandleLookUp(loop, buffer, callback);
 
-  buffer->response.ResetHeader(read_size, kFuseSuccess, unique);
-}
+        case FUSE_GETATTR:
+            return HandleGetAttr(loop, buffer, callback);
 
-void HandleWrite(FuseBuffer* buffer, FuseAppLoopCallback* callback) {
-  const uint64_t unique = buffer->request.header.unique;
-  const uint64_t nodeid = buffer->request.header.nodeid;
-  const uint64_t offset = buffer->request.write_in.offset;
-  const uint32_t size = buffer->request.write_in.size;
+        case FUSE_OPEN:
+            callback->OnOpen(buffer->request.header.unique, buffer->request.header.nodeid);
+            return true;
 
-  if (size > kFuseMaxWrite) {
-    buffer->response.Reset(0, -EINVAL, buffer->request.header.unique);
-    return;
-  }
+        case FUSE_READ:
+            return HandleRead(loop, buffer, callback);
 
-  const int32_t write_size = callback->OnWrite(nodeid, offset, size,
-                                               buffer->request.write_data);
-  if (write_size < 0) {
-    buffer->response.Reset(0, write_size, buffer->request.header.unique);
-    return;
-  }
+        case FUSE_WRITE:
+            return HandleWrite(loop, buffer, callback);
 
-  buffer->response.Reset(sizeof(fuse_write_out), kFuseSuccess, unique);
-  buffer->response.write_out.size = write_size;
+        case FUSE_RELEASE:
+            callback->OnRelease(buffer->request.header.unique, buffer->request.header.nodeid);
+            return true;
+
+        case FUSE_FSYNC:
+            callback->OnFsync(buffer->request.header.unique, buffer->request.header.nodeid);
+            return true;
+
+        default:
+            buffer->HandleNotImpl();
+            return buffer->response.Write(fd);
+    }
 }
 
 } // namespace
 
-bool StartFuseAppLoop(int raw_fd, FuseAppLoopCallback* callback) {
-  base::unique_fd fd(raw_fd);
-  FuseBuffer buffer;
+FuseAppLoopCallback::~FuseAppLoopCallback() = default;
 
-  LOG(DEBUG) << "Start fuse loop.";
-  while (callback->IsActive()) {
-    if (!buffer.request.Read(fd)) {
-      return false;
+FuseAppLoop::FuseAppLoop(base::unique_fd&& fd) : fd_(std::move(fd)) {}
+
+void FuseAppLoop::Break() {
+    const int64_t value = 1;
+    if (write(break_fd_, &value, sizeof(value)) == -1) {
+        PLOG(ERROR) << "Failed to send a break event";
+    }
+}
+
+bool FuseAppLoop::ReplySimple(uint64_t unique, int32_t result) {
+    if (result == -ENOSYS) {
+        // We should not return -ENOSYS because the kernel stops delivering FUSE
+        // command after receiving -ENOSYS as a result for the command.
+        result = -EBADF;
+    }
+    FuseSimpleResponse response;
+    response.Reset(0, result, unique);
+    return response.Write(fd_);
+}
+
+bool FuseAppLoop::ReplyLookup(uint64_t unique, uint64_t inode, int64_t size) {
+    FuseSimpleResponse response;
+    response.Reset(sizeof(fuse_entry_out), 0, unique);
+    response.entry_out.nodeid = inode;
+    response.entry_out.attr_valid = 10;
+    response.entry_out.entry_valid = 10;
+    response.entry_out.attr.ino = inode;
+    response.entry_out.attr.mode = S_IFREG | 0777;
+    response.entry_out.attr.size = size;
+    return response.Write(fd_);
+}
+
+bool FuseAppLoop::ReplyGetAttr(uint64_t unique, uint64_t inode, int64_t size, int mode) {
+    CHECK(mode == (S_IFREG | 0777) || mode == (S_IFDIR | 0777));
+    FuseSimpleResponse response;
+    response.Reset(sizeof(fuse_attr_out), 0, unique);
+    response.attr_out.attr_valid = 10;
+    response.attr_out.attr.ino = inode;
+    response.attr_out.attr.mode = mode;
+    response.attr_out.attr.size = size;
+    return response.Write(fd_);
+}
+
+bool FuseAppLoop::ReplyOpen(uint64_t unique, uint64_t fh) {
+    FuseSimpleResponse response;
+    response.Reset(sizeof(fuse_open_out), kFuseSuccess, unique);
+    response.open_out.fh = fh;
+    return response.Write(fd_);
+}
+
+bool FuseAppLoop::ReplyWrite(uint64_t unique, uint32_t size) {
+    CHECK(size <= kFuseMaxWrite);
+    FuseSimpleResponse response;
+    response.Reset(sizeof(fuse_write_out), kFuseSuccess, unique);
+    response.write_out.size = size;
+    return response.Write(fd_);
+}
+
+bool FuseAppLoop::ReplyRead(uint64_t unique, uint32_t size, const void* data) {
+    CHECK(size <= kFuseMaxRead);
+    FuseSimpleResponse response;
+    response.ResetHeader(size, kFuseSuccess, unique);
+    return response.WriteWithBody(fd_, sizeof(FuseResponse), data);
+}
+
+void FuseAppLoop::Start(FuseAppLoopCallback* callback) {
+    break_fd_.reset(eventfd(/* initval */ 0, EFD_CLOEXEC));
+    if (break_fd_.get() == -1) {
+        PLOG(ERROR) << "Failed to open FD for break event";
+        return;
     }
 
-    const uint32_t opcode = buffer.request.header.opcode;
-    LOG(VERBOSE) << "Read a fuse packet, opcode=" << opcode;
-    switch (opcode) {
-      case FUSE_FORGET:
-        // Do not reply to FUSE_FORGET.
-        continue;
-
-      case FUSE_LOOKUP:
-        HandleLookUp(&buffer, callback);
-        break;
-
-      case FUSE_GETATTR:
-        HandleGetAttr(&buffer, callback);
-        break;
-
-      case FUSE_OPEN:
-        HandleOpen(&buffer, callback);
-        break;
-
-      case FUSE_READ:
-        HandleRead(&buffer, callback);
-        break;
-
-      case FUSE_WRITE:
-        HandleWrite(&buffer, callback);
-        break;
-
-      case FUSE_RELEASE:
-        HandleRelease(&buffer, callback);
-        break;
-
-      case FUSE_FSYNC:
-        HandleFsync(&buffer, callback);
-        break;
-
-      default:
-        buffer.HandleNotImpl();
-        break;
+    base::unique_fd epoll_fd(epoll_create1(EPOLL_CLOEXEC));
+    if (epoll_fd.get() == -1) {
+        PLOG(ERROR) << "Failed to open FD for epoll";
+        return;
     }
 
-    if (!buffer.response.Write(fd)) {
-      LOG(ERROR) << "Failed to write a response to the device.";
-      return false;
-    }
-  }
+    int last_event;
+    int break_event;
 
-  return true;
+    std::unique_ptr<EpollController> epoll_controller(new EpollController(std::move(epoll_fd)));
+    if (!epoll_controller->AddFd(fd_, EPOLLIN, &last_event)) {
+        return;
+    }
+    if (!epoll_controller->AddFd(break_fd_, EPOLLIN, &break_event)) {
+        return;
+    }
+
+    last_event = 0;
+    break_event = 0;
+
+    FuseBuffer buffer;
+    while (true) {
+        if (!epoll_controller->Wait(1)) {
+            break;
+        }
+        last_event = 0;
+        *reinterpret_cast<int*>(epoll_controller->events()[0].data.ptr) =
+            epoll_controller->events()[0].events;
+
+        if (break_event != 0 || (last_event & ~EPOLLIN) != 0) {
+            break;
+        }
+
+        if (!HandleMessage(this, &buffer, fd_, callback)) {
+            break;
+        }
+    }
+
+    LOG(VERBOSE) << "FuseAppLoop exit";
 }
 
 }  // namespace fuse
diff --git a/libappfuse/FuseBridgeLoop.cc b/libappfuse/FuseBridgeLoop.cc
index 2386bf8..8b0c53e 100644
--- a/libappfuse/FuseBridgeLoop.cc
+++ b/libappfuse/FuseBridgeLoop.cc
@@ -16,85 +16,372 @@
 
 #include "libappfuse/FuseBridgeLoop.h"
 
+#include <sys/epoll.h>
+#include <sys/socket.h>
+
+#include <unordered_map>
+
 #include <android-base/logging.h>
 #include <android-base/unique_fd.h>
 
+#include "libappfuse/EpollController.h"
+
 namespace android {
 namespace fuse {
+namespace {
 
-bool StartFuseBridgeLoop(
-    int raw_dev_fd, int raw_proxy_fd, FuseBridgeLoopCallback* callback) {
-  base::unique_fd dev_fd(raw_dev_fd);
-  base::unique_fd proxy_fd(raw_proxy_fd);
-  FuseBuffer buffer;
-  size_t open_count = 0;
+enum class FuseBridgeState { kWaitToReadEither, kWaitToReadProxy, kWaitToWriteProxy, kClosing };
 
-  LOG(DEBUG) << "Start fuse loop.";
-  while (true) {
-    if (!buffer.request.Read(dev_fd)) {
-      return false;
+struct FuseBridgeEntryEvent {
+    FuseBridgeEntry* entry;
+    int events;
+};
+
+void GetObservedEvents(FuseBridgeState state, int* device_events, int* proxy_events) {
+    switch (state) {
+        case FuseBridgeState::kWaitToReadEither:
+            *device_events = EPOLLIN;
+            *proxy_events = EPOLLIN;
+            return;
+        case FuseBridgeState::kWaitToReadProxy:
+            *device_events = 0;
+            *proxy_events = EPOLLIN;
+            return;
+        case FuseBridgeState::kWaitToWriteProxy:
+            *device_events = 0;
+            *proxy_events = EPOLLOUT;
+            return;
+        case FuseBridgeState::kClosing:
+            *device_events = 0;
+            *proxy_events = 0;
+            return;
+    }
+}
+
+void LogResponseError(const std::string& message, const FuseResponse& response) {
+    LOG(ERROR) << message << ": header.len=" << response.header.len
+               << " header.error=" << response.header.error
+               << " header.unique=" << response.header.unique;
+}
+}
+
+class FuseBridgeEntry {
+  public:
+    FuseBridgeEntry(int mount_id, base::unique_fd&& dev_fd, base::unique_fd&& proxy_fd)
+        : mount_id_(mount_id),
+          device_fd_(std::move(dev_fd)),
+          proxy_fd_(std::move(proxy_fd)),
+          state_(FuseBridgeState::kWaitToReadEither),
+          last_state_(FuseBridgeState::kWaitToReadEither),
+          last_device_events_({this, 0}),
+          last_proxy_events_({this, 0}),
+          open_count_(0) {}
+
+    // Transfer bytes depends on availability of FDs and the internal |state_|.
+    void Transfer(FuseBridgeLoopCallback* callback) {
+        constexpr int kUnexpectedEventMask = ~(EPOLLIN | EPOLLOUT);
+        const bool unexpected_event = (last_device_events_.events & kUnexpectedEventMask) ||
+                                      (last_proxy_events_.events & kUnexpectedEventMask);
+        const bool device_read_ready = last_device_events_.events & EPOLLIN;
+        const bool proxy_read_ready = last_proxy_events_.events & EPOLLIN;
+        const bool proxy_write_ready = last_proxy_events_.events & EPOLLOUT;
+
+        last_device_events_.events = 0;
+        last_proxy_events_.events = 0;
+
+        LOG(VERBOSE) << "Transfer device_read_ready=" << device_read_ready
+                     << " proxy_read_ready=" << proxy_read_ready
+                     << " proxy_write_ready=" << proxy_write_ready;
+
+        if (unexpected_event) {
+            LOG(ERROR) << "Invalid epoll event is observed";
+            state_ = FuseBridgeState::kClosing;
+            return;
+        }
+
+        switch (state_) {
+            case FuseBridgeState::kWaitToReadEither:
+                if (proxy_read_ready) {
+                    state_ = ReadFromProxy();
+                } else if (device_read_ready) {
+                    state_ = ReadFromDevice(callback);
+                }
+                return;
+
+            case FuseBridgeState::kWaitToReadProxy:
+                CHECK(proxy_read_ready);
+                state_ = ReadFromProxy();
+                return;
+
+            case FuseBridgeState::kWaitToWriteProxy:
+                CHECK(proxy_write_ready);
+                state_ = WriteToProxy();
+                return;
+
+            case FuseBridgeState::kClosing:
+                return;
+        }
     }
 
-    const uint32_t opcode = buffer.request.header.opcode;
-    LOG(VERBOSE) << "Read a fuse packet, opcode=" << opcode;
-    switch (opcode) {
-      case FUSE_FORGET:
-        // Do not reply to FUSE_FORGET.
-        continue;
+    bool IsClosing() const { return state_ == FuseBridgeState::kClosing; }
 
-      case FUSE_LOOKUP:
-      case FUSE_GETATTR:
-      case FUSE_OPEN:
-      case FUSE_READ:
-      case FUSE_WRITE:
-      case FUSE_RELEASE:
-      case FUSE_FSYNC:
-        if (!buffer.request.Write(proxy_fd)) {
-          LOG(ERROR) << "Failed to write a request to the proxy.";
-          return false;
+    int mount_id() const { return mount_id_; }
+
+  private:
+    friend class BridgeEpollController;
+
+    FuseBridgeState ReadFromProxy() {
+        switch (buffer_.response.ReadOrAgain(proxy_fd_)) {
+            case ResultOrAgain::kSuccess:
+                break;
+            case ResultOrAgain::kFailure:
+                return FuseBridgeState::kClosing;
+            case ResultOrAgain::kAgain:
+                return FuseBridgeState::kWaitToReadProxy;
         }
-        if (!buffer.response.Read(proxy_fd)) {
-          LOG(ERROR) << "Failed to read a response from the proxy.";
-          return false;
+
+        if (!buffer_.response.Write(device_fd_)) {
+            LogResponseError("Failed to write a reply from proxy to device", buffer_.response);
+            return FuseBridgeState::kClosing;
         }
-        break;
 
-      case FUSE_INIT:
-        buffer.HandleInit();
-        break;
+        auto it = opcode_map_.find(buffer_.response.header.unique);
+        if (it != opcode_map_.end()) {
+            switch (it->second) {
+                case FUSE_OPEN:
+                    if (buffer_.response.header.error == fuse::kFuseSuccess) {
+                        open_count_++;
+                    }
+                    break;
 
-      default:
-        buffer.HandleNotImpl();
-        break;
+                case FUSE_RELEASE:
+                    if (open_count_ > 0) {
+                        open_count_--;
+                    } else {
+                        LOG(WARNING) << "Unexpected FUSE_RELEASE before opening a file.";
+                        break;
+                    }
+                    if (open_count_ == 0) {
+                        return FuseBridgeState::kClosing;
+                    }
+                    break;
+            }
+            opcode_map_.erase(it);
+        }
+
+        return FuseBridgeState::kWaitToReadEither;
     }
 
-    if (!buffer.response.Write(dev_fd)) {
-      LOG(ERROR) << "Failed to write a response to the device.";
-      return false;
+    FuseBridgeState ReadFromDevice(FuseBridgeLoopCallback* callback) {
+        LOG(VERBOSE) << "ReadFromDevice";
+        if (!buffer_.request.Read(device_fd_)) {
+            return FuseBridgeState::kClosing;
+        }
+
+        const uint32_t opcode = buffer_.request.header.opcode;
+        const uint64_t unique = buffer_.request.header.unique;
+        LOG(VERBOSE) << "Read a fuse packet, opcode=" << opcode << " unique=" << unique;
+        if (unique == 0) {
+            return FuseBridgeState::kWaitToReadEither;
+        }
+        switch (opcode) {
+            case FUSE_FORGET:
+                // Do not reply to FUSE_FORGET.
+                return FuseBridgeState::kWaitToReadEither;
+
+            case FUSE_LOOKUP:
+            case FUSE_GETATTR:
+            case FUSE_OPEN:
+            case FUSE_READ:
+            case FUSE_WRITE:
+            case FUSE_RELEASE:
+            case FUSE_FSYNC:
+                if (opcode == FUSE_OPEN || opcode == FUSE_RELEASE) {
+                    opcode_map_.emplace(buffer_.request.header.unique, opcode);
+                }
+                return WriteToProxy();
+
+            case FUSE_INIT:
+                buffer_.HandleInit();
+                break;
+
+            default:
+                buffer_.HandleNotImpl();
+                break;
+        }
+
+        if (!buffer_.response.Write(device_fd_)) {
+            LogResponseError("Failed to write a response to device", buffer_.response);
+            return FuseBridgeState::kClosing;
+        }
+
+        if (opcode == FUSE_INIT) {
+            callback->OnMount(mount_id_);
+        }
+
+        return FuseBridgeState::kWaitToReadEither;
     }
 
-    switch (opcode) {
-      case FUSE_INIT:
-        callback->OnMount();
-        break;
-      case FUSE_OPEN:
-        if (buffer.response.header.error == fuse::kFuseSuccess) {
-          open_count++;
+    FuseBridgeState WriteToProxy() {
+        switch (buffer_.request.WriteOrAgain(proxy_fd_)) {
+            case ResultOrAgain::kSuccess:
+                return FuseBridgeState::kWaitToReadEither;
+            case ResultOrAgain::kFailure:
+                LOG(ERROR) << "Failed to write a request to proxy:"
+                           << " header.len=" << buffer_.request.header.len
+                           << " header.opcode=" << buffer_.request.header.opcode
+                           << " header.unique=" << buffer_.request.header.unique
+                           << " header.nodeid=" << buffer_.request.header.nodeid;
+                return FuseBridgeState::kClosing;
+            case ResultOrAgain::kAgain:
+                return FuseBridgeState::kWaitToWriteProxy;
         }
-        break;
-      case FUSE_RELEASE:
-        if (open_count != 0) {
-            open_count--;
-        } else {
-            LOG(WARNING) << "Unexpected FUSE_RELEASE before opening a file.";
-            break;
-        }
-        if (open_count == 0) {
-          return true;
-        }
-        break;
     }
-  }
+
+    const int mount_id_;
+    base::unique_fd device_fd_;
+    base::unique_fd proxy_fd_;
+    FuseBuffer buffer_;
+    FuseBridgeState state_;
+    FuseBridgeState last_state_;
+    FuseBridgeEntryEvent last_device_events_;
+    FuseBridgeEntryEvent last_proxy_events_;
+
+    // Remember map between unique and opcode in fuse_in_header so that we can
+    // refer the opcode later.
+    std::unordered_map<uint64_t, uint32_t> opcode_map_;
+
+    int open_count_;
+
+    DISALLOW_COPY_AND_ASSIGN(FuseBridgeEntry);
+};
+
+class BridgeEpollController : private EpollController {
+  public:
+    BridgeEpollController(base::unique_fd&& poll_fd) : EpollController(std::move(poll_fd)) {}
+
+    bool AddBridgePoll(FuseBridgeEntry* bridge) const {
+        return InvokeControl(EPOLL_CTL_ADD, bridge);
+    }
+
+    bool UpdateOrDeleteBridgePoll(FuseBridgeEntry* bridge) const {
+        return InvokeControl(
+            bridge->state_ != FuseBridgeState::kClosing ? EPOLL_CTL_MOD : EPOLL_CTL_DEL, bridge);
+    }
+
+    bool Wait(size_t bridge_count, std::unordered_set<FuseBridgeEntry*>* entries_out) {
+        CHECK(entries_out);
+        const size_t event_count = std::max<size_t>(bridge_count * 2, 1);
+        if (!EpollController::Wait(event_count)) {
+            return false;
+        }
+        entries_out->clear();
+        for (const auto& event : events()) {
+            FuseBridgeEntryEvent* const entry_event =
+                reinterpret_cast<FuseBridgeEntryEvent*>(event.data.ptr);
+            entry_event->events = event.events;
+            entries_out->insert(entry_event->entry);
+        }
+        return true;
+    }
+
+  private:
+    bool InvokeControl(int op, FuseBridgeEntry* bridge) const {
+        LOG(VERBOSE) << "InvokeControl op=" << op << " bridge=" << bridge->mount_id_
+                     << " state=" << static_cast<int>(bridge->state_)
+                     << " last_state=" << static_cast<int>(bridge->last_state_);
+
+        int last_device_events;
+        int last_proxy_events;
+        int device_events;
+        int proxy_events;
+        GetObservedEvents(bridge->last_state_, &last_device_events, &last_proxy_events);
+        GetObservedEvents(bridge->state_, &device_events, &proxy_events);
+        bool result = true;
+        if (op != EPOLL_CTL_MOD || last_device_events != device_events) {
+            result &= EpollController::InvokeControl(op, bridge->device_fd_, device_events,
+                                                     &bridge->last_device_events_);
+        }
+        if (op != EPOLL_CTL_MOD || last_proxy_events != proxy_events) {
+            result &= EpollController::InvokeControl(op, bridge->proxy_fd_, proxy_events,
+                                                     &bridge->last_proxy_events_);
+        }
+        return result;
+    }
+};
+
+FuseBridgeLoop::FuseBridgeLoop() : opened_(true) {
+    base::unique_fd epoll_fd(epoll_create1(/* no flag */ 0));
+    if (epoll_fd.get() == -1) {
+        PLOG(ERROR) << "Failed to open FD for epoll";
+        opened_ = false;
+        return;
+    }
+    epoll_controller_.reset(new BridgeEpollController(std::move(epoll_fd)));
+}
+
+FuseBridgeLoop::~FuseBridgeLoop() { CHECK(bridges_.empty()); }
+
+bool FuseBridgeLoop::AddBridge(int mount_id, base::unique_fd dev_fd, base::unique_fd proxy_fd) {
+    LOG(VERBOSE) << "Adding bridge " << mount_id;
+
+    std::unique_ptr<FuseBridgeEntry> bridge(
+        new FuseBridgeEntry(mount_id, std::move(dev_fd), std::move(proxy_fd)));
+    std::lock_guard<std::mutex> lock(mutex_);
+    if (!opened_) {
+        LOG(ERROR) << "Tried to add a mount to a closed bridge";
+        return false;
+    }
+    if (bridges_.count(mount_id)) {
+        LOG(ERROR) << "Tried to add a mount point that has already been added";
+        return false;
+    }
+    if (!epoll_controller_->AddBridgePoll(bridge.get())) {
+        return false;
+    }
+
+    bridges_.emplace(mount_id, std::move(bridge));
+    return true;
+}
+
+bool FuseBridgeLoop::ProcessEventLocked(const std::unordered_set<FuseBridgeEntry*>& entries,
+                                        FuseBridgeLoopCallback* callback) {
+    for (auto entry : entries) {
+        entry->Transfer(callback);
+        if (!epoll_controller_->UpdateOrDeleteBridgePoll(entry)) {
+            return false;
+        }
+        if (entry->IsClosing()) {
+            const int mount_id = entry->mount_id();
+            callback->OnClosed(mount_id);
+            bridges_.erase(mount_id);
+            if (bridges_.size() == 0) {
+                // All bridges are now closed.
+                return false;
+            }
+        }
+    }
+    return true;
+}
+
+void FuseBridgeLoop::Start(FuseBridgeLoopCallback* callback) {
+    LOG(DEBUG) << "Start fuse bridge loop";
+    std::unordered_set<FuseBridgeEntry*> entries;
+    while (true) {
+        const bool wait_result = epoll_controller_->Wait(bridges_.size(), &entries);
+        LOG(VERBOSE) << "Receive epoll events";
+        {
+            std::lock_guard<std::mutex> lock(mutex_);
+            if (!(wait_result && ProcessEventLocked(entries, callback))) {
+                for (auto it = bridges_.begin(); it != bridges_.end();) {
+                    callback->OnClosed(it->second->mount_id());
+                    it = bridges_.erase(it);
+                }
+                opened_ = false;
+                return;
+            }
+        }
+    }
 }
 
 }  // namespace fuse
diff --git a/libappfuse/FuseBuffer.cc b/libappfuse/FuseBuffer.cc
index 8fb2dbc..1eab46c 100644
--- a/libappfuse/FuseBuffer.cc
+++ b/libappfuse/FuseBuffer.cc
@@ -23,81 +23,168 @@
 #include <algorithm>
 #include <type_traits>
 
+#include <sys/socket.h>
+#include <sys/uio.h>
+
 #include <android-base/file.h>
 #include <android-base/logging.h>
 #include <android-base/macros.h>
 
 namespace android {
 namespace fuse {
+namespace {
 
-static_assert(
-    std::is_standard_layout<FuseBuffer>::value,
-    "FuseBuffer must be standard layout union.");
+constexpr useconds_t kRetrySleepForWriting = 1000;  // 1 ms
 
 template <typename T>
-bool FuseMessage<T>::CheckHeaderLength(const char* name) const {
-  const auto& header = static_cast<const T*>(this)->header;
-  if (header.len >= sizeof(header) && header.len <= sizeof(T)) {
+bool CheckHeaderLength(const FuseMessage<T>* self, const char* name, size_t max_size) {
+    const auto& header = static_cast<const T*>(self)->header;
+    if (header.len >= sizeof(header) && header.len <= max_size) {
+        return true;
+    } else {
+        LOG(ERROR) << "Invalid header length is found in " << name << ": " << header.len;
+        return false;
+    }
+}
+
+template <typename T>
+ResultOrAgain ReadInternal(FuseMessage<T>* self, int fd, int sockflag) {
+    char* const buf = reinterpret_cast<char*>(self);
+    const ssize_t result = sockflag ? TEMP_FAILURE_RETRY(recv(fd, buf, sizeof(T), sockflag))
+                                    : TEMP_FAILURE_RETRY(read(fd, buf, sizeof(T)));
+
+    switch (result) {
+        case 0:
+            // Expected EOF.
+            return ResultOrAgain::kFailure;
+        case -1:
+            if (errno == EAGAIN) {
+                return ResultOrAgain::kAgain;
+            }
+            PLOG(ERROR) << "Failed to read a FUSE message";
+            return ResultOrAgain::kFailure;
+    }
+
+    const auto& header = static_cast<const T*>(self)->header;
+    if (result < static_cast<ssize_t>(sizeof(header))) {
+        LOG(ERROR) << "Read bytes " << result << " are shorter than header size " << sizeof(header);
+        return ResultOrAgain::kFailure;
+    }
+
+    if (!CheckHeaderLength<T>(self, "Read", sizeof(T))) {
+        return ResultOrAgain::kFailure;
+    }
+
+    if (static_cast<uint32_t>(result) != header.len) {
+        LOG(ERROR) << "Read bytes " << result << " are different from header.len " << header.len;
+        return ResultOrAgain::kFailure;
+    }
+
+    return ResultOrAgain::kSuccess;
+}
+
+template <typename T>
+ResultOrAgain WriteInternal(const FuseMessage<T>* self, int fd, int sockflag, const void* data,
+                            size_t max_size) {
+    if (!CheckHeaderLength<T>(self, "Write", max_size)) {
+        return ResultOrAgain::kFailure;
+    }
+
+    const char* const buf = reinterpret_cast<const char*>(self);
+    const auto& header = static_cast<const T*>(self)->header;
+
+    while (true) {
+        int result;
+        if (sockflag) {
+            CHECK(data == nullptr);
+            result = TEMP_FAILURE_RETRY(send(fd, buf, header.len, sockflag));
+        } else if (data) {
+            const struct iovec vec[] = {{const_cast<char*>(buf), sizeof(header)},
+                                        {const_cast<void*>(data), header.len - sizeof(header)}};
+            result = TEMP_FAILURE_RETRY(writev(fd, vec, arraysize(vec)));
+        } else {
+            result = TEMP_FAILURE_RETRY(write(fd, buf, header.len));
+        }
+        if (result == -1) {
+            switch (errno) {
+                case ENOBUFS:
+                    // When returning ENOBUFS, epoll still reports the FD is writable. Just usleep
+                    // and retry again.
+                    usleep(kRetrySleepForWriting);
+                    continue;
+                case EAGAIN:
+                    return ResultOrAgain::kAgain;
+                default:
+                    PLOG(ERROR) << "Failed to write a FUSE message: "
+                                << "fd=" << fd << " "
+                                << "sockflag=" << sockflag << " "
+                                << "data=" << data;
+                    return ResultOrAgain::kFailure;
+            }
+        }
+
+        if (static_cast<unsigned int>(result) != header.len) {
+            LOG(ERROR) << "Written bytes " << result << " is different from length in header "
+                       << header.len;
+            return ResultOrAgain::kFailure;
+        }
+        return ResultOrAgain::kSuccess;
+    }
+}
+}
+
+static_assert(std::is_standard_layout<FuseBuffer>::value,
+              "FuseBuffer must be standard layout union.");
+
+bool SetupMessageSockets(base::unique_fd (*result)[2]) {
+    base::unique_fd fds[2];
+    {
+        int raw_fds[2];
+        if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, raw_fds) == -1) {
+            PLOG(ERROR) << "Failed to create sockets for proxy";
+            return false;
+        }
+        fds[0].reset(raw_fds[0]);
+        fds[1].reset(raw_fds[1]);
+    }
+
+    constexpr int kMaxMessageSize = sizeof(FuseBuffer);
+    if (setsockopt(fds[0], SOL_SOCKET, SO_SNDBUFFORCE, &kMaxMessageSize, sizeof(int)) != 0 ||
+        setsockopt(fds[1], SOL_SOCKET, SO_SNDBUFFORCE, &kMaxMessageSize, sizeof(int)) != 0) {
+        PLOG(ERROR) << "Failed to update buffer size for socket";
+        return false;
+    }
+
+    (*result)[0] = std::move(fds[0]);
+    (*result)[1] = std::move(fds[1]);
     return true;
-  } else {
-    LOG(ERROR) << "Invalid header length is found in " << name << ": " <<
-        header.len;
-    return false;
-  }
 }
 
 template <typename T>
 bool FuseMessage<T>::Read(int fd) {
-  char* const buf = reinterpret_cast<char*>(this);
-  const ssize_t result = TEMP_FAILURE_RETRY(::read(fd, buf, sizeof(T)));
-  if (result < 0) {
-    PLOG(ERROR) << "Failed to read a FUSE message";
-    return false;
-  }
+    return ReadInternal(this, fd, 0) == ResultOrAgain::kSuccess;
+}
 
-  const auto& header = static_cast<const T*>(this)->header;
-  if (result < static_cast<ssize_t>(sizeof(header))) {
-    LOG(ERROR) << "Read bytes " << result << " are shorter than header size " <<
-        sizeof(header);
-    return false;
-  }
-
-  if (!CheckHeaderLength("Read")) {
-    return false;
-  }
-
-  if (static_cast<uint32_t>(result) > header.len) {
-    LOG(ERROR) << "Read bytes " << result << " are longer than header.len " <<
-        header.len;
-    return false;
-  }
-
-  if (!base::ReadFully(fd, buf + result, header.len - result)) {
-    PLOG(ERROR) << "ReadFully failed";
-    return false;
-  }
-
-  return true;
+template <typename T>
+ResultOrAgain FuseMessage<T>::ReadOrAgain(int fd) {
+    return ReadInternal(this, fd, MSG_DONTWAIT);
 }
 
 template <typename T>
 bool FuseMessage<T>::Write(int fd) const {
-  if (!CheckHeaderLength("Write")) {
-    return false;
-  }
-
-  const char* const buf = reinterpret_cast<const char*>(this);
-  const auto& header = static_cast<const T*>(this)->header;
-  if (!base::WriteFully(fd, buf, header.len)) {
-    PLOG(ERROR) << "WriteFully failed";
-    return false;
-  }
-
-  return true;
+    return WriteInternal(this, fd, 0, nullptr, sizeof(T)) == ResultOrAgain::kSuccess;
 }
 
-template class FuseMessage<FuseRequest>;
-template class FuseMessage<FuseResponse>;
+template <typename T>
+bool FuseMessage<T>::WriteWithBody(int fd, size_t max_size, const void* data) const {
+    CHECK(data != nullptr);
+    return WriteInternal(this, fd, 0, data, max_size) == ResultOrAgain::kSuccess;
+}
+
+template <typename T>
+ResultOrAgain FuseMessage<T>::WriteOrAgain(int fd) const {
+    return WriteInternal(this, fd, MSG_DONTWAIT, nullptr, sizeof(T));
+}
 
 void FuseRequest::Reset(
     uint32_t data_length, uint32_t opcode, uint64_t unique) {
@@ -107,17 +194,18 @@
   header.unique = unique;
 }
 
-void FuseResponse::ResetHeader(
-    uint32_t data_length, int32_t error, uint64_t unique) {
-  CHECK_LE(error, 0) << "error should be zero or negative.";
-  header.len = sizeof(fuse_out_header) + data_length;
-  header.error = error;
-  header.unique = unique;
+template <size_t N>
+void FuseResponseBase<N>::ResetHeader(uint32_t data_length, int32_t error, uint64_t unique) {
+    CHECK_LE(error, 0) << "error should be zero or negative.";
+    header.len = sizeof(fuse_out_header) + data_length;
+    header.error = error;
+    header.unique = unique;
 }
 
-void FuseResponse::Reset(uint32_t data_length, int32_t error, uint64_t unique) {
-  memset(this, 0, sizeof(fuse_out_header) + data_length);
-  ResetHeader(data_length, error, unique);
+template <size_t N>
+void FuseResponseBase<N>::Reset(uint32_t data_length, int32_t error, uint64_t unique) {
+    memset(this, 0, sizeof(fuse_out_header) + data_length);
+    ResetHeader(data_length, error, unique);
 }
 
 void FuseBuffer::HandleInit() {
@@ -163,9 +251,17 @@
 void FuseBuffer::HandleNotImpl() {
   LOG(VERBOSE) << "NOTIMPL op=" << request.header.opcode << " uniq="
       << request.header.unique << " nid=" << request.header.nodeid;
-  const uint64_t unique = request.header.unique;
+  // Add volatile as a workaround for compiler issue which removes the temporary
+  // variable.
+  const volatile uint64_t unique = request.header.unique;
   response.Reset(0, -ENOSYS, unique);
 }
 
+template class FuseMessage<FuseRequest>;
+template class FuseMessage<FuseResponse>;
+template class FuseMessage<FuseSimpleResponse>;
+template struct FuseResponseBase<0u>;
+template struct FuseResponseBase<kFuseMaxRead>;
+
 }  // namespace fuse
 }  // namespace android
diff --git a/libappfuse/OWNERS b/libappfuse/OWNERS
new file mode 100644
index 0000000..cd7cb74
--- /dev/null
+++ b/libappfuse/OWNERS
@@ -0,0 +1 @@
+hirono@google.com
diff --git a/libappfuse/include/libappfuse/EpollController.h b/libappfuse/include/libappfuse/EpollController.h
new file mode 100644
index 0000000..622bd2c
--- /dev/null
+++ b/libappfuse/include/libappfuse/EpollController.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2017 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 specic language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_LIBAPPFUSE_EPOLLCONTROLLER_H_
+#define ANDROID_LIBAPPFUSE_EPOLLCONTROLLER_H_
+
+#include <sys/epoll.h>
+
+#include <vector>
+
+#include <android-base/macros.h>
+#include <android-base/unique_fd.h>
+
+namespace android {
+namespace fuse {
+
+class EpollController {
+  public:
+    explicit EpollController(base::unique_fd&& poll_fd);
+    bool Wait(size_t event_count);
+    bool AddFd(int fd, int events, void* data);
+    bool UpdateFd(int fd, int events, void* data);
+    bool RemoveFd(int fd);
+
+    const std::vector<epoll_event>& events() const;
+
+  protected:
+    bool InvokeControl(int op, int fd, int events, void* data) const;
+
+  private:
+    base::unique_fd poll_fd_;
+    std::vector<epoll_event> events_;
+
+    DISALLOW_COPY_AND_ASSIGN(EpollController);
+};
+}
+}
+
+#endif  // ANDROID_LIBAPPFUSE_EPOLLCONTROLLER_H_
diff --git a/libappfuse/include/libappfuse/FuseAppLoop.h b/libappfuse/include/libappfuse/FuseAppLoop.h
index c3edfcc..f2ef2b5 100644
--- a/libappfuse/include/libappfuse/FuseAppLoop.h
+++ b/libappfuse/include/libappfuse/FuseAppLoop.h
@@ -17,23 +17,51 @@
 #ifndef ANDROID_LIBAPPFUSE_FUSEAPPLOOP_H_
 #define ANDROID_LIBAPPFUSE_FUSEAPPLOOP_H_
 
+#include <memory>
+#include <mutex>
+
+#include <android-base/unique_fd.h>
+
 #include "libappfuse/FuseBuffer.h"
 
 namespace android {
 namespace fuse {
 
+class EpollController;
+
 class FuseAppLoopCallback {
  public:
-  virtual bool IsActive() = 0;
-  virtual int64_t OnGetSize(uint64_t inode) = 0;
-  virtual int32_t OnFsync(uint64_t inode) = 0;
-  virtual int32_t OnWrite(
-      uint64_t inode, uint64_t offset, uint32_t size, const void* data) = 0;
-  virtual int32_t OnRead(
-      uint64_t inode, uint64_t offset, uint32_t size, void* data) = 0;
-  virtual int32_t OnOpen(uint64_t inode) = 0;
-  virtual int32_t OnRelease(uint64_t inode) = 0;
-  virtual ~FuseAppLoopCallback() = default;
+   virtual void OnLookup(uint64_t unique, uint64_t inode) = 0;
+   virtual void OnGetAttr(uint64_t unique, uint64_t inode) = 0;
+   virtual void OnFsync(uint64_t unique, uint64_t inode) = 0;
+   virtual void OnWrite(uint64_t unique, uint64_t inode, uint64_t offset, uint32_t size,
+                        const void* data) = 0;
+   virtual void OnRead(uint64_t unique, uint64_t inode, uint64_t offset, uint32_t size) = 0;
+   virtual void OnOpen(uint64_t unique, uint64_t inode) = 0;
+   virtual void OnRelease(uint64_t unique, uint64_t inode) = 0;
+   virtual ~FuseAppLoopCallback();
+};
+
+class FuseAppLoop final {
+  public:
+    FuseAppLoop(base::unique_fd&& fd);
+
+    void Start(FuseAppLoopCallback* callback);
+    void Break();
+
+    bool ReplySimple(uint64_t unique, int32_t result);
+    bool ReplyLookup(uint64_t unique, uint64_t inode, int64_t size);
+    bool ReplyGetAttr(uint64_t unique, uint64_t inode, int64_t size, int mode);
+    bool ReplyOpen(uint64_t unique, uint64_t fh);
+    bool ReplyWrite(uint64_t unique, uint32_t size);
+    bool ReplyRead(uint64_t unique, uint32_t size, const void* data);
+
+  private:
+    base::unique_fd fd_;
+    base::unique_fd break_fd_;
+
+    // Lock for multi-threading.
+    std::mutex mutex_;
 };
 
 bool StartFuseAppLoop(int fd, FuseAppLoopCallback* callback);
diff --git a/libappfuse/include/libappfuse/FuseBridgeLoop.h b/libappfuse/include/libappfuse/FuseBridgeLoop.h
index 1f71cf2..6bfda98 100644
--- a/libappfuse/include/libappfuse/FuseBridgeLoop.h
+++ b/libappfuse/include/libappfuse/FuseBridgeLoop.h
@@ -17,6 +17,13 @@
 #ifndef ANDROID_LIBAPPFUSE_FUSEBRIDGELOOP_H_
 #define ANDROID_LIBAPPFUSE_FUSEBRIDGELOOP_H_
 
+#include <map>
+#include <mutex>
+#include <queue>
+#include <unordered_set>
+
+#include <android-base/macros.h>
+
 #include "libappfuse/FuseBuffer.h"
 
 namespace android {
@@ -24,12 +31,41 @@
 
 class FuseBridgeLoopCallback {
  public:
-  virtual void OnMount() = 0;
-  virtual ~FuseBridgeLoopCallback() = default;
+   virtual void OnMount(int mount_id) = 0;
+   virtual void OnClosed(int mount_id) = 0;
+   virtual ~FuseBridgeLoopCallback() = default;
 };
 
-bool StartFuseBridgeLoop(
-    int dev_fd, int proxy_fd, FuseBridgeLoopCallback* callback);
+class FuseBridgeEntry;
+class BridgeEpollController;
+
+class FuseBridgeLoop final {
+  public:
+    FuseBridgeLoop();
+    ~FuseBridgeLoop();
+
+    void Start(FuseBridgeLoopCallback* callback);
+
+    // Add bridge to the loop. It's OK to invoke the method from a different
+    // thread from one which invokes |Start|.
+    bool AddBridge(int mount_id, base::unique_fd dev_fd, base::unique_fd proxy_fd);
+
+  private:
+    bool ProcessEventLocked(const std::unordered_set<FuseBridgeEntry*>& entries,
+                            FuseBridgeLoopCallback* callback);
+
+    std::unique_ptr<BridgeEpollController> epoll_controller_;
+
+    // Map between |mount_id| and bridge entry.
+    std::map<int, std::unique_ptr<FuseBridgeEntry>> bridges_;
+
+    // Lock for multi-threading.
+    std::mutex mutex_;
+
+    bool opened_;
+
+    DISALLOW_COPY_AND_ASSIGN(FuseBridgeLoop);
+};
 
 }  // namespace fuse
 }  // namespace android
diff --git a/libappfuse/include/libappfuse/FuseBuffer.h b/libappfuse/include/libappfuse/FuseBuffer.h
index 7abd2fa..7a70bf3 100644
--- a/libappfuse/include/libappfuse/FuseBuffer.h
+++ b/libappfuse/include/libappfuse/FuseBuffer.h
@@ -17,6 +17,7 @@
 #ifndef ANDROID_LIBAPPFUSE_FUSEBUFFER_H_
 #define ANDROID_LIBAPPFUSE_FUSEBUFFER_H_
 
+#include <android-base/unique_fd.h>
 #include <linux/fuse.h>
 
 namespace android {
@@ -28,13 +29,23 @@
 constexpr size_t kFuseMaxRead = 128 * 1024;
 constexpr int32_t kFuseSuccess = 0;
 
+// Setup sockets to transfer FuseMessage.
+bool SetupMessageSockets(base::unique_fd (*sockets)[2]);
+
+enum class ResultOrAgain {
+    kSuccess,
+    kFailure,
+    kAgain,
+};
+
 template<typename T>
 class FuseMessage {
  public:
   bool Read(int fd);
   bool Write(int fd) const;
- private:
-  bool CheckHeaderLength(const char* name) const;
+  bool WriteWithBody(int fd, size_t max_size, const void* data) const;
+  ResultOrAgain ReadOrAgain(int fd);
+  ResultOrAgain WriteOrAgain(int fd) const;
 };
 
 // FuseRequest represents file operation requests from /dev/fuse. It starts
@@ -54,33 +65,37 @@
     // for FUSE_READ
     fuse_read_in read_in;
     // for FUSE_LOOKUP
-    char lookup_name[0];
+    char lookup_name[kFuseMaxWrite];
   };
   void Reset(uint32_t data_length, uint32_t opcode, uint64_t unique);
 };
 
 // FuseResponse represents file operation responses to /dev/fuse. It starts
 // from fuse_out_header. The body layout depends on the operation code.
-struct FuseResponse : public FuseMessage<FuseResponse> {
-  fuse_out_header header;
-  union {
-    // for FUSE_INIT
-    fuse_init_out init_out;
-    // for FUSE_LOOKUP
-    fuse_entry_out entry_out;
-    // for FUSE_GETATTR
-    fuse_attr_out attr_out;
-    // for FUSE_OPEN
-    fuse_open_out open_out;
-    // for FUSE_READ
-    char read_data[kFuseMaxRead];
-    // for FUSE_WRITE
-    fuse_write_out write_out;
-  };
-  void Reset(uint32_t data_length, int32_t error, uint64_t unique);
-  void ResetHeader(uint32_t data_length, int32_t error, uint64_t unique);
+template <size_t N>
+struct FuseResponseBase : public FuseMessage<FuseResponseBase<N>> {
+    fuse_out_header header;
+    union {
+        // for FUSE_INIT
+        fuse_init_out init_out;
+        // for FUSE_LOOKUP
+        fuse_entry_out entry_out;
+        // for FUSE_GETATTR
+        fuse_attr_out attr_out;
+        // for FUSE_OPEN
+        fuse_open_out open_out;
+        // for FUSE_READ
+        char read_data[N];
+        // for FUSE_WRITE
+        fuse_write_out write_out;
+    };
+    void Reset(uint32_t data_length, int32_t error, uint64_t unique);
+    void ResetHeader(uint32_t data_length, int32_t error, uint64_t unique);
 };
 
+using FuseResponse = FuseResponseBase<kFuseMaxRead>;
+using FuseSimpleResponse = FuseResponseBase<0u>;
+
 // To reduce memory usage, FuseBuffer shares the memory region for request and
 // response.
 union FuseBuffer final {
diff --git a/libappfuse/tests/FuseAppLoopTest.cc b/libappfuse/tests/FuseAppLoopTest.cc
index 25906cf..98e3665 100644
--- a/libappfuse/tests/FuseAppLoopTest.cc
+++ b/libappfuse/tests/FuseAppLoopTest.cc
@@ -23,6 +23,9 @@
 #include <gtest/gtest.h>
 #include <thread>
 
+#include "libappfuse/EpollController.h"
+#include "libappfuse/FuseBridgeLoop.h"
+
 namespace android {
 namespace fuse {
 namespace {
@@ -37,85 +40,61 @@
 class Callback : public FuseAppLoopCallback {
  public:
   std::vector<CallbackRequest> requests;
+  FuseAppLoop* loop;
 
-  bool IsActive() override {
-    return true;
+  void OnGetAttr(uint64_t seq, uint64_t inode) override {
+      EXPECT_NE(FUSE_ROOT_ID, static_cast<int>(inode));
+      EXPECT_TRUE(loop->ReplyGetAttr(seq, inode, kTestFileSize, S_IFREG | 0777));
   }
 
-  int64_t OnGetSize(uint64_t inode) override {
-    if (inode == FUSE_ROOT_ID) {
-      return 0;
-    } else {
-      return kTestFileSize;
-    }
+  void OnLookup(uint64_t unique, uint64_t inode) override {
+      EXPECT_NE(FUSE_ROOT_ID, static_cast<int>(inode));
+      EXPECT_TRUE(loop->ReplyLookup(unique, inode, kTestFileSize));
   }
 
-  int32_t OnFsync(uint64_t inode) override {
-    requests.push_back({
-      .code = FUSE_FSYNC,
-      .inode = inode
-    });
-    return 0;
+  void OnFsync(uint64_t seq, uint64_t inode) override {
+      requests.push_back({.code = FUSE_FSYNC, .inode = inode});
+      loop->ReplySimple(seq, 0);
   }
 
-  int32_t OnWrite(uint64_t inode,
-                  uint64_t offset ATTRIBUTE_UNUSED,
-                  uint32_t size ATTRIBUTE_UNUSED,
-                  const void* data ATTRIBUTE_UNUSED) override {
-    requests.push_back({
-      .code = FUSE_WRITE,
-      .inode = inode
-    });
-    return 0;
+  void OnWrite(uint64_t seq, uint64_t inode, uint64_t offset ATTRIBUTE_UNUSED,
+               uint32_t size ATTRIBUTE_UNUSED, const void* data ATTRIBUTE_UNUSED) override {
+      requests.push_back({.code = FUSE_WRITE, .inode = inode});
+      loop->ReplyWrite(seq, 0);
   }
 
-  int32_t OnRead(uint64_t inode,
-                 uint64_t offset ATTRIBUTE_UNUSED,
-                 uint32_t size ATTRIBUTE_UNUSED,
-                 void* data ATTRIBUTE_UNUSED) override {
-    requests.push_back({
-      .code = FUSE_READ,
-      .inode = inode
-    });
-    return 0;
+  void OnRead(uint64_t seq, uint64_t inode, uint64_t offset ATTRIBUTE_UNUSED,
+              uint32_t size ATTRIBUTE_UNUSED) override {
+      requests.push_back({.code = FUSE_READ, .inode = inode});
+      loop->ReplySimple(seq, 0);
   }
 
-  int32_t OnOpen(uint64_t inode) override {
-    requests.push_back({
-      .code = FUSE_OPEN,
-      .inode = inode
-    });
-    return 0;
+  void OnOpen(uint64_t seq, uint64_t inode) override {
+      requests.push_back({.code = FUSE_OPEN, .inode = inode});
+      loop->ReplyOpen(seq, inode);
   }
 
-  int32_t OnRelease(uint64_t inode) override {
-    requests.push_back({
-      .code = FUSE_RELEASE,
-      .inode = inode
-    });
-    return 0;
+  void OnRelease(uint64_t seq, uint64_t inode) override {
+      requests.push_back({.code = FUSE_RELEASE, .inode = inode});
+      loop->ReplySimple(seq, 0);
   }
 };
 
 class FuseAppLoopTest : public ::testing::Test {
- private:
-  std::thread thread_;
-
  protected:
-  base::unique_fd sockets_[2];
-  Callback callback_;
-  FuseRequest request_;
-  FuseResponse response_;
+   std::thread thread_;
+   base::unique_fd sockets_[2];
+   Callback callback_;
+   FuseRequest request_;
+   FuseResponse response_;
+   std::unique_ptr<FuseAppLoop> loop_;
 
-  void SetUp() override {
-    base::SetMinimumLogSeverity(base::VERBOSE);
-    int sockets[2];
-    ASSERT_EQ(0, socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sockets));
-    sockets_[0].reset(sockets[0]);
-    sockets_[1].reset(sockets[1]);
-    thread_ = std::thread([this] {
-      StartFuseAppLoop(sockets_[1].release(), &callback_);
-    });
+   void SetUp() override {
+       base::SetMinimumLogSeverity(base::VERBOSE);
+       ASSERT_TRUE(SetupMessageSockets(&sockets_));
+       loop_.reset(new FuseAppLoop(std::move(sockets_[1])));
+       callback_.loop = loop_.get();
+       thread_ = std::thread([this] { loop_->Start(&callback_); });
   }
 
   void CheckCallback(
@@ -303,5 +282,18 @@
   CheckCallback(sizeof(fuse_write_in), FUSE_WRITE, sizeof(fuse_write_out));
 }
 
+TEST_F(FuseAppLoopTest, Break) {
+    // Ensure that the loop started.
+    request_.Reset(sizeof(fuse_open_in), FUSE_OPEN, 1);
+    request_.header.nodeid = 10;
+    ASSERT_TRUE(request_.Write(sockets_[0]));
+    ASSERT_TRUE(response_.Read(sockets_[0]));
+
+    loop_->Break();
+    if (thread_.joinable()) {
+        thread_.join();
+    }
+}
+
 }  // namespace fuse
 }  // namespace android
diff --git a/libappfuse/tests/FuseBridgeLoopTest.cc b/libappfuse/tests/FuseBridgeLoopTest.cc
index e74d9e7..0a28451 100644
--- a/libappfuse/tests/FuseBridgeLoopTest.cc
+++ b/libappfuse/tests/FuseBridgeLoopTest.cc
@@ -32,10 +32,12 @@
 class Callback : public FuseBridgeLoopCallback {
  public:
   bool mounted;
-  Callback() : mounted(false) {}
-  void OnMount() override {
-    mounted = true;
-  }
+  bool closed;
+  Callback() : mounted(false), closed(false) {}
+
+  void OnMount(int /*mount_id*/) override { mounted = true; }
+
+  void OnClosed(int /* mount_id */) override { closed = true; }
 };
 
 class FuseBridgeLoopTest : public ::testing::Test {
@@ -50,18 +52,12 @@
 
   void SetUp() override {
     base::SetMinimumLogSeverity(base::VERBOSE);
-    int dev_sockets[2];
-    int proxy_sockets[2];
-    ASSERT_EQ(0, socketpair(AF_UNIX, SOCK_SEQPACKET, 0, dev_sockets));
-    ASSERT_EQ(0, socketpair(AF_UNIX, SOCK_SEQPACKET, 0, proxy_sockets));
-    dev_sockets_[0].reset(dev_sockets[0]);
-    dev_sockets_[1].reset(dev_sockets[1]);
-    proxy_sockets_[0].reset(proxy_sockets[0]);
-    proxy_sockets_[1].reset(proxy_sockets[1]);
-
+    ASSERT_TRUE(SetupMessageSockets(&dev_sockets_));
+    ASSERT_TRUE(SetupMessageSockets(&proxy_sockets_));
     thread_ = std::thread([this] {
-      StartFuseBridgeLoop(
-          dev_sockets_[1].release(), proxy_sockets_[0].release(), &callback_);
+        FuseBridgeLoop loop;
+        loop.AddBridge(1, std::move(dev_sockets_[1]), std::move(proxy_sockets_[0]));
+        loop.Start(&callback_);
     });
   }
 
@@ -71,6 +67,7 @@
     memset(&request_, 0, sizeof(FuseRequest));
     request_.header.opcode = opcode;
     request_.header.len = sizeof(fuse_in_header);
+    request_.header.unique = 1;
     ASSERT_TRUE(request_.Write(dev_sockets_[0]));
 
     memset(&response_, 0, sizeof(FuseResponse));
@@ -122,6 +119,7 @@
     if (thread_.joinable()) {
       thread_.join();
     }
+    ASSERT_TRUE(callback_.closed);
   }
 
   void TearDown() override {
diff --git a/libappfuse/tests/FuseBufferTest.cc b/libappfuse/tests/FuseBufferTest.cc
index 1a1abd5..ade34ac 100644
--- a/libappfuse/tests/FuseBufferTest.cc
+++ b/libappfuse/tests/FuseBufferTest.cc
@@ -112,30 +112,6 @@
   TestWriteInvalidLength(sizeof(fuse_in_header) - 1);
 }
 
-TEST(FuseMessageTest, ShortWriteAndRead) {
-  int raw_fds[2];
-  ASSERT_EQ(0, socketpair(AF_UNIX, SOCK_STREAM, 0, raw_fds));
-
-  android::base::unique_fd fds[2];
-  fds[0].reset(raw_fds[0]);
-  fds[1].reset(raw_fds[1]);
-
-  const int send_buffer_size = 1024;
-  ASSERT_EQ(0, setsockopt(fds[0], SOL_SOCKET, SO_SNDBUF, &send_buffer_size,
-                          sizeof(int)));
-
-  bool succeed = false;
-  const int sender_fd = fds[0].get();
-  std::thread thread([sender_fd, &succeed] {
-    FuseRequest request;
-    request.header.len = 1024 * 4;
-    succeed = request.Write(sender_fd);
-  });
-  thread.detach();
-  FuseRequest request;
-  ASSERT_TRUE(request.Read(fds[1]));
-}
-
 TEST(FuseResponseTest, Reset) {
   FuseResponse response;
   // Write 1 to the first ten bytes.
@@ -211,5 +187,29 @@
   EXPECT_EQ(-ENOSYS, buffer.response.header.error);
 }
 
+TEST(SetupMessageSocketsTest, Stress) {
+    constexpr int kCount = 1000;
+
+    FuseRequest request;
+    request.header.len = sizeof(FuseRequest);
+
+    base::unique_fd fds[2];
+    SetupMessageSockets(&fds);
+
+    std::thread thread([&fds] {
+        FuseRequest request;
+        for (int i = 0; i < kCount; ++i) {
+            ASSERT_TRUE(request.Read(fds[1]));
+            usleep(1000);
+        }
+    });
+
+    for (int i = 0; i < kCount; ++i) {
+        ASSERT_TRUE(request.Write(fds[0]));
+    }
+
+    thread.join();
+}
+
 } // namespace fuse
 } // namespace android
diff --git a/libasyncio/Android.bp b/libasyncio/Android.bp
new file mode 100644
index 0000000..4ab439d
--- /dev/null
+++ b/libasyncio/Android.bp
@@ -0,0 +1,48 @@
+//
+// Copyright (C) 2017 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_defaults {
+    name: "libasyncio_defaults",
+    cflags: [
+        "-Wall",
+        "-Werror",
+        "-Wextra",
+    ],
+}
+
+cc_library {
+    name: "libasyncio",
+    defaults: ["libasyncio_defaults"],
+    vendor_available: true,
+    recovery_available: true,
+    host_supported: true,
+    srcs: [
+        "AsyncIO.cpp",
+    ],
+
+    export_include_dirs: ["include"],
+    target: {
+        darwin: {
+            enabled: false,
+        },
+        linux_bionic: {
+            enabled: true,
+        },
+        windows: {
+            enabled: false,
+        },
+    },
+}
diff --git a/libasyncio/AsyncIO.cpp b/libasyncio/AsyncIO.cpp
new file mode 100644
index 0000000..6149f09
--- /dev/null
+++ b/libasyncio/AsyncIO.cpp
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2016 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 <asyncio/AsyncIO.h>
+#include <sys/syscall.h>
+#include <unistd.h>
+#include <cstdint>
+#include <cstring>
+
+int io_setup(unsigned nr, aio_context_t* ctxp) {
+    return syscall(__NR_io_setup, nr, ctxp);
+}
+
+int io_destroy(aio_context_t ctx) {
+    return syscall(__NR_io_destroy, ctx);
+}
+
+int io_submit(aio_context_t ctx, long nr, iocb** iocbpp) {
+    return syscall(__NR_io_submit, ctx, nr, iocbpp);
+}
+
+int io_getevents(aio_context_t ctx, long min_nr, long max_nr, io_event* events, timespec* timeout) {
+    return syscall(__NR_io_getevents, ctx, min_nr, max_nr, events, timeout);
+}
+
+int io_cancel(aio_context_t ctx, iocb* iocbp, io_event* result) {
+    return syscall(__NR_io_cancel, ctx, iocbp, result);
+}
+
+void io_prep(iocb* iocb, int fd, const void* buf, uint64_t count, int64_t offset, bool read) {
+    memset(iocb, 0, sizeof(*iocb));
+    iocb->aio_fildes = fd;
+    iocb->aio_lio_opcode = read ? IOCB_CMD_PREAD : IOCB_CMD_PWRITE;
+    iocb->aio_reqprio = 0;
+    iocb->aio_buf = reinterpret_cast<uint64_t>(buf);
+    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
new file mode 100644
index 0000000..9620d2a
--- /dev/null
+++ b/libasyncio/include/asyncio/AsyncIO.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2016 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 _ASYNCIO_H
+#define _ASYNCIO_H
+
+#include <linux/aio_abi.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <sys/cdefs.h>
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Provides kernel aio operations.
+ */
+
+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, 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
+};
+#endif
+
+#endif  // ASYNCIO_H
diff --git a/libbacktrace/Android.bp b/libbacktrace/Android.bp
index e02aaf2..a10e636 100644
--- a/libbacktrace/Android.bp
+++ b/libbacktrace/Android.bp
@@ -22,12 +22,6 @@
         "-Werror",
     ],
 
-    // The latest clang (r230699) does not allow SP/PC to be declared in inline asm lists.
-    clang_cflags: ["-Wno-inline-asm"],
-
-    include_dirs: ["external/libunwind/include/tdep"],
-
-
     target: {
         darwin: {
             enabled: false,
@@ -41,32 +35,40 @@
         lib64: {
             suffix: "64",
         },
-    }
+    },
 }
 
 libbacktrace_sources = [
     "Backtrace.cpp",
     "BacktraceCurrent.cpp",
     "BacktracePtrace.cpp",
-    "thread_utils.c",
     "ThreadEntry.cpp",
-    "UnwindCurrent.cpp",
-    "UnwindMap.cpp",
-    "UnwindPtrace.cpp",
+    "UnwindStack.cpp",
+    "UnwindStackMap.cpp",
 ]
 
 cc_library_headers {
     name: "libbacktrace_headers",
     vendor_available: true,
+    recovery_available: true,
     export_include_dirs: ["include"],
 }
 
 cc_library {
     name: "libbacktrace",
-    vendor_available: true,
+    vendor_available: false,
+    recovery_available: true,
+    vndk: {
+        enabled: true,
+        support_system_process: true,
+    },
     defaults: ["libbacktrace_common"],
     host_supported: true,
 
+    cflags: [
+        "-Wexit-time-destructors",
+    ],
+
     srcs: [
         "BacktraceMap.cpp",
     ],
@@ -76,6 +78,9 @@
     target: {
         darwin: {
             enabled: true,
+            shared_libs: [
+                "libbase",
+            ],
         },
         linux: {
             srcs: libbacktrace_sources,
@@ -83,34 +88,30 @@
             shared_libs: [
                 "libbase",
                 "liblog",
-                "libunwind",
+                "libunwindstack",
+                "libdexfile",
             ],
 
-            static_libs: ["libcutils"],
-            host_ldlibs: ["-lrt"],
-        },
-        linux_bionic: {
-            enabled: true,
-            srcs: libbacktrace_sources,
-
-            shared_libs: [
-                "libbase",
-                "liblog",
-                "libunwind",
+            static_libs: [
+                "libprocinfo",
             ],
 
-            static_libs: ["libcutils"],
+            // libdexfile will eventually properly export headers, for now
+            // include these directly.
+            include_dirs: [
+                "art/runtime",
+            ],
         },
         android: {
-            srcs: libbacktrace_sources,
-
-            shared_libs: [
-                "libbase",
-                "liblog",
-                "libunwind",
-            ],
-
-            static_libs: ["libasync_safe", "libcutils"],
+            static_libs: ["libasync_safe"],
+        },
+        vendor: {
+            cflags: ["-DNO_LIBDEXFILE_SUPPORT"],
+            exclude_shared_libs: ["libdexfile"],
+        },
+        recovery: {
+            cflags: ["-DNO_LIBDEXFILE_SUPPORT"],
+            exclude_shared_libs: ["libdexfile"],
         },
     },
     whole_static_libs: ["libdemangle"],
@@ -126,42 +127,8 @@
     cflags: ["-O0"],
     srcs: ["backtrace_testlib.cpp"],
 
-    target: {
-        linux: {
-            shared_libs: [
-                "libunwind",
-            ],
-        },
-        android: {
-            shared_libs: [
-                "libunwind",
-            ],
-        },
-    }
-}
-
-//-------------------------------------------------------------------------
-// The libbacktrace_offline static library.
-//-------------------------------------------------------------------------
-cc_library_static {
-    name: "libbacktrace_offline",
-    defaults: ["libbacktrace_common"],
-    host_supported: true,
-    srcs: ["BacktraceOffline.cpp"],
-
-    cflags: [
-        "-D__STDC_CONSTANT_MACROS",
-        "-D__STDC_LIMIT_MACROS",
-        "-D__STDC_FORMAT_MACROS",
-    ],
-
-    header_libs: ["llvm-headers"],
-
-    // Use shared libraries so their headers get included during build.
-    shared_libs = [
-        "libbase",
-        "libunwind",
-        "libziparchive",
+    shared_libs: [
+        "libunwindstack",
     ],
 }
 
@@ -176,7 +143,6 @@
         "backtrace_offline_test.cpp",
         "backtrace_test.cpp",
         "GetPss.cpp",
-        "thread_utils.c",
     ],
 
     cflags: [
@@ -188,46 +154,56 @@
     shared_libs: [
         "libbacktrace_test",
         "libbacktrace",
+        "libdexfile",
         "libbase",
-        "libcutils",
         "liblog",
-        "libunwind",
+        "libunwindstack",
     ],
 
     group_static_libs: true,
 
-    // Statically link LLVMlibraries to remove dependency on llvm shared library.
-    static_libs = [
-        "libbacktrace_offline",
-        "libLLVMObject",
-        "libLLVMBitReader",
-        "libLLVMMC",
-        "libLLVMMCParser",
-        "libLLVMCore",
-        "libLLVMSupport",
-
-        "libziparchive",
-        "libz",
-    ],
-
-    header_libs: ["llvm-headers"],
-
     target: {
         android: {
             cflags: ["-DENABLE_PSS_TESTS"],
             shared_libs: [
-                "libdl",
                 "libutils",
             ],
         },
-        linux: {
-            host_ldlibs: [
-                "-lpthread",
-                "-lrt",
-                "-ldl",
-                "-lncurses",
-            ],
+        linux_glibc: {
             static_libs: ["libutils"],
         },
     },
+
+    // libdexfile will eventually properly export headers, for now
+    // include these directly.
+    include_dirs: [
+        "art/runtime",
+    ],
+
+    test_suites: ["device-tests"],
+    data: [
+        "testdata/arm/*",
+        "testdata/arm64/*",
+        "testdata/x86/*",
+        "testdata/x86_64/*",
+    ],
+    required: [
+        "libbacktrace_test",
+    ],
+}
+
+cc_benchmark {
+    name: "backtrace_benchmarks",
+    defaults: ["libbacktrace_common"],
+
+    srcs: [
+        "backtrace_benchmarks.cpp",
+        "backtrace_read_benchmarks.cpp",
+    ],
+
+    shared_libs: [
+        "libbacktrace",
+        "libbase",
+        "libunwindstack",
+    ],
 }
diff --git a/libbacktrace/Backtrace.cpp b/libbacktrace/Backtrace.cpp
index e46d353..6bec63c 100644
--- a/libbacktrace/Backtrace.cpp
+++ b/libbacktrace/Backtrace.cpp
@@ -23,6 +23,7 @@
 #include <string>
 
 #include <android-base/stringprintf.h>
+#include <android-base/threads.h>
 
 #include <backtrace/Backtrace.h>
 #include <backtrace/BacktraceMap.h>
@@ -30,9 +31,7 @@
 #include <demangle.h>
 
 #include "BacktraceLog.h"
-#include "thread_utils.h"
-#include "UnwindCurrent.h"
-#include "UnwindPtrace.h"
+#include "UnwindStack.h"
 
 using android::base::StringPrintf;
 
@@ -54,7 +53,7 @@
   }
 }
 
-std::string Backtrace::GetFunctionName(uintptr_t pc, uintptr_t* offset, const backtrace_map_t* map) {
+std::string Backtrace::GetFunctionName(uint64_t pc, uint64_t* offset, const backtrace_map_t* map) {
   backtrace_map_t map_value;
   if (map == nullptr) {
     FillInMap(pc, &map_value);
@@ -67,7 +66,7 @@
   return demangle(GetFunctionNameRaw(pc, offset).c_str());
 }
 
-bool Backtrace::VerifyReadWordArgs(uintptr_t ptr, word_t* out_value) {
+bool Backtrace::VerifyReadWordArgs(uint64_t ptr, word_t* out_value) {
   if (ptr & (sizeof(word_t)-1)) {
     BACK_LOGW("invalid pointer %p", reinterpret_cast<void*>(ptr));
     *out_value = static_cast<word_t>(-1);
@@ -84,35 +83,30 @@
 }
 
 std::string Backtrace::FormatFrameData(const backtrace_frame_data_t* frame) {
-  uintptr_t relative_pc;
   std::string map_name;
   if (BacktraceMap::IsValid(frame->map)) {
-    relative_pc = BacktraceMap::GetRelativePc(frame->map, frame->pc);
+    map_name = frame->map.Name();
     if (!frame->map.name.empty()) {
-      map_name = frame->map.name.c_str();
       if (map_name[0] == '[' && map_name[map_name.size() - 1] == ']') {
         map_name.resize(map_name.size() - 1);
         map_name += StringPrintf(":%" PRIPTR "]", frame->map.start);
       }
-    } else {
-      map_name = StringPrintf("<anonymous:%" PRIPTR ">", frame->map.start);
     }
   } else {
     map_name = "<unknown>";
-    relative_pc = frame->pc;
   }
 
-  std::string line(StringPrintf("#%02zu pc %" PRIPTR "  ", frame->num, relative_pc));
+  std::string line(StringPrintf("#%02zu pc %" PRIPTR "  ", frame->num, frame->rel_pc));
   line += map_name;
   // Special handling for non-zero offset maps, we need to print that
   // information.
   if (frame->map.offset != 0) {
-    line += " (offset " + StringPrintf("0x%" PRIxPTR, frame->map.offset) + ")";
+    line += " (offset " + StringPrintf("0x%" PRIx64, frame->map.offset) + ")";
   }
   if (!frame->func_name.empty()) {
     line += " (" + frame->func_name;
     if (frame->func_offset) {
-      line += StringPrintf("+%" PRIuPTR, frame->func_offset);
+      line += StringPrintf("+%" PRIu64, frame->func_offset);
     }
     line += ')';
   }
@@ -120,7 +114,7 @@
   return line;
 }
 
-void Backtrace::FillInMap(uintptr_t pc, backtrace_map_t* map) {
+void Backtrace::FillInMap(uint64_t pc, backtrace_map_t* map) {
   if (map_ != nullptr) {
     map_->FillIn(pc, map);
   }
@@ -130,36 +124,51 @@
   if (pid == BACKTRACE_CURRENT_PROCESS) {
     pid = getpid();
     if (tid == BACKTRACE_CURRENT_THREAD) {
-      tid = gettid();
+      tid = android::base::GetThreadId();
     }
   } else if (tid == BACKTRACE_CURRENT_THREAD) {
     tid = pid;
   }
 
   if (pid == getpid()) {
-    return new UnwindCurrent(pid, tid, map);
+    return new UnwindStackCurrent(pid, tid, map);
   } else {
-    return new UnwindPtrace(pid, tid, map);
+    return new UnwindStackPtrace(pid, tid, map);
   }
 }
 
 std::string Backtrace::GetErrorString(BacktraceUnwindError error) {
-  switch (error) {
-  case BACKTRACE_UNWIND_NO_ERROR:
-    return "No error";
-  case BACKTRACE_UNWIND_ERROR_SETUP_FAILED:
-    return "Setup failed";
-  case BACKTRACE_UNWIND_ERROR_MAP_MISSING:
-    return "No map found";
-  case BACKTRACE_UNWIND_ERROR_INTERNAL:
-    return "Internal libbacktrace error, please submit a bugreport";
-  case BACKTRACE_UNWIND_ERROR_THREAD_DOESNT_EXIST:
-    return "Thread doesn't exist";
-  case BACKTRACE_UNWIND_ERROR_THREAD_TIMEOUT:
-    return "Thread has not responded to signal in time";
-  case BACKTRACE_UNWIND_ERROR_UNSUPPORTED_OPERATION:
-    return "Attempt to use an unsupported feature";
-  case BACKTRACE_UNWIND_ERROR_NO_CONTEXT:
-    return "Attempt to do an offline unwind without a context";
+  switch (error.error_code) {
+    case BACKTRACE_UNWIND_NO_ERROR:
+      return "No error";
+    case BACKTRACE_UNWIND_ERROR_SETUP_FAILED:
+      return "Setup failed";
+    case BACKTRACE_UNWIND_ERROR_MAP_MISSING:
+      return "No map found";
+    case BACKTRACE_UNWIND_ERROR_INTERNAL:
+      return "Internal libbacktrace error, please submit a bugreport";
+    case BACKTRACE_UNWIND_ERROR_THREAD_DOESNT_EXIST:
+      return "Thread doesn't exist";
+    case BACKTRACE_UNWIND_ERROR_THREAD_TIMEOUT:
+      return "Thread has not responded to signal in time";
+    case BACKTRACE_UNWIND_ERROR_UNSUPPORTED_OPERATION:
+      return "Attempt to use an unsupported feature";
+    case BACKTRACE_UNWIND_ERROR_NO_CONTEXT:
+      return "Attempt to do an offline unwind without a context";
+    case BACKTRACE_UNWIND_ERROR_EXCEED_MAX_FRAMES_LIMIT:
+      return "Exceed MAX_BACKTRACE_FRAMES limit";
+    case BACKTRACE_UNWIND_ERROR_ACCESS_MEM_FAILED:
+      return android::base::StringPrintf("Failed to read memory at addr 0x%" PRIx64,
+                                         error.error_info.addr);
+    case BACKTRACE_UNWIND_ERROR_ACCESS_REG_FAILED:
+      return android::base::StringPrintf("Failed to read register %" PRIu64, error.error_info.regno);
+    case BACKTRACE_UNWIND_ERROR_FIND_PROC_INFO_FAILED:
+      return "Failed to find a function in debug sections";
+    case BACKTRACE_UNWIND_ERROR_EXECUTE_DWARF_INSTRUCTION_FAILED:
+      return "Failed to execute dwarf instructions in debug sections";
+    case BACKTRACE_UNWIND_ERROR_UNWIND_INFO:
+      return "Failed to unwind due to invalid unwind information";
+    case BACKTRACE_UNWIND_ERROR_REPEATED_FRAME:
+      return "Failed to unwind due to same sp/pc repeating";
   }
 }
diff --git a/libbacktrace/BacktraceCurrent.cpp b/libbacktrace/BacktraceCurrent.cpp
index fb76b85..39cb995 100644
--- a/libbacktrace/BacktraceCurrent.cpp
+++ b/libbacktrace/BacktraceCurrent.cpp
@@ -28,15 +28,15 @@
 
 #include <string>
 
+#include <android-base/threads.h>
 #include <backtrace/Backtrace.h>
 #include <backtrace/BacktraceMap.h>
 
 #include "BacktraceAsyncSafeLog.h"
 #include "BacktraceCurrent.h"
 #include "ThreadEntry.h"
-#include "thread_utils.h"
 
-bool BacktraceCurrent::ReadWord(uintptr_t ptr, word_t* out_value) {
+bool BacktraceCurrent::ReadWord(uint64_t ptr, word_t* out_value) {
   if (!VerifyReadWordArgs(ptr, out_value)) {
     return false;
   }
@@ -53,7 +53,7 @@
   }
 }
 
-size_t BacktraceCurrent::Read(uintptr_t addr, uint8_t* buffer, size_t bytes) {
+size_t BacktraceCurrent::Read(uint64_t addr, uint8_t* buffer, size_t bytes) {
   backtrace_map_t map;
   FillInMap(addr, &map);
   if (!BacktraceMap::IsValid(map) || !(map.flags & PROT_READ)) {
@@ -64,19 +64,19 @@
   return bytes;
 }
 
-bool BacktraceCurrent::Unwind(size_t num_ignore_frames, ucontext_t* ucontext) {
+bool BacktraceCurrent::Unwind(size_t num_ignore_frames, void* ucontext) {
   if (GetMap() == nullptr) {
     // Without a map object, we can't do anything.
-    error_ = BACKTRACE_UNWIND_ERROR_MAP_MISSING;
+    error_.error_code = BACKTRACE_UNWIND_ERROR_MAP_MISSING;
     return false;
   }
 
-  error_ = BACKTRACE_UNWIND_NO_ERROR;
+  error_.error_code = BACKTRACE_UNWIND_NO_ERROR;
   if (ucontext) {
     return UnwindFromContext(num_ignore_frames, ucontext);
   }
 
-  if (Tid() != gettid()) {
+  if (Tid() != android::base::GetThreadId()) {
     return UnwindThread(num_ignore_frames);
   }
 
@@ -114,16 +114,17 @@
 static void SignalLogOnly(int, siginfo_t*, void*) {
   ErrnoRestorer restore;
 
-  BACK_ASYNC_SAFE_LOGE("pid %d, tid %d: Received a spurious signal %d\n", getpid(), gettid(),
-                       THREAD_SIGNAL);
+  BACK_ASYNC_SAFE_LOGE("pid %d, tid %d: Received a spurious signal %d\n", getpid(),
+                       static_cast<int>(android::base::GetThreadId()), THREAD_SIGNAL);
 }
 
 static void SignalHandler(int, siginfo_t*, void* sigcontext) {
   ErrnoRestorer restore;
 
-  ThreadEntry* entry = ThreadEntry::Get(getpid(), gettid(), false);
+  ThreadEntry* entry = ThreadEntry::Get(getpid(), android::base::GetThreadId(), false);
   if (!entry) {
-    BACK_ASYNC_SAFE_LOGE("pid %d, tid %d entry not found", getpid(), gettid());
+    BACK_ASYNC_SAFE_LOGE("pid %d, tid %d entry not found", getpid(),
+                         static_cast<int>(android::base::GetThreadId()));
     return;
   }
 
@@ -163,7 +164,7 @@
     BACK_ASYNC_SAFE_LOGE("sigaction failed: %s", strerror(errno));
     ThreadEntry::Remove(entry);
     pthread_mutex_unlock(&g_sigaction_mutex);
-    error_ = BACKTRACE_UNWIND_ERROR_INTERNAL;
+    error_.error_code = BACKTRACE_UNWIND_ERROR_INTERNAL;
     return false;
   }
 
@@ -171,9 +172,9 @@
     // Do not emit an error message, this might be expected. Set the
     // error and let the caller decide.
     if (errno == ESRCH) {
-      error_ = BACKTRACE_UNWIND_ERROR_THREAD_DOESNT_EXIST;
+      error_.error_code = BACKTRACE_UNWIND_ERROR_THREAD_DOESNT_EXIST;
     } else {
-      error_ = BACKTRACE_UNWIND_ERROR_INTERNAL;
+      error_.error_code = BACKTRACE_UNWIND_ERROR_INTERNAL;
     }
 
     sigaction(THREAD_SIGNAL, &oldact, nullptr);
@@ -218,9 +219,9 @@
   } else {
     // Check to see if the thread has disappeared.
     if (tgkill(Pid(), Tid(), 0) == -1 && errno == ESRCH) {
-      error_ = BACKTRACE_UNWIND_ERROR_THREAD_DOESNT_EXIST;
+      error_.error_code = BACKTRACE_UNWIND_ERROR_THREAD_DOESNT_EXIST;
     } else {
-      error_ = BACKTRACE_UNWIND_ERROR_THREAD_TIMEOUT;
+      error_.error_code = BACKTRACE_UNWIND_ERROR_THREAD_TIMEOUT;
       BACK_ASYNC_SAFE_LOGE("Timed out waiting for signal handler to get ucontext data.");
     }
   }
diff --git a/libbacktrace/BacktraceCurrent.h b/libbacktrace/BacktraceCurrent.h
index 072ffd2..48c14ea 100644
--- a/libbacktrace/BacktraceCurrent.h
+++ b/libbacktrace/BacktraceCurrent.h
@@ -19,7 +19,6 @@
 
 #include <stdint.h>
 #include <sys/types.h>
-#include <ucontext.h>
 
 #include <backtrace/Backtrace.h>
 
@@ -40,11 +39,11 @@
   BacktraceCurrent(pid_t pid, pid_t tid, BacktraceMap* map) : Backtrace(pid, tid, map) {}
   virtual ~BacktraceCurrent() {}
 
-  size_t Read(uintptr_t addr, uint8_t* buffer, size_t bytes) override;
+  size_t Read(uint64_t addr, uint8_t* buffer, size_t bytes) override;
 
-  bool ReadWord(uintptr_t ptr, word_t* out_value) override;
+  bool ReadWord(uint64_t ptr, word_t* out_value) override;
 
-  bool Unwind(size_t num_ignore_frames, ucontext_t* ucontext) override;
+  bool Unwind(size_t num_ignore_frames, void* ucontext) override;
 
  protected:
   bool DiscardFrame(const backtrace_frame_data_t& frame);
@@ -52,7 +51,7 @@
  private:
   bool UnwindThread(size_t num_ignore_frames);
 
-  virtual bool UnwindFromContext(size_t num_ignore_frames, ucontext_t* ucontext) = 0;
+  virtual bool UnwindFromContext(size_t num_ignore_frames, void* ucontext) = 0;
 };
 
 #endif // _LIBBACKTRACE_BACKTRACE_CURRENT_H
diff --git a/libbacktrace/BacktraceMap.cpp b/libbacktrace/BacktraceMap.cpp
index 0e31495..6a967f7 100644
--- a/libbacktrace/BacktraceMap.cpp
+++ b/libbacktrace/BacktraceMap.cpp
@@ -24,10 +24,21 @@
 
 #include <log/log.h>
 
-#include <backtrace/backtrace_constants.h>
+#include <android-base/stringprintf.h>
+#include <backtrace/Backtrace.h>
 #include <backtrace/BacktraceMap.h>
+#include <backtrace/backtrace_constants.h>
+#if defined(__linux__)
+#include <procinfo/process_map.h>
+#endif
 
-#include "thread_utils.h"
+using android::base::StringPrintf;
+
+std::string backtrace_map_t::Name() const {
+  if (!name.empty()) return name;
+  if (start == 0 && end == 0) return "";
+  return StringPrintf("<anonymous:%" PRIPTR ">", start);
+}
 
 BacktraceMap::BacktraceMap(pid_t pid) : pid_(pid) {
   if (pid_ < 0) {
@@ -38,38 +49,31 @@
 BacktraceMap::~BacktraceMap() {
 }
 
-void BacktraceMap::FillIn(uintptr_t addr, backtrace_map_t* map) {
+void BacktraceMap::FillIn(uint64_t addr, backtrace_map_t* map) {
   ScopedBacktraceMapIteratorLock lock(this);
-  for (BacktraceMap::const_iterator it = begin(); it != end(); ++it) {
-    if (addr >= it->start && addr < it->end) {
-      *map = *it;
+  for (auto it = begin(); it != end(); ++it) {
+    const backtrace_map_t* entry = *it;
+    if (addr >= entry->start && addr < entry->end) {
+      *map = *entry;
       return;
     }
   }
   *map = {};
 }
 
-bool BacktraceMap::ParseLine(const char* line, backtrace_map_t* map) {
+#if defined(__APPLE__)
+static bool ParseLine(const char* line, backtrace_map_t* map) {
   uint64_t start;
   uint64_t end;
   char permissions[5];
   int name_pos;
 
-#if defined(__APPLE__)
 // Mac OS vmmap(1) output:
 // __TEXT                 0009f000-000a1000 [    8K     8K] r-x/rwx SM=COW  /Volumes/android/dalvik-dev/out/host/darwin-x86/bin/libcorkscrew_test\n
 // 012345678901234567890123456789012345678901234567890123456789
 // 0         1         2         3         4         5
   if (sscanf(line, "%*21c %" SCNx64 "-%" SCNx64 " [%*13c] %3c/%*3c SM=%*3c  %n",
              &start, &end, permissions, &name_pos) != 3) {
-#else
-// Linux /proc/<pid>/maps lines:
-// 6f000000-6f01e000 rwxp 00000000 00:0c 16389419   /system/lib/libcomposer.so\n
-// 012345678901234567890123456789012345678901234567890123456789
-// 0         1         2         3         4         5
-  if (sscanf(line, "%" SCNx64 "-%" SCNx64 " %4s %*x %*x:%*x %*d %n",
-             &start, &end, permissions, &name_pos) != 3) {
-#endif
     return false;
   }
 
@@ -96,24 +100,15 @@
         map->flags, map->name.c_str());
   return true;
 }
+#endif  // defined(__APPLE__)
 
 bool BacktraceMap::Build() {
 #if defined(__APPLE__)
   char cmd[sizeof(pid_t)*3 + sizeof("vmmap -w -resident -submap -allSplitLibs -interleaved ") + 1];
-#else
-  char path[sizeof(pid_t)*3 + sizeof("/proc//maps") + 1];
-#endif
   char line[1024];
-
-#if defined(__APPLE__)
   // cmd is guaranteed to always be big enough to hold this string.
   snprintf(cmd, sizeof(cmd), "vmmap -w -resident -submap -allSplitLibs -interleaved %d", pid_);
   FILE* fp = popen(cmd, "r");
-#else
-  // path is guaranteed to always be big enough to hold this string.
-  snprintf(path, sizeof(path), "/proc/%d/maps", pid_);
-  FILE* fp = fopen(path, "r");
-#endif
   if (fp == nullptr) {
     return false;
   }
@@ -124,13 +119,19 @@
       maps_.push_back(map);
     }
   }
-#if defined(__APPLE__)
   pclose(fp);
-#else
-  fclose(fp);
-#endif
-
   return true;
+#else
+  return android::procinfo::ReadProcessMaps(
+      pid_, [&](uint64_t start, uint64_t end, uint16_t flags, uint64_t, const char* name) {
+        maps_.resize(maps_.size() + 1);
+        backtrace_map_t& map = maps_.back();
+        map.start = start;
+        map.end = end;
+        map.flags = flags;
+        map.name = name;
+      });
+#endif
 }
 
 #if defined(__APPLE__)
@@ -145,13 +146,3 @@
   return map;
 }
 #endif
-
-BacktraceMap* BacktraceMap::Create(pid_t pid, const std::vector<backtrace_map_t>& maps) {
-    BacktraceMap* backtrace_map = new BacktraceMap(pid);
-    backtrace_map->maps_.insert(backtrace_map->maps_.begin(), maps.begin(), maps.end());
-    std::sort(backtrace_map->maps_.begin(), backtrace_map->maps_.end(),
-            [](const backtrace_map_t& map1, const backtrace_map_t& map2) {
-              return map1.start < map2.start;
-            });
-    return backtrace_map;
-}
diff --git a/libbacktrace/BacktraceOffline.cpp b/libbacktrace/BacktraceOffline.cpp
deleted file mode 100644
index 0a2f5a3..0000000
--- a/libbacktrace/BacktraceOffline.cpp
+++ /dev/null
@@ -1,885 +0,0 @@
-/*
- * Copyright (C) 2015 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 "BacktraceOffline.h"
-
-extern "C" {
-#define UNW_REMOTE_ONLY
-#include <dwarf.h>
-}
-
-#include <pthread.h>
-#include <stdint.h>
-#include <stdio.h>
-#include <string.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <ucontext.h>
-#include <unistd.h>
-
-#include <memory>
-#include <mutex>
-#include <string>
-#include <vector>
-
-#include <android-base/file.h>
-#include <android-base/macros.h>
-#include <backtrace/Backtrace.h>
-#include <backtrace/BacktraceMap.h>
-#include <ziparchive/zip_archive.h>
-
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wunused-parameter"
-
-#include <llvm/ADT/StringRef.h>
-#include <llvm/Object/Binary.h>
-#include <llvm/Object/ELFObjectFile.h>
-#include <llvm/Object/ObjectFile.h>
-
-#pragma clang diagnostic pop
-
-#include "BacktraceLog.h"
-
-struct EhFrame {
-  uint64_t hdr_vaddr;
-  uint64_t vaddr;
-  uint64_t fde_table_offset;
-  uintptr_t min_func_vaddr;
-  std::vector<uint8_t> hdr_data;
-  std::vector<uint8_t> data;
-};
-
-struct ArmIdxEntry {
-  uint32_t func_offset;
-  uint32_t value;
-};
-
-struct ArmExidx {
-  uint64_t exidx_vaddr;
-  uint64_t extab_vaddr;
-  std::vector<ArmIdxEntry> exidx_data;
-  std::vector<uint8_t> extab_data;
-  // There is a one-to-one map from exidx_data.func_offset to func_vaddr_array.
-  std::vector<uint32_t> func_vaddr_array;
-};
-
-struct DebugFrameInfo {
-  bool has_arm_exidx;
-  bool has_eh_frame;
-  bool has_debug_frame;
-  bool has_gnu_debugdata;
-
-  EhFrame eh_frame;
-  ArmExidx arm_exidx;
-
-  uint64_t min_vaddr;
-  uint64_t text_end_vaddr;
-
-  DebugFrameInfo() : has_arm_exidx(false), has_eh_frame(false),
-      has_debug_frame(false), has_gnu_debugdata(false) { }
-};
-
-void Space::Clear() {
-  start = 0;
-  end = 0;
-  data = nullptr;
-}
-
-size_t Space::Read(uint64_t addr, uint8_t* buffer, size_t size) {
-  if (addr >= start && addr < end) {
-    size_t read_size = std::min(size, static_cast<size_t>(end - addr));
-    memcpy(buffer, data + (addr - start), read_size);
-    return read_size;
-  }
-  return 0;
-}
-
-static int FindProcInfo(unw_addr_space_t addr_space, unw_word_t ip, unw_proc_info* proc_info,
-                        int need_unwind_info, void* arg) {
-  BacktraceOffline* backtrace = reinterpret_cast<BacktraceOffline*>(arg);
-  bool result = backtrace->FindProcInfo(addr_space, ip, proc_info, need_unwind_info);
-  return result ? 0 : -UNW_EINVAL;
-}
-
-static void PutUnwindInfo(unw_addr_space_t, unw_proc_info_t*, void*) {
-}
-
-static int GetDynInfoListAddr(unw_addr_space_t, unw_word_t*, void*) {
-  return -UNW_ENOINFO;
-}
-
-static int AccessMem(unw_addr_space_t, unw_word_t addr, unw_word_t* value, int write, void* arg) {
-  if (write == 1) {
-    return -UNW_EINVAL;
-  }
-  BacktraceOffline* backtrace = reinterpret_cast<BacktraceOffline*>(arg);
-  *value = 0;
-  size_t read_size = backtrace->Read(addr, reinterpret_cast<uint8_t*>(value), sizeof(unw_word_t));
-  // Strictly we should check if read_size matches sizeof(unw_word_t), but it is possible in
-  // .eh_frame_hdr that the section can end at a position not aligned in sizeof(unw_word_t), and
-  // we should permit the read at the end of the section.
-  return (read_size > 0u ? 0 : -UNW_EINVAL);
-}
-
-static int AccessReg(unw_addr_space_t, unw_regnum_t unwind_reg, unw_word_t* value, int write,
-                     void* arg) {
-  if (write == 1) {
-    return -UNW_EINVAL;
-  }
-  BacktraceOffline* backtrace = reinterpret_cast<BacktraceOffline*>(arg);
-  uint64_t reg_value;
-  bool result = backtrace->ReadReg(unwind_reg, &reg_value);
-  if (result) {
-    *value = static_cast<unw_word_t>(reg_value);
-  }
-  return result ? 0 : -UNW_EINVAL;
-}
-
-static int AccessFpReg(unw_addr_space_t, unw_regnum_t, unw_fpreg_t*, int, void*) {
-  return -UNW_EINVAL;
-}
-
-static int Resume(unw_addr_space_t, unw_cursor_t*, void*) {
-  return -UNW_EINVAL;
-}
-
-static int GetProcName(unw_addr_space_t, unw_word_t, char*, size_t, unw_word_t*, void*) {
-  return -UNW_EINVAL;
-}
-
-static unw_accessors_t accessors = {
-    .find_proc_info = FindProcInfo,
-    .put_unwind_info = PutUnwindInfo,
-    .get_dyn_info_list_addr = GetDynInfoListAddr,
-    .access_mem = AccessMem,
-    .access_reg = AccessReg,
-    .access_fpreg = AccessFpReg,
-    .resume = Resume,
-    .get_proc_name = GetProcName,
-};
-
-bool BacktraceOffline::Unwind(size_t num_ignore_frames, ucontext_t* context) {
-  if (context == nullptr) {
-    BACK_LOGW("The context is needed for offline backtracing.");
-    error_ = BACKTRACE_UNWIND_ERROR_NO_CONTEXT;
-    return false;
-  }
-  context_ = context;
-  error_ = BACKTRACE_UNWIND_NO_ERROR;
-
-  unw_addr_space_t addr_space = unw_create_addr_space(&accessors, 0);
-  unw_cursor_t cursor;
-  int ret = unw_init_remote(&cursor, addr_space, this);
-  if (ret != 0) {
-    BACK_LOGW("unw_init_remote failed %d", ret);
-    unw_destroy_addr_space(addr_space);
-    error_ = BACKTRACE_UNWIND_ERROR_SETUP_FAILED;
-    return false;
-  }
-  size_t num_frames = 0;
-  do {
-    unw_word_t pc;
-    ret = unw_get_reg(&cursor, UNW_REG_IP, &pc);
-    if (ret < 0) {
-      BACK_LOGW("Failed to read IP %d", ret);
-      break;
-    }
-    unw_word_t sp;
-    ret = unw_get_reg(&cursor, UNW_REG_SP, &sp);
-    if (ret < 0) {
-      BACK_LOGW("Failed to read SP %d", ret);
-      break;
-    }
-
-    if (num_ignore_frames == 0) {
-      frames_.resize(num_frames + 1);
-      backtrace_frame_data_t* frame = &frames_[num_frames];
-      frame->num = num_frames;
-      frame->pc = static_cast<uintptr_t>(pc);
-      frame->sp = static_cast<uintptr_t>(sp);
-      frame->stack_size = 0;
-
-      if (num_frames > 0) {
-        backtrace_frame_data_t* prev = &frames_[num_frames - 1];
-        prev->stack_size = frame->sp - prev->sp;
-      }
-      frame->func_name = GetFunctionName(frame->pc, &frame->func_offset);
-      FillInMap(frame->pc, &frame->map);
-      num_frames++;
-    } else {
-      num_ignore_frames--;
-    }
-    ret = unw_step(&cursor);
-  } while (ret > 0 && num_frames < MAX_BACKTRACE_FRAMES);
-
-  unw_destroy_addr_space(addr_space);
-  context_ = nullptr;
-  return true;
-}
-
-bool BacktraceOffline::ReadWord(uintptr_t ptr, word_t* out_value) {
-  size_t bytes_read = Read(ptr, reinterpret_cast<uint8_t*>(out_value), sizeof(word_t));
-  return bytes_read == sizeof(word_t);
-}
-
-size_t BacktraceOffline::Read(uintptr_t addr, uint8_t* buffer, size_t bytes) {
-  // Normally, libunwind needs stack information and call frame information to do remote unwinding.
-  // If call frame information is stored in .debug_frame, libunwind can read it from file
-  // by itself. If call frame information is stored in .eh_frame, we need to provide data in
-  // .eh_frame/.eh_frame_hdr sections.
-  // The order of readings below doesn't matter, as the spaces don't overlap with each other.
-  size_t read_size = eh_frame_hdr_space_.Read(addr, buffer, bytes);
-  if (read_size != 0) {
-    return read_size;
-  }
-  read_size = eh_frame_space_.Read(addr, buffer, bytes);
-  if (read_size != 0) {
-    return read_size;
-  }
-  read_size = arm_exidx_space_.Read(addr, buffer, bytes);
-  if (read_size != 0) {
-    return read_size;
-  }
-  read_size = arm_extab_space_.Read(addr, buffer, bytes);
-  if (read_size != 0) {
-    return read_size;
-  }
-  read_size = stack_space_.Read(addr, buffer, bytes);
-  return read_size;
-}
-
-bool BacktraceOffline::FindProcInfo(unw_addr_space_t addr_space, uint64_t ip,
-                                    unw_proc_info_t* proc_info, int need_unwind_info) {
-  backtrace_map_t map;
-  FillInMap(ip, &map);
-  if (!BacktraceMap::IsValid(map)) {
-    return false;
-  }
-  const std::string& filename = map.name;
-  DebugFrameInfo* debug_frame = GetDebugFrameInFile(filename);
-  if (debug_frame == nullptr) {
-    return false;
-  }
-
-  eh_frame_hdr_space_.Clear();
-  eh_frame_space_.Clear();
-  arm_exidx_space_.Clear();
-  arm_extab_space_.Clear();
-
-  // vaddr in the elf file.
-  uint64_t ip_vaddr = ip - map.start + debug_frame->min_vaddr;
-
-  // The unwind info can come from .ARM.exidx or .eh_frame, or .debug_frame/.gnu_debugdata.
-  // First check .eh_frame/.debug_frame, then check .ARM.exidx. Because .eh_frame/.debug_frame has
-  // function range for each entry, by matching ip address with the function range, we know exactly
-  // whether the ip address hits an entry. But .ARM.exidx doesn't have function range for each
-  // entry, it thinks that an ip address hits an entry when (entry.addr <= ip < next_entry.addr).
-  // To prevent ip addresses hit in .eh_frame/.debug_frame being regarded as addresses hit in
-  // .ARM.exidx, we need to check .eh_frame/.debug_frame first.
-  if (debug_frame->has_eh_frame) {
-    if (ip_vaddr >= debug_frame->eh_frame.min_func_vaddr &&
-        ip_vaddr < debug_frame->text_end_vaddr) {
-      // Prepare eh_frame_hdr space and eh_frame space.
-      eh_frame_hdr_space_.start = ip - ip_vaddr + debug_frame->eh_frame.hdr_vaddr;
-      eh_frame_hdr_space_.end =
-          eh_frame_hdr_space_.start + debug_frame->eh_frame.hdr_data.size();
-      eh_frame_hdr_space_.data = debug_frame->eh_frame.hdr_data.data();
-      eh_frame_space_.start = ip - ip_vaddr + debug_frame->eh_frame.vaddr;
-      eh_frame_space_.end = eh_frame_space_.start + debug_frame->eh_frame.data.size();
-      eh_frame_space_.data = debug_frame->eh_frame.data.data();
-
-      unw_dyn_info di;
-      memset(&di, '\0', sizeof(di));
-      di.start_ip = map.start;
-      di.end_ip = map.end;
-      di.format = UNW_INFO_FORMAT_REMOTE_TABLE;
-      di.u.rti.name_ptr = 0;
-      di.u.rti.segbase = eh_frame_hdr_space_.start;
-      di.u.rti.table_data =
-          eh_frame_hdr_space_.start + debug_frame->eh_frame.fde_table_offset;
-      di.u.rti.table_len = (eh_frame_hdr_space_.end - di.u.rti.table_data) / sizeof(unw_word_t);
-      // TODO: Do it ourselves is more efficient than calling this function.
-      int ret = dwarf_search_unwind_table(addr_space, ip, &di, proc_info, need_unwind_info, this);
-      if (ret == 0) {
-        return true;
-      }
-    }
-  }
-  if (debug_frame->has_debug_frame || debug_frame->has_gnu_debugdata) {
-    unw_dyn_info_t di;
-    unw_word_t segbase = map.start - map.offset;
-    // TODO: http://b/32916571
-    // TODO: Do it ourselves is more efficient than calling libunwind functions.
-    int found = dwarf_find_debug_frame(0, &di, ip, segbase, filename.c_str(), map.start, map.end);
-    if (found == 1) {
-      int ret = dwarf_search_unwind_table(addr_space, ip, &di, proc_info, need_unwind_info, this);
-      if (ret == 0) {
-        return true;
-      }
-    }
-  }
-
-  if (debug_frame->has_arm_exidx) {
-    auto& func_vaddrs = debug_frame->arm_exidx.func_vaddr_array;
-    if (ip_vaddr >= func_vaddrs[0] && ip_vaddr < debug_frame->text_end_vaddr) {
-      // Use binary search to find the correct function.
-      auto it = std::upper_bound(func_vaddrs.begin(), func_vaddrs.end(),
-                                 static_cast<uint32_t>(ip_vaddr));
-      if (it != func_vaddrs.begin()) {
-        --it;
-        // Found the exidx entry.
-        size_t index = it - func_vaddrs.begin();
-        proc_info->start_ip = *it;
-        proc_info->format = UNW_INFO_FORMAT_ARM_EXIDX;
-        proc_info->unwind_info = reinterpret_cast<void*>(
-            static_cast<uintptr_t>(index * sizeof(ArmIdxEntry) +
-                                   debug_frame->arm_exidx.exidx_vaddr +
-                                   debug_frame->min_vaddr));
-        eh_frame_hdr_space_.Clear();
-        eh_frame_space_.Clear();
-        // Prepare arm_exidx space and arm_extab space.
-        arm_exidx_space_.start = debug_frame->min_vaddr + debug_frame->arm_exidx.exidx_vaddr;
-        arm_exidx_space_.end = arm_exidx_space_.start +
-            debug_frame->arm_exidx.exidx_data.size() * sizeof(ArmIdxEntry);
-        arm_exidx_space_.data = reinterpret_cast<const uint8_t*>(
-            debug_frame->arm_exidx.exidx_data.data());
-
-        arm_extab_space_.start = debug_frame->min_vaddr + debug_frame->arm_exidx.extab_vaddr;
-        arm_extab_space_.end = arm_extab_space_.start +
-            debug_frame->arm_exidx.extab_data.size();
-        arm_extab_space_.data = debug_frame->arm_exidx.extab_data.data();
-        return true;
-      }
-    }
-  }
-  return false;
-}
-
-bool BacktraceOffline::ReadReg(size_t reg, uint64_t* value) {
-  bool result = true;
-#if defined(__arm__)
-  switch (reg) {
-    case UNW_ARM_R0:
-      *value = context_->uc_mcontext.arm_r0;
-      break;
-    case UNW_ARM_R1:
-      *value = context_->uc_mcontext.arm_r1;
-      break;
-    case UNW_ARM_R2:
-      *value = context_->uc_mcontext.arm_r2;
-      break;
-    case UNW_ARM_R3:
-      *value = context_->uc_mcontext.arm_r3;
-      break;
-    case UNW_ARM_R4:
-      *value = context_->uc_mcontext.arm_r4;
-      break;
-    case UNW_ARM_R5:
-      *value = context_->uc_mcontext.arm_r5;
-      break;
-    case UNW_ARM_R6:
-      *value = context_->uc_mcontext.arm_r6;
-      break;
-    case UNW_ARM_R7:
-      *value = context_->uc_mcontext.arm_r7;
-      break;
-    case UNW_ARM_R8:
-      *value = context_->uc_mcontext.arm_r8;
-      break;
-    case UNW_ARM_R9:
-      *value = context_->uc_mcontext.arm_r9;
-      break;
-    case UNW_ARM_R10:
-      *value = context_->uc_mcontext.arm_r10;
-      break;
-    case UNW_ARM_R11:
-      *value = context_->uc_mcontext.arm_fp;
-      break;
-    case UNW_ARM_R12:
-      *value = context_->uc_mcontext.arm_ip;
-      break;
-    case UNW_ARM_R13:
-      *value = context_->uc_mcontext.arm_sp;
-      break;
-    case UNW_ARM_R14:
-      *value = context_->uc_mcontext.arm_lr;
-      break;
-    case UNW_ARM_R15:
-      *value = context_->uc_mcontext.arm_pc;
-      break;
-    default:
-      result = false;
-  }
-#elif defined(__aarch64__)
-  if (reg <= UNW_AARCH64_PC) {
-    *value = context_->uc_mcontext.regs[reg];
-  } else {
-    result = false;
-  }
-#elif defined(__x86_64__)
-  switch (reg) {
-    case UNW_X86_64_R8:
-      *value = context_->uc_mcontext.gregs[REG_R8];
-      break;
-    case UNW_X86_64_R9:
-      *value = context_->uc_mcontext.gregs[REG_R9];
-      break;
-    case UNW_X86_64_R10:
-      *value = context_->uc_mcontext.gregs[REG_R10];
-      break;
-    case UNW_X86_64_R11:
-      *value = context_->uc_mcontext.gregs[REG_R11];
-      break;
-    case UNW_X86_64_R12:
-      *value = context_->uc_mcontext.gregs[REG_R12];
-      break;
-    case UNW_X86_64_R13:
-      *value = context_->uc_mcontext.gregs[REG_R13];
-      break;
-    case UNW_X86_64_R14:
-      *value = context_->uc_mcontext.gregs[REG_R14];
-      break;
-    case UNW_X86_64_R15:
-      *value = context_->uc_mcontext.gregs[REG_R15];
-      break;
-    case UNW_X86_64_RDI:
-      *value = context_->uc_mcontext.gregs[REG_RDI];
-      break;
-    case UNW_X86_64_RSI:
-      *value = context_->uc_mcontext.gregs[REG_RSI];
-      break;
-    case UNW_X86_64_RBP:
-      *value = context_->uc_mcontext.gregs[REG_RBP];
-      break;
-    case UNW_X86_64_RBX:
-      *value = context_->uc_mcontext.gregs[REG_RBX];
-      break;
-    case UNW_X86_64_RDX:
-      *value = context_->uc_mcontext.gregs[REG_RDX];
-      break;
-    case UNW_X86_64_RAX:
-      *value = context_->uc_mcontext.gregs[REG_RAX];
-      break;
-    case UNW_X86_64_RCX:
-      *value = context_->uc_mcontext.gregs[REG_RCX];
-      break;
-    case UNW_X86_64_RSP:
-      *value = context_->uc_mcontext.gregs[REG_RSP];
-      break;
-    case UNW_X86_64_RIP:
-      *value = context_->uc_mcontext.gregs[REG_RIP];
-      break;
-    default:
-      result = false;
-  }
-#elif defined(__i386__)
-  switch (reg) {
-    case UNW_X86_GS:
-      *value = context_->uc_mcontext.gregs[REG_GS];
-      break;
-    case UNW_X86_FS:
-      *value = context_->uc_mcontext.gregs[REG_FS];
-      break;
-    case UNW_X86_ES:
-      *value = context_->uc_mcontext.gregs[REG_ES];
-      break;
-    case UNW_X86_DS:
-      *value = context_->uc_mcontext.gregs[REG_DS];
-      break;
-    case UNW_X86_EAX:
-      *value = context_->uc_mcontext.gregs[REG_EAX];
-      break;
-    case UNW_X86_EBX:
-      *value = context_->uc_mcontext.gregs[REG_EBX];
-      break;
-    case UNW_X86_ECX:
-      *value = context_->uc_mcontext.gregs[REG_ECX];
-      break;
-    case UNW_X86_EDX:
-      *value = context_->uc_mcontext.gregs[REG_EDX];
-      break;
-    case UNW_X86_ESI:
-      *value = context_->uc_mcontext.gregs[REG_ESI];
-      break;
-    case UNW_X86_EDI:
-      *value = context_->uc_mcontext.gregs[REG_EDI];
-      break;
-    case UNW_X86_EBP:
-      *value = context_->uc_mcontext.gregs[REG_EBP];
-      break;
-    case UNW_X86_EIP:
-      *value = context_->uc_mcontext.gregs[REG_EIP];
-      break;
-    case UNW_X86_ESP:
-      *value = context_->uc_mcontext.gregs[REG_ESP];
-      break;
-    case UNW_X86_TRAPNO:
-      *value = context_->uc_mcontext.gregs[REG_TRAPNO];
-      break;
-    case UNW_X86_CS:
-      *value = context_->uc_mcontext.gregs[REG_CS];
-      break;
-    case UNW_X86_EFLAGS:
-      *value = context_->uc_mcontext.gregs[REG_EFL];
-      break;
-    case UNW_X86_SS:
-      *value = context_->uc_mcontext.gregs[REG_SS];
-      break;
-    default:
-      result = false;
-  }
-#else
-  UNUSED(reg);
-  UNUSED(value);
-  result = false;
-#endif
-  return result;
-}
-
-std::string BacktraceOffline::GetFunctionNameRaw(uintptr_t, uintptr_t* offset) {
-  // We don't have enough information to support this. And it is expensive.
-  *offset = 0;
-  return "";
-}
-
-static std::mutex g_lock;
-static std::unordered_map<std::string, std::unique_ptr<DebugFrameInfo>>* g_debug_frames = nullptr;
-
-static DebugFrameInfo* ReadDebugFrameFromFile(const std::string& filename);
-
-DebugFrameInfo* BacktraceOffline::GetDebugFrameInFile(const std::string& filename) {
-  if (cache_file_) {
-    std::lock_guard<std::mutex> lock(g_lock);
-    if (g_debug_frames != nullptr) {
-      auto it = g_debug_frames->find(filename);
-      if (it != g_debug_frames->end()) {
-        return it->second.get();
-      }
-    }
-  }
-  DebugFrameInfo* debug_frame = ReadDebugFrameFromFile(filename);
-  if (cache_file_) {
-    std::lock_guard<std::mutex> lock(g_lock);
-    if (g_debug_frames == nullptr) {
-      g_debug_frames = new std::unordered_map<std::string, std::unique_ptr<DebugFrameInfo>>;
-    }
-    auto pair = g_debug_frames->emplace(filename, std::unique_ptr<DebugFrameInfo>(debug_frame));
-    if (!pair.second) {
-      debug_frame = pair.first->second.get();
-    }
-  }
-  return debug_frame;
-}
-
-static bool OmitEncodedValue(uint8_t encode, const uint8_t*& p) {
-  if (encode == DW_EH_PE_omit) {
-    return 0;
-  }
-  uint8_t format = encode & 0x0f;
-  switch (format) {
-    case DW_EH_PE_ptr:
-      p += sizeof(unw_word_t);
-      break;
-    case DW_EH_PE_uleb128:
-    case DW_EH_PE_sleb128:
-      while ((*p & 0x80) != 0) {
-        ++p;
-      }
-      ++p;
-      break;
-    case DW_EH_PE_udata2:
-    case DW_EH_PE_sdata2:
-      p += 2;
-      break;
-    case DW_EH_PE_udata4:
-    case DW_EH_PE_sdata4:
-      p += 4;
-      break;
-    case DW_EH_PE_udata8:
-    case DW_EH_PE_sdata8:
-      p += 8;
-      break;
-    default:
-      return false;
-  }
-  return true;
-}
-
-static bool GetFdeTableOffsetInEhFrameHdr(const std::vector<uint8_t>& data,
-                                          uint64_t* table_offset_in_eh_frame_hdr) {
-  const uint8_t* p = data.data();
-  const uint8_t* end = p + data.size();
-  if (p + 4 > end) {
-    return false;
-  }
-  uint8_t version = *p++;
-  if (version != 1) {
-    return false;
-  }
-  uint8_t eh_frame_ptr_encode = *p++;
-  uint8_t fde_count_encode = *p++;
-  uint8_t fde_table_encode = *p++;
-
-  if (fde_table_encode != (DW_EH_PE_datarel | DW_EH_PE_sdata4)) {
-    return false;
-  }
-
-  if (!OmitEncodedValue(eh_frame_ptr_encode, p) || !OmitEncodedValue(fde_count_encode, p)) {
-    return false;
-  }
-  if (p >= end) {
-    return false;
-  }
-  *table_offset_in_eh_frame_hdr = p - data.data();
-  return true;
-}
-
-template <class ELFT>
-DebugFrameInfo* ReadDebugFrameFromELFFile(const llvm::object::ELFFile<ELFT>* elf) {
-  DebugFrameInfo* result = new DebugFrameInfo;
-  result->text_end_vaddr = std::numeric_limits<uint64_t>::max();
-
-  bool has_eh_frame_hdr = false;
-  bool has_eh_frame = false;
-
-  for (auto it = elf->section_begin(); it != elf->section_end(); ++it) {
-    llvm::ErrorOr<llvm::StringRef> name = elf->getSectionName(&*it);
-    if (name) {
-      std::string s = name.get();
-      if (s == ".debug_frame") {
-        result->has_debug_frame = true;
-      } else if (s == ".gnu_debugdata") {
-        result->has_gnu_debugdata = true;
-      } else if (s == ".eh_frame_hdr") {
-        result->eh_frame.hdr_vaddr = it->sh_addr;
-        llvm::ErrorOr<llvm::ArrayRef<uint8_t>> data = elf->getSectionContents(&*it);
-        if (data) {
-          result->eh_frame.hdr_data.insert(result->eh_frame.hdr_data.end(),
-              data->data(), data->data() + data->size());
-
-          uint64_t fde_table_offset;
-          if (GetFdeTableOffsetInEhFrameHdr(result->eh_frame.hdr_data,
-                                             &fde_table_offset)) {
-            result->eh_frame.fde_table_offset = fde_table_offset;
-            // Make sure we have at least one entry in fde_table.
-            if (fde_table_offset + 2 * sizeof(int32_t) <= data->size()) {
-              intptr_t eh_frame_hdr_vaddr = it->sh_addr;
-              int32_t sdata;
-              uint8_t* p = result->eh_frame.hdr_data.data() + fde_table_offset;
-              memcpy(&sdata, p, sizeof(sdata));
-              result->eh_frame.min_func_vaddr = eh_frame_hdr_vaddr + sdata;
-              has_eh_frame_hdr = true;
-            }
-          }
-        }
-      } else if (s == ".eh_frame") {
-        result->eh_frame.vaddr = it->sh_addr;
-        llvm::ErrorOr<llvm::ArrayRef<uint8_t>> data = elf->getSectionContents(&*it);
-        if (data) {
-          result->eh_frame.data.insert(result->eh_frame.data.end(),
-                                                data->data(), data->data() + data->size());
-          has_eh_frame = true;
-        }
-      } else if (s == ".ARM.exidx") {
-        result->arm_exidx.exidx_vaddr = it->sh_addr;
-        llvm::ErrorOr<llvm::ArrayRef<uint8_t>> data = elf->getSectionContents(&*it);
-        if (data) {
-          size_t entry_count = data->size() / sizeof(ArmIdxEntry);
-          result->arm_exidx.exidx_data.resize(entry_count);
-          memcpy(result->arm_exidx.exidx_data.data(), data->data(),
-                 entry_count * sizeof(ArmIdxEntry));
-          if (entry_count > 0u) {
-            // Change IdxEntry.func_offset into vaddr.
-            result->arm_exidx.func_vaddr_array.reserve(entry_count);
-            uint32_t vaddr = it->sh_addr;
-            for (auto& entry : result->arm_exidx.exidx_data) {
-              uint32_t func_offset = entry.func_offset + vaddr;
-              // Clear bit 31 for the prel31 offset.
-              // Arm sets bit 0 to mark it as thumb code, remove the flag.
-              result->arm_exidx.func_vaddr_array.push_back(
-                  func_offset & 0x7ffffffe);
-              vaddr += 8;
-            }
-            result->has_arm_exidx = true;
-          }
-        }
-      } else if (s == ".ARM.extab") {
-        result->arm_exidx.extab_vaddr = it->sh_addr;
-        llvm::ErrorOr<llvm::ArrayRef<uint8_t>> data = elf->getSectionContents(&*it);
-        if (data) {
-          result->arm_exidx.extab_data.insert(result->arm_exidx.extab_data.end(),
-                                              data->data(), data->data() + data->size());
-        }
-      } else if (s == ".text") {
-        result->text_end_vaddr = it->sh_addr + it->sh_size;
-      }
-    }
-  }
-
-  if (has_eh_frame_hdr && has_eh_frame) {
-    result->has_eh_frame = true;
-  }
-
-  result->min_vaddr = std::numeric_limits<uint64_t>::max();
-  for (auto it = elf->program_header_begin(); it != elf->program_header_end(); ++it) {
-    if ((it->p_type == llvm::ELF::PT_LOAD) && (it->p_flags & llvm::ELF::PF_X)) {
-      if (it->p_vaddr < result->min_vaddr) {
-        result->min_vaddr = it->p_vaddr;
-      }
-    }
-  }
-  if (!result->has_eh_frame && !result->has_arm_exidx && !result->has_debug_frame &&
-      !result->has_gnu_debugdata) {
-    delete result;
-    return nullptr;
-  }
-  return result;
-}
-
-static bool IsValidElfPath(const std::string& filename) {
-  static const char elf_magic[] = {0x7f, 'E', 'L', 'F'};
-
-  struct stat st;
-  if (stat(filename.c_str(), &st) != 0 || !S_ISREG(st.st_mode)) {
-    return false;
-  }
-  FILE* fp = fopen(filename.c_str(), "reb");
-  if (fp == nullptr) {
-    return false;
-  }
-  char buf[4];
-  if (fread(buf, 4, 1, fp) != 1) {
-    fclose(fp);
-    return false;
-  }
-  fclose(fp);
-  return memcmp(buf, elf_magic, 4) == 0;
-}
-
-static bool IsValidApkPath(const std::string& apk_path) {
-  static const char zip_preamble[] = {0x50, 0x4b, 0x03, 0x04};
-  struct stat st;
-  if (stat(apk_path.c_str(), &st) != 0 || !S_ISREG(st.st_mode)) {
-    return false;
-  }
-  FILE* fp = fopen(apk_path.c_str(), "reb");
-  if (fp == nullptr) {
-    return false;
-  }
-  char buf[4];
-  if (fread(buf, 4, 1, fp) != 1) {
-    fclose(fp);
-    return false;
-  }
-  fclose(fp);
-  return memcmp(buf, zip_preamble, 4) == 0;
-}
-
-class ScopedZiparchiveHandle {
- public:
-  explicit ScopedZiparchiveHandle(ZipArchiveHandle handle) : handle_(handle) {
-  }
-
-  ~ScopedZiparchiveHandle() {
-    CloseArchive(handle_);
-  }
-
- private:
-  ZipArchiveHandle handle_;
-};
-
-llvm::object::OwningBinary<llvm::object::Binary> OpenEmbeddedElfFile(const std::string& filename) {
-  llvm::object::OwningBinary<llvm::object::Binary> nothing;
-  size_t pos = filename.find("!/");
-  if (pos == std::string::npos) {
-    return nothing;
-  }
-  std::string apk_file = filename.substr(0, pos);
-  std::string elf_file = filename.substr(pos + 2);
-  if (!IsValidApkPath(apk_file)) {
-    BACK_LOGW("%s is not a valid apk file", apk_file.c_str());
-    return nothing;
-  }
-  ZipArchiveHandle handle;
-  int32_t ret_code = OpenArchive(apk_file.c_str(), &handle);
-  if (ret_code != 0) {
-    CloseArchive(handle);
-    BACK_LOGW("failed to open archive %s: %s", apk_file.c_str(), ErrorCodeString(ret_code));
-    return nothing;
-  }
-  ScopedZiparchiveHandle scoped_handle(handle);
-  ZipEntry zentry;
-  ret_code = FindEntry(handle, ZipString(elf_file.c_str()), &zentry);
-  if (ret_code != 0) {
-    BACK_LOGW("failed to find %s in %s: %s", elf_file.c_str(), apk_file.c_str(),
-              ErrorCodeString(ret_code));
-    return nothing;
-  }
-  if (zentry.method != kCompressStored || zentry.compressed_length != zentry.uncompressed_length) {
-    BACK_LOGW("%s is compressed in %s, which doesn't support running directly", elf_file.c_str(),
-              apk_file.c_str());
-    return nothing;
-  }
-  auto buffer_or_err = llvm::MemoryBuffer::getOpenFileSlice(GetFileDescriptor(handle), apk_file,
-                                                            zentry.uncompressed_length,
-                                                            zentry.offset);
-  if (!buffer_or_err) {
-    BACK_LOGW("failed to read %s in %s: %s", elf_file.c_str(), apk_file.c_str(),
-              buffer_or_err.getError().message().c_str());
-    return nothing;
-  }
-  auto binary_or_err = llvm::object::createBinary(buffer_or_err.get()->getMemBufferRef());
-  if (!binary_or_err) {
-    BACK_LOGW("failed to create binary for %s in %s: %s", elf_file.c_str(), apk_file.c_str(),
-              llvm::toString(binary_or_err.takeError()).c_str());
-    return nothing;
-  }
-  return llvm::object::OwningBinary<llvm::object::Binary>(std::move(binary_or_err.get()),
-                                                          std::move(buffer_or_err.get()));
-}
-
-static DebugFrameInfo* ReadDebugFrameFromFile(const std::string& filename) {
-  llvm::object::OwningBinary<llvm::object::Binary> owning_binary;
-  if (filename.find("!/") != std::string::npos) {
-    owning_binary = OpenEmbeddedElfFile(filename);
-  } else {
-    if (!IsValidElfPath(filename)) {
-      return nullptr;
-    }
-    auto binary_or_err = llvm::object::createBinary(llvm::StringRef(filename));
-    if (!binary_or_err) {
-      return nullptr;
-    }
-    owning_binary = std::move(binary_or_err.get());
-  }
-  llvm::object::Binary* binary = owning_binary.getBinary();
-  auto obj = llvm::dyn_cast<llvm::object::ObjectFile>(binary);
-  if (obj == nullptr) {
-    return nullptr;
-  }
-  if (auto elf = llvm::dyn_cast<llvm::object::ELF32LEObjectFile>(obj)) {
-    return ReadDebugFrameFromELFFile(elf->getELFFile());
-  }
-  if (auto elf = llvm::dyn_cast<llvm::object::ELF64LEObjectFile>(obj)) {
-    return ReadDebugFrameFromELFFile(elf->getELFFile());
-  }
-  return nullptr;
-}
-
-Backtrace* Backtrace::CreateOffline(pid_t pid, pid_t tid, BacktraceMap* map,
-                                    const backtrace_stackinfo_t& stack, bool cache_file) {
-  return new BacktraceOffline(pid, tid, map, stack, cache_file);
-}
diff --git a/libbacktrace/BacktraceOffline.h b/libbacktrace/BacktraceOffline.h
deleted file mode 100644
index c0b686e..0000000
--- a/libbacktrace/BacktraceOffline.h
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * Copyright (C) 2015 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 _LIBBACKTRACE_UNWIND_OFFLINE_H
-#define _LIBBACKTRACE_UNWIND_OFFLINE_H
-
-#include <libunwind.h>
-#include <stdint.h>
-#include <sys/types.h>
-#include <ucontext.h>
-
-#include <unordered_map>
-#include <unordered_set>
-
-#include <backtrace/Backtrace.h>
-
-struct Space {
-  uint64_t start;
-  uint64_t end;
-  const uint8_t* data;
-
-  Space() {
-    Clear();
-  }
-
-  void Clear();
-  size_t Read(uint64_t addr, uint8_t* buffer, size_t size);
-};
-
-struct DebugFrameInfo;
-
-class BacktraceOffline : public Backtrace {
- public:
-  BacktraceOffline(pid_t pid, pid_t tid, BacktraceMap* map, const backtrace_stackinfo_t& stack,
-                   bool cache_file)
-      : Backtrace(pid, tid, map),
-        cache_file_(cache_file),
-        context_(nullptr) {
-    stack_space_.start = stack.start;
-    stack_space_.end = stack.end;
-    stack_space_.data = stack.data;
-  }
-
-  virtual ~BacktraceOffline() = default;
-
-  bool Unwind(size_t num_ignore_frames, ucontext_t* context) override;
-
-  bool ReadWord(uintptr_t ptr, word_t* out_value) override;
-
-  size_t Read(uintptr_t addr, uint8_t* buffer, size_t bytes) override;
-
-  bool FindProcInfo(unw_addr_space_t addr_space, uint64_t ip, unw_proc_info_t* proc_info,
-                    int need_unwind_info);
-
-  bool ReadReg(size_t reg_index, uint64_t* value);
-
- protected:
-  std::string GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset) override;
-  DebugFrameInfo* GetDebugFrameInFile(const std::string& filename);
-
-  bool cache_file_;
-  ucontext_t* context_;
-  Space eh_frame_hdr_space_;
-  Space eh_frame_space_;
-  Space arm_extab_space_;
-  Space arm_exidx_space_;
-  Space stack_space_;
-};
-
-#endif  // _LIBBACKTRACE_BACKTRACE_OFFLINE_H
diff --git a/libbacktrace/BacktracePtrace.cpp b/libbacktrace/BacktracePtrace.cpp
index fd8b713..9da457d 100644
--- a/libbacktrace/BacktracePtrace.cpp
+++ b/libbacktrace/BacktracePtrace.cpp
@@ -28,10 +28,9 @@
 
 #include "BacktraceLog.h"
 #include "BacktracePtrace.h"
-#include "thread_utils.h"
 
 #if !defined(__APPLE__)
-static bool PtraceRead(pid_t tid, uintptr_t addr, word_t* out_value) {
+static bool PtraceRead(pid_t tid, uint64_t addr, word_t* out_value) {
   // ptrace() returns -1 and sets errno when the operation fails.
   // To disambiguate -1 from a valid result, we clear errno beforehand.
   errno = 0;
@@ -43,7 +42,7 @@
 }
 #endif
 
-bool BacktracePtrace::ReadWord(uintptr_t ptr, word_t* out_value) {
+bool BacktracePtrace::ReadWord(uint64_t ptr, word_t* out_value) {
 #if defined(__APPLE__)
   BACK_LOGW("MacOS does not support reading from another pid.");
   return false;
@@ -62,7 +61,7 @@
 #endif
 }
 
-size_t BacktracePtrace::Read(uintptr_t addr, uint8_t* buffer, size_t bytes) {
+size_t BacktracePtrace::Read(uint64_t addr, uint8_t* buffer, size_t bytes) {
 #if defined(__APPLE__)
   BACK_LOGW("MacOS does not support reading from another pid.");
   return 0;
diff --git a/libbacktrace/BacktracePtrace.h b/libbacktrace/BacktracePtrace.h
index 760817b..1ae3adf 100644
--- a/libbacktrace/BacktracePtrace.h
+++ b/libbacktrace/BacktracePtrace.h
@@ -29,9 +29,9 @@
   BacktracePtrace(pid_t pid, pid_t tid, BacktraceMap* map) : Backtrace(pid, tid, map) {}
   virtual ~BacktracePtrace() {}
 
-  size_t Read(uintptr_t addr, uint8_t* buffer, size_t bytes);
+  size_t Read(uint64_t addr, uint8_t* buffer, size_t bytes) override;
 
-  bool ReadWord(uintptr_t ptr, word_t* out_value);
+  bool ReadWord(uint64_t ptr, word_t* out_value) override;
 };
 
 #endif // _LIBBACKTRACE_BACKTRACE_PTRACE_H
diff --git a/libbacktrace/OWNERS b/libbacktrace/OWNERS
new file mode 100644
index 0000000..bfeedca
--- /dev/null
+++ b/libbacktrace/OWNERS
@@ -0,0 +1,2 @@
+cferris@google.com
+jmgao@google.com
diff --git a/libbacktrace/UnwindCurrent.cpp b/libbacktrace/UnwindCurrent.cpp
deleted file mode 100644
index 3c509e6..0000000
--- a/libbacktrace/UnwindCurrent.cpp
+++ /dev/null
@@ -1,160 +0,0 @@
-/*
- * Copyright (C) 2013 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 <stdint.h>
-#include <ucontext.h>
-
-#include <memory>
-#include <string>
-
-#define UNW_LOCAL_ONLY
-#include <libunwind.h>
-
-#include <android-base/logging.h>
-#include <backtrace/Backtrace.h>
-
-#include "BacktraceLog.h"
-#include "UnwindCurrent.h"
-
-std::string UnwindCurrent::GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset) {
-  if (!initialized_) {
-    // If init local is not called, then trying to get a function name will
-    // fail, so try to initialize first.
-    std::unique_ptr<unw_cursor_t> cursor(new unw_cursor_t);
-    if (unw_init_local(cursor.get(), &context_) < 0) {
-      return "";
-    }
-    initialized_ = true;
-  }
-
-  *offset = 0;
-  char buf[512];
-  unw_word_t value;
-  if (unw_get_proc_name_by_ip(unw_local_addr_space, pc, buf, sizeof(buf),
-                              &value, &context_) >= 0 && buf[0] != '\0') {
-    *offset = static_cast<uintptr_t>(value);
-    return buf;
-  }
-  return "";
-}
-
-void UnwindCurrent::GetUnwContextFromUcontext(const ucontext_t* ucontext) {
-  unw_tdep_context_t* unw_context = reinterpret_cast<unw_tdep_context_t*>(&context_);
-
-#if defined(__arm__)
-  unw_context->regs[0] = ucontext->uc_mcontext.arm_r0;
-  unw_context->regs[1] = ucontext->uc_mcontext.arm_r1;
-  unw_context->regs[2] = ucontext->uc_mcontext.arm_r2;
-  unw_context->regs[3] = ucontext->uc_mcontext.arm_r3;
-  unw_context->regs[4] = ucontext->uc_mcontext.arm_r4;
-  unw_context->regs[5] = ucontext->uc_mcontext.arm_r5;
-  unw_context->regs[6] = ucontext->uc_mcontext.arm_r6;
-  unw_context->regs[7] = ucontext->uc_mcontext.arm_r7;
-  unw_context->regs[8] = ucontext->uc_mcontext.arm_r8;
-  unw_context->regs[9] = ucontext->uc_mcontext.arm_r9;
-  unw_context->regs[10] = ucontext->uc_mcontext.arm_r10;
-  unw_context->regs[11] = ucontext->uc_mcontext.arm_fp;
-  unw_context->regs[12] = ucontext->uc_mcontext.arm_ip;
-  unw_context->regs[13] = ucontext->uc_mcontext.arm_sp;
-  unw_context->regs[14] = ucontext->uc_mcontext.arm_lr;
-  unw_context->regs[15] = ucontext->uc_mcontext.arm_pc;
-#else
-  unw_context->uc_mcontext = ucontext->uc_mcontext;
-#endif
-}
-
-bool UnwindCurrent::UnwindFromContext(size_t num_ignore_frames, ucontext_t* ucontext) {
-  if (ucontext == nullptr) {
-    int ret = unw_getcontext(&context_);
-    if (ret < 0) {
-      BACK_LOGW("unw_getcontext failed %d", ret);
-      error_ = BACKTRACE_UNWIND_ERROR_SETUP_FAILED;
-      return false;
-    }
-  } else {
-    GetUnwContextFromUcontext(ucontext);
-  }
-
-  // The cursor structure is pretty large, do not put it on the stack.
-  std::unique_ptr<unw_cursor_t> cursor(new unw_cursor_t);
-  int ret = unw_init_local(cursor.get(), &context_);
-  if (ret < 0) {
-    BACK_LOGW("unw_init_local failed %d", ret);
-    error_ = BACKTRACE_UNWIND_ERROR_SETUP_FAILED;
-    return false;
-  }
-  initialized_ = true;
-
-  size_t num_frames = 0;
-  do {
-    unw_word_t pc;
-    ret = unw_get_reg(cursor.get(), UNW_REG_IP, &pc);
-    if (ret < 0) {
-      BACK_LOGW("Failed to read IP %d", ret);
-      break;
-    }
-    unw_word_t sp;
-    ret = unw_get_reg(cursor.get(), UNW_REG_SP, &sp);
-    if (ret < 0) {
-      BACK_LOGW("Failed to read SP %d", ret);
-      break;
-    }
-
-    frames_.resize(num_frames+1);
-    backtrace_frame_data_t* frame = &frames_.at(num_frames);
-    frame->num = num_frames;
-    frame->pc = static_cast<uintptr_t>(pc);
-    frame->sp = static_cast<uintptr_t>(sp);
-    frame->stack_size = 0;
-
-    FillInMap(frame->pc, &frame->map);
-    // Check to see if we should skip this frame because it's coming
-    // from within the library, and we are doing a local unwind.
-    if (ucontext != nullptr || num_frames != 0 || !DiscardFrame(*frame)) {
-      if (num_ignore_frames == 0) {
-        // GetFunctionName is an expensive call, only do it if we are
-        // keeping the frame.
-        frame->func_name = GetFunctionName(frame->pc, &frame->func_offset, &frame->map);
-        if (num_frames > 0) {
-          // Set the stack size for the previous frame.
-          backtrace_frame_data_t* prev = &frames_.at(num_frames-1);
-          prev->stack_size = frame->sp - prev->sp;
-        }
-        num_frames++;
-      } else {
-        num_ignore_frames--;
-        // Set the number of frames to zero to remove the frame added
-        // above. By definition, if we still have frames to ignore
-        // there should only be one frame in the vector.
-        CHECK(num_frames == 0);
-        frames_.resize(0);
-      }
-    }
-    // If the pc is in a device map, then don't try to step.
-    if (frame->map.flags & PROT_DEVICE_MAP) {
-      break;
-    }
-    // Verify the sp is not in a device map too.
-    backtrace_map_t map;
-    FillInMap(frame->sp, &map);
-    if (map.flags & PROT_DEVICE_MAP) {
-      break;
-    }
-    ret = unw_step (cursor.get());
-  } while (ret > 0 && num_frames < MAX_BACKTRACE_FRAMES);
-
-  return true;
-}
diff --git a/libbacktrace/UnwindCurrent.h b/libbacktrace/UnwindCurrent.h
deleted file mode 100644
index 3656104..0000000
--- a/libbacktrace/UnwindCurrent.h
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright (C) 2013 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 _LIBBACKTRACE_UNWIND_CURRENT_H
-#define _LIBBACKTRACE_UNWIND_CURRENT_H
-
-#include <stdint.h>
-#include <sys/types.h>
-#include <ucontext.h>
-
-#include <string>
-
-#include <backtrace/Backtrace.h>
-#include <backtrace/BacktraceMap.h>
-
-#include "BacktraceCurrent.h"
-
-#define UNW_LOCAL_ONLY
-#include <libunwind.h>
-
-class UnwindCurrent : public BacktraceCurrent {
- public:
-  UnwindCurrent(pid_t pid, pid_t tid, BacktraceMap* map) : BacktraceCurrent(pid, tid, map) {}
-  virtual ~UnwindCurrent() {}
-
-  std::string GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset) override;
-
- private:
-  void GetUnwContextFromUcontext(const ucontext_t* ucontext);
-
-  bool UnwindFromContext(size_t num_ignore_frames, ucontext_t* ucontext) override;
-
-  unw_context_t context_;
-
-  bool initialized_ = false;
-};
-
-#endif // _LIBBACKTRACE_UNWIND_CURRENT_H
diff --git a/libbacktrace/UnwindMap.cpp b/libbacktrace/UnwindMap.cpp
index af79562..798c769 100644
--- a/libbacktrace/UnwindMap.cpp
+++ b/libbacktrace/UnwindMap.cpp
@@ -57,7 +57,7 @@
     map.start = unw_map.start;
     map.end = unw_map.end;
     map.offset = unw_map.offset;
-    map.load_base = unw_map.load_base;
+    map.load_bias = unw_map.load_base;
     map.flags = unw_map.flags;
     map.name = unw_map.path;
 
@@ -106,7 +106,7 @@
       map.start = unw_map.start;
       map.end = unw_map.end;
       map.offset = unw_map.offset;
-      map.load_base = unw_map.load_base;
+      map.load_bias = unw_map.load_base;
       map.flags = unw_map.flags;
       map.name = unw_map.path;
 
@@ -134,7 +134,7 @@
   return (map_created_ = (unw_map_local_create() == 0)) && GenerateMap();;
 }
 
-void UnwindMapLocal::FillIn(uintptr_t addr, backtrace_map_t* map) {
+void UnwindMapLocal::FillIn(uint64_t addr, backtrace_map_t* map) {
   BacktraceMap::FillIn(addr, map);
   if (!IsValid(*map)) {
     // Check to see if the underlying map changed and regenerate the map
@@ -146,24 +146,3 @@
     }
   }
 }
-
-//-------------------------------------------------------------------------
-// BacktraceMap create function.
-//-------------------------------------------------------------------------
-BacktraceMap* BacktraceMap::Create(pid_t pid, bool uncached) {
-  BacktraceMap* map;
-
-  if (uncached) {
-    // Force use of the base class to parse the maps when this call is made.
-    map = new BacktraceMap(pid);
-  } else if (pid == getpid()) {
-    map = new UnwindMapLocal();
-  } else {
-    map = new UnwindMapRemote(pid);
-  }
-  if (!map->Build()) {
-    delete map;
-    return nullptr;
-  }
-  return map;
-}
diff --git a/libbacktrace/UnwindMap.h b/libbacktrace/UnwindMap.h
index 6ffdafd..15544e8 100644
--- a/libbacktrace/UnwindMap.h
+++ b/libbacktrace/UnwindMap.h
@@ -55,7 +55,7 @@
 
   bool Build() override;
 
-  void FillIn(uintptr_t addr, backtrace_map_t* map) override;
+  void FillIn(uint64_t addr, backtrace_map_t* map) override;
 
   void LockIterator() override { pthread_rwlock_rdlock(&map_lock_); }
   void UnlockIterator() override { pthread_rwlock_unlock(&map_lock_); }
diff --git a/libbacktrace/UnwindPtrace.cpp b/libbacktrace/UnwindPtrace.cpp
deleted file mode 100644
index 42ac1bc..0000000
--- a/libbacktrace/UnwindPtrace.cpp
+++ /dev/null
@@ -1,181 +0,0 @@
-/*
- * Copyright (C) 2013 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 <stdint.h>
-#include <sys/types.h>
-#include <ucontext.h>
-
-#include <libunwind.h>
-#include <libunwind-ptrace.h>
-
-#include <backtrace/Backtrace.h>
-#include <backtrace/BacktraceMap.h>
-
-#include "BacktraceLog.h"
-#include "UnwindMap.h"
-#include "UnwindPtrace.h"
-
-UnwindPtrace::UnwindPtrace(pid_t pid, pid_t tid, BacktraceMap* map)
-    : BacktracePtrace(pid, tid, map), addr_space_(nullptr), upt_info_(nullptr) {
-}
-
-UnwindPtrace::~UnwindPtrace() {
-  if (upt_info_) {
-    _UPT_destroy(upt_info_);
-    upt_info_ = nullptr;
-  }
-
-  if (addr_space_) {
-    // Remove the map from the address space before destroying it.
-    // It will be freed in the UnwindMap destructor.
-    unw_map_set(addr_space_, nullptr);
-
-    unw_destroy_addr_space(addr_space_);
-    addr_space_ = nullptr;
-  }
-}
-
-bool UnwindPtrace::Init() {
-  if (upt_info_) {
-    return true;
-  }
-
-  if (addr_space_) {
-    // If somehow the addr_space_ gets initialized but upt_info_ doesn't,
-    // then that indicates there is some kind of failure.
-    return false;
-  }
-
-  addr_space_ = unw_create_addr_space(&_UPT_accessors, 0);
-  if (!addr_space_) {
-    BACK_LOGW("unw_create_addr_space failed.");
-    error_ = BACKTRACE_UNWIND_ERROR_SETUP_FAILED;
-    return false;
-  }
-
-  UnwindMap* map = static_cast<UnwindMap*>(GetMap());
-  unw_map_set(addr_space_, map->GetMapCursor());
-
-  upt_info_ = reinterpret_cast<struct UPT_info*>(_UPT_create(Tid()));
-  if (!upt_info_) {
-    BACK_LOGW("Failed to create upt info.");
-    error_ = BACKTRACE_UNWIND_ERROR_SETUP_FAILED;
-    return false;
-  }
-
-  return true;
-}
-
-bool UnwindPtrace::Unwind(size_t num_ignore_frames, ucontext_t* ucontext) {
-  if (GetMap() == nullptr) {
-    // Without a map object, we can't do anything.
-    error_ = BACKTRACE_UNWIND_ERROR_MAP_MISSING;
-    return false;
-  }
-
-  error_ = BACKTRACE_UNWIND_NO_ERROR;
-
-  if (ucontext) {
-    BACK_LOGW("Unwinding from a specified context not supported yet.");
-    error_ = BACKTRACE_UNWIND_ERROR_UNSUPPORTED_OPERATION;
-    return false;
-  }
-
-  if (!Init()) {
-    return false;
-  }
-
-  unw_cursor_t cursor;
-  int ret = unw_init_remote(&cursor, addr_space_, upt_info_);
-  if (ret < 0) {
-    BACK_LOGW("unw_init_remote failed %d", ret);
-    error_ = BACKTRACE_UNWIND_ERROR_SETUP_FAILED;
-    return false;
-  }
-
-  size_t num_frames = 0;
-  do {
-    unw_word_t pc;
-    ret = unw_get_reg(&cursor, UNW_REG_IP, &pc);
-    if (ret < 0) {
-      BACK_LOGW("Failed to read IP %d", ret);
-      break;
-    }
-    unw_word_t sp;
-    ret = unw_get_reg(&cursor, UNW_REG_SP, &sp);
-    if (ret < 0) {
-      BACK_LOGW("Failed to read SP %d", ret);
-      break;
-    }
-
-    if (num_ignore_frames == 0) {
-      frames_.resize(num_frames+1);
-      backtrace_frame_data_t* frame = &frames_.at(num_frames);
-      frame->num = num_frames;
-      frame->pc = static_cast<uintptr_t>(pc);
-      frame->sp = static_cast<uintptr_t>(sp);
-      frame->stack_size = 0;
-
-      if (num_frames > 0) {
-        backtrace_frame_data_t* prev = &frames_.at(num_frames-1);
-        prev->stack_size = frame->sp - prev->sp;
-      }
-
-      FillInMap(frame->pc, &frame->map);
-
-      frame->func_name = GetFunctionName(frame->pc, &frame->func_offset, &frame->map);
-
-      num_frames++;
-      // If the pc is in a device map, then don't try to step.
-      if (frame->map.flags & PROT_DEVICE_MAP) {
-        break;
-      }
-    } else {
-      // If the pc is in a device map, then don't try to step.
-      backtrace_map_t map;
-      FillInMap(pc, &map);
-      if (map.flags & PROT_DEVICE_MAP) {
-        break;
-      }
-      num_ignore_frames--;
-    }
-    // Verify the sp is not in a device map.
-    backtrace_map_t map;
-    FillInMap(sp, &map);
-    if (map.flags & PROT_DEVICE_MAP) {
-      break;
-    }
-    ret = unw_step (&cursor);
-  } while (ret > 0 && num_frames < MAX_BACKTRACE_FRAMES);
-
-  return true;
-}
-
-std::string UnwindPtrace::GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset) {
-  if (!Init()) {
-    return "";
-  }
-
-  *offset = 0;
-  char buf[512];
-  unw_word_t value;
-  if (unw_get_proc_name_by_ip(addr_space_, pc, buf, sizeof(buf), &value,
-                              upt_info_) >= 0 && buf[0] != '\0') {
-    *offset = static_cast<uintptr_t>(value);
-    return buf;
-  }
-  return "";
-}
diff --git a/libbacktrace/UnwindPtrace.h b/libbacktrace/UnwindPtrace.h
deleted file mode 100644
index 4688110..0000000
--- a/libbacktrace/UnwindPtrace.h
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright (C) 2013 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 _LIBBACKTRACE_UNWIND_PTRACE_H
-#define _LIBBACKTRACE_UNWIND_PTRACE_H
-
-#include <stdint.h>
-#include <sys/types.h>
-
-#include <string>
-
-#ifdef UNW_LOCAL_ONLY
-#undef UNW_LOCAL_ONLY
-#endif
-#include <libunwind.h>
-
-#include "BacktracePtrace.h"
-
-class UnwindPtrace : public BacktracePtrace {
- public:
-  UnwindPtrace(pid_t pid, pid_t tid, BacktraceMap* map);
-  virtual ~UnwindPtrace();
-
-  bool Unwind(size_t num_ignore_frames, ucontext_t* ucontext) override;
-
-  std::string GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset) override;
-
- private:
-  bool Init();
-
-  unw_addr_space_t addr_space_;
-  struct UPT_info* upt_info_;
-};
-
-#endif // _LIBBACKTRACE_UNWIND_PTRACE_H
diff --git a/libbacktrace/UnwindStack.cpp b/libbacktrace/UnwindStack.cpp
new file mode 100644
index 0000000..4e7f761
--- /dev/null
+++ b/libbacktrace/UnwindStack.cpp
@@ -0,0 +1,256 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#define _GNU_SOURCE 1
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <memory>
+#include <set>
+#include <string>
+
+#include <backtrace/Backtrace.h>
+#include <demangle.h>
+#include <unwindstack/Elf.h>
+#include <unwindstack/MapInfo.h>
+#include <unwindstack/Maps.h>
+#include <unwindstack/Memory.h>
+#include <unwindstack/Regs.h>
+#include <unwindstack/RegsGetLocal.h>
+
+#if !defined(NO_LIBDEXFILE_SUPPORT)
+#include <unwindstack/DexFiles.h>
+#endif
+#include <unwindstack/Unwinder.h>
+
+#include "BacktraceLog.h"
+#include "UnwindStack.h"
+#include "UnwindStackMap.h"
+
+bool Backtrace::Unwind(unwindstack::Regs* regs, BacktraceMap* back_map,
+                       std::vector<backtrace_frame_data_t>* frames, size_t num_ignore_frames,
+                       std::vector<std::string>* skip_names, BacktraceUnwindError* error) {
+  UnwindStackMap* stack_map = reinterpret_cast<UnwindStackMap*>(back_map);
+  auto process_memory = stack_map->process_memory();
+  unwindstack::Unwinder unwinder(MAX_BACKTRACE_FRAMES + num_ignore_frames, stack_map->stack_maps(),
+                                 regs, stack_map->process_memory());
+  unwinder.SetResolveNames(stack_map->ResolveNames());
+  if (stack_map->GetJitDebug() != nullptr) {
+    unwinder.SetJitDebug(stack_map->GetJitDebug(), regs->Arch());
+  }
+#if !defined(NO_LIBDEXFILE_SUPPORT)
+  if (stack_map->GetDexFiles() != nullptr) {
+    unwinder.SetDexFiles(stack_map->GetDexFiles(), regs->Arch());
+  }
+#endif
+  unwinder.Unwind(skip_names, &stack_map->GetSuffixesToIgnore());
+  if (error != nullptr) {
+    switch (unwinder.LastErrorCode()) {
+      case unwindstack::ERROR_NONE:
+        error->error_code = BACKTRACE_UNWIND_NO_ERROR;
+        break;
+
+      case unwindstack::ERROR_MEMORY_INVALID:
+        error->error_code = BACKTRACE_UNWIND_ERROR_ACCESS_MEM_FAILED;
+        error->error_info.addr = unwinder.LastErrorAddress();
+        break;
+
+      case unwindstack::ERROR_UNWIND_INFO:
+        error->error_code = BACKTRACE_UNWIND_ERROR_UNWIND_INFO;
+        break;
+
+      case unwindstack::ERROR_UNSUPPORTED:
+        error->error_code = BACKTRACE_UNWIND_ERROR_UNSUPPORTED_OPERATION;
+        break;
+
+      case unwindstack::ERROR_INVALID_MAP:
+        error->error_code = BACKTRACE_UNWIND_ERROR_MAP_MISSING;
+        break;
+
+      case unwindstack::ERROR_MAX_FRAMES_EXCEEDED:
+        error->error_code = BACKTRACE_UNWIND_ERROR_EXCEED_MAX_FRAMES_LIMIT;
+        break;
+
+      case unwindstack::ERROR_REPEATED_FRAME:
+        error->error_code = BACKTRACE_UNWIND_ERROR_REPEATED_FRAME;
+        break;
+    }
+  }
+
+  if (num_ignore_frames >= unwinder.NumFrames()) {
+    frames->resize(0);
+    return true;
+  }
+
+  auto unwinder_frames = unwinder.frames();
+  frames->resize(unwinder.NumFrames() - num_ignore_frames);
+  size_t cur_frame = 0;
+  for (size_t i = num_ignore_frames; i < unwinder.NumFrames(); i++) {
+    auto frame = &unwinder_frames[i];
+
+    backtrace_frame_data_t* back_frame = &frames->at(cur_frame);
+
+    back_frame->num = cur_frame++;
+
+    back_frame->rel_pc = frame->rel_pc;
+    back_frame->pc = frame->pc;
+    back_frame->sp = frame->sp;
+
+    back_frame->func_name = demangle(frame->function_name.c_str());
+    back_frame->func_offset = frame->function_offset;
+
+    back_frame->map.name = frame->map_name;
+    back_frame->map.start = frame->map_start;
+    back_frame->map.end = frame->map_end;
+    back_frame->map.offset = frame->map_offset;
+    back_frame->map.load_bias = frame->map_load_bias;
+    back_frame->map.flags = frame->map_flags;
+  }
+
+  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) {}
+
+std::string UnwindStackCurrent::GetFunctionNameRaw(uint64_t pc, uint64_t* offset) {
+  return GetMap()->GetFunctionName(pc, offset);
+}
+
+bool UnwindStackCurrent::UnwindFromContext(size_t num_ignore_frames, void* ucontext) {
+  std::unique_ptr<unwindstack::Regs> regs;
+  if (ucontext == nullptr) {
+    regs.reset(unwindstack::Regs::CreateFromLocal());
+    // Fill in the registers from this function. Do it here to avoid
+    // one extra function call appearing in the unwind.
+    unwindstack::RegsGetLocal(regs.get());
+  } else {
+    regs.reset(unwindstack::Regs::CreateFromUcontext(unwindstack::Regs::CurrentArch(), ucontext));
+  }
+
+  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_);
+}
+
+UnwindStackPtrace::UnwindStackPtrace(pid_t pid, pid_t tid, BacktraceMap* map)
+    : BacktracePtrace(pid, tid, map), memory_(pid) {}
+
+std::string UnwindStackPtrace::GetFunctionNameRaw(uint64_t pc, uint64_t* offset) {
+  return GetMap()->GetFunctionName(pc, offset);
+}
+
+bool UnwindStackPtrace::Unwind(size_t num_ignore_frames, void* context) {
+  std::unique_ptr<unwindstack::Regs> regs;
+  if (context == nullptr) {
+    regs.reset(unwindstack::Regs::RemoteGet(Tid()));
+  } else {
+    regs.reset(unwindstack::Regs::CreateFromUcontext(unwindstack::Regs::CurrentArch(), context));
+  }
+
+  return Backtrace::Unwind(regs.get(), GetMap(), &frames_, num_ignore_frames, nullptr, &error_);
+}
+
+size_t UnwindStackPtrace::Read(uint64_t addr, uint8_t* buffer, size_t bytes) {
+  return memory_.Read(addr, buffer, bytes);
+}
+
+UnwindStackOffline::UnwindStackOffline(ArchEnum arch, pid_t pid, pid_t tid, BacktraceMap* map,
+                                       bool map_shared)
+    : Backtrace(pid, tid, map), arch_(arch) {
+  map_shared_ = map_shared;
+}
+
+bool UnwindStackOffline::Unwind(size_t num_ignore_frames, void* ucontext) {
+  if (ucontext == nullptr) {
+    return false;
+  }
+
+  unwindstack::ArchEnum arch;
+  switch (arch_) {
+    case ARCH_ARM:
+      arch = unwindstack::ARCH_ARM;
+      break;
+    case ARCH_ARM64:
+      arch = unwindstack::ARCH_ARM64;
+      break;
+    case ARCH_X86:
+      arch = unwindstack::ARCH_X86;
+      break;
+    case ARCH_X86_64:
+      arch = unwindstack::ARCH_X86_64;
+      break;
+    default:
+      return false;
+  }
+
+  std::unique_ptr<unwindstack::Regs> regs(unwindstack::Regs::CreateFromUcontext(arch, ucontext));
+
+  return Backtrace::Unwind(regs.get(), GetMap(), &frames_, num_ignore_frames, nullptr, &error_);
+}
+
+std::string UnwindStackOffline::GetFunctionNameRaw(uint64_t, uint64_t*) {
+  return "";
+}
+
+size_t UnwindStackOffline::Read(uint64_t, uint8_t*, size_t) {
+  return 0;
+}
+
+bool UnwindStackOffline::ReadWord(uint64_t, word_t*) {
+  return false;
+}
+
+Backtrace* Backtrace::CreateOffline(ArchEnum arch, pid_t pid, pid_t tid,
+                                    const std::vector<backtrace_map_t>& maps,
+                                    const backtrace_stackinfo_t& stack) {
+  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.release(), false);
+}
+
+Backtrace* Backtrace::CreateOffline(ArchEnum arch, pid_t pid, pid_t tid, BacktraceMap* map) {
+  if (map == nullptr) {
+    return nullptr;
+  }
+  return new UnwindStackOffline(arch, pid, tid, map, true);
+}
+
+void Backtrace::SetGlobalElfCache(bool enable) {
+  unwindstack::Elf::SetCachingEnabled(enable);
+}
diff --git a/libbacktrace/UnwindStack.h b/libbacktrace/UnwindStack.h
new file mode 100644
index 0000000..33c4282
--- /dev/null
+++ b/libbacktrace/UnwindStack.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2017 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 _LIBBACKTRACE_UNWIND_STACK_H
+#define _LIBBACKTRACE_UNWIND_STACK_H
+
+#include <stdint.h>
+
+#include <string>
+
+#include <backtrace/BacktraceMap.h>
+#include <unwindstack/Memory.h>
+
+#include "BacktraceCurrent.h"
+#include "BacktracePtrace.h"
+
+class UnwindStackCurrent : public BacktraceCurrent {
+ public:
+  UnwindStackCurrent(pid_t pid, pid_t tid, BacktraceMap* map);
+  virtual ~UnwindStackCurrent() = default;
+
+  std::string GetFunctionNameRaw(uint64_t pc, uint64_t* offset) override;
+
+  bool UnwindFromContext(size_t num_ignore_frames, void* ucontext) override;
+};
+
+class UnwindStackPtrace : public BacktracePtrace {
+ public:
+  UnwindStackPtrace(pid_t pid, pid_t tid, BacktraceMap* map);
+  virtual ~UnwindStackPtrace() = default;
+
+  bool Unwind(size_t num_ignore_frames, void* context) override;
+
+  std::string GetFunctionNameRaw(uint64_t pc, uint64_t* offset) override;
+
+  size_t Read(uint64_t addr, uint8_t* buffer, size_t bytes) override;
+
+ private:
+  unwindstack::MemoryRemote memory_;
+};
+
+class UnwindStackOffline : public Backtrace {
+ public:
+  UnwindStackOffline(ArchEnum arch, pid_t pid, pid_t tid, BacktraceMap* map, bool map_shared);
+
+  bool Unwind(size_t num_ignore_frames, void* context) override;
+
+  std::string GetFunctionNameRaw(uint64_t pc, uint64_t* offset);
+
+  size_t Read(uint64_t addr, uint8_t* buffer, size_t bytes) override;
+
+  bool ReadWord(uint64_t ptr, word_t* out_value) override;
+
+ private:
+  ArchEnum arch_;
+};
+
+#endif  // _LIBBACKTRACE_UNWIND_STACK_H
diff --git a/libbacktrace/UnwindStackMap.cpp b/libbacktrace/UnwindStackMap.cpp
new file mode 100644
index 0000000..d2d6ab8
--- /dev/null
+++ b/libbacktrace/UnwindStackMap.cpp
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) 2017 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 <stdint.h>
+#include <stdlib.h>
+#include <sys/types.h>
+
+#include <string>
+#include <vector>
+
+#include <backtrace/BacktraceMap.h>
+#include <unwindstack/Elf.h>
+#include <unwindstack/MapInfo.h>
+#include <unwindstack/Maps.h>
+
+#include "UnwindStackMap.h"
+
+//-------------------------------------------------------------------------
+UnwindStackMap::UnwindStackMap(pid_t pid) : BacktraceMap(pid) {}
+
+bool UnwindStackMap::Build() {
+  if (pid_ == 0) {
+    pid_ = getpid();
+    stack_maps_.reset(new unwindstack::LocalMaps);
+  } else {
+    stack_maps_.reset(new unwindstack::RemoteMaps(pid_));
+  }
+
+  // Create the process memory object.
+  process_memory_ = unwindstack::Memory::CreateProcessMemory(pid_);
+
+  // Create a JitDebug object for getting jit unwind information.
+  std::vector<std::string> search_libs_{"libart.so", "libartd.so"};
+  jit_debug_.reset(new unwindstack::JitDebug(process_memory_, search_libs_));
+#if !defined(NO_LIBDEXFILE_SUPPORT)
+  dex_files_.reset(new unwindstack::DexFiles(process_memory_, search_libs_));
+#endif
+
+  if (!stack_maps_->Parse()) {
+    return false;
+  }
+
+  // Iterate through the maps and fill in the backtrace_map_t structure.
+  for (auto* map_info : *stack_maps_) {
+    backtrace_map_t map;
+    map.start = map_info->start;
+    map.end = map_info->end;
+    map.offset = map_info->offset;
+    // Set to -1 so that it is demand loaded.
+    map.load_bias = static_cast<uint64_t>(-1);
+    map.flags = map_info->flags;
+    map.name = map_info->name;
+
+    maps_.push_back(map);
+  }
+
+  return true;
+}
+
+void UnwindStackMap::FillIn(uint64_t addr, backtrace_map_t* map) {
+  BacktraceMap::FillIn(addr, map);
+  if (map->load_bias != static_cast<uint64_t>(-1)) {
+    return;
+  }
+
+  // Fill in the load_bias.
+  unwindstack::MapInfo* map_info = stack_maps_->Find(addr);
+  if (map_info == nullptr) {
+    return;
+  }
+  map->load_bias = map_info->GetLoadBias(process_memory_);
+}
+
+uint64_t UnwindStackMap::GetLoadBias(size_t index) {
+  if (index >= stack_maps_->Total()) {
+    return 0;
+  }
+
+  unwindstack::MapInfo* map_info = stack_maps_->Get(index);
+  if (map_info == nullptr) {
+    return 0;
+  }
+  return map_info->GetLoadBias(process_memory_);
+}
+
+std::string UnwindStackMap::GetFunctionName(uint64_t pc, uint64_t* offset) {
+  *offset = 0;
+  unwindstack::Maps* maps = stack_maps();
+
+  // Get the map for this
+  unwindstack::MapInfo* map_info = maps->Find(pc);
+  if (map_info == nullptr || map_info->flags & PROT_DEVICE_MAP) {
+    return "";
+  }
+
+  unwindstack::Elf* elf = map_info->GetElf(process_memory(), true);
+
+  std::string name;
+  uint64_t func_offset;
+  if (!elf->GetFunctionName(elf->GetRelPc(pc, map_info), &name, &func_offset)) {
+    return "";
+  }
+  *offset = func_offset;
+  return name;
+}
+
+std::shared_ptr<unwindstack::Memory> UnwindStackMap::GetProcessMemory() {
+  return process_memory_;
+}
+
+UnwindStackOfflineMap::UnwindStackOfflineMap(pid_t pid) : UnwindStackMap(pid) {}
+
+bool UnwindStackOfflineMap::Build() {
+  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);
+  }
+
+  std::sort(maps_.begin(), maps_.end(),
+            [](const backtrace_map_t& a, const backtrace_map_t& b) { return a.start < b.start; });
+
+  unwindstack::Maps* maps = new unwindstack::Maps;
+  stack_maps_.reset(maps);
+  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.
+  if (memory_ == nullptr) {
+    memory_ = new unwindstack::MemoryOfflineBuffer(stack.data, stack.start, stack.end);
+    process_memory_.reset(memory_);
+  } else {
+    memory_->Reset(stack.data, stack.start, stack.end);
+  }
+  return true;
+}
+
+//-------------------------------------------------------------------------
+// BacktraceMap create function.
+//-------------------------------------------------------------------------
+BacktraceMap* BacktraceMap::Create(pid_t pid, bool uncached) {
+  BacktraceMap* map;
+
+  if (uncached) {
+    // Force use of the base class to parse the maps when this call is made.
+    map = new BacktraceMap(pid);
+  } else if (pid == getpid()) {
+    map = new UnwindStackMap(0);
+  } else {
+    map = new UnwindStackMap(pid);
+  }
+  if (!map->Build()) {
+    delete map;
+    return nullptr;
+  }
+  return map;
+}
+
+//-------------------------------------------------------------------------
+// BacktraceMap create offline function.
+//-------------------------------------------------------------------------
+BacktraceMap* BacktraceMap::CreateOffline(pid_t pid, const std::vector<backtrace_map_t>& maps) {
+  UnwindStackOfflineMap* map = new UnwindStackOfflineMap(pid);
+  if (!map->Build(maps)) {
+    delete map;
+    return nullptr;
+  }
+  return map;
+}
diff --git a/libbacktrace/UnwindStackMap.h b/libbacktrace/UnwindStackMap.h
new file mode 100644
index 0000000..039f4a2
--- /dev/null
+++ b/libbacktrace/UnwindStackMap.h
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2017 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 _LIBBACKTRACE_UNWINDSTACK_MAP_H
+#define _LIBBACKTRACE_UNWINDSTACK_MAP_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <memory>
+#include <mutex>
+#include <unordered_map>
+#include <vector>
+
+#include <backtrace/Backtrace.h>
+#include <backtrace/BacktraceMap.h>
+#if !defined(NO_LIBDEXFILE_SUPPORT)
+#include <unwindstack/DexFiles.h>
+#endif
+#include <unwindstack/JitDebug.h>
+#include <unwindstack/Maps.h>
+
+// Forward declarations.
+class UnwindDexFile;
+
+class UnwindStackMap : public BacktraceMap {
+ public:
+  explicit UnwindStackMap(pid_t pid);
+  ~UnwindStackMap() = default;
+
+  bool Build() override;
+
+  void FillIn(uint64_t addr, backtrace_map_t* map) override;
+
+  virtual std::string GetFunctionName(uint64_t pc, uint64_t* offset) override;
+  virtual std::shared_ptr<unwindstack::Memory> GetProcessMemory() override final;
+
+  unwindstack::Maps* stack_maps() { return stack_maps_.get(); }
+
+  const std::shared_ptr<unwindstack::Memory>& process_memory() { return process_memory_; }
+
+  unwindstack::JitDebug* GetJitDebug() { return jit_debug_.get(); }
+
+#if !defined(NO_LIBDEXFILE_SUPPORT)
+  unwindstack::DexFiles* GetDexFiles() { return dex_files_.get(); }
+#endif
+
+ protected:
+  uint64_t GetLoadBias(size_t index) override;
+
+  std::unique_ptr<unwindstack::Maps> stack_maps_;
+  std::shared_ptr<unwindstack::Memory> process_memory_;
+  std::unique_ptr<unwindstack::JitDebug> jit_debug_;
+#if !defined(NO_LIBDEXFILE_SUPPORT)
+  std::unique_ptr<unwindstack::DexFiles> dex_files_;
+#endif
+};
+
+class UnwindStackOfflineMap : public UnwindStackMap {
+ public:
+  UnwindStackOfflineMap(pid_t pid);
+  ~UnwindStackOfflineMap() = default;
+
+  bool Build() override;
+
+  bool Build(const std::vector<backtrace_map_t>& maps);
+
+  bool CreateProcessMemory(const backtrace_stackinfo_t& stack);
+
+ private:
+  unwindstack::MemoryOfflineBuffer* memory_ = nullptr;
+};
+
+#endif  // _LIBBACKTRACE_UNWINDSTACK_MAP_H
diff --git a/libbacktrace/backtrace_benchmarks.cpp b/libbacktrace/backtrace_benchmarks.cpp
new file mode 100644
index 0000000..099ac60
--- /dev/null
+++ b/libbacktrace/backtrace_benchmarks.cpp
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2017 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 <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/prctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <string>
+
+#include <android-base/file.h>
+#include <android-base/threads.h>
+
+#include <benchmark/benchmark.h>
+
+#include <backtrace/Backtrace.h>
+#include <backtrace/BacktraceMap.h>
+#include <unwindstack/Memory.h>
+
+// Definitions of prctl arguments to set a vma name in Android kernels.
+#define ANDROID_PR_SET_VMA 0x53564d41
+#define ANDROID_PR_SET_VMA_ANON_NAME 0
+
+constexpr size_t kNumMaps = 2000;
+
+static bool CountMaps(pid_t pid, size_t* num_maps) {
+  // Minimize the calls that might allocate memory. If too much memory
+  // gets allocated, then this routine will add extra maps and the next
+  // call will fail to get the same number of maps as before.
+  int fd =
+      open((std::string("/proc/") + std::to_string(pid) + "/maps").c_str(), O_RDONLY | O_CLOEXEC);
+  if (fd == -1) {
+    fprintf(stderr, "Cannot open map file for pid %d: %s\n", pid, strerror(errno));
+    return false;
+  }
+  *num_maps = 0;
+  while (true) {
+    char buffer[2048];
+    ssize_t bytes = read(fd, buffer, sizeof(buffer));
+    if (bytes <= 0) {
+      break;
+    }
+    // Count the '\n'.
+    for (size_t i = 0; i < static_cast<size_t>(bytes); i++) {
+      if (buffer[i] == '\n') {
+        ++*num_maps;
+      }
+    }
+  }
+
+  close(fd);
+  return true;
+}
+
+static void CreateMap(benchmark::State& state, BacktraceMap* (*map_func)(pid_t, bool)) {
+  // Create a remote process so that the map data is exactly the same.
+  // Also, so that we can create a set number of maps.
+  pid_t pid;
+  if ((pid = fork()) == 0) {
+    size_t num_maps;
+    if (!CountMaps(getpid(), &num_maps)) {
+      exit(1);
+    }
+    // Create uniquely named maps.
+    std::vector<void*> maps;
+    for (size_t i = num_maps; i < kNumMaps; i++) {
+      int flags = PROT_READ | PROT_WRITE;
+      // Alternate page type to make sure a map entry is added for each call.
+      if ((i % 2) == 0) {
+        flags |= PROT_EXEC;
+      }
+      void* memory = mmap(nullptr, PAGE_SIZE, flags, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+      if (memory == MAP_FAILED) {
+        fprintf(stderr, "Failed to create map: %s\n", strerror(errno));
+        exit(1);
+      }
+      memset(memory, 0x1, PAGE_SIZE);
+      if (prctl(ANDROID_PR_SET_VMA, ANDROID_PR_SET_VMA_ANON_NAME, memory, PAGE_SIZE, "test_map") ==
+          -1) {
+        fprintf(stderr, "Failed: %s\n", strerror(errno));
+      }
+      maps.push_back(memory);
+    }
+
+    if (!CountMaps(getpid(), &num_maps)) {
+      exit(1);
+    }
+
+    if (num_maps < kNumMaps) {
+      fprintf(stderr, "Maps set incorrectly: %zu found, %zu expected at least.\n", num_maps,
+              kNumMaps);
+      std::string str;
+      android::base::ReadFileToString("/proc/self/maps", &str);
+      fprintf(stderr, "%s\n", str.c_str());
+      exit(1);
+    }
+
+    // Wait for an hour at most.
+    sleep(3600);
+    exit(1);
+  } else if (pid < 0) {
+    fprintf(stderr, "Fork failed: %s\n", strerror(errno));
+    return;
+  }
+
+  size_t num_maps = 0;
+  for (size_t i = 0; i < 2000; i++) {
+    if (CountMaps(pid, &num_maps) && num_maps >= kNumMaps) {
+      break;
+    }
+    usleep(1000);
+  }
+  if (num_maps < kNumMaps) {
+    fprintf(stderr, "Timed out waiting for the number of maps available: %zu\n", num_maps);
+    return;
+  }
+
+  while (state.KeepRunning()) {
+    BacktraceMap* map = map_func(pid, false);
+    if (map == nullptr) {
+      fprintf(stderr, "Failed to create map\n");
+      return;
+    }
+    delete map;
+  }
+
+  kill(pid, SIGKILL);
+  waitpid(pid, nullptr, 0);
+}
+
+static void BM_create_map(benchmark::State& state) {
+  CreateMap(state, BacktraceMap::Create);
+}
+BENCHMARK(BM_create_map);
+
+using BacktraceCreateFn = decltype(Backtrace::Create);
+
+static void CreateBacktrace(benchmark::State& state, BacktraceMap* map, BacktraceCreateFn fn) {
+  while (state.KeepRunning()) {
+    std::unique_ptr<Backtrace> backtrace(fn(getpid(), android::base::GetThreadId(), map));
+    backtrace->Unwind(0);
+  }
+}
+
+static void BM_create_backtrace(benchmark::State& state) {
+  std::unique_ptr<BacktraceMap> backtrace_map(BacktraceMap::Create(getpid()));
+  CreateBacktrace(state, backtrace_map.get(), Backtrace::Create);
+}
+BENCHMARK(BM_create_backtrace);
+
+BENCHMARK_MAIN();
diff --git a/libbacktrace/backtrace_offline_test.cpp b/libbacktrace/backtrace_offline_test.cpp
index 465b3f9..7d1027e 100644
--- a/libbacktrace/backtrace_offline_test.cpp
+++ b/libbacktrace/backtrace_offline_test.cpp
@@ -15,9 +15,9 @@
  */
 
 #include <inttypes.h>
-#include <libunwind.h>
 #include <pthread.h>
 #include <stdint.h>
+#include <stdlib.h>
 #include <string.h>
 
 #include <functional>
@@ -27,11 +27,13 @@
 #include <vector>
 
 #include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/macros.h>
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
+#include <android-base/threads.h>
 #include <backtrace/Backtrace.h>
 #include <backtrace/BacktraceMap.h>
-#include <cutils/threads.h>
 
 #include <gtest/gtest.h>
 
@@ -42,51 +44,25 @@
 int test_level_three(int, int, int, int, void (*)(void*), void*);
 int test_level_four(int, int, int, int, void (*)(void*), void*);
 int test_recursive_call(int, void (*)(void*), void*);
-void test_get_context_and_wait(unw_context_t* unw_context, volatile int* exit_flag);
-}
-
-static ucontext_t GetUContextFromUnwContext(const unw_context_t& unw_context) {
-  ucontext_t ucontext;
-  memset(&ucontext, 0, sizeof(ucontext));
-#if defined(__arm__)
-  ucontext.uc_mcontext.arm_r0 = unw_context.regs[0];
-  ucontext.uc_mcontext.arm_r1 = unw_context.regs[1];
-  ucontext.uc_mcontext.arm_r2 = unw_context.regs[2];
-  ucontext.uc_mcontext.arm_r3 = unw_context.regs[3];
-  ucontext.uc_mcontext.arm_r4 = unw_context.regs[4];
-  ucontext.uc_mcontext.arm_r5 = unw_context.regs[5];
-  ucontext.uc_mcontext.arm_r6 = unw_context.regs[6];
-  ucontext.uc_mcontext.arm_r7 = unw_context.regs[7];
-  ucontext.uc_mcontext.arm_r8 = unw_context.regs[8];
-  ucontext.uc_mcontext.arm_r9 = unw_context.regs[9];
-  ucontext.uc_mcontext.arm_r10 = unw_context.regs[10];
-  ucontext.uc_mcontext.arm_fp = unw_context.regs[11];
-  ucontext.uc_mcontext.arm_ip = unw_context.regs[12];
-  ucontext.uc_mcontext.arm_sp = unw_context.regs[13];
-  ucontext.uc_mcontext.arm_lr = unw_context.regs[14];
-  ucontext.uc_mcontext.arm_pc = unw_context.regs[15];
-#else
-  ucontext.uc_mcontext = unw_context.uc_mcontext;
-#endif
-  return ucontext;
+void test_get_context_and_wait(void* context, volatile int* exit_flag);
 }
 
 struct FunctionSymbol {
   std::string name;
-  uintptr_t start;
-  uintptr_t end;
+  uint64_t start;
+  uint64_t end;
 };
 
 static std::vector<FunctionSymbol> GetFunctionSymbols() {
   std::vector<FunctionSymbol> symbols = {
       {"unknown_start", 0, 0},
-      {"test_level_one", reinterpret_cast<uintptr_t>(&test_level_one), 0},
-      {"test_level_two", reinterpret_cast<uintptr_t>(&test_level_two), 0},
-      {"test_level_three", reinterpret_cast<uintptr_t>(&test_level_three), 0},
-      {"test_level_four", reinterpret_cast<uintptr_t>(&test_level_four), 0},
-      {"test_recursive_call", reinterpret_cast<uintptr_t>(&test_recursive_call), 0},
-      {"test_get_context_and_wait", reinterpret_cast<uintptr_t>(&test_get_context_and_wait), 0},
-      {"unknown_end", static_cast<uintptr_t>(-1), static_cast<uintptr_t>(-1)},
+      {"test_level_one", reinterpret_cast<uint64_t>(&test_level_one), 0},
+      {"test_level_two", reinterpret_cast<uint64_t>(&test_level_two), 0},
+      {"test_level_three", reinterpret_cast<uint64_t>(&test_level_three), 0},
+      {"test_level_four", reinterpret_cast<uint64_t>(&test_level_four), 0},
+      {"test_recursive_call", reinterpret_cast<uint64_t>(&test_recursive_call), 0},
+      {"test_get_context_and_wait", reinterpret_cast<uint64_t>(&test_get_context_and_wait), 0},
+      {"unknown_end", static_cast<uint64_t>(-1), static_cast<uint64_t>(-1)},
   };
   std::sort(
       symbols.begin(), symbols.end(),
@@ -106,36 +82,39 @@
   return s;
 }
 
-static void HexStringToRawData(const char* s, void* data, size_t size) {
-  uint8_t* p = static_cast<uint8_t*>(data);
+static void HexStringToRawData(const char* s, std::vector<uint8_t>* data, size_t size) {
   for (size_t i = 0; i < size; ++i) {
     int value;
     sscanf(s, "%02x", &value);
-    *p++ = static_cast<uint8_t>(value);
+    data->push_back(value);
     s += 2;
   }
 }
 
 struct OfflineThreadArg {
-  unw_context_t unw_context;
+  std::vector<uint8_t> ucontext;
   pid_t tid;
   volatile int exit_flag;
 };
 
 static void* OfflineThreadFunc(void* arg) {
   OfflineThreadArg* fn_arg = reinterpret_cast<OfflineThreadArg*>(arg);
-  fn_arg->tid = gettid();
-  test_get_context_and_wait(&fn_arg->unw_context, &fn_arg->exit_flag);
+  fn_arg->tid = android::base::GetThreadId();
+  test_get_context_and_wait(&fn_arg->ucontext, &fn_arg->exit_flag);
   return nullptr;
 }
 
+std::string GetTestPath(const std::string& arch, const std::string& path) {
+  return android::base::GetExecutableDirectory() + "/testdata/" + arch + '/' + path;
+}
+
 // This test is disable because it is for generating test data.
 TEST(libbacktrace, DISABLED_generate_offline_testdata) {
   // Create a thread to generate the needed stack and registers information.
   const size_t stack_size = 16 * 1024;
   void* stack = mmap(NULL, stack_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
   ASSERT_NE(MAP_FAILED, stack);
-  uintptr_t stack_addr = reinterpret_cast<uintptr_t>(stack);
+  uint64_t stack_addr = reinterpret_cast<uint64_t>(stack);
   pthread_attr_t attr;
   ASSERT_EQ(0, pthread_attr_init(&attr));
   ASSERT_EQ(0, pthread_attr_setstack(&attr, reinterpret_cast<void*>(stack), stack_size));
@@ -143,7 +122,7 @@
   OfflineThreadArg arg;
   arg.exit_flag = 0;
   ASSERT_EQ(0, pthread_create(&thread, &attr, OfflineThreadFunc, &arg));
-  // Wait for the offline thread to generate the stack and unw_context information.
+  // Wait for the offline thread to generate the stack and context information.
   sleep(1);
   // Copy the stack information.
   std::vector<uint8_t> stack_data(reinterpret_cast<uint8_t*>(stack),
@@ -166,14 +145,16 @@
   testdata += android::base::StringPrintf("pid: %d tid: %d\n", getpid(), arg.tid);
   // 2. Dump maps
   for (auto it = map->begin(); it != map->end(); ++it) {
-    testdata += android::base::StringPrintf(
-        "map: start: %" PRIxPTR " end: %" PRIxPTR " offset: %" PRIxPTR
-        " load_base: %" PRIxPTR " flags: %d name: %s\n",
-        it->start, it->end, it->offset, it->load_base, it->flags, it->name.c_str());
+    const backtrace_map_t* entry = *it;
+    testdata +=
+        android::base::StringPrintf("map: start: %" PRIx64 " end: %" PRIx64 " offset: %" PRIx64
+                                    " load_bias: %" PRIx64 " flags: %d name: %s\n",
+                                    entry->start, entry->end, entry->offset, entry->load_bias,
+                                    entry->flags, entry->name.c_str());
   }
-  // 3. Dump registers
-  testdata += android::base::StringPrintf("registers: %zu ", sizeof(arg.unw_context));
-  testdata += RawDataToHexString(&arg.unw_context, sizeof(arg.unw_context));
+  // 3. Dump ucontext
+  testdata += android::base::StringPrintf("ucontext: %zu ", arg.ucontext.size());
+  testdata += RawDataToHexString(arg.ucontext.data(), arg.ucontext.size());
   testdata.push_back('\n');
 
   // 4. Dump stack
@@ -186,9 +167,9 @@
   // 5. Dump function symbols
   std::vector<FunctionSymbol> function_symbols = GetFunctionSymbols();
   for (const auto& symbol : function_symbols) {
-    testdata += android::base::StringPrintf(
-        "function: start: %" PRIxPTR " end: %" PRIxPTR" name: %s\n",
-        symbol.start, symbol.end, symbol.name.c_str());
+    testdata +=
+        android::base::StringPrintf("function: start: %" PRIx64 " end: %" PRIx64 " name: %s\n",
+                                    symbol.start, symbol.end, symbol.name.c_str());
   }
 
   ASSERT_TRUE(android::base::WriteStringToFile(testdata, "offline_testdata"));
@@ -196,7 +177,7 @@
 
 // Return the name of the function which matches the address. Although we don't know the
 // exact end of each function, it is accurate enough for the tests.
-static std::string FunctionNameForAddress(uintptr_t addr,
+static std::string FunctionNameForAddress(uint64_t addr,
                                           const std::vector<FunctionSymbol>& symbols) {
   for (auto& symbol : symbols) {
     if (addr >= symbol.start && addr < symbol.end) {
@@ -206,25 +187,11 @@
   return "";
 }
 
-static std::string GetArch() {
-#if defined(__arm__)
-  return "arm";
-#elif defined(__aarch64__)
-  return "aarch64";
-#elif defined(__i386__)
-  return "x86";
-#elif defined(__x86_64__)
-  return "x86_64";
-#else
-  return "";
-#endif
-}
-
 struct OfflineTestData {
   int pid;
   int tid;
   std::vector<backtrace_map_t> maps;
-  unw_context_t unw_context;
+  std::vector<uint8_t> ucontext;
   backtrace_stackinfo_t stack_info;
   std::vector<uint8_t> stack;
   std::vector<FunctionSymbol> symbols;
@@ -237,7 +204,6 @@
   }
   // Parse offline_testdata.
   std::vector<std::string> lines = android::base::Split(s, "\n");
-  memset(&testdata->unw_context, 0, sizeof(testdata->unw_context));
   for (const auto& line : lines) {
     if (android::base::StartsWith(line, "pid:")) {
       sscanf(line.c_str(), "pid: %d tid: %d", &testdata->pid, &testdata->tid);
@@ -246,56 +212,43 @@
       backtrace_map_t& map = testdata->maps.back();
       int pos;
       sscanf(line.c_str(),
-             "map: start: %" SCNxPTR " end: %" SCNxPTR " offset: %" SCNxPTR
-             " load_base: %" SCNxPTR " flags: %d name: %n",
-             &map.start, &map.end, &map.offset, &map.load_base, &map.flags, &pos);
+             "map: start: %" SCNx64 " end: %" SCNx64 " offset: %" SCNx64 " load_bias: %" SCNx64
+             " flags: %d name: %n",
+             &map.start, &map.end, &map.offset, &map.load_bias, &map.flags, &pos);
       map.name = android::base::Trim(line.substr(pos));
-    } else if (android::base::StartsWith(line, "registers:")) {
+    } else if (android::base::StartsWith(line, "ucontext:")) {
       size_t size;
       int pos;
-      sscanf(line.c_str(), "registers: %zu %n", &size, &pos);
-      if (sizeof(testdata->unw_context) != size) {
-        return false;
-      }
-      HexStringToRawData(&line[pos], &testdata->unw_context, size);
+      testdata->ucontext.clear();
+      sscanf(line.c_str(), "ucontext: %zu %n", &size, &pos);
+      HexStringToRawData(&line[pos], &testdata->ucontext, size);
     } else if (android::base::StartsWith(line, "stack:")) {
       size_t size;
       int pos;
       sscanf(line.c_str(),
              "stack: start: %" SCNx64 " end: %" SCNx64 " size: %zu %n",
              &testdata->stack_info.start, &testdata->stack_info.end, &size, &pos);
-      testdata->stack.resize(size);
-      HexStringToRawData(&line[pos], &testdata->stack[0], size);
+      CHECK_EQ(testdata->stack_info.end - testdata->stack_info.start, size);
+      testdata->stack.clear();
+      HexStringToRawData(&line[pos], &testdata->stack, size);
       testdata->stack_info.data = testdata->stack.data();
     } else if (android::base::StartsWith(line, "function:")) {
       testdata->symbols.resize(testdata->symbols.size() + 1);
       FunctionSymbol& symbol = testdata->symbols.back();
       int pos;
-      sscanf(line.c_str(),
-             "function: start: %" SCNxPTR " end: %" SCNxPTR " name: %n",
-             &symbol.start, &symbol.end, &pos);
+      sscanf(line.c_str(), "function: start: %" SCNx64 " end: %" SCNx64 " name: %n", &symbol.start,
+             &symbol.end, &pos);
       symbol.name = line.substr(pos);
     }
   }
   return true;
 }
 
-static void BacktraceOfflineTest(const std::string& testlib_name) {
-  const std::string arch = GetArch();
-  if (arch.empty()) {
-    GTEST_LOG_(INFO) << "This test does nothing on current arch.";
-    return;
-  }
-  const std::string testlib_path = "testdata/" + arch + "/" + testlib_name;
-  struct stat st;
-  if (stat(testlib_path.c_str(), &st) == -1) {
-    GTEST_LOG_(INFO) << "This test is skipped as " << testlib_path << " doesn't exist.";
-    return;
-  }
-
-  const std::string offline_testdata_path = "testdata/" + arch + "/offline_testdata";
+static void BacktraceOfflineTest(std::string arch_str, const std::string& testlib_name) {
+  const std::string testlib_path(GetTestPath(arch_str, testlib_name));
+  const std::string offline_testdata_path(GetTestPath(arch_str, "offline_testdata"));
   OfflineTestData testdata;
-  ASSERT_TRUE(ReadOfflineTestData(offline_testdata_path, &testdata));
+  ASSERT_TRUE(ReadOfflineTestData(offline_testdata_path, &testdata)) << "Failed " << arch_str;
 
   // Fix path of libbacktrace_testlib.so.
   for (auto& map : testdata.maps) {
@@ -304,19 +257,27 @@
     }
   }
 
-  // Do offline backtrace.
-  std::unique_ptr<BacktraceMap> map(BacktraceMap::Create(testdata.pid, testdata.maps));
-  ASSERT_TRUE(map != nullptr);
+  Backtrace::ArchEnum arch;
+  if (arch_str == "arm") {
+    arch = Backtrace::ARCH_ARM;
+  } else if (arch_str == "arm64") {
+    arch = Backtrace::ARCH_ARM64;
+  } else if (arch_str == "x86") {
+    arch = Backtrace::ARCH_X86;
+  } else if (arch_str == "x86_64") {
+    arch = Backtrace::ARCH_X86_64;
+  } else {
+    abort();
+  }
 
-  std::unique_ptr<Backtrace> backtrace(
-      Backtrace::CreateOffline(testdata.pid, testdata.tid, map.get(), testdata.stack_info));
-  ASSERT_TRUE(backtrace != nullptr);
+  std::unique_ptr<Backtrace> backtrace(Backtrace::CreateOffline(
+      arch, testdata.pid, testdata.tid, testdata.maps, testdata.stack_info));
+  ASSERT_TRUE(backtrace != nullptr) << "Failed " << arch_str;
 
-  ucontext_t ucontext = GetUContextFromUnwContext(testdata.unw_context);
-  ASSERT_TRUE(backtrace->Unwind(0, &ucontext));
+  ASSERT_TRUE(backtrace->Unwind(0, testdata.ucontext.data())) << "Failed " << arch_str;
 
   // Collect pc values of the call stack frames.
-  std::vector<uintptr_t> pc_values;
+  std::vector<uint64_t> pc_values;
   for (size_t i = 0; i < backtrace->NumFrames(); ++i) {
     pc_values.push_back(backtrace->GetFrame(i)->pc);
   }
@@ -329,72 +290,115 @@
     }
   }
 
-  ASSERT_GE(test_one_index, 3u);
-  ASSERT_EQ("test_level_one", FunctionNameForAddress(pc_values[test_one_index], testdata.symbols));
-  ASSERT_EQ("test_level_two", FunctionNameForAddress(pc_values[test_one_index - 1],
-                                                     testdata.symbols));
-  ASSERT_EQ("test_level_three", FunctionNameForAddress(pc_values[test_one_index - 2],
-                                                       testdata.symbols));
-  ASSERT_EQ("test_level_four", FunctionNameForAddress(pc_values[test_one_index - 3],
-                                                      testdata.symbols));
+  ASSERT_GE(test_one_index, 3u) << "Failed " << arch_str;
+  ASSERT_EQ("test_level_one", FunctionNameForAddress(pc_values[test_one_index], testdata.symbols))
+      << "Failed " << arch_str;
+  ASSERT_EQ("test_level_two", FunctionNameForAddress(pc_values[test_one_index - 1], testdata.symbols))
+      << "Failed " << arch_str;
+  ASSERT_EQ("test_level_three",
+            FunctionNameForAddress(pc_values[test_one_index - 2], testdata.symbols))
+      << "Failed " << arch_str;
+  ASSERT_EQ("test_level_four",
+            FunctionNameForAddress(pc_values[test_one_index - 3], testdata.symbols))
+      << "Failed " << arch_str;
 }
 
+// For now, these tests can only run on the given architectures.
 TEST(libbacktrace, offline_eh_frame) {
-  BacktraceOfflineTest("libbacktrace_test_eh_frame.so");
+  BacktraceOfflineTest("arm64", "libbacktrace_test_eh_frame.so");
+  BacktraceOfflineTest("x86_64", "libbacktrace_test_eh_frame.so");
 }
 
 TEST(libbacktrace, offline_debug_frame) {
-  BacktraceOfflineTest("libbacktrace_test_debug_frame.so");
+  BacktraceOfflineTest("arm", "libbacktrace_test_debug_frame.so");
+  BacktraceOfflineTest("x86", "libbacktrace_test_debug_frame.so");
 }
 
 TEST(libbacktrace, offline_gnu_debugdata) {
-  BacktraceOfflineTest("libbacktrace_test_gnu_debugdata.so");
+  BacktraceOfflineTest("arm", "libbacktrace_test_gnu_debugdata.so");
+  BacktraceOfflineTest("x86", "libbacktrace_test_gnu_debugdata.so");
 }
 
 TEST(libbacktrace, offline_arm_exidx) {
-  BacktraceOfflineTest("libbacktrace_test_arm_exidx.so");
+  BacktraceOfflineTest("arm", "libbacktrace_test_arm_exidx.so");
+}
+
+static void LibUnwindingTest(const std::string& arch_str, const std::string& testdata_name,
+                             const std::string& testlib_name) {
+  const std::string testlib_path(GetTestPath(arch_str, testlib_name));
+  struct stat st;
+  ASSERT_EQ(0, stat(testlib_path.c_str(), &st)) << "can't find testlib " << testlib_path;
+
+  const std::string offline_testdata_path(GetTestPath(arch_str, testdata_name));
+  OfflineTestData testdata;
+  ASSERT_TRUE(ReadOfflineTestData(offline_testdata_path, &testdata));
+
+  // Fix path of the testlib.
+  for (auto& map : testdata.maps) {
+    if (map.name.find(testlib_name) != std::string::npos) {
+      map.name = testlib_path;
+    }
+  }
+
+  Backtrace::ArchEnum arch;
+  if (arch_str == "arm") {
+    arch = Backtrace::ARCH_ARM;
+  } else if (arch_str == "arm64") {
+    arch = Backtrace::ARCH_ARM64;
+  } else if (arch_str == "x86") {
+    arch = Backtrace::ARCH_X86;
+  } else if (arch_str == "x86_64") {
+    arch = Backtrace::ARCH_X86_64;
+  } else {
+    ASSERT_TRUE(false) << "Unsupported arch " << arch_str;
+    abort();
+  }
+
+  // Do offline backtrace.
+  std::unique_ptr<Backtrace> backtrace(Backtrace::CreateOffline(
+      arch, testdata.pid, testdata.tid, testdata.maps, testdata.stack_info));
+  ASSERT_TRUE(backtrace != nullptr);
+
+  ASSERT_TRUE(backtrace->Unwind(0, testdata.ucontext.data()));
+
+  ASSERT_EQ(testdata.symbols.size(), backtrace->NumFrames());
+  for (size_t i = 0; i < backtrace->NumFrames(); ++i) {
+    std::string name = FunctionNameForAddress(backtrace->GetFrame(i)->rel_pc, testdata.symbols);
+    ASSERT_EQ(name, testdata.symbols[i].name);
+  }
+  ASSERT_TRUE(backtrace->GetError().error_code == BACKTRACE_UNWIND_ERROR_ACCESS_MEM_FAILED ||
+              backtrace->GetError().error_code == BACKTRACE_UNWIND_ERROR_MAP_MISSING ||
+              backtrace->GetError().error_code == BACKTRACE_UNWIND_ERROR_REPEATED_FRAME);
 }
 
 // This test tests the situation that ranges of functions covered by .eh_frame and .ARM.exidx
 // overlap with each other, which appears in /system/lib/libart.so.
 TEST(libbacktrace, offline_unwind_mix_eh_frame_and_arm_exidx) {
-  const std::string arch = GetArch();
-  if (arch.empty() || arch != "arm") {
-    GTEST_LOG_(INFO) << "This test does nothing on current arch.";
-    return;
-  }
-  const std::string testlib_path = "testdata/" + arch + "/libart.so";
-  struct stat st;
-  ASSERT_EQ(0, stat(testlib_path.c_str(), &st)) << "can't find testlib " << testlib_path;
+  LibUnwindingTest("arm", "offline_testdata_for_libart", "libart.so");
+}
 
-  const std::string offline_testdata_path = "testdata/" + arch + "/offline_testdata_for_libart";
-  OfflineTestData testdata;
-  ASSERT_TRUE(ReadOfflineTestData(offline_testdata_path, &testdata));
+TEST(libbacktrace, offline_debug_frame_with_load_bias) {
+  LibUnwindingTest("arm", "offline_testdata_for_libandroid_runtime", "libandroid_runtime.so");
+}
 
-  // Fix path of /system/lib/libart.so.
-  for (auto& map : testdata.maps) {
-    if (map.name.find("libart.so") != std::string::npos) {
-      map.name = testlib_path;
-    }
-  }
+TEST(libbacktrace, offline_try_armexidx_after_debug_frame) {
+  LibUnwindingTest("arm", "offline_testdata_for_libGLESv2_adreno", "libGLESv2_adreno.so");
+}
 
-  // Do offline backtrace.
-  std::unique_ptr<BacktraceMap> map(BacktraceMap::Create(testdata.pid, testdata.maps));
-  ASSERT_TRUE(map != nullptr);
+TEST(libbacktrace, offline_cie_with_P_augmentation) {
+  // Make sure we can unwind through functions with CIE entry containing P augmentation, which
+  // makes unwinding library reading personality handler from memory. One example is
+  // /system/lib64/libskia.so.
+  LibUnwindingTest("arm64", "offline_testdata_for_libskia", "libskia.so");
+}
 
-  std::unique_ptr<Backtrace> backtrace(
-      Backtrace::CreateOffline(testdata.pid, testdata.tid, map.get(), testdata.stack_info));
-  ASSERT_TRUE(backtrace != nullptr);
+TEST(libbacktrace, offline_empty_eh_frame_hdr) {
+  // Make sure we can unwind through libraries with empty .eh_frame_hdr section. One example is
+  // /vendor/lib64/egl/eglSubDriverAndroid.so.
+  LibUnwindingTest("arm64", "offline_testdata_for_eglSubDriverAndroid", "eglSubDriverAndroid.so");
+}
 
-  ucontext_t ucontext = GetUContextFromUnwContext(testdata.unw_context);
-  ASSERT_TRUE(backtrace->Unwind(0, &ucontext));
-
-  // The last frame is outside of libart.so
-  ASSERT_EQ(testdata.symbols.size() + 1, backtrace->NumFrames());
-  for (size_t i = 0; i + 1 < backtrace->NumFrames(); ++i) {
-    uintptr_t vaddr_in_file = backtrace->GetFrame(i)->pc - testdata.maps[0].start +
-        testdata.maps[0].load_base;
-    std::string name = FunctionNameForAddress(vaddr_in_file, testdata.symbols);
-    ASSERT_EQ(name, testdata.symbols[i].name);
-  }
+TEST(libbacktrace, offline_max_frames_limit) {
+  // The length of callchain can reach 256 when recording an application.
+  ASSERT_GE(MAX_BACKTRACE_FRAMES, 256);
 }
diff --git a/libbacktrace/backtrace_read_benchmarks.cpp b/libbacktrace/backtrace_read_benchmarks.cpp
new file mode 100644
index 0000000..6a688b0
--- /dev/null
+++ b/libbacktrace/backtrace_read_benchmarks.cpp
@@ -0,0 +1,197 @@
+/*
+ * Copyright (C) 2017 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 <errno.h>
+#include <signal.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/ptrace.h>
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <memory>
+#include <vector>
+
+#include <benchmark/benchmark.h>
+
+#include <backtrace/Backtrace.h>
+
+#define AT_COMMON_SIZES Arg(1)->Arg(4)->Arg(8)->Arg(16)->Arg(100)->Arg(200)->Arg(500)->Arg(1024)
+
+static void Attach(pid_t pid) {
+  if (ptrace(PTRACE_ATTACH, pid, 0, 0) == -1) {
+    perror("Failed to attach");
+    abort();
+  }
+
+  siginfo_t si;
+  // Wait for up to 5 seconds.
+  for (size_t i = 0; i < 5000; i++) {
+    if (ptrace(PTRACE_GETSIGINFO, pid, 0, &si) == 0) {
+      return;
+    }
+    usleep(1000);
+  }
+  printf("Remote process failed to stop in five seconds.\n");
+  abort();
+}
+
+class ScopedPidReaper {
+ public:
+  ScopedPidReaper(pid_t pid) : pid_(pid) {}
+  ~ScopedPidReaper() {
+    kill(pid_, SIGKILL);
+    waitpid(pid_, nullptr, 0);
+  }
+
+ private:
+  pid_t pid_;
+};
+
+static size_t ProcessVmRead(pid_t pid, uint64_t remote_src, void* dst, size_t len) {
+  struct iovec dst_iov = {
+      .iov_base = dst, .iov_len = len,
+  };
+
+  struct iovec src_iov = {
+      .iov_base = reinterpret_cast<void*>(remote_src), .iov_len = len,
+  };
+
+  ssize_t rc = process_vm_readv(pid, &dst_iov, 1, &src_iov, 1, 0);
+  return rc == -1 ? 0 : rc;
+}
+
+static bool PtraceReadLong(pid_t pid, uint64_t addr, long* value) {
+  // ptrace() returns -1 and sets errno when the operation fails.
+  // To disambiguate -1 from a valid result, we clear errno beforehand.
+  errno = 0;
+  *value = ptrace(PTRACE_PEEKTEXT, pid, reinterpret_cast<void*>(addr), nullptr);
+  if (*value == -1 && errno) {
+    return false;
+  }
+  return true;
+}
+
+static size_t PtraceRead(pid_t pid, uint64_t addr, void* dst, size_t bytes) {
+  size_t bytes_read = 0;
+  long data;
+  for (size_t i = 0; i < bytes / sizeof(long); i++) {
+    if (!PtraceReadLong(pid, addr, &data)) {
+      return bytes_read;
+    }
+    memcpy(dst, &data, sizeof(long));
+    dst = reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(dst) + sizeof(long));
+    addr += sizeof(long);
+    bytes_read += sizeof(long);
+  }
+
+  size_t left_over = bytes & (sizeof(long) - 1);
+  if (left_over) {
+    if (!PtraceReadLong(pid, addr, &data)) {
+      return bytes_read;
+    }
+    memcpy(dst, &data, left_over);
+    bytes_read += left_over;
+  }
+  return bytes_read;
+}
+
+static void CreateRemoteProcess(size_t size, void** map, pid_t* pid) {
+  *map = mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+  if (*map == MAP_FAILED) {
+    perror("Can't allocate memory");
+    abort();
+  }
+  memset(*map, 0xaa, size);
+
+  if ((*pid = fork()) == 0) {
+    for (volatile int i = 0;; i++)
+      ;
+    exit(1);
+  }
+  if (*pid < 0) {
+    perror("Failed to fork");
+    abort();
+  }
+  Attach(*pid);
+  // Don't need this map in the current process any more.
+  munmap(*map, size);
+}
+
+static void BM_read_with_ptrace(benchmark::State& state) {
+  void* map;
+  pid_t pid;
+  CreateRemoteProcess(state.range(0), &map, &pid);
+  ScopedPidReaper reap(pid);
+
+  std::vector<uint8_t> read_buffer(state.range(0));
+  uint64_t addr = reinterpret_cast<uint64_t>(map);
+  while (state.KeepRunning()) {
+    if (PtraceRead(pid, addr, read_buffer.data(), read_buffer.size()) != read_buffer.size()) {
+      printf("Unexpected bad read.\n");
+      abort();
+    }
+  }
+  ptrace(PTRACE_DETACH, pid, 0, 0);
+}
+BENCHMARK(BM_read_with_ptrace)->AT_COMMON_SIZES;
+
+static void BM_read_with_process_vm_read(benchmark::State& state) {
+  void* map;
+  pid_t pid;
+  CreateRemoteProcess(state.range(0), &map, &pid);
+  ScopedPidReaper reap(pid);
+
+  std::vector<uint8_t> read_buffer(state.range(0));
+  uint64_t addr = reinterpret_cast<uint64_t>(map);
+  while (state.KeepRunning()) {
+    if (ProcessVmRead(pid, addr, read_buffer.data(), read_buffer.size()) != read_buffer.size()) {
+      printf("Unexpected bad read.\n");
+      abort();
+    }
+  }
+  ptrace(PTRACE_DETACH, pid, 0, 0);
+}
+BENCHMARK(BM_read_with_process_vm_read)->AT_COMMON_SIZES;
+
+static void BM_read_with_backtrace_object(benchmark::State& state) {
+  void* map;
+  pid_t pid;
+  CreateRemoteProcess(state.range(0), &map, &pid);
+  ScopedPidReaper reap(pid);
+
+  std::unique_ptr<Backtrace> backtrace(Backtrace::Create(pid, BACKTRACE_CURRENT_THREAD));
+  if (backtrace.get() == nullptr) {
+    printf("Failed to create backtrace.\n");
+    abort();
+  }
+
+  uint64_t addr = reinterpret_cast<uint64_t>(map);
+  std::vector<uint8_t> read_buffer(state.range(0));
+  while (state.KeepRunning()) {
+    if (backtrace->Read(addr, read_buffer.data(), read_buffer.size()) != read_buffer.size()) {
+      printf("Unexpected bad read.\n");
+      abort();
+    }
+  }
+  ptrace(PTRACE_DETACH, pid, 0, 0);
+}
+BENCHMARK(BM_read_with_backtrace_object)->AT_COMMON_SIZES;
diff --git a/libbacktrace/backtrace_test.cpp b/libbacktrace/backtrace_test.cpp
index fb463b0..06a32c7 100644
--- a/libbacktrace/backtrace_test.cpp
+++ b/libbacktrace/backtrace_test.cpp
@@ -31,6 +31,7 @@
 #include <sys/types.h>
 #include <sys/wait.h>
 #include <time.h>
+#include <ucontext.h>
 #include <unistd.h>
 
 #include <algorithm>
@@ -45,16 +46,16 @@
 
 #include <android-base/macros.h>
 #include <android-base/stringprintf.h>
+#include <android-base/test_utils.h>
+#include <android-base/threads.h>
 #include <android-base/unique_fd.h>
 #include <cutils/atomic.h>
-#include <cutils/threads.h>
 
 #include <gtest/gtest.h>
 
 // For the THREAD_SIGNAL definition.
 #include "BacktraceCurrent.h"
 #include "backtrace_testlib.h"
-#include "thread_utils.h"
 
 // Number of microseconds per milliseconds.
 #define US_PER_MSEC             1000
@@ -68,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;
@@ -77,11 +81,20 @@
 
 struct dump_thread_t {
   thread_t thread;
+  BacktraceMap* map;
   Backtrace* backtrace;
   int32_t* now;
   int32_t done;
 };
 
+typedef Backtrace* (*create_func_t)(pid_t, pid_t, BacktraceMap*);
+typedef BacktraceMap* (*map_create_func_t)(pid_t, bool);
+
+static void VerifyLevelDump(Backtrace* backtrace, create_func_t create_func = nullptr,
+                            map_create_func_t map_func = nullptr);
+static void VerifyMaxDump(Backtrace* backtrace, create_func_t create_func = nullptr,
+                          map_create_func_t map_func = nullptr);
+
 static uint64_t NanoTime() {
   struct timespec t = { 0, 0 };
   clock_gettime(CLOCK_MONOTONIC, &t);
@@ -134,6 +147,17 @@
   ASSERT_EQ(waitpid(pid, nullptr, 0), pid);
 }
 
+#if !defined(__ANDROID__) || defined(__arm__)
+// On host and arm target we aren't guaranteed that we will terminate cleanly.
+#define VERIFY_NO_ERROR(error_code)                               \
+  ASSERT_TRUE(error_code == BACKTRACE_UNWIND_NO_ERROR ||          \
+              error_code == BACKTRACE_UNWIND_ERROR_UNWIND_INFO || \
+              error_code == BACKTRACE_UNWIND_ERROR_MAP_MISSING)   \
+      << "Unknown error code " << std::to_string(error_code);
+#else
+#define VERIFY_NO_ERROR(error_code) ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, error_code);
+#endif
+
 static bool ReadyLevelBacktrace(Backtrace* backtrace) {
   // See if test_level_four is in the backtrace.
   bool found = false;
@@ -147,7 +171,7 @@
   return found;
 }
 
-static void VerifyLevelDump(Backtrace* backtrace) {
+static void VerifyLevelDump(Backtrace* backtrace, create_func_t, map_create_func_t) {
   ASSERT_GT(backtrace->NumFrames(), static_cast<size_t>(0))
     << DumpFrames(backtrace);
   ASSERT_LT(backtrace->NumFrames(), static_cast<size_t>(MAX_BACKTRACE_FRAMES))
@@ -180,7 +204,7 @@
       Backtrace::Create(BACKTRACE_CURRENT_PROCESS, BACKTRACE_CURRENT_THREAD));
   ASSERT_TRUE(backtrace.get() != nullptr);
   ASSERT_TRUE(backtrace->Unwind(0));
-  ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, backtrace->GetError());
+  VERIFY_NO_ERROR(backtrace->GetError().error_code);
 
   VerifyLevelDump(backtrace.get());
 }
@@ -189,7 +213,7 @@
   return (backtrace->NumFrames() == MAX_BACKTRACE_FRAMES);
 }
 
-static void VerifyMaxDump(Backtrace* backtrace) {
+static void VerifyMaxDump(Backtrace* backtrace, create_func_t, map_create_func_t) {
   ASSERT_EQ(backtrace->NumFrames(), static_cast<size_t>(MAX_BACKTRACE_FRAMES))
     << DumpFrames(backtrace);
   // Verify that the last frame is our recursive call.
@@ -202,7 +226,7 @@
       Backtrace::Create(BACKTRACE_CURRENT_PROCESS, BACKTRACE_CURRENT_THREAD));
   ASSERT_TRUE(backtrace.get() != nullptr);
   ASSERT_TRUE(backtrace->Unwind(0));
-  ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, backtrace->GetError());
+  ASSERT_EQ(BACKTRACE_UNWIND_ERROR_EXCEED_MAX_FRAMES_LIMIT, backtrace->GetError().error_code);
 
   VerifyMaxDump(backtrace.get());
 }
@@ -232,29 +256,66 @@
   std::unique_ptr<Backtrace> backtrace(Backtrace::Create(getpid(), getpid()));
   ASSERT_TRUE(backtrace.get() != nullptr);
   ASSERT_TRUE(backtrace->Unwind(0));
-  ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, backtrace->GetError());
+  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);
 }
 
 static void VerifyIgnoreFrames(Backtrace* bt_all, Backtrace* bt_ign1, Backtrace* bt_ign2,
                                const char* cur_proc) {
-  EXPECT_EQ(bt_all->NumFrames(), bt_ign1->NumFrames() + 1)
-    << "All backtrace:\n" << DumpFrames(bt_all) << "Ignore 1 backtrace:\n" << DumpFrames(bt_ign1);
-  EXPECT_EQ(bt_all->NumFrames(), bt_ign2->NumFrames() + 2)
-    << "All backtrace:\n" << DumpFrames(bt_all) << "Ignore 2 backtrace:\n" << DumpFrames(bt_ign2);
+  ASSERT_EQ(bt_all->NumFrames(), bt_ign1->NumFrames() + 1) << "All backtrace:\n"
+                                                           << DumpFrames(bt_all)
+                                                           << "Ignore 1 backtrace:\n"
+                                                           << DumpFrames(bt_ign1);
+  ASSERT_EQ(bt_all->NumFrames(), bt_ign2->NumFrames() + 2) << "All backtrace:\n"
+                                                           << DumpFrames(bt_all)
+                                                           << "Ignore 2 backtrace:\n"
+                                                           << DumpFrames(bt_ign2);
 
   // Check all of the frames are the same > the current frame.
   bool check = (cur_proc == nullptr);
@@ -279,19 +340,19 @@
       Backtrace::Create(BACKTRACE_CURRENT_PROCESS, BACKTRACE_CURRENT_THREAD));
   ASSERT_TRUE(all.get() != nullptr);
   ASSERT_TRUE(all->Unwind(0));
-  ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, all->GetError());
+  VERIFY_NO_ERROR(all->GetError().error_code);
 
   std::unique_ptr<Backtrace> ign1(
       Backtrace::Create(BACKTRACE_CURRENT_PROCESS, BACKTRACE_CURRENT_THREAD));
   ASSERT_TRUE(ign1.get() != nullptr);
   ASSERT_TRUE(ign1->Unwind(1));
-  ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, ign1->GetError());
+  VERIFY_NO_ERROR(ign1->GetError().error_code);
 
   std::unique_ptr<Backtrace> ign2(
       Backtrace::Create(BACKTRACE_CURRENT_PROCESS, BACKTRACE_CURRENT_THREAD));
   ASSERT_TRUE(ign2.get() != nullptr);
   ASSERT_TRUE(ign2->Unwind(2));
-  ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, ign2->GetError());
+  VERIFY_NO_ERROR(ign2->GetError().error_code);
 
   VerifyIgnoreFrames(all.get(), ign1.get(), ign2.get(), "VerifyLevelIgnoreFrames");
 }
@@ -304,8 +365,9 @@
   ASSERT_NE(test_recursive_call(MAX_BACKTRACE_FRAMES+10, VerifyMaxBacktrace, nullptr), 0);
 }
 
-static void VerifyProcTest(pid_t pid, pid_t tid, bool share_map, bool (*ReadyFunc)(Backtrace*),
-                           void (*VerifyFunc)(Backtrace*)) {
+static void VerifyProcTest(pid_t pid, pid_t tid, bool (*ReadyFunc)(Backtrace*),
+                           void (*VerifyFunc)(Backtrace*, create_func_t, map_create_func_t),
+                           create_func_t create_func, map_create_func_t map_create_func) {
   pid_t ptrace_tid;
   if (tid < 0) {
     ptrace_tid = pid;
@@ -322,15 +384,12 @@
       WaitForStop(ptrace_tid);
 
       std::unique_ptr<BacktraceMap> map;
-      if (share_map) {
-        map.reset(BacktraceMap::Create(pid));
-      }
-      std::unique_ptr<Backtrace> backtrace(Backtrace::Create(pid, tid, map.get()));
+      map.reset(map_create_func(pid, false));
+      std::unique_ptr<Backtrace> backtrace(create_func(pid, tid, map.get()));
       ASSERT_TRUE(backtrace.get() != nullptr);
       ASSERT_TRUE(backtrace->Unwind(0));
-      ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, backtrace->GetError());
       if (ReadyFunc(backtrace.get())) {
-        VerifyFunc(backtrace.get());
+        VerifyFunc(backtrace.get(), create_func, map_create_func);
         verified = true;
       } else {
         last_dump = DumpFrames(backtrace.get());
@@ -349,21 +408,8 @@
     ASSERT_NE(test_level_one(1, 2, 3, 4, nullptr, nullptr), 0);
     _exit(1);
   }
-  VerifyProcTest(pid, BACKTRACE_CURRENT_THREAD, false, ReadyLevelBacktrace, VerifyLevelDump);
-
-  kill(pid, SIGKILL);
-  int status;
-  ASSERT_EQ(waitpid(pid, &status, 0), pid);
-}
-
-TEST(libbacktrace, ptrace_trace_shared_map) {
-  pid_t pid;
-  if ((pid = fork()) == 0) {
-    ASSERT_NE(test_level_one(1, 2, 3, 4, nullptr, nullptr), 0);
-    _exit(1);
-  }
-
-  VerifyProcTest(pid, BACKTRACE_CURRENT_THREAD, true, ReadyLevelBacktrace, VerifyLevelDump);
+  VerifyProcTest(pid, BACKTRACE_CURRENT_THREAD, ReadyLevelBacktrace, VerifyLevelDump,
+                 Backtrace::Create, BacktraceMap::Create);
 
   kill(pid, SIGKILL);
   int status;
@@ -376,23 +422,26 @@
     ASSERT_NE(test_recursive_call(MAX_BACKTRACE_FRAMES+10, nullptr, nullptr), 0);
     _exit(1);
   }
-  VerifyProcTest(pid, BACKTRACE_CURRENT_THREAD, false, ReadyMaxBacktrace, VerifyMaxDump);
+  VerifyProcTest(pid, BACKTRACE_CURRENT_THREAD, ReadyMaxBacktrace, VerifyMaxDump, Backtrace::Create,
+                 BacktraceMap::Create);
 
   kill(pid, SIGKILL);
   int status;
   ASSERT_EQ(waitpid(pid, &status, 0), pid);
 }
 
-static void VerifyProcessIgnoreFrames(Backtrace* bt_all) {
-  std::unique_ptr<Backtrace> ign1(Backtrace::Create(bt_all->Pid(), BACKTRACE_CURRENT_THREAD));
+static void VerifyProcessIgnoreFrames(Backtrace* bt_all, create_func_t create_func,
+                                      map_create_func_t map_create_func) {
+  std::unique_ptr<BacktraceMap> map(map_create_func(bt_all->Pid(), false));
+  std::unique_ptr<Backtrace> ign1(create_func(bt_all->Pid(), BACKTRACE_CURRENT_THREAD, map.get()));
   ASSERT_TRUE(ign1.get() != nullptr);
   ASSERT_TRUE(ign1->Unwind(1));
-  ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, ign1->GetError());
+  VERIFY_NO_ERROR(ign1->GetError().error_code);
 
-  std::unique_ptr<Backtrace> ign2(Backtrace::Create(bt_all->Pid(), BACKTRACE_CURRENT_THREAD));
+  std::unique_ptr<Backtrace> ign2(create_func(bt_all->Pid(), BACKTRACE_CURRENT_THREAD, map.get()));
   ASSERT_TRUE(ign2.get() != nullptr);
   ASSERT_TRUE(ign2->Unwind(2));
-  ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, ign2->GetError());
+  VERIFY_NO_ERROR(ign2->GetError().error_code);
 
   VerifyIgnoreFrames(bt_all, ign1.get(), ign2.get(), nullptr);
 }
@@ -403,7 +452,8 @@
     ASSERT_NE(test_level_one(1, 2, 3, 4, nullptr, nullptr), 0);
     _exit(1);
   }
-  VerifyProcTest(pid, BACKTRACE_CURRENT_THREAD, false, ReadyLevelBacktrace, VerifyProcessIgnoreFrames);
+  VerifyProcTest(pid, BACKTRACE_CURRENT_THREAD, ReadyLevelBacktrace, VerifyProcessIgnoreFrames,
+                 Backtrace::Create, BacktraceMap::Create);
 
   kill(pid, SIGKILL);
   int status;
@@ -466,17 +516,18 @@
     if (pid == *it) {
       continue;
     }
-    VerifyProcTest(pid, *it, false, ReadyLevelBacktrace, VerifyLevelDump);
+    VerifyProcTest(pid, *it, ReadyLevelBacktrace, VerifyLevelDump, Backtrace::Create,
+                   BacktraceMap::Create);
   }
 
   FinishRemoteProcess(pid);
 }
 
 void VerifyLevelThread(void*) {
-  std::unique_ptr<Backtrace> backtrace(Backtrace::Create(getpid(), gettid()));
+  std::unique_ptr<Backtrace> backtrace(Backtrace::Create(getpid(), android::base::GetThreadId()));
   ASSERT_TRUE(backtrace.get() != nullptr);
   ASSERT_TRUE(backtrace->Unwind(0));
-  ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, backtrace->GetError());
+  VERIFY_NO_ERROR(backtrace->GetError().error_code);
 
   VerifyLevelDump(backtrace.get());
 }
@@ -486,10 +537,10 @@
 }
 
 static void VerifyMaxThread(void*) {
-  std::unique_ptr<Backtrace> backtrace(Backtrace::Create(getpid(), gettid()));
+  std::unique_ptr<Backtrace> backtrace(Backtrace::Create(getpid(), android::base::GetThreadId()));
   ASSERT_TRUE(backtrace.get() != nullptr);
   ASSERT_TRUE(backtrace->Unwind(0));
-  ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, backtrace->GetError());
+  ASSERT_EQ(BACKTRACE_UNWIND_ERROR_EXCEED_MAX_FRAMES_LIMIT, backtrace->GetError().error_code);
 
   VerifyMaxDump(backtrace.get());
 }
@@ -501,7 +552,7 @@
 static void* ThreadLevelRun(void* data) {
   thread_t* thread = reinterpret_cast<thread_t*>(data);
 
-  thread->tid = gettid();
+  thread->tid = android::base::GetThreadId();
   EXPECT_NE(test_level_one(1, 2, 3, 4, ThreadSetState, data), 0);
   return nullptr;
 }
@@ -531,7 +582,7 @@
   std::unique_ptr<Backtrace> backtrace(Backtrace::Create(getpid(), thread_data.tid));
   ASSERT_TRUE(backtrace.get() != nullptr);
   ASSERT_TRUE(backtrace->Unwind(0));
-  ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, backtrace->GetError());
+  VERIFY_NO_ERROR(backtrace->GetError().error_code);
 
   VerifyLevelDump(backtrace.get());
 
@@ -571,17 +622,17 @@
   std::unique_ptr<Backtrace> all(Backtrace::Create(getpid(), thread_data.tid));
   ASSERT_TRUE(all.get() != nullptr);
   ASSERT_TRUE(all->Unwind(0));
-  ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, all->GetError());
+  VERIFY_NO_ERROR(all->GetError().error_code);
 
   std::unique_ptr<Backtrace> ign1(Backtrace::Create(getpid(), thread_data.tid));
   ASSERT_TRUE(ign1.get() != nullptr);
   ASSERT_TRUE(ign1->Unwind(1));
-  ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, ign1->GetError());
+  VERIFY_NO_ERROR(ign1->GetError().error_code);
 
   std::unique_ptr<Backtrace> ign2(Backtrace::Create(getpid(), thread_data.tid));
   ASSERT_TRUE(ign2.get() != nullptr);
   ASSERT_TRUE(ign2->Unwind(2));
-  ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, ign2->GetError());
+  VERIFY_NO_ERROR(ign2->GetError().error_code);
 
   VerifyIgnoreFrames(all.get(), ign1.get(), ign2.get(), nullptr);
 
@@ -592,7 +643,7 @@
 static void* ThreadMaxRun(void* data) {
   thread_t* thread = reinterpret_cast<thread_t*>(data);
 
-  thread->tid = gettid();
+  thread->tid = android::base::GetThreadId();
   EXPECT_NE(test_recursive_call(MAX_BACKTRACE_FRAMES+10, ThreadSetState, data), 0);
   return nullptr;
 }
@@ -612,7 +663,7 @@
   std::unique_ptr<Backtrace> backtrace(Backtrace::Create(getpid(), thread_data.tid));
   ASSERT_TRUE(backtrace.get() != nullptr);
   ASSERT_TRUE(backtrace->Unwind(0));
-  ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, backtrace->GetError());
+  ASSERT_EQ(BACKTRACE_UNWIND_ERROR_EXCEED_MAX_FRAMES_LIMIT, backtrace->GetError().error_code);
 
   VerifyMaxDump(backtrace.get());
 
@@ -629,7 +680,7 @@
   }
 
   // The status of the actual unwind will be checked elsewhere.
-  dump->backtrace = Backtrace::Create(getpid(), dump->thread.tid);
+  dump->backtrace = Backtrace::Create(getpid(), dump->thread.tid, dump->map);
   dump->backtrace->Unwind(0);
 
   android_atomic_acquire_store(1, &dump->done);
@@ -637,8 +688,8 @@
   return nullptr;
 }
 
-TEST(libbacktrace, thread_multiple_dump) {
-  // Dump NUM_THREADS simultaneously.
+static void MultipleThreadDumpTest(bool share_map) {
+  // Dump NUM_THREADS simultaneously using the same map.
   std::vector<thread_t> runners(NUM_THREADS);
   std::vector<dump_thread_t> dumpers(NUM_THREADS);
 
@@ -659,12 +710,17 @@
 
   // Start all of the dumpers at once, they will spin until they are signalled
   // to begin their dump run.
+  std::unique_ptr<BacktraceMap> map;
+  if (share_map) {
+    map.reset(BacktraceMap::Create(getpid()));
+  }
   int32_t dump_now = 0;
   for (size_t i = 0; i < NUM_THREADS; i++) {
     dumpers[i].thread.tid = runners[i].tid;
     dumpers[i].thread.state = 0;
     dumpers[i].done = 0;
     dumpers[i].now = &dump_now;
+    dumpers[i].map = map.get();
 
     ASSERT_TRUE(pthread_create(&dumpers[i].thread.threadId, &attr, ThreadDump, &dumpers[i]) == 0);
   }
@@ -686,47 +742,12 @@
   }
 }
 
-TEST(libbacktrace, thread_multiple_dump_same_thread) {
-  pthread_attr_t attr;
-  pthread_attr_init(&attr);
-  pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
-  thread_t runner;
-  runner.tid = 0;
-  runner.state = 0;
-  ASSERT_TRUE(pthread_create(&runner.threadId, &attr, ThreadMaxRun, &runner) == 0);
+TEST(libbacktrace, thread_multiple_dump) {
+  MultipleThreadDumpTest(false);
+}
 
-  // Wait for tids to be set.
-  ASSERT_TRUE(WaitForNonZero(&runner.state, 30));
-
-  // Start all of the dumpers at once, they will spin until they are signalled
-  // to begin their dump run.
-  int32_t dump_now = 0;
-  // Dump the same thread NUM_THREADS simultaneously.
-  std::vector<dump_thread_t> dumpers(NUM_THREADS);
-  for (size_t i = 0; i < NUM_THREADS; i++) {
-    dumpers[i].thread.tid = runner.tid;
-    dumpers[i].thread.state = 0;
-    dumpers[i].done = 0;
-    dumpers[i].now = &dump_now;
-
-    ASSERT_TRUE(pthread_create(&dumpers[i].thread.threadId, &attr, ThreadDump, &dumpers[i]) == 0);
-  }
-
-  // Start all of the dumpers going at once.
-  android_atomic_acquire_store(1, &dump_now);
-
-  for (size_t i = 0; i < NUM_THREADS; i++) {
-    ASSERT_TRUE(WaitForNonZero(&dumpers[i].done, 30));
-
-    ASSERT_TRUE(dumpers[i].backtrace != nullptr);
-    VerifyMaxDump(dumpers[i].backtrace);
-
-    delete dumpers[i].backtrace;
-    dumpers[i].backtrace = nullptr;
-  }
-
-  // Tell the runner thread to exit its infinite loop.
-  android_atomic_acquire_store(0, &runner.state);
+TEST(libbacktrace, thread_multiple_dump_same_map) {
+  MultipleThreadDumpTest(true);
 }
 
 // This test is for UnwindMaps that should share the same map cursor when
@@ -739,21 +760,21 @@
   Backtrace* back1 = Backtrace::Create(getpid(), BACKTRACE_CURRENT_THREAD, map1);
   ASSERT_TRUE(back1 != nullptr);
   EXPECT_TRUE(back1->Unwind(0));
-  ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, back1->GetError());
+  VERIFY_NO_ERROR(back1->GetError().error_code);
   delete back1;
   delete map1;
 
   Backtrace* back2 = Backtrace::Create(getpid(), BACKTRACE_CURRENT_THREAD, map2);
   ASSERT_TRUE(back2 != nullptr);
   EXPECT_TRUE(back2->Unwind(0));
-  ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, back2->GetError());
+  VERIFY_NO_ERROR(back2->GetError().error_code);
   delete back2;
   delete map2;
 
   Backtrace* back3 = Backtrace::Create(getpid(), BACKTRACE_CURRENT_THREAD, map3);
   ASSERT_TRUE(back3 != nullptr);
   EXPECT_TRUE(back3->Unwind(0));
-  ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, back3->GetError());
+  VERIFY_NO_ERROR(back3->GetError().error_code);
   delete back3;
   delete map3;
 }
@@ -771,8 +792,8 @@
   delete back_map;
 
   ASSERT_FALSE(BacktraceMap::IsValid(map));
-  ASSERT_EQ(static_cast<uintptr_t>(0), map.start);
-  ASSERT_EQ(static_cast<uintptr_t>(0), map.end);
+  ASSERT_EQ(static_cast<uint64_t>(0), map.start);
+  ASSERT_EQ(static_cast<uint64_t>(0), map.end);
   ASSERT_EQ(0, map.flags);
   ASSERT_EQ("", map.name);
 }
@@ -784,6 +805,7 @@
   backtrace_frame_data_t frame;
   frame.num = 1;
   frame.pc = 2;
+  frame.rel_pc = 2;
   frame.sp = 0;
   frame.stack_size = 0;
   frame.func_offset = 0;
@@ -799,9 +821,10 @@
 
   // Check map name empty, but exists.
   frame.pc = 0xb0020;
+  frame.rel_pc = 0x20;
   frame.map.start = 0xb0000;
   frame.map.end = 0xbffff;
-  frame.map.load_base = 0;
+  frame.map.load_bias = 0;
 #if defined(__LP64__)
   EXPECT_EQ("#01 pc 0000000000000020  <anonymous:00000000000b0000>",
 #else
@@ -813,7 +836,7 @@
   frame.pc = 0xc0020;
   frame.map.start = 0xc0000;
   frame.map.end = 0xcffff;
-  frame.map.load_base = 0;
+  frame.map.load_bias = 0;
   frame.map.name = "[anon:thread signal stack]";
 #if defined(__LP64__)
   EXPECT_EQ("#01 pc 0000000000000020  [anon:thread signal stack:00000000000c0000]",
@@ -824,6 +847,7 @@
 
   // Check relative pc is set and map name is set.
   frame.pc = 0x12345679;
+  frame.rel_pc = 0x12345678;
   frame.map.name = "MapFake";
   frame.map.start =  1;
   frame.map.end =  1;
@@ -852,9 +876,10 @@
 #endif
             backtrace->FormatFrameData(&frame));
 
-  // Check func_name is set, func offset is non-zero, and load_base is non-zero.
+  // Check func_name is set, func offset is non-zero, and load_bias is non-zero.
+  frame.rel_pc = 0x123456dc;
   frame.func_offset = 645;
-  frame.map.load_base = 100;
+  frame.map.load_bias = 100;
 #if defined(__LP64__)
   EXPECT_EQ("#01 pc 00000000123456dc  MapFake (ProcFake+645)",
 #else
@@ -873,12 +898,40 @@
 }
 
 struct map_test_t {
-  uintptr_t start;
-  uintptr_t end;
+  uint64_t start;
+  uint64_t end;
 };
 
 static bool map_sort(map_test_t i, map_test_t j) { return i.start < j.start; }
 
+static std::string GetTestMapsAsString(const std::vector<map_test_t>& maps) {
+  if (maps.size() == 0) {
+    return "No test map entries\n";
+  }
+  std::string map_txt;
+  for (auto map : maps) {
+    map_txt += android::base::StringPrintf("%" PRIx64 "-%" PRIx64 "\n", map.start, map.end);
+  }
+  return map_txt;
+}
+
+static std::string GetMapsAsString(BacktraceMap* maps) {
+  if (maps->size() == 0) {
+    return "No map entries\n";
+  }
+  std::string map_txt;
+  for (const backtrace_map_t* map : *maps) {
+    map_txt += android::base::StringPrintf(
+        "%" PRIx64 "-%" PRIx64 " flags: 0x%x offset: 0x%" PRIx64 " load_bias: 0x%" PRIx64,
+        map->start, map->end, map->flags, map->offset, map->load_bias);
+    if (!map->name.empty()) {
+      map_txt += ' ' + map->name;
+    }
+    map_txt += '\n';
+  }
+  return map_txt;
+}
+
 static void VerifyMap(pid_t pid) {
   char buffer[4096];
   snprintf(buffer, sizeof(buffer), "/proc/%d/maps", pid);
@@ -888,7 +941,7 @@
   std::vector<map_test_t> test_maps;
   while (fgets(buffer, sizeof(buffer), map_file)) {
     map_test_t map;
-    ASSERT_EQ(2, sscanf(buffer, "%" SCNxPTR "-%" SCNxPTR " ", &map.start, &map.end));
+    ASSERT_EQ(2, sscanf(buffer, "%" SCNx64 "-%" SCNx64 " ", &map.start, &map.end));
     test_maps.push_back(map);
   }
   fclose(map_file);
@@ -897,12 +950,20 @@
   std::unique_ptr<BacktraceMap> map(BacktraceMap::Create(pid));
 
   // Basic test that verifies that the map is in the expected order.
-  ScopedBacktraceMapIteratorLock lock(map.get());
-  std::vector<map_test_t>::const_iterator test_it = test_maps.begin();
-  for (BacktraceMap::const_iterator it = map->begin(); it != map->end(); ++it) {
-    ASSERT_TRUE(test_it != test_maps.end());
-    ASSERT_EQ(test_it->start, it->start);
-    ASSERT_EQ(test_it->end, it->end);
+  auto test_it = test_maps.begin();
+  for (auto it = map->begin(); it != map->end(); ++it) {
+    ASSERT_TRUE(test_it != test_maps.end()) << "Mismatch in number of maps, expected test maps:\n"
+                                            << GetTestMapsAsString(test_maps) << "Actual maps:\n"
+                                            << GetMapsAsString(map.get());
+    ASSERT_EQ(test_it->start, (*it)->start) << "Mismatch in map data, expected test maps:\n"
+                                            << GetTestMapsAsString(test_maps) << "Actual maps:\n"
+                                            << GetMapsAsString(map.get());
+    ASSERT_EQ(test_it->end, (*it)->end) << "Mismatch maps in map data, expected test maps:\n"
+                                        << GetTestMapsAsString(test_maps) << "Actual maps:\n"
+                                        << GetMapsAsString(map.get());
+    // Make sure the load bias get set to a value.
+    ASSERT_NE(static_cast<uint64_t>(-1), (*it)->load_bias) << "Found uninitialized load_bias\n"
+                                                           << GetMapsAsString(map.get());
     ++test_it;
   }
   ASSERT_TRUE(test_it == test_maps.end());
@@ -932,7 +993,7 @@
 static void* ThreadReadTest(void* data) {
   thread_t* thread_data = reinterpret_cast<thread_t*>(data);
 
-  thread_data->tid = gettid();
+  thread_data->tid = android::base::GetThreadId();
 
   // Create two map pages.
   // Mark the second page as not-readable.
@@ -970,14 +1031,14 @@
   return nullptr;
 }
 
-static void RunReadTest(Backtrace* backtrace, uintptr_t read_addr) {
+static void RunReadTest(Backtrace* backtrace, uint64_t read_addr) {
   size_t pagesize = static_cast<size_t>(sysconf(_SC_PAGE_SIZE));
 
   // Create a page of data to use to do quick compares.
   uint8_t* expected = new uint8_t[pagesize];
   InitMemory(expected, pagesize);
 
-  uint8_t* data = new uint8_t[2*pagesize];
+  uint8_t* data = new uint8_t[2 * pagesize];
   // Verify that we can only read one page worth of data.
   size_t bytes_read = backtrace->Read(read_addr, data, 2 * pagesize);
   ASSERT_EQ(pagesize, bytes_read);
@@ -1021,15 +1082,17 @@
   std::unique_ptr<Backtrace> backtrace(Backtrace::Create(getpid(), thread_data.tid));
   ASSERT_TRUE(backtrace.get() != nullptr);
 
-  RunReadTest(backtrace.get(), reinterpret_cast<uintptr_t>(thread_data.data));
+  RunReadTest(backtrace.get(), reinterpret_cast<uint64_t>(thread_data.data));
 
   android_atomic_acquire_store(0, &thread_data.state);
 
   ASSERT_TRUE(WaitForNonZero(&thread_data.state, 10));
 }
 
-volatile uintptr_t g_ready = 0;
-volatile uintptr_t g_addr = 0;
+// The code requires these variables are the same size.
+volatile uint64_t g_ready = 0;
+volatile uint64_t g_addr = 0;
+static_assert(sizeof(g_ready) == sizeof(g_addr), "g_ready/g_addr must be same size");
 
 static void ForkedReadTest() {
   // Create two map pages.
@@ -1049,7 +1112,7 @@
   // Set up a simple pattern in memory.
   InitMemory(memory, pagesize);
 
-  g_addr = reinterpret_cast<uintptr_t>(memory);
+  g_addr = reinterpret_cast<uint64_t>(memory);
   g_ready = 1;
 
   while (1) {
@@ -1075,17 +1138,15 @@
       std::unique_ptr<Backtrace> backtrace(Backtrace::Create(pid, pid));
       ASSERT_TRUE(backtrace.get() != nullptr);
 
-      uintptr_t read_addr;
-      size_t bytes_read = backtrace->Read(reinterpret_cast<uintptr_t>(&g_ready),
-                                          reinterpret_cast<uint8_t*>(&read_addr),
-                                          sizeof(uintptr_t));
-      ASSERT_EQ(sizeof(uintptr_t), bytes_read);
+      uint64_t read_addr;
+      size_t bytes_read = backtrace->Read(reinterpret_cast<uint64_t>(&g_ready),
+                                          reinterpret_cast<uint8_t*>(&read_addr), sizeof(g_ready));
+      ASSERT_EQ(sizeof(g_ready), bytes_read);
       if (read_addr) {
         // The forked process is ready to be read.
-        bytes_read = backtrace->Read(reinterpret_cast<uintptr_t>(&g_addr),
-                                     reinterpret_cast<uint8_t*>(&read_addr),
-                                     sizeof(uintptr_t));
-        ASSERT_EQ(sizeof(uintptr_t), bytes_read);
+        bytes_read = backtrace->Read(reinterpret_cast<uint64_t>(&g_addr),
+                                     reinterpret_cast<uint8_t*>(&read_addr), sizeof(g_addr));
+        ASSERT_EQ(sizeof(g_addr), bytes_read);
 
         RunReadTest(backtrace.get(), read_addr);
 
@@ -1125,49 +1186,45 @@
   ASSERT_TRUE(expected_functions.empty()) << "Not all functions found in shared library.";
 }
 
-static const char* CopySharedLibrary() {
-#if defined(__LP64__)
-  const char* lib_name = "lib64";
-#else
-  const char* lib_name = "lib";
-#endif
+static void CopySharedLibrary(const char* tmp_dir, std::string* tmp_so_name) {
+  std::string system_dir;
 
 #if defined(__BIONIC__)
-  const char* tmp_so_name = "/data/local/tmp/libbacktrace_test.so";
-  std::string cp_cmd = android::base::StringPrintf("cp /system/%s/libbacktrace_test.so %s",
-                                                   lib_name, tmp_so_name);
+  system_dir = "/system/lib";
 #else
-  const char* tmp_so_name = "/tmp/libbacktrace_test.so";
-  if (getenv("ANDROID_HOST_OUT") == NULL) {
-    fprintf(stderr, "ANDROID_HOST_OUT not set, make sure you run lunch.");
-    return nullptr;
-  }
-  std::string cp_cmd = android::base::StringPrintf("cp %s/%s/libbacktrace_test.so %s",
-                                                   getenv("ANDROID_HOST_OUT"), lib_name,
-                                                   tmp_so_name);
+  const char* host_out_env = getenv("ANDROID_HOST_OUT");
+  ASSERT_TRUE(host_out_env != nullptr);
+  system_dir = std::string(host_out_env) + "/lib";
 #endif
 
-  // Copy the shared so to a tempory directory.
-  system(cp_cmd.c_str());
+#if defined(__LP64__)
+  system_dir += "64";
+#endif
 
-  return tmp_so_name;
+  *tmp_so_name = std::string(tmp_dir) + "/libbacktrace_test.so";
+  std::string cp_cmd =
+      android::base::StringPrintf("cp %s/libbacktrace_test.so %s", system_dir.c_str(), tmp_dir);
+
+  // Copy the shared so to a tempory directory.
+  ASSERT_EQ(0, system(cp_cmd.c_str()));
 }
 
 TEST(libbacktrace, check_unreadable_elf_local) {
-  const char* tmp_so_name = CopySharedLibrary();
-  ASSERT_TRUE(tmp_so_name != nullptr);
+  TemporaryDir td;
+  std::string tmp_so_name;
+  ASSERT_NO_FATAL_FAILURE(CopySharedLibrary(td.path, &tmp_so_name));
 
   struct stat buf;
-  ASSERT_TRUE(stat(tmp_so_name, &buf) != -1);
-  uintptr_t map_size = buf.st_size;
+  ASSERT_TRUE(stat(tmp_so_name.c_str(), &buf) != -1);
+  uint64_t map_size = buf.st_size;
 
-  int fd = open(tmp_so_name, O_RDONLY);
+  int fd = open(tmp_so_name.c_str(), O_RDONLY);
   ASSERT_TRUE(fd != -1);
 
-  void* map = mmap(NULL, map_size, PROT_READ | PROT_EXEC, MAP_PRIVATE, fd, 0);
+  void* map = mmap(nullptr, map_size, PROT_READ | PROT_EXEC, MAP_PRIVATE, fd, 0);
   ASSERT_TRUE(map != MAP_FAILED);
   close(fd);
-  ASSERT_TRUE(unlink(tmp_so_name) != -1);
+  ASSERT_TRUE(unlink(tmp_so_name.c_str()) != -1);
 
   std::vector<std::string> found_functions;
   std::unique_ptr<Backtrace> backtrace(Backtrace::Create(BACKTRACE_CURRENT_PROCESS,
@@ -1178,11 +1235,10 @@
   backtrace->Unwind(0);
 
   // Loop through the entire map, and get every function we can find.
-  map_size += reinterpret_cast<uintptr_t>(map);
+  map_size += reinterpret_cast<uint64_t>(map);
   std::string last_func;
-  for (uintptr_t read_addr = reinterpret_cast<uintptr_t>(map);
-       read_addr < map_size; read_addr += 4) {
-    uintptr_t offset;
+  for (uint64_t read_addr = reinterpret_cast<uint64_t>(map); read_addr < map_size; read_addr += 4) {
+    uint64_t offset;
     std::string func_name = backtrace->GetFunctionName(read_addr, &offset);
     if (!func_name.empty() && last_func != func_name) {
       found_functions.push_back(func_name);
@@ -1190,43 +1246,44 @@
     last_func = func_name;
   }
 
-  ASSERT_TRUE(munmap(map, map_size - reinterpret_cast<uintptr_t>(map)) == 0);
+  ASSERT_TRUE(munmap(map, map_size - reinterpret_cast<uint64_t>(map)) == 0);
 
   VerifyFunctionsFound(found_functions);
 }
 
 TEST(libbacktrace, check_unreadable_elf_remote) {
-  const char* tmp_so_name = CopySharedLibrary();
-  ASSERT_TRUE(tmp_so_name != nullptr);
+  TemporaryDir td;
+  std::string tmp_so_name;
+  ASSERT_NO_FATAL_FAILURE(CopySharedLibrary(td.path, &tmp_so_name));
 
   g_ready = 0;
 
   struct stat buf;
-  ASSERT_TRUE(stat(tmp_so_name, &buf) != -1);
-  uintptr_t map_size = buf.st_size;
+  ASSERT_TRUE(stat(tmp_so_name.c_str(), &buf) != -1);
+  uint64_t map_size = buf.st_size;
 
   pid_t pid;
   if ((pid = fork()) == 0) {
-    int fd = open(tmp_so_name, O_RDONLY);
+    int fd = open(tmp_so_name.c_str(), O_RDONLY);
     if (fd == -1) {
-      fprintf(stderr, "Failed to open file %s: %s\n", tmp_so_name, strerror(errno));
-      unlink(tmp_so_name);
+      fprintf(stderr, "Failed to open file %s: %s\n", tmp_so_name.c_str(), strerror(errno));
+      unlink(tmp_so_name.c_str());
       exit(0);
     }
 
-    void* map = mmap(NULL, map_size, PROT_READ | PROT_EXEC, MAP_PRIVATE, fd, 0);
+    void* map = mmap(nullptr, map_size, PROT_READ | PROT_EXEC, MAP_PRIVATE, fd, 0);
     if (map == MAP_FAILED) {
       fprintf(stderr, "Failed to map in memory: %s\n", strerror(errno));
-      unlink(tmp_so_name);
+      unlink(tmp_so_name.c_str());
       exit(0);
     }
     close(fd);
-    if (unlink(tmp_so_name) == -1) {
+    if (unlink(tmp_so_name.c_str()) == -1) {
       fprintf(stderr, "Failed to unlink: %s\n", strerror(errno));
       exit(0);
     }
 
-    g_addr = reinterpret_cast<uintptr_t>(map);
+    g_addr = reinterpret_cast<uint64_t>(map);
     g_ready = 1;
     while (true) {
       usleep(US_PER_MSEC);
@@ -1246,10 +1303,14 @@
     std::unique_ptr<Backtrace> backtrace(Backtrace::Create(pid, BACKTRACE_CURRENT_THREAD));
     ASSERT_TRUE(backtrace.get() != nullptr);
 
-    uintptr_t read_addr;
-    ASSERT_EQ(sizeof(uintptr_t), backtrace->Read(reinterpret_cast<uintptr_t>(&g_ready), reinterpret_cast<uint8_t*>(&read_addr), sizeof(uintptr_t)));
+    uint64_t read_addr;
+    ASSERT_EQ(sizeof(g_ready),
+              backtrace->Read(reinterpret_cast<uint64_t>(&g_ready),
+                              reinterpret_cast<uint8_t*>(&read_addr), sizeof(g_ready)));
     if (read_addr) {
-      ASSERT_EQ(sizeof(uintptr_t), backtrace->Read(reinterpret_cast<uintptr_t>(&g_addr), reinterpret_cast<uint8_t*>(&read_addr), sizeof(uintptr_t)));
+      ASSERT_EQ(sizeof(g_addr),
+                backtrace->Read(reinterpret_cast<uint64_t>(&g_addr),
+                                reinterpret_cast<uint8_t*>(&read_addr), sizeof(uint64_t)));
 
       // Needed before GetFunctionName will work.
       backtrace->Unwind(0);
@@ -1258,7 +1319,7 @@
       map_size += read_addr;
       std::string last_func;
       for (; read_addr < map_size; read_addr += 4) {
-        uintptr_t offset;
+        uint64_t offset;
         std::string func_name = backtrace->GetFunctionName(read_addr, &offset);
         if (!func_name.empty() && last_func != func_name) {
           found_functions.push_back(func_name);
@@ -1281,7 +1342,7 @@
   VerifyFunctionsFound(found_functions);
 }
 
-static bool FindFuncFrameInBacktrace(Backtrace* backtrace, uintptr_t test_func, size_t* frame_num) {
+static bool FindFuncFrameInBacktrace(Backtrace* backtrace, uint64_t test_func, size_t* frame_num) {
   backtrace_map_t map;
   backtrace->FillInMap(test_func, &map);
   if (!BacktraceMap::IsValid(map)) {
@@ -1300,7 +1361,7 @@
   return false;
 }
 
-static void VerifyUnreadableElfFrame(Backtrace* backtrace, uintptr_t test_func, size_t frame_num) {
+static void VerifyUnreadableElfFrame(Backtrace* backtrace, uint64_t test_func, size_t frame_num) {
   ASSERT_LT(backtrace->NumFrames(), static_cast<size_t>(MAX_BACKTRACE_FRAMES))
     << DumpFrames(backtrace);
 
@@ -1308,48 +1369,54 @@
   // Make sure that there is at least one more frame above the test func call.
   ASSERT_LT(frame_num, backtrace->NumFrames()) << DumpFrames(backtrace);
 
-  uintptr_t diff = backtrace->GetFrame(frame_num)->pc - test_func;
+  uint64_t diff = backtrace->GetFrame(frame_num)->pc - test_func;
   ASSERT_LT(diff, 200U) << DumpFrames(backtrace);
 }
 
-static void VerifyUnreadableElfBacktrace(uintptr_t test_func) {
+static void VerifyUnreadableElfBacktrace(void* func) {
   std::unique_ptr<Backtrace> backtrace(Backtrace::Create(BACKTRACE_CURRENT_PROCESS,
                                                          BACKTRACE_CURRENT_THREAD));
   ASSERT_TRUE(backtrace.get() != nullptr);
   ASSERT_TRUE(backtrace->Unwind(0));
-  ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, backtrace->GetError());
+  VERIFY_NO_ERROR(backtrace->GetError().error_code);
 
   size_t frame_num;
-  ASSERT_TRUE(FindFuncFrameInBacktrace(backtrace.get(), test_func, &frame_num));
+  uint64_t test_func = reinterpret_cast<uint64_t>(func);
+  ASSERT_TRUE(FindFuncFrameInBacktrace(backtrace.get(), test_func, &frame_num))
+      << DumpFrames(backtrace.get());
 
   VerifyUnreadableElfFrame(backtrace.get(), test_func, frame_num);
 }
 
-typedef int (*test_func_t)(int, int, int, int, void (*)(uintptr_t), uintptr_t);
+typedef int (*test_func_t)(int, int, int, int, void (*)(void*), void*);
 
 TEST(libbacktrace, unwind_through_unreadable_elf_local) {
-  const char* tmp_so_name = CopySharedLibrary();
-  ASSERT_TRUE(tmp_so_name != nullptr);
-  void* lib_handle = dlopen(tmp_so_name, RTLD_NOW);
+  TemporaryDir td;
+  std::string tmp_so_name;
+  ASSERT_NO_FATAL_FAILURE(CopySharedLibrary(td.path, &tmp_so_name));
+
+  void* lib_handle = dlopen(tmp_so_name.c_str(), RTLD_NOW);
   ASSERT_TRUE(lib_handle != nullptr);
-  ASSERT_TRUE(unlink(tmp_so_name) != -1);
+  ASSERT_TRUE(unlink(tmp_so_name.c_str()) != -1);
 
   test_func_t test_func;
   test_func = reinterpret_cast<test_func_t>(dlsym(lib_handle, "test_level_one"));
   ASSERT_TRUE(test_func != nullptr);
 
-  ASSERT_NE(test_func(1, 2, 3, 4, VerifyUnreadableElfBacktrace,
-                      reinterpret_cast<uintptr_t>(test_func)), 0);
+  ASSERT_NE(test_func(1, 2, 3, 4, VerifyUnreadableElfBacktrace, reinterpret_cast<void*>(test_func)),
+            0);
 
   ASSERT_TRUE(dlclose(lib_handle) == 0);
 }
 
 TEST(libbacktrace, unwind_through_unreadable_elf_remote) {
-  const char* tmp_so_name = CopySharedLibrary();
-  ASSERT_TRUE(tmp_so_name != nullptr);
-  void* lib_handle = dlopen(tmp_so_name, RTLD_NOW);
+  TemporaryDir td;
+  std::string tmp_so_name;
+  ASSERT_NO_FATAL_FAILURE(CopySharedLibrary(td.path, &tmp_so_name));
+
+  void* lib_handle = dlopen(tmp_so_name.c_str(), RTLD_NOW);
   ASSERT_TRUE(lib_handle != nullptr);
-  ASSERT_TRUE(unlink(tmp_so_name) != -1);
+  ASSERT_TRUE(unlink(tmp_so_name.c_str()) != -1);
 
   test_func_t test_func;
   test_func = reinterpret_cast<test_func_t>(dlsym(lib_handle, "test_level_one"));
@@ -1374,13 +1441,13 @@
     std::unique_ptr<Backtrace> backtrace(Backtrace::Create(pid, BACKTRACE_CURRENT_THREAD));
     ASSERT_TRUE(backtrace.get() != nullptr);
     ASSERT_TRUE(backtrace->Unwind(0));
-    ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, backtrace->GetError());
+    VERIFY_NO_ERROR(backtrace->GetError().error_code);
 
     size_t frame_num;
-    if (FindFuncFrameInBacktrace(backtrace.get(),
-                                 reinterpret_cast<uintptr_t>(test_func), &frame_num)) {
-
-      VerifyUnreadableElfFrame(backtrace.get(), reinterpret_cast<uintptr_t>(test_func), frame_num);
+    if (FindFuncFrameInBacktrace(backtrace.get(), reinterpret_cast<uint64_t>(test_func),
+                                 &frame_num) &&
+        frame_num != 0) {
+      VerifyUnreadableElfFrame(backtrace.get(), reinterpret_cast<uint64_t>(test_func), frame_num);
       done = true;
     }
 
@@ -1403,7 +1470,7 @@
       Backtrace::Create(BACKTRACE_CURRENT_PROCESS, 99999999));
   ASSERT_TRUE(backtrace.get() != nullptr);
   ASSERT_FALSE(backtrace->Unwind(0));
-  ASSERT_EQ(BACKTRACE_UNWIND_ERROR_THREAD_DOESNT_EXIST, backtrace->GetError());
+  ASSERT_EQ(BACKTRACE_UNWIND_ERROR_THREAD_DOESNT_EXIST, backtrace->GetError().error_code);
 }
 
 TEST(libbacktrace, local_get_function_name_before_unwind) {
@@ -1412,8 +1479,8 @@
   ASSERT_TRUE(backtrace.get() != nullptr);
 
   // Verify that trying to get a function name before doing an unwind works.
-  uintptr_t cur_func_offset = reinterpret_cast<uintptr_t>(&test_level_one) + 1;
-  size_t offset;
+  uint64_t cur_func_offset = reinterpret_cast<uint64_t>(&test_level_one) + 1;
+  uint64_t offset;
   ASSERT_NE(std::string(""), backtrace->GetFunctionName(cur_func_offset, &offset));
 }
 
@@ -1425,14 +1492,14 @@
   std::unique_ptr<Backtrace> backtrace(Backtrace::Create(pid, pid));
 
   // Verify that trying to get a function name before doing an unwind works.
-  uintptr_t cur_func_offset = reinterpret_cast<uintptr_t>(&test_level_one) + 1;
-  size_t offset;
+  uint64_t cur_func_offset = reinterpret_cast<uint64_t>(&test_level_one) + 1;
+  uint64_t offset;
   ASSERT_NE(std::string(""), backtrace->GetFunctionName(cur_func_offset, &offset));
 
   FinishRemoteProcess(pid);
 }
 
-static void SetUcontextSp(uintptr_t sp, ucontext_t* ucontext) {
+static void SetUcontextSp(uint64_t sp, ucontext_t* ucontext) {
 #if defined(__arm__)
   ucontext->uc_mcontext.arm_sp = sp;
 #elif defined(__aarch64__)
@@ -1448,7 +1515,7 @@
 #endif
 }
 
-static void SetUcontextPc(uintptr_t pc, ucontext_t* ucontext) {
+static void SetUcontextPc(uint64_t pc, ucontext_t* ucontext) {
 #if defined(__arm__)
   ucontext->uc_mcontext.arm_pc = pc;
 #elif defined(__aarch64__)
@@ -1464,7 +1531,7 @@
 #endif
 }
 
-static void SetUcontextLr(uintptr_t lr, ucontext_t* ucontext) {
+static void SetUcontextLr(uint64_t lr, ucontext_t* ucontext) {
 #if defined(__arm__)
   ucontext->uc_mcontext.arm_lr = lr;
 #elif defined(__aarch64__)
@@ -1499,7 +1566,7 @@
 }
 
 static void UnwindFromDevice(Backtrace* backtrace, void* device_map) {
-  uintptr_t device_map_uint = reinterpret_cast<uintptr_t>(device_map);
+  uint64_t device_map_uint = reinterpret_cast<uint64_t>(device_map);
 
   backtrace_map_t map;
   backtrace->FillInMap(device_map_uint, &map);
@@ -1507,12 +1574,12 @@
   ASSERT_EQ(PROT_DEVICE_MAP, map.flags & PROT_DEVICE_MAP);
 
   // Quick sanity checks.
-  size_t offset;
+  uint64_t offset;
   ASSERT_EQ(std::string(""), backtrace->GetFunctionName(device_map_uint, &offset));
   ASSERT_EQ(std::string(""), backtrace->GetFunctionName(device_map_uint, &offset, &map));
   ASSERT_EQ(std::string(""), backtrace->GetFunctionName(0, &offset));
 
-  uintptr_t cur_func_offset = reinterpret_cast<uintptr_t>(&test_level_one) + 1;
+  uint64_t cur_func_offset = reinterpret_cast<uint64_t>(&test_level_one) + 1;
   // Now verify the device map flag actually causes the function name to be empty.
   backtrace->FillInMap(cur_func_offset, &map);
   ASSERT_TRUE((map.flags & PROT_DEVICE_MAP) == 0);
@@ -1525,7 +1592,7 @@
   // Create a context that has the pc in the device map, but the sp
   // in a non-device map.
   memset(&ucontext, 0, sizeof(ucontext));
-  SetUcontextSp(reinterpret_cast<uintptr_t>(&ucontext), &ucontext);
+  SetUcontextSp(reinterpret_cast<uint64_t>(&ucontext), &ucontext);
   SetUcontextPc(device_map_uint, &ucontext);
   SetUcontextLr(cur_func_offset, &ucontext);
 
@@ -1535,7 +1602,7 @@
   ASSERT_EQ(1U, backtrace->NumFrames());
   const backtrace_frame_data_t* frame = backtrace->GetFrame(0);
   ASSERT_EQ(device_map_uint, frame->pc);
-  ASSERT_EQ(reinterpret_cast<uintptr_t>(&ucontext), frame->sp);
+  ASSERT_EQ(reinterpret_cast<uint64_t>(&ucontext), frame->sp);
 
   // Check what happens when skipping the first frame.
   ASSERT_TRUE(backtrace->Unwind(1, &ucontext));
@@ -1586,11 +1653,7 @@
   // Now create an unwind object.
   std::unique_ptr<Backtrace> backtrace(Backtrace::Create(pid, pid));
 
-  // TODO: Currently unwind from context doesn't work on remote
-  // unwind. Keep this test because the new unwinder should support
-  // this eventually, or we can delete this test.
-  // properly with unwind from context.
-  // UnwindFromDevice(backtrace.get(), device_map);
+  UnwindFromDevice(backtrace.get(), device_map);
 
   FinishRemoteProcess(pid);
 
@@ -1629,7 +1692,8 @@
     ;
 }
 
-static void UnwindThroughSignal(bool use_action) {
+static void UnwindThroughSignal(bool use_action, create_func_t create_func,
+                                map_create_func_t map_create_func) {
   volatile int value = 0;
   pid_t pid;
   if ((pid = fork()) == 0) {
@@ -1655,9 +1719,10 @@
 
     WaitForStop(pid);
 
-    std::unique_ptr<Backtrace> backtrace(Backtrace::Create(pid, pid));
+    std::unique_ptr<BacktraceMap> map(map_create_func(pid, false));
+    std::unique_ptr<Backtrace> backtrace(create_func(pid, pid, map.get()));
 
-    size_t bytes_read = backtrace->Read(reinterpret_cast<uintptr_t>(const_cast<int*>(&value)),
+    size_t bytes_read = backtrace->Read(reinterpret_cast<uint64_t>(const_cast<int*>(&value)),
                                         reinterpret_cast<uint8_t*>(&read_value), sizeof(read_value));
     ASSERT_EQ(sizeof(read_value), bytes_read);
 
@@ -1673,6 +1738,7 @@
   // Wait for the process to get to the signal handler loop.
   Backtrace::const_iterator frame_iter;
   start = NanoTime();
+  std::unique_ptr<BacktraceMap> map;
   std::unique_ptr<Backtrace> backtrace;
   while (true) {
     usleep(1000);
@@ -1681,7 +1747,9 @@
 
     WaitForStop(pid);
 
-    backtrace.reset(Backtrace::Create(pid, pid));
+    map.reset(map_create_func(pid, false));
+    ASSERT_TRUE(map.get() != nullptr);
+    backtrace.reset(create_func(pid, pid, map.get()));
     ASSERT_TRUE(backtrace->Unwind(0));
     bool found = false;
     for (frame_iter = backtrace->begin(); frame_iter != backtrace->end(); ++frame_iter) {
@@ -1737,9 +1805,26 @@
   FinishRemoteProcess(pid);
 }
 
-TEST(libbacktrace, unwind_remote_through_signal_using_handler) { UnwindThroughSignal(false); }
+TEST(libbacktrace, unwind_remote_through_signal_using_handler) {
+  UnwindThroughSignal(false, Backtrace::Create, BacktraceMap::Create);
+}
 
-TEST(libbacktrace, unwind_remote_through_signal_using_action) { UnwindThroughSignal(true); }
+TEST(libbacktrace, unwind_remote_through_signal_using_action) {
+  UnwindThroughSignal(true, Backtrace::Create, BacktraceMap::Create);
+}
+
+static void TestFrameSkipNumbering(create_func_t create_func, map_create_func_t map_create_func) {
+  std::unique_ptr<BacktraceMap> map(map_create_func(getpid(), false));
+  std::unique_ptr<Backtrace> backtrace(
+      create_func(getpid(), android::base::GetThreadId(), map.get()));
+  backtrace->Unwind(1);
+  ASSERT_NE(0U, backtrace->NumFrames());
+  ASSERT_EQ(0U, backtrace->GetFrame(0)->num);
+}
+
+TEST(libbacktrace, unwind_frame_skip_numbering) {
+  TestFrameSkipNumbering(Backtrace::Create, BacktraceMap::Create);
+}
 
 #if defined(ENABLE_PSS_TESTS)
 #include "GetPss.h"
@@ -1747,12 +1832,14 @@
 #define MAX_LEAK_BYTES (32*1024UL)
 
 static void CheckForLeak(pid_t pid, pid_t tid) {
+  std::unique_ptr<BacktraceMap> map(BacktraceMap::Create(pid));
+
   // Do a few runs to get the PSS stable.
   for (size_t i = 0; i < 100; i++) {
-    Backtrace* backtrace = Backtrace::Create(pid, tid);
+    Backtrace* backtrace = Backtrace::Create(pid, tid, map.get());
     ASSERT_TRUE(backtrace != nullptr);
     ASSERT_TRUE(backtrace->Unwind(0));
-    ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, backtrace->GetError());
+    VERIFY_NO_ERROR(backtrace->GetError().error_code);
     delete backtrace;
   }
   size_t stable_pss = GetPssBytes();
@@ -1760,10 +1847,10 @@
 
   // Loop enough that even a small leak should be detectable.
   for (size_t i = 0; i < 4096; i++) {
-    Backtrace* backtrace = Backtrace::Create(pid, tid);
+    Backtrace* backtrace = Backtrace::Create(pid, tid, map.get());
     ASSERT_TRUE(backtrace != nullptr);
     ASSERT_TRUE(backtrace->Unwind(0));
-    ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, backtrace->GetError());
+    VERIFY_NO_ERROR(backtrace->GetError().error_code);
     delete backtrace;
   }
   size_t new_pss = GetPssBytes();
diff --git a/libbacktrace/backtrace_testlib.cpp b/libbacktrace/backtrace_testlib.cpp
index 3081d64..fec7d98 100644
--- a/libbacktrace/backtrace_testlib.cpp
+++ b/libbacktrace/backtrace_testlib.cpp
@@ -14,9 +14,15 @@
  * limitations under the License.
  */
 
-#include <libunwind.h>
 #include <signal.h>
 #include <stdio.h>
+#include <unistd.h>
+
+#include <memory>
+#include <vector>
+
+#include <unwindstack/Regs.h>
+#include <unwindstack/RegsGetLocal.h>
 
 #include "backtrace_testlib.h"
 
@@ -66,21 +72,70 @@
 }
 
 typedef struct {
-  unw_context_t* unw_context;
+  std::vector<uint8_t>* ucontext;
   volatile int* exit_flag;
 } GetContextArg;
 
 static void GetContextAndExit(void* data) {
-  GetContextArg* arg = (GetContextArg*)data;
-  unw_getcontext(arg->unw_context);
+  GetContextArg* arg = reinterpret_cast<GetContextArg*>(data);
+
+  std::unique_ptr<unwindstack::Regs> regs(unwindstack::Regs::CreateFromLocal());
+  unwindstack::RegsGetLocal(regs.get());
+
+  ucontext_t ucontext;
+  memset(&ucontext, 0, sizeof(ucontext));
+#if defined(__arm__)
+  memcpy(&ucontext.uc_mcontext, regs->RawData(), sizeof(uint32_t) * 16);
+#elif defined(__aarch64__)
+  memcpy(&ucontext.uc_mcontext, regs->RawData(), sizeof(uint64_t) * 33);
+#elif defined(__i386__)
+  uint32_t* reg_data = reinterpret_cast<uint32_t*>(regs->RawData());
+  ucontext.uc_mcontext.gregs[0] = reg_data[15];
+  ucontext.uc_mcontext.gregs[1] = reg_data[14];
+  ucontext.uc_mcontext.gregs[2] = reg_data[13];
+  ucontext.uc_mcontext.gregs[3] = reg_data[12];
+  ucontext.uc_mcontext.gregs[4] = reg_data[7];
+  ucontext.uc_mcontext.gregs[5] = reg_data[6];
+  ucontext.uc_mcontext.gregs[6] = reg_data[5];
+  ucontext.uc_mcontext.gregs[7] = reg_data[4];
+  ucontext.uc_mcontext.gregs[8] = reg_data[3];
+  ucontext.uc_mcontext.gregs[9] = reg_data[2];
+  ucontext.uc_mcontext.gregs[10] = reg_data[1];
+  ucontext.uc_mcontext.gregs[11] = reg_data[0];
+  ucontext.uc_mcontext.gregs[14] = reg_data[8];
+  ucontext.uc_mcontext.gregs[15] = reg_data[10];
+#elif defined(__x86_64__)
+  uint64_t* reg_data = reinterpret_cast<uint64_t*>(regs->RawData());
+  ucontext.uc_mcontext.gregs[0] = reg_data[8];
+  ucontext.uc_mcontext.gregs[1] = reg_data[9];
+  ucontext.uc_mcontext.gregs[2] = reg_data[10];
+  ucontext.uc_mcontext.gregs[3] = reg_data[11];
+  ucontext.uc_mcontext.gregs[4] = reg_data[12];
+  ucontext.uc_mcontext.gregs[5] = reg_data[13];
+  ucontext.uc_mcontext.gregs[6] = reg_data[14];
+  ucontext.uc_mcontext.gregs[7] = reg_data[15];
+  ucontext.uc_mcontext.gregs[8] = reg_data[5];
+  ucontext.uc_mcontext.gregs[9] = reg_data[4];
+  ucontext.uc_mcontext.gregs[10] = reg_data[6];
+  ucontext.uc_mcontext.gregs[11] = reg_data[3];
+  ucontext.uc_mcontext.gregs[12] = reg_data[1];
+  ucontext.uc_mcontext.gregs[13] = reg_data[0];
+  ucontext.uc_mcontext.gregs[14] = reg_data[2];
+  ucontext.uc_mcontext.gregs[15] = reg_data[7];
+  ucontext.uc_mcontext.gregs[16] = reg_data[16];
+#endif
+
+  arg->ucontext->resize(sizeof(ucontext));
+  memcpy(arg->ucontext->data(), &ucontext, sizeof(ucontext));
+
   // Don't touch the stack anymore.
   while (*arg->exit_flag == 0) {
   }
 }
 
-void test_get_context_and_wait(unw_context_t* unw_context, volatile int* exit_flag) {
+void test_get_context_and_wait(void* ucontext, volatile int* exit_flag) {
   GetContextArg arg;
-  arg.unw_context = unw_context;
+  arg.ucontext = reinterpret_cast<std::vector<uint8_t>*>(ucontext);
   arg.exit_flag = exit_flag;
   test_level_one(1, 2, 3, 4, GetContextAndExit, &arg);
 }
diff --git a/libbacktrace/backtrace_testlib.h b/libbacktrace/backtrace_testlib.h
index 16fedc4..9b55e56 100644
--- a/libbacktrace/backtrace_testlib.h
+++ b/libbacktrace/backtrace_testlib.h
@@ -19,8 +19,6 @@
 
 #include <sys/cdefs.h>
 
-#include <libunwind.h>
-
 __BEGIN_DECLS
 
 void test_loop_forever();
@@ -31,7 +29,7 @@
 int test_level_two(int, int, int, int, void (*)(void*), void*);
 int test_level_one(int, int, int, int, void (*)(void*), void*);
 int test_recursive_call(int, void (*)(void*), void*);
-void test_get_context_and_wait(unw_context_t*, volatile int*);
+void test_get_context_and_wait(void*, volatile int*);
 
 __END_DECLS
 
diff --git a/libbacktrace/include/backtrace/Backtrace.h b/libbacktrace/include/backtrace/Backtrace.h
index 4f73a65..10e790b 100644
--- a/libbacktrace/include/backtrace/Backtrace.h
+++ b/libbacktrace/include/backtrace/Backtrace.h
@@ -26,15 +26,15 @@
 #include <backtrace/backtrace_constants.h>
 #include <backtrace/BacktraceMap.h>
 
-#if __LP64__
-#define PRIPTR "016" PRIxPTR
+#if defined(__LP64__)
+#define PRIPTR "016" PRIx64
 typedef uint64_t word_t;
 #else
-#define PRIPTR "08" PRIxPTR
+#define PRIPTR "08" PRIx64
 typedef uint32_t word_t;
 #endif
 
-enum BacktraceUnwindError : uint32_t {
+enum BacktraceUnwindErrorCode : uint32_t {
   BACKTRACE_UNWIND_NO_ERROR,
   // Something failed while trying to perform the setup to begin the unwind.
   BACKTRACE_UNWIND_ERROR_SETUP_FAILED,
@@ -50,34 +50,68 @@
   BACKTRACE_UNWIND_ERROR_UNSUPPORTED_OPERATION,
   // Attempt to do an offline unwind without a context.
   BACKTRACE_UNWIND_ERROR_NO_CONTEXT,
+  // The count of frames exceed MAX_BACKTRACE_FRAMES.
+  BACKTRACE_UNWIND_ERROR_EXCEED_MAX_FRAMES_LIMIT,
+  // Failed to read memory.
+  BACKTRACE_UNWIND_ERROR_ACCESS_MEM_FAILED,
+  // Failed to read registers.
+  BACKTRACE_UNWIND_ERROR_ACCESS_REG_FAILED,
+  // Failed to find a function in debug sections.
+  BACKTRACE_UNWIND_ERROR_FIND_PROC_INFO_FAILED,
+  // Failed to execute dwarf instructions in debug sections.
+  BACKTRACE_UNWIND_ERROR_EXECUTE_DWARF_INSTRUCTION_FAILED,
+  // Unwind information is incorrect.
+  BACKTRACE_UNWIND_ERROR_UNWIND_INFO,
+  // Unwind information stopped due to sp/pc repeating.
+  BACKTRACE_UNWIND_ERROR_REPEATED_FRAME,
+};
+
+struct BacktraceUnwindError {
+  enum BacktraceUnwindErrorCode error_code;
+
+  union {
+    // for BACKTRACE_UNWIND_ERROR_ACCESS_MEM_FAILED
+    uint64_t addr;
+    // for BACKTRACE_UNWIND_ERROR_ACCESS_REG_FAILED
+    uint64_t regno;
+  } error_info;
+
+  BacktraceUnwindError() : error_code(BACKTRACE_UNWIND_NO_ERROR) {}
 };
 
 struct backtrace_frame_data_t {
   size_t num;             // The current fame number.
-  uintptr_t pc;           // The absolute pc.
-  uintptr_t sp;           // The top of the stack.
+  uint64_t pc;            // The absolute pc.
+  uint64_t rel_pc;        // The relative pc.
+  uint64_t sp;            // The top of the stack.
   size_t stack_size;      // The size of the stack, zero indicate an unknown stack size.
   backtrace_map_t map;    // The map associated with the given pc.
   std::string func_name;  // The function name associated with this pc, NULL if not found.
-  uintptr_t func_offset;  // pc relative to the start of the function, only valid if func_name is not NULL.
+  uint64_t func_offset;  // pc relative to the start of the function, only valid if func_name is not
+                         // NULL.
 };
 
-#if defined(__APPLE__)
-struct __darwin_ucontext;
-typedef __darwin_ucontext ucontext_t;
-#else
-struct ucontext;
-typedef ucontext ucontext_t;
-#endif
-
 struct backtrace_stackinfo_t {
   uint64_t start;
   uint64_t end;
   const uint8_t* data;
 };
 
+namespace unwindstack {
+class Regs;
+}
+
 class Backtrace {
-public:
+ public:
+  enum ArchEnum : uint8_t {
+    ARCH_ARM,
+    ARCH_ARM64,
+    ARCH_X86,
+    ARCH_X86_64,
+  };
+
+  static void SetGlobalElfCache(bool enable);
+
   // Create the correct Backtrace object based on what is to be unwound.
   // If pid < 0 or equals the current pid, then the Backtrace object
   // corresponds to the current process.
@@ -88,7 +122,17 @@
   // Tracing a thread in a different process is not supported.
   // If map is NULL, then create the map and manage it internally.
   // If map is not NULL, the map is still owned by the caller.
-  static Backtrace* Create(pid_t pid, pid_t tid, BacktraceMap* map = NULL);
+  static Backtrace* Create(pid_t pid, pid_t tid, BacktraceMap* map = nullptr);
+
+  // Create an offline Backtrace object that can be used to do an unwind without a process
+  // that is still running. By default, information is only cached in the map
+  // file. If the calling code creates the map, data can be cached between
+  // unwinds. If not, all cached data will be destroyed when the Backtrace
+  // object is destroyed.
+  static Backtrace* CreateOffline(ArchEnum arch, pid_t pid, pid_t tid,
+                                  const std::vector<backtrace_map_t>& maps,
+                                  const backtrace_stackinfo_t& stack);
+  static Backtrace* CreateOffline(ArchEnum arch, pid_t pid, pid_t tid, BacktraceMap* map);
 
   // Create an offline Backtrace object that can be used to do an unwind without a process
   // that is still running. If cache_file is set to true, then elf information will be cached
@@ -101,30 +145,39 @@
   virtual ~Backtrace();
 
   // Get the current stack trace and store in the backtrace_ structure.
-  virtual bool Unwind(size_t num_ignore_frames, ucontext_t* context = NULL) = 0;
+  virtual bool Unwind(size_t num_ignore_frames, void* context = nullptr) = 0;
+
+  static bool Unwind(unwindstack::Regs* regs, BacktraceMap* back_map,
+                     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.
-  virtual std::string GetFunctionName(uintptr_t pc, uintptr_t* offset,
-                                      const backtrace_map_t* map = NULL);
+  virtual std::string GetFunctionName(uint64_t pc, uint64_t* offset,
+                                      const backtrace_map_t* map = nullptr);
 
   // Fill in the map data associated with the given pc.
-  virtual void FillInMap(uintptr_t pc, backtrace_map_t* map);
+  virtual void FillInMap(uint64_t pc, backtrace_map_t* map);
 
   // Read the data at a specific address.
-  virtual bool ReadWord(uintptr_t ptr, word_t* out_value) = 0;
+  virtual bool ReadWord(uint64_t ptr, word_t* out_value) = 0;
 
   // Read arbitrary data from a specific address. If a read request would
   // span from one map to another, this call only reads up until the end
   // of the current map.
   // Returns the total number of bytes actually read.
-  virtual size_t Read(uintptr_t addr, uint8_t* buffer, size_t bytes) = 0;
+  virtual size_t Read(uint64_t addr, uint8_t* buffer, size_t bytes) = 0;
 
   // Create a string representing the formatted line of backtrace information
   // for a single frame.
   virtual std::string FormatFrameData(size_t frame_num);
-  virtual std::string FormatFrameData(const backtrace_frame_data_t* frame);
+  static std::string FormatFrameData(const backtrace_frame_data_t* frame);
 
   pid_t Pid() const { return pid_; }
   pid_t Tid() const { return tid_; }
@@ -132,7 +185,7 @@
 
   const backtrace_frame_data_t* GetFrame(size_t frame_num) {
     if (frame_num >= frames_.size()) {
-      return NULL;
+      return nullptr;
     }
     return &frames_[frame_num];
   }
@@ -151,14 +204,17 @@
 
   std::string GetErrorString(BacktraceUnwindError error);
 
-protected:
+  // 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);
 
   // The name returned is not demangled, GetFunctionName() takes care of
   // demangling the name.
-  virtual std::string GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset) = 0;
+  virtual std::string GetFunctionNameRaw(uint64_t pc, uint64_t* offset) = 0;
 
-  virtual bool VerifyReadWordArgs(uintptr_t ptr, word_t* out_value);
+  virtual bool VerifyReadWordArgs(uint64_t ptr, word_t* out_value);
 
   bool BuildMap();
 
@@ -170,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 8ab0dfa..a9cfce4 100644
--- a/libbacktrace/include/backtrace/BacktraceMap.h
+++ b/libbacktrace/include/backtrace/BacktraceMap.h
@@ -30,22 +30,37 @@
 #endif
 
 #include <deque>
+#include <iterator>
 #include <string>
 #include <vector>
 
+// Forward declaration.
+struct backtrace_stackinfo_t;
+
 // 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 {
-  uintptr_t start = 0;
-  uintptr_t end = 0;
-  uintptr_t offset = 0;
-  uintptr_t load_base = 0;
+  uint64_t start = 0;
+  uint64_t end = 0;
+  uint64_t offset = 0;
+  uint64_t load_bias = 0;
   int flags = 0;
   std::string name;
+
+  // Returns `name` if non-empty, or `<anonymous:0x...>` otherwise.
+  std::string Name() const;
 };
 
+namespace unwindstack {
+class Memory;
+}
+
 class BacktraceMap {
 public:
   // If uncached is true, then parse the current process map as of the call.
@@ -53,16 +68,63 @@
   // is unsupported.
   static BacktraceMap* Create(pid_t pid, bool uncached = false);
 
-  static BacktraceMap* Create(pid_t pid, const std::vector<backtrace_map_t>& maps);
+  static BacktraceMap* CreateOffline(pid_t pid, const std::vector<backtrace_map_t>& maps);
 
   virtual ~BacktraceMap();
 
+  class iterator : public std::iterator<std::bidirectional_iterator_tag, backtrace_map_t*> {
+   public:
+    iterator(BacktraceMap* map, size_t index) : map_(map), index_(index) {}
+
+    iterator& operator++() {
+      index_++;
+      return *this;
+    }
+    iterator& operator++(int increment) {
+      index_ += increment;
+      return *this;
+    }
+    iterator& operator--() {
+      index_--;
+      return *this;
+    }
+    iterator& operator--(int decrement) {
+      index_ -= decrement;
+      return *this;
+    }
+
+    bool operator==(const iterator& rhs) { return this->index_ == rhs.index_; }
+    bool operator!=(const iterator& rhs) { return this->index_ != rhs.index_; }
+
+    const backtrace_map_t* operator*() {
+      if (index_ >= map_->size()) {
+        return nullptr;
+      }
+      backtrace_map_t* map = &map_->maps_[index_];
+      if (map->load_bias == static_cast<uint64_t>(-1)) {
+        map->load_bias = map_->GetLoadBias(index_);
+      }
+      return map;
+    }
+
+   private:
+    BacktraceMap* map_ = nullptr;
+    size_t index_ = 0;
+  };
+
+  iterator begin() { return iterator(this, 0); }
+  iterator end() { return iterator(this, maps_.size()); }
+
   // Fill in the map data structure for the given address.
-  virtual void FillIn(uintptr_t addr, backtrace_map_t* map);
+  virtual void FillIn(uint64_t addr, backtrace_map_t* map);
+
+  // Only supported with the new unwinder.
+  virtual std::string GetFunctionName(uint64_t /*pc*/, uint64_t* /*offset*/) { return ""; }
+  virtual std::shared_ptr<unwindstack::Memory> GetProcessMemory() { return nullptr; }
 
   // The flags returned are the same flags as used by the mmap call.
   // The values are PROT_*.
-  int GetFlags(uintptr_t pc) {
+  int GetFlags(uint64_t pc) {
     backtrace_map_t map;
     FillIn(pc, &map);
     if (IsValid(map)) {
@@ -71,9 +133,9 @@
     return PROT_NONE;
   }
 
-  bool IsReadable(uintptr_t pc) { return GetFlags(pc) & PROT_READ; }
-  bool IsWritable(uintptr_t pc) { return GetFlags(pc) & PROT_WRITE; }
-  bool IsExecutable(uintptr_t pc) { return GetFlags(pc) & PROT_EXEC; }
+  bool IsReadable(uint64_t pc) { return GetFlags(pc) & PROT_READ; }
+  bool IsWritable(uint64_t pc) { return GetFlags(pc) & PROT_WRITE; }
+  bool IsExecutable(uint64_t pc) { return GetFlags(pc) & PROT_EXEC; }
 
   // In order to use the iterators on this object, a caller must
   // call the LockIterator and UnlockIterator function to guarantee
@@ -81,13 +143,7 @@
   virtual void LockIterator() {}
   virtual void UnlockIterator() {}
 
-  typedef std::deque<backtrace_map_t>::iterator iterator;
-  iterator begin() { return maps_.begin(); }
-  iterator end() { return maps_.end(); }
-
-  typedef std::deque<backtrace_map_t>::const_iterator const_iterator;
-  const_iterator begin() const { return maps_.begin(); }
-  const_iterator end() const { return maps_.end(); }
+  size_t size() const { return maps_.size(); }
 
   virtual bool Build();
 
@@ -95,21 +151,28 @@
     return map.end > 0;
   }
 
-  static uintptr_t GetRelativePc(const backtrace_map_t& map, uintptr_t pc) {
-    if (IsValid(map)) {
-      return pc - map.start + map.load_base;
-    } else {
-      return pc;
-    }
+  void SetSuffixesToIgnore(std::vector<std::string> suffixes) {
+    suffixes_to_ignore_.insert(suffixes_to_ignore_.end(), suffixes.begin(), suffixes.end());
   }
 
-protected:
+  const std::vector<std::string>& GetSuffixesToIgnore() { return suffixes_to_ignore_; }
+
+  // Disabling the resolving of names results in the function name being
+  // set to an empty string and the function offset being set to zero
+  // in the frame data when unwinding.
+  void SetResolveNames(bool resolve) { resolve_names_ = resolve; }
+
+  bool ResolveNames() { return resolve_names_; }
+
+ protected:
   BacktraceMap(pid_t pid);
 
-  virtual bool ParseLine(const char* line, backtrace_map_t* map);
+  virtual uint64_t GetLoadBias(size_t /* index */) { return 0; }
 
-  std::deque<backtrace_map_t> maps_;
   pid_t pid_;
+  std::deque<backtrace_map_t> maps_;
+  std::vector<std::string> suffixes_to_ignore_;
+  bool resolve_names_ = true;
 };
 
 class ScopedBacktraceMapIteratorLock {
diff --git a/libbacktrace/include/backtrace/backtrace_constants.h b/libbacktrace/include/backtrace/backtrace_constants.h
index f8c1575..1a2da36 100644
--- a/libbacktrace/include/backtrace/backtrace_constants.h
+++ b/libbacktrace/include/backtrace/backtrace_constants.h
@@ -20,11 +20,11 @@
 // When the pid to be traced is set to this value, then trace the current
 // process. If the tid value is not BACKTRACE_NO_TID, then the specified
 // thread from the current process will be traced.
-#define BACKTRACE_CURRENT_PROCESS -1
+#define BACKTRACE_CURRENT_PROCESS (-1)
 // When the tid to be traced is set to this value, then trace the specified
 // current thread of the specified pid.
-#define BACKTRACE_CURRENT_THREAD -1
+#define BACKTRACE_CURRENT_THREAD (-1)
 
-#define MAX_BACKTRACE_FRAMES 64
+#define MAX_BACKTRACE_FRAMES 256
 
 #endif // _BACKTRACE_BACKTRACE_CONSTANTS_H
diff --git a/libbacktrace/testdata/aarch64/offline_testdata b/libbacktrace/testdata/aarch64/offline_testdata
deleted file mode 100644
index ba44e09..0000000
--- a/libbacktrace/testdata/aarch64/offline_testdata
+++ /dev/null
@@ -1,107 +0,0 @@
-pid: 32438 tid: 32439
-map: start: 557066e000 end: 55706ee000 offset: 0 load_base: 0 flags: 5 name: /data/backtrace_test64
-map: start: 55706ef000 end: 55706f2000 offset: 80000 load_base: 0 flags: 1 name: /data/backtrace_test64
-map: start: 55706f2000 end: 55706f3000 offset: 83000 load_base: 0 flags: 3 name: /data/backtrace_test64
-map: start: 7014200000 end: 7014600000 offset: 0 load_base: 0 flags: 3 name: [anon:libc_malloc]
-map: start: 701464c000 end: 701465c000 offset: 0 load_base: 0 flags: 5 name: /system/lib64/libcutils.so
-map: start: 701465c000 end: 701465d000 offset: 0 load_base: 0 flags: 0 name: 
-map: start: 701465d000 end: 701465e000 offset: 10000 load_base: 0 flags: 1 name: /system/lib64/libcutils.so
-map: start: 701465e000 end: 701465f000 offset: 11000 load_base: 0 flags: 3 name: /system/lib64/libcutils.so
-map: start: 7014691000 end: 70146b5000 offset: 0 load_base: 0 flags: 5 name: /system/lib64/liblzma.so
-map: start: 70146b5000 end: 70146b6000 offset: 23000 load_base: 0 flags: 1 name: /system/lib64/liblzma.so
-map: start: 70146b6000 end: 70146b7000 offset: 24000 load_base: 0 flags: 3 name: /system/lib64/liblzma.so
-map: start: 70146b7000 end: 70146bc000 offset: 0 load_base: 0 flags: 3 name: [anon:.bss]
-map: start: 70146c9000 end: 70158b5000 offset: 0 load_base: af000 flags: 5 name: /system/lib64/libLLVM.so
-map: start: 70158b5000 end: 701596b000 offset: 11eb000 load_base: 0 flags: 1 name: /system/lib64/libLLVM.so
-map: start: 701596b000 end: 701596c000 offset: 12a1000 load_base: 0 flags: 3 name: /system/lib64/libLLVM.so
-map: start: 701596c000 end: 701599f000 offset: 0 load_base: 0 flags: 3 name: [anon:.bss]
-map: start: 70159c2000 end: 70159f9000 offset: 0 load_base: 0 flags: 5 name: /system/lib64/libm.so
-map: start: 70159f9000 end: 70159fa000 offset: 36000 load_base: 0 flags: 1 name: /system/lib64/libm.so
-map: start: 70159fa000 end: 70159fb000 offset: 37000 load_base: 0 flags: 3 name: /system/lib64/libm.so
-map: start: 7015a1e000 end: 7015a2e000 offset: 0 load_base: 0 flags: 5 name: /system/lib64/libbacktrace.so
-map: start: 7015a2e000 end: 7015a2f000 offset: f000 load_base: 0 flags: 1 name: /system/lib64/libbacktrace.so
-map: start: 7015a2f000 end: 7015a30000 offset: 10000 load_base: 0 flags: 3 name: /system/lib64/libbacktrace.so
-map: start: 7015a5e000 end: 7015a7d000 offset: 0 load_base: 1000 flags: 5 name: /system/lib64/libutils.so
-map: start: 7015a7d000 end: 7015a7e000 offset: 0 load_base: 0 flags: 0 name: 
-map: start: 7015a7e000 end: 7015a7f000 offset: 1f000 load_base: 0 flags: 1 name: /system/lib64/libutils.so
-map: start: 7015a7f000 end: 7015a80000 offset: 20000 load_base: 0 flags: 3 name: /system/lib64/libutils.so
-map: start: 7015a99000 end: 7015b6d000 offset: 0 load_base: 9000 flags: 5 name: /system/lib64/libc++.so
-map: start: 7015b6d000 end: 7015b6e000 offset: 0 load_base: 0 flags: 0 name: 
-map: start: 7015b6e000 end: 7015b76000 offset: d4000 load_base: 0 flags: 1 name: /system/lib64/libc++.so
-map: start: 7015b76000 end: 7015b77000 offset: dc000 load_base: 0 flags: 3 name: /system/lib64/libc++.so
-map: start: 7015b77000 end: 7015b7a000 offset: 0 load_base: 0 flags: 3 name: [anon:.bss]
-map: start: 7015b81000 end: 7015b92000 offset: 0 load_base: 0 flags: 5 name: /system/lib64/liblog.so
-map: start: 7015b92000 end: 7015b93000 offset: 10000 load_base: 0 flags: 1 name: /system/lib64/liblog.so
-map: start: 7015b93000 end: 7015b94000 offset: 11000 load_base: 0 flags: 3 name: /system/lib64/liblog.so
-map: start: 7015be3000 end: 7015ca3000 offset: 0 load_base: 0 flags: 5 name: /system/lib64/libc.so
-map: start: 7015ca3000 end: 7015ca9000 offset: bf000 load_base: 0 flags: 1 name: /system/lib64/libc.so
-map: start: 7015ca9000 end: 7015cab000 offset: c5000 load_base: 0 flags: 3 name: /system/lib64/libc.so
-map: start: 7015cab000 end: 7015cac000 offset: 0 load_base: 0 flags: 3 name: [anon:.bss]
-map: start: 7015cac000 end: 7015cad000 offset: 0 load_base: 0 flags: 1 name: [anon:.bss]
-map: start: 7015cad000 end: 7015cb4000 offset: 0 load_base: 0 flags: 3 name: [anon:.bss]
-map: start: 7015cf5000 end: 7015cf6000 offset: 0 load_base: 0 flags: 5 name: /data/libbacktrace_test.so
-map: start: 7015cf6000 end: 7015cf7000 offset: 0 load_base: 0 flags: 1 name: /data/libbacktrace_test.so
-map: start: 7015cf7000 end: 7015cf8000 offset: 1000 load_base: 0 flags: 3 name: /data/libbacktrace_test.so
-map: start: 7015d1f000 end: 7015d39000 offset: 0 load_base: 0 flags: 5 name: /system/lib64/libunwind.so
-map: start: 7015d39000 end: 7015d3a000 offset: 19000 load_base: 0 flags: 1 name: /system/lib64/libunwind.so
-map: start: 7015d3a000 end: 7015d3b000 offset: 1a000 load_base: 0 flags: 3 name: /system/lib64/libunwind.so
-map: start: 7015d3b000 end: 7015da4000 offset: 0 load_base: 0 flags: 3 name: [anon:.bss]
-map: start: 7015de8000 end: 7015df7000 offset: 0 load_base: 0 flags: 5 name: /system/lib64/libbase.so
-map: start: 7015df7000 end: 7015df8000 offset: 0 load_base: 0 flags: 0 name: 
-map: start: 7015df8000 end: 7015df9000 offset: f000 load_base: 0 flags: 1 name: /system/lib64/libbase.so
-map: start: 7015df9000 end: 7015dfa000 offset: 10000 load_base: 0 flags: 3 name: /system/lib64/libbase.so
-map: start: 7015e35000 end: 7015e36000 offset: 0 load_base: 0 flags: 3 name: [anon:linker_alloc]
-map: start: 7015e4f000 end: 7015e50000 offset: 0 load_base: 0 flags: 3 name: [anon:linker_alloc_small_objects]
-map: start: 7015f11000 end: 7015f13000 offset: 0 load_base: 0 flags: 5 name: /system/lib64/libnetd_client.so
-map: start: 7015f13000 end: 7015f14000 offset: 1000 load_base: 0 flags: 1 name: /system/lib64/libnetd_client.so
-map: start: 7015f14000 end: 7015f15000 offset: 2000 load_base: 0 flags: 3 name: /system/lib64/libnetd_client.so
-map: start: 7015f6c000 end: 7015f79000 offset: 0 load_base: 0 flags: 3 name: 
-map: start: 7015f79000 end: 7015f99000 offset: 0 load_base: 0 flags: 32769 name: /dev/__properties__/u:object_r:default_prop:s0
-map: start: 7015f99000 end: 7015f9a000 offset: 0 load_base: 0 flags: 3 name: [anon:linker_alloc_vector]
-map: start: 7015f9a000 end: 7015f9b000 offset: 0 load_base: 0 flags: 3 name: [anon:linker_alloc_small_objects]
-map: start: 7015fa6000 end: 7015fa7000 offset: 0 load_base: 0 flags: 3 name: [anon:linker_alloc_vector]
-map: start: 7015fa8000 end: 7015fa9000 offset: 0 load_base: 0 flags: 1 name: [anon:linker_alloc]
-map: start: 7015faf000 end: 7015fcf000 offset: 0 load_base: 0 flags: 32769 name: /dev/__properties__/properties_serial
-map: start: 7015fcf000 end: 7015fd0000 offset: 0 load_base: 0 flags: 3 name: [anon:linker_alloc_vector]
-map: start: 7015fd0000 end: 7015fd1000 offset: 0 load_base: 0 flags: 3 name: [anon:linker_alloc_small_objects]
-map: start: 7015fd1000 end: 7015fd2000 offset: 0 load_base: 0 flags: 3 name: [anon:linker_alloc_vector]
-map: start: 7015fd4000 end: 7015fd7000 offset: 0 load_base: 0 flags: 3 name: 
-map: start: 7015fd7000 end: 7015fdb000 offset: 0 load_base: 0 flags: 1 name: [anon:atexit handlers]
-map: start: 7015fdb000 end: 7015fdc000 offset: 0 load_base: 0 flags: 3 name: [anon:linker_alloc]
-map: start: 7015fdc000 end: 7015fdd000 offset: 0 load_base: 0 flags: 3 name: 
-map: start: 7015fdd000 end: 7015fde000 offset: 0 load_base: 0 flags: 1 name: [anon:linker_alloc]
-map: start: 7015fde000 end: 7015ffe000 offset: 0 load_base: 0 flags: 32769 name: /dev/__properties__/u:object_r:debug_prop:s0
-map: start: 7015ffe000 end: 701601e000 offset: 0 load_base: 0 flags: 32769 name: /dev/__properties__/properties_serial
-map: start: 701601e000 end: 701601f000 offset: 0 load_base: 0 flags: 3 name: [anon:linker_alloc_vector]
-map: start: 701601f000 end: 7016020000 offset: 0 load_base: 0 flags: 0 name: 
-map: start: 7016020000 end: 7016021000 offset: 0 load_base: 0 flags: 3 name: 
-map: start: 7016021000 end: 7016022000 offset: 0 load_base: 0 flags: 0 name: 
-map: start: 7016022000 end: 7016023000 offset: 0 load_base: 0 flags: 3 name: [anon:linker_alloc_lob]
-map: start: 7016023000 end: 7016025000 offset: 0 load_base: 0 flags: 1 name: [anon:linker_alloc]
-map: start: 7016025000 end: 7016026000 offset: 0 load_base: 0 flags: 3 name: [anon:linker_alloc_small_objects]
-map: start: 7016026000 end: 7016027000 offset: 0 load_base: 0 flags: 3 name: [anon:linker_alloc_vector]
-map: start: 7016027000 end: 7016028000 offset: 0 load_base: 0 flags: 3 name: [anon:linker_alloc_small_objects]
-map: start: 7016028000 end: 7016029000 offset: 0 load_base: 0 flags: 3 name: [anon:arc4random data]
-map: start: 7016029000 end: 701602a000 offset: 0 load_base: 0 flags: 3 name: [anon:linker_alloc_small_objects]
-map: start: 701602a000 end: 701602b000 offset: 0 load_base: 0 flags: 1 name: [anon:atexit handlers]
-map: start: 701602b000 end: 701602c000 offset: 0 load_base: 0 flags: 0 name: [anon:thread signal stack guard page]
-map: start: 701602c000 end: 7016030000 offset: 0 load_base: 0 flags: 3 name: [anon:thread signal stack]
-map: start: 7016030000 end: 7016031000 offset: 0 load_base: 0 flags: 3 name: [anon:arc4random data]
-map: start: 7016031000 end: 7016033000 offset: 0 load_base: 0 flags: 5 name: [vdso]
-map: start: 7016033000 end: 70160dd000 offset: 0 load_base: 0 flags: 5 name: /system/bin/linker64
-map: start: 70160dd000 end: 70160e0000 offset: a9000 load_base: 0 flags: 1 name: /system/bin/linker64
-map: start: 70160e0000 end: 70160e1000 offset: ac000 load_base: 0 flags: 3 name: /system/bin/linker64
-map: start: 70160e1000 end: 70160e4000 offset: 0 load_base: 0 flags: 3 name: 
-map: start: 70160e4000 end: 70160e5000 offset: 0 load_base: 0 flags: 1 name: 
-map: start: 70160e5000 end: 70160e8000 offset: 0 load_base: 0 flags: 3 name: 
-map: start: 7fd8baf000 end: 7fd8be6000 offset: 0 load_base: 0 flags: 3 name: [stack]
-registers: 4560 679a0b1670000000f3eb6d705500000090f56e7055000000000000000000000000206f705500000014e0677055000000d038bed87f0000004cde67705500000041000000000000001900000000000000c00f241470000000d3aec914588f4bcd0400000000000000e493af157000000090f56e7055000000060000000000000023c00b1670000000b1d1fd15700000003039bed87f000000c898041670000000304cbed87f000000b8130e1670000000303cbed87f000000f838bed87f0000000e0000000000000015000000000000001c00000000000000ec59cf1570000000b863fd15700000005064fd15700000000000000000000000ec59cf15700000000200000000000000b863fd1570000000144abed87f0000006064fd15700000005064fd157000000000010000000000005826bed87f000000d86fcf15700000006057cf157000000000000000000000005064fd15700000005064fd15700000005064fd1570000000b67e00000000000040fd677055000000d064fd15700000000030fd157000000002000000000000000100000000000000fcb58a56000000000063fd15700000009857cf1570000000c062fd15700000001c5acf157000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000003003167000000001000000030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000033000000000000000300000000000000003303167000000008330316700000000000000006000000f8320316700000005839bed87f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000c84cbed87f000000e84cbed87f000000984dbed87f00000078170e167000000002fd0000000000001400000000000000ff8100000100000000000000000000000000000000000000000000000000000078145700000000000010000000000000902b000000000000bdd04058000000000000000000000000bdd04058000000000000000000000000ccb58a560000000042487408000000000000000000000000cc57041670000000004704167000000010c0fd157000000010c0fd157000000090c0fd15700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000010000000000000002f48bed87f000000d3aec914588f4bcd2045bed87f0000002045bed87f0000002f48bed87f00000001000000000000002f000080000000005045bed87f0000000045bed87f000000c0a0c315700000006045bed87f0000006045bed87f000000010000000000000001000000000000000344bed87f00000001000000100000009c3fbed87f0000009b3fbed87f0000000344bed87f0000009a3fbed87f0000000100000000000000953fbed87f0000004344bed80100000001000000010000002c48bed87f0000000444bed87f0000004344bed80100000000000000000000000100000000000000b03fbed87f000000000000000200000000000000000000000000000000000000d3aec914588f4bcd000000000100000000000000000000000100000000000000f03fbed87f000000000000000200000000000000000000000000000000000000d3aec914588f4bcd00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a03fbed87f0000000000000000000000000000000000000000000000000000000000000000000000d3aec914588f4bcd08226f70550000000000000000000000000000000000000000000000000000001048bed87f0000008047bed87f0000006047bed87f000000e0ffffff80ffffff03000000000000000000000000000000891d6e7055000000d3aec914588f4bcd5047bed87f0000005047bed87f000000861d6e705500000003000000000000002f00008000000000f0a6ca15700000004047bed87f000000c0a0c31570000000891d6e7055000000d3aec914000000000100000000000000a047bed800000000f9006e70550000000100000000000000dc41bed87f000000db41bed87f0000004346bed87f000000da41bed87f0000000100000000000000d541bed87f00000001000000010000000100000001000000861d6e70550000004446bed87f0000002c42bed80300000000000000000000000100000000000000f041bed87f0000009b1e6e700100000000000000000000000000000000000000d3aec914588f4bcd8e1e6e70550000000d000000000000002f00008000000000f0a6ca15700000003048bed87f000000c0a0c31570000000661e6e700100000000000000000000002f000000000000001000000000000000e21e6e7055000000d3aec914588f4bcdb048bed87f000000b048bed87f000000e21e6e70550000002f000000000000002f00008000000000f0a6ca1570000000a048bed87f000000c0a0c315700000008e1e6e7055000000000000000000000022000000000000000000000000000000205827147000000022000000000000003c43bed87f0000003b43bed87f000000a347bed87f0000003a43bed87f00000001000000000000003543bed87f000000f048bed8010000000100000001000000dd1e6e7055000000a447bed87f0000008c43bed82f000000000000000000000001000000000000005043bed87f000000861d6e700300000000000000000000000000000000000000d3aec914588f4bcd721e6e7055000000f447bed87f000000981123141800000000000000000000000100000000000000a043bed87f000000861d6e70030000000000000000000000d042bed87f0000000000000000000000981123147000000098112314700000009811231470000000000000000000000000000000000000000000000000000000000000007f0000000000000000000000504abed87f000000b049bed87f0000008049bed87f00000000000000000000004043bed87f0000000000000000000000991123147000000000000000000000008e1e6e70550000000d00000000000000a04abed87f000000000000000000000000000000000000000000000000000000504abed87f000000e049bed87f000000a049bed87f000000c8ffffff80ffffff591e6e70550000000d0000000000000098112314700000000000000000000000df1e6e7055000000010000000000000020582714700000002200000000000000f049bed87f000000c8ffffff80ffffff9811231470000000980023147000000098112314700000009811231470000000741e6e7055000000060000000000000009f12914700000000c00000000000000b049bed87f000000b049bed87f000000b049bed87f000000b049bed87f000000b049bed87f000000b049bed87f000000b049bed87f000000b049bed87f000000b149bed87f000000b149bed87f000000f049be317f000000f049bed87f000000f049bed87f000000b149bed87f000000b049bed87f000000b049bed87f000000b049bed87f000000b049bed87f000000b049bed87f000000b049bed87f000000b049bed87f000000f149bed87f000000f049bed87f000000d3aec914588f4bcdfcb58a56000000006cbb687055000000160000000000000098112314700000009911231470000000991123147000000098112314700000009911231470000000fcb58a5600000000010000000000000000000000000000000100000000000000604a050000000000e845bed87f0000006048bed87f000000a046bed87f00000017000000000000002c48bed87f0000009046bed87f000000ac73c515700000009911231470000000d3aec914588f4bcd1048bed87f0000008047bed87f0000006047bed87f000000e8ffffff80ffffffffffffffffffffff99112314700000006148bed87f000000981123141500000008020000ffffffff6048bed87f000000160000000000000058112314700000001700000000000000a849bed87f0000000000000000000000284abed87f0000001700000000000000284abed87f000000284abed87f000000d249bed87f00000001000000000000009a112314700000001600000000000000284abed87f000000ffffffffffffffff284abed87f000000284abed87f000000284abed87f000000284abed87f0000001700000000000000ffffffffffffffff284abed87f000000284abed87f000000ff49bed87f000000284abed87f00000000000000000000000000000000000000d049bed87f000000d049bed87f000000004abed87f000000d049bed87f0000000100000000000000d049bed87f000000d049bed87f000000d049bed87f00000017000000000000000100000000000000ffffffffffffffff991123147000000001000000000000003448bed87f0000009911231470000000b0ca687055000000ffffffffffffffff010000000000000099112314700000007047bed87f000000d3aec914588f4bcdfcb58a56000000000100000000000000000000000000000050226f70550000000000000000000000b44b05000000000000102a1470000000861d6e70550000000300000000000000f0a6ca1570000000a047bed87f0000006c79c31570000000f048bed87f0000008048bed87f0000004048bed87f000000c8ffffff80ffffff0000000000000000d3aec914588f4bcdf849bed87f000000f0a6ca15700000000200000000000000085b0e1670000000e048bed87f0000002c6fc515700000009048bed87f000000d3aec914588f4bcd0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a89912314700000000000000000000000e9216f70550000008991231470000000e449bed87f0000008991231470000000000000000000000000000000000000008891231470000000899123147000000089912314700000008891231470000000170000000000000088912314700000008891231470000000d3aec914588f4bcd899123147000000016000000000000000000000000000000e9216f705500000088912314700000008891231470000000889123147000000088912314700000008891231470000000f0a6ca15700000000049bed87f0000006c79c31570000000889123147000000088912314700000008891231470000000889123147000000088912314700000008891231470000000889123147000000089912314700000008991231470000000085b0e1670000000404abed87f0000002c6fc51570000000c80a6f7055000000d3aec914588f4bcd0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a3b3325736d001b5b6d002c20776865726520002573203d202573000a526570650000000000000000000000000000000000000000000000008891231470000000000000000000000088912314700000008891231470000000889123147000000088912314700000000000000000000000889123147000000088912314700000008891231470000000470000000000000000502a1470000000d3aec914588f4bcdf05727147000000000b2221470000000604abed87f0000005c716c7055000000fcb58a56000000007c706c7055000000fcb58a5600000000ea4b050000000000
-stack: start: 7015fd3000 end: 7015fd7000 size: 16384 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f838bed87f0000004038bed87f000000b863fd1570000000b863fd1570000000b863fd1570000000ec59cf15700000001c000000150000000e000000070000003063fd15700000001c58cf1570000000b863fd1570000000ec59cf1570000000100000000c00000008000000040000006063fd15700000007c58cf1570000000b863fd1570000000ec59cf1570000000080000000600000004000000020000009063fd1570000000dc58cf1570000000b863fd1570000000ec59cf157000000004000000030000000200000001000000d063fd1570000000c459cf15700000000100000000000000144abed87f0000004038bed87f0000004038bed87f000000144abed87f000000d3aec914588f4bcd1064fd157000000074fd6770550000004038bed87f0000004038bed87f000000ec84c41570000000e484c41570000000c484c4157000000000000000000000004064fd15700000004015c01570000000b67e0000000000000000000000000000705a0e1670000000185b0e167000000000000000000000000000000000000000705a0e16700000000000000000000000b77e0000b67e000000000000550000000030fd157000000050340000000000000010000000000000000000000000000000b222147000000000102a14700000000000000000000000000000000000000040fd6770550000004038bed87f000000000000000000000000a0fa1570000000010000000000000000000000000000000000000000000000e864fd15700000005064fd1570000000000000000000000000000000000000000000000000000000d3aec914588f4bcd0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
-function: start: 0 end: 7015cf5760 name: unknown_start
-function: start: 7015cf5760 end: 7015cf57cc name: test_level_four
-function: start: 7015cf57cc end: 7015cf582c name: test_level_three
-function: start: 7015cf582c end: 7015cf588c name: test_level_two
-function: start: 7015cf588c end: 7015cf58ec name: test_level_one
-function: start: 7015cf58ec end: 7015cf5968 name: test_recursive_call
-function: start: 7015cf5968 end: ffffffffffffffff name: test_get_context_and_wait
-function: start: ffffffffffffffff end: ffffffffffffffff name: unknown_end
diff --git a/libbacktrace/testdata/arm/libGLESv2_adreno.so b/libbacktrace/testdata/arm/libGLESv2_adreno.so
new file mode 100644
index 0000000..871f6dc
--- /dev/null
+++ b/libbacktrace/testdata/arm/libGLESv2_adreno.so
Binary files differ
diff --git a/libbacktrace/testdata/arm/libandroid_runtime.so b/libbacktrace/testdata/arm/libandroid_runtime.so
new file mode 100644
index 0000000..e4283e6
--- /dev/null
+++ b/libbacktrace/testdata/arm/libandroid_runtime.so
Binary files differ
diff --git a/libbacktrace/testdata/arm/offline_testdata b/libbacktrace/testdata/arm/offline_testdata
index 43d305a..d5b8f47 100644
--- a/libbacktrace/testdata/arm/offline_testdata
+++ b/libbacktrace/testdata/arm/offline_testdata
@@ -1,99 +1,99 @@
 pid: 32232 tid: 32233
-map: start: aad19000 end: aad6c000 offset: 0 load_base: 0 flags: 5 name: /data/backtrace_test32
-map: start: aad6c000 end: aad6e000 offset: 52000 load_base: 0 flags: 1 name: /data/backtrace_test32
-map: start: aad6e000 end: aad6f000 offset: 54000 load_base: 0 flags: 3 name: /data/backtrace_test32
-map: start: e7380000 end: e7400000 offset: 0 load_base: 0 flags: 3 name: [anon:libc_malloc]
-map: start: e745f000 end: e7463000 offset: 0 load_base: 0 flags: 5 name: /system/lib/libnetd_client.so
-map: start: e7463000 end: e7464000 offset: 3000 load_base: 0 flags: 1 name: /system/lib/libnetd_client.so
-map: start: e7464000 end: e7465000 offset: 4000 load_base: 0 flags: 3 name: /system/lib/libnetd_client.so
-map: start: e7480000 end: e7500000 offset: 0 load_base: 0 flags: 3 name: [anon:libc_malloc]
-map: start: e7558000 end: e756c000 offset: 0 load_base: 0 flags: 5 name: /system/lib/libunwind.so
-map: start: e756c000 end: e756d000 offset: 0 load_base: 0 flags: 0 name: 
-map: start: e756d000 end: e756e000 offset: 14000 load_base: 0 flags: 1 name: /system/lib/libunwind.so
-map: start: e756e000 end: e756f000 offset: 15000 load_base: 0 flags: 3 name: /system/lib/libunwind.so
-map: start: e756f000 end: e75b5000 offset: 0 load_base: 0 flags: 3 name: [anon:.bss]
-map: start: e75d4000 end: e75e1000 offset: 0 load_base: 0 flags: 5 name: /system/lib/libbase.so
-map: start: e75e1000 end: e75e2000 offset: c000 load_base: 0 flags: 1 name: /system/lib/libbase.so
-map: start: e75e2000 end: e75e3000 offset: d000 load_base: 0 flags: 3 name: /system/lib/libbase.so
-map: start: e7600000 end: e7616000 offset: 0 load_base: 0 flags: 5 name: /system/lib/liblzma.so
-map: start: e7616000 end: e7617000 offset: 15000 load_base: 0 flags: 1 name: /system/lib/liblzma.so
-map: start: e7617000 end: e7618000 offset: 16000 load_base: 0 flags: 3 name: /system/lib/liblzma.so
-map: start: e7618000 end: e761d000 offset: 0 load_base: 0 flags: 3 name: [anon:.bss]
-map: start: e7647000 end: e7656000 offset: 0 load_base: 0 flags: 5 name: /system/lib/liblog.so
-map: start: e7656000 end: e7657000 offset: e000 load_base: 0 flags: 1 name: /system/lib/liblog.so
-map: start: e7657000 end: e7658000 offset: f000 load_base: 0 flags: 3 name: /system/lib/liblog.so
-map: start: e7681000 end: e76a2000 offset: 0 load_base: 0 flags: 5 name: /system/lib/libm.so
-map: start: e76a2000 end: e76a3000 offset: 20000 load_base: 0 flags: 1 name: /system/lib/libm.so
-map: start: e76a3000 end: e76a4000 offset: 21000 load_base: 0 flags: 3 name: /system/lib/libm.so
-map: start: e76eb000 end: e76ee000 offset: 0 load_base: 0 flags: 5 name: /data/libbacktrace_test.so
-map: start: e76ee000 end: e76ef000 offset: 2000 load_base: 0 flags: 1 name: /data/libbacktrace_test.so
-map: start: e76ef000 end: e76f0000 offset: 3000 load_base: 0 flags: 3 name: /data/libbacktrace_test.so
-map: start: e7712000 end: e771f000 offset: 0 load_base: 0 flags: 5 name: /system/lib/libbacktrace.so
-map: start: e771f000 end: e7720000 offset: 0 load_base: 0 flags: 0 name: 
-map: start: e7720000 end: e7721000 offset: d000 load_base: 0 flags: 1 name: /system/lib/libbacktrace.so
-map: start: e7721000 end: e7722000 offset: e000 load_base: 0 flags: 3 name: /system/lib/libbacktrace.so
-map: start: e7761000 end: e7778000 offset: 0 load_base: 0 flags: 5 name: /system/lib/libutils.so
-map: start: e7778000 end: e7779000 offset: 16000 load_base: 0 flags: 1 name: /system/lib/libutils.so
-map: start: e7779000 end: e777a000 offset: 17000 load_base: 0 flags: 3 name: /system/lib/libutils.so
-map: start: e77a5000 end: e782d000 offset: 0 load_base: 0 flags: 5 name: /system/lib/libc.so
-map: start: e782d000 end: e7831000 offset: 87000 load_base: 0 flags: 1 name: /system/lib/libc.so
-map: start: e7831000 end: e7833000 offset: 8b000 load_base: 0 flags: 3 name: /system/lib/libc.so
-map: start: e7833000 end: e7834000 offset: 0 load_base: 0 flags: 3 name: [anon:.bss]
-map: start: e7834000 end: e7835000 offset: 0 load_base: 0 flags: 1 name: [anon:.bss]
-map: start: e7835000 end: e783b000 offset: 0 load_base: 0 flags: 3 name: [anon:.bss]
-map: start: e7845000 end: e8437000 offset: 0 load_base: 2b000 flags: 5 name: /system/lib/libLLVM.so
-map: start: e8437000 end: e8438000 offset: 0 load_base: 0 flags: 0 name: 
-map: start: e8438000 end: e848a000 offset: bf2000 load_base: 0 flags: 1 name: /system/lib/libLLVM.so
-map: start: e848a000 end: e848b000 offset: c44000 load_base: 0 flags: 3 name: /system/lib/libLLVM.so
-map: start: e848b000 end: e84a1000 offset: 0 load_base: 0 flags: 3 name: [anon:.bss]
-map: start: e84eb000 end: e84f7000 offset: 0 load_base: 0 flags: 5 name: /system/lib/libcutils.so
-map: start: e84f7000 end: e84f8000 offset: 0 load_base: 0 flags: 0 name: 
-map: start: e84f8000 end: e84f9000 offset: c000 load_base: 0 flags: 1 name: /system/lib/libcutils.so
-map: start: e84f9000 end: e84fa000 offset: d000 load_base: 0 flags: 3 name: /system/lib/libcutils.so
-map: start: e852e000 end: e85b3000 offset: 0 load_base: 2000 flags: 5 name: /system/lib/libc++.so
-map: start: e85b3000 end: e85b4000 offset: 0 load_base: 0 flags: 0 name: 
-map: start: e85b4000 end: e85b8000 offset: 85000 load_base: 0 flags: 1 name: /system/lib/libc++.so
-map: start: e85b8000 end: e85b9000 offset: 89000 load_base: 0 flags: 3 name: /system/lib/libc++.so
-map: start: e85b9000 end: e85ba000 offset: 0 load_base: 0 flags: 3 name: [anon:.bss]
-map: start: e85ce000 end: e85cf000 offset: 0 load_base: 0 flags: 3 name: [anon:linker_alloc]
-map: start: e85e4000 end: e85e5000 offset: 0 load_base: 0 flags: 3 name: [anon:linker_alloc_small_objects]
-map: start: e8607000 end: e8608000 offset: 0 load_base: 0 flags: 1 name: [anon:linker_alloc]
-map: start: e8680000 end: e8700000 offset: 0 load_base: 0 flags: 3 name: [anon:libc_malloc]
-map: start: e870d000 end: e8719000 offset: 0 load_base: 0 flags: 3 name: 
-map: start: e8719000 end: e871b000 offset: 0 load_base: 0 flags: 1 name: [anon:atexit handlers]
-map: start: e871b000 end: e873b000 offset: 0 load_base: 0 flags: 32769 name: /dev/__properties__/u:object_r:default_prop:s0
-map: start: e873b000 end: e875b000 offset: 0 load_base: 0 flags: 32769 name: /dev/__properties__/properties_serial
-map: start: e875b000 end: e875c000 offset: 0 load_base: 0 flags: 3 name: [anon:linker_alloc_vector]
-map: start: e875c000 end: e875d000 offset: 0 load_base: 0 flags: 3 name: [anon:arc4random data]
-map: start: e875d000 end: e875e000 offset: 0 load_base: 0 flags: 3 name: [anon:linker_alloc]
-map: start: e875e000 end: e875f000 offset: 0 load_base: 0 flags: 1 name: [anon:linker_alloc]
-map: start: e875f000 end: e877f000 offset: 0 load_base: 0 flags: 32769 name: /dev/__properties__/u:object_r:debug_prop:s0
-map: start: e877f000 end: e879f000 offset: 0 load_base: 0 flags: 32769 name: /dev/__properties__/properties_serial
-map: start: e879f000 end: e87a0000 offset: 0 load_base: 0 flags: 3 name: [anon:linker_alloc_vector]
-map: start: e87a0000 end: e87a1000 offset: 0 load_base: 0 flags: 3 name: [anon:linker_alloc_small_objects]
-map: start: e87a1000 end: e87a2000 offset: 0 load_base: 0 flags: 3 name: [anon:linker_alloc_vector]
-map: start: e87a2000 end: e87a3000 offset: 0 load_base: 0 flags: 0 name: 
-map: start: e87a3000 end: e87a4000 offset: 0 load_base: 0 flags: 3 name: 
-map: start: e87a4000 end: e87a5000 offset: 0 load_base: 0 flags: 0 name: 
-map: start: e87a5000 end: e87a6000 offset: 0 load_base: 0 flags: 3 name: [anon:linker_alloc_lob]
-map: start: e87a6000 end: e87a7000 offset: 0 load_base: 0 flags: 1 name: [anon:linker_alloc]
-map: start: e87a7000 end: e87a8000 offset: 0 load_base: 0 flags: 3 name: [anon:linker_alloc_vector]
-map: start: e87a8000 end: e87a9000 offset: 0 load_base: 0 flags: 3 name: [anon:linker_alloc_small_objects]
-map: start: e87a9000 end: e87aa000 offset: 0 load_base: 0 flags: 3 name: [anon:linker_alloc_vector]
-map: start: e87aa000 end: e87ab000 offset: 0 load_base: 0 flags: 3 name: [anon:linker_alloc_small_objects]
-map: start: e87ab000 end: e87ac000 offset: 0 load_base: 0 flags: 1 name: [anon:atexit handlers]
-map: start: e87ac000 end: e87ad000 offset: 0 load_base: 0 flags: 0 name: [anon:thread signal stack guard page]
-map: start: e87ad000 end: e87af000 offset: 0 load_base: 0 flags: 3 name: [anon:thread signal stack]
-map: start: e87af000 end: e87b0000 offset: 0 load_base: 0 flags: 3 name: [anon:arc4random data]
-map: start: e87b0000 end: e880d000 offset: 0 load_base: 0 flags: 5 name: /system/bin/linker
-map: start: e880d000 end: e880f000 offset: 5c000 load_base: 0 flags: 1 name: /system/bin/linker
-map: start: e880f000 end: e8810000 offset: 5e000 load_base: 0 flags: 3 name: /system/bin/linker
-map: start: e8810000 end: e8812000 offset: 0 load_base: 0 flags: 3 name: 
-map: start: e8812000 end: e8813000 offset: 0 load_base: 0 flags: 1 name: 
-map: start: e8813000 end: e8815000 offset: 0 load_base: 0 flags: 3 name: 
-map: start: ff886000 end: ff8a9000 offset: 0 load_base: 0 flags: 3 name: [stack]
-map: start: ffff0000 end: ffff1000 offset: 0 load_base: 0 flags: 5 name: [vectors]
-registers: 64 34868affdc8871e8150000001c0000001c000000150000000e00000007000000e08771e834868aff2354d2aa24f9ffffdc8871e88c8771e875b86ee778ba6ee7
+map: start: aad19000 end: aad6c000 offset: 0 load_bias: 0 flags: 5 name: /data/backtrace_test32
+map: start: aad6c000 end: aad6e000 offset: 52000 load_bias: 0 flags: 1 name: /data/backtrace_test32
+map: start: aad6e000 end: aad6f000 offset: 54000 load_bias: 0 flags: 3 name: /data/backtrace_test32
+map: start: e7380000 end: e7400000 offset: 0 load_bias: 0 flags: 3 name: [anon:libc_malloc]
+map: start: e745f000 end: e7463000 offset: 0 load_bias: 0 flags: 5 name: /system/lib/libnetd_client.so
+map: start: e7463000 end: e7464000 offset: 3000 load_bias: 0 flags: 1 name: /system/lib/libnetd_client.so
+map: start: e7464000 end: e7465000 offset: 4000 load_bias: 0 flags: 3 name: /system/lib/libnetd_client.so
+map: start: e7480000 end: e7500000 offset: 0 load_bias: 0 flags: 3 name: [anon:libc_malloc]
+map: start: e7558000 end: e756c000 offset: 0 load_bias: 0 flags: 5 name: /system/lib/libunwind.so
+map: start: e756c000 end: e756d000 offset: 0 load_bias: 0 flags: 0 name: 
+map: start: e756d000 end: e756e000 offset: 14000 load_bias: 0 flags: 1 name: /system/lib/libunwind.so
+map: start: e756e000 end: e756f000 offset: 15000 load_bias: 0 flags: 3 name: /system/lib/libunwind.so
+map: start: e756f000 end: e75b5000 offset: 0 load_bias: 0 flags: 3 name: [anon:.bss]
+map: start: e75d4000 end: e75e1000 offset: 0 load_bias: 0 flags: 5 name: /system/lib/libbase.so
+map: start: e75e1000 end: e75e2000 offset: c000 load_bias: 0 flags: 1 name: /system/lib/libbase.so
+map: start: e75e2000 end: e75e3000 offset: d000 load_bias: 0 flags: 3 name: /system/lib/libbase.so
+map: start: e7600000 end: e7616000 offset: 0 load_bias: 0 flags: 5 name: /system/lib/liblzma.so
+map: start: e7616000 end: e7617000 offset: 15000 load_bias: 0 flags: 1 name: /system/lib/liblzma.so
+map: start: e7617000 end: e7618000 offset: 16000 load_bias: 0 flags: 3 name: /system/lib/liblzma.so
+map: start: e7618000 end: e761d000 offset: 0 load_bias: 0 flags: 3 name: [anon:.bss]
+map: start: e7647000 end: e7656000 offset: 0 load_bias: 0 flags: 5 name: /system/lib/liblog.so
+map: start: e7656000 end: e7657000 offset: e000 load_bias: 0 flags: 1 name: /system/lib/liblog.so
+map: start: e7657000 end: e7658000 offset: f000 load_bias: 0 flags: 3 name: /system/lib/liblog.so
+map: start: e7681000 end: e76a2000 offset: 0 load_bias: 0 flags: 5 name: /system/lib/libm.so
+map: start: e76a2000 end: e76a3000 offset: 20000 load_bias: 0 flags: 1 name: /system/lib/libm.so
+map: start: e76a3000 end: e76a4000 offset: 21000 load_bias: 0 flags: 3 name: /system/lib/libm.so
+map: start: e76eb000 end: e76ee000 offset: 0 load_bias: 0 flags: 5 name: /data/libbacktrace_test.so
+map: start: e76ee000 end: e76ef000 offset: 2000 load_bias: 0 flags: 1 name: /data/libbacktrace_test.so
+map: start: e76ef000 end: e76f0000 offset: 3000 load_bias: 0 flags: 3 name: /data/libbacktrace_test.so
+map: start: e7712000 end: e771f000 offset: 0 load_bias: 0 flags: 5 name: /system/lib/libbacktrace.so
+map: start: e771f000 end: e7720000 offset: 0 load_bias: 0 flags: 0 name: 
+map: start: e7720000 end: e7721000 offset: d000 load_bias: 0 flags: 1 name: /system/lib/libbacktrace.so
+map: start: e7721000 end: e7722000 offset: e000 load_bias: 0 flags: 3 name: /system/lib/libbacktrace.so
+map: start: e7761000 end: e7778000 offset: 0 load_bias: 0 flags: 5 name: /system/lib/libutils.so
+map: start: e7778000 end: e7779000 offset: 16000 load_bias: 0 flags: 1 name: /system/lib/libutils.so
+map: start: e7779000 end: e777a000 offset: 17000 load_bias: 0 flags: 3 name: /system/lib/libutils.so
+map: start: e77a5000 end: e782d000 offset: 0 load_bias: 0 flags: 5 name: /system/lib/libc.so
+map: start: e782d000 end: e7831000 offset: 87000 load_bias: 0 flags: 1 name: /system/lib/libc.so
+map: start: e7831000 end: e7833000 offset: 8b000 load_bias: 0 flags: 3 name: /system/lib/libc.so
+map: start: e7833000 end: e7834000 offset: 0 load_bias: 0 flags: 3 name: [anon:.bss]
+map: start: e7834000 end: e7835000 offset: 0 load_bias: 0 flags: 1 name: [anon:.bss]
+map: start: e7835000 end: e783b000 offset: 0 load_bias: 0 flags: 3 name: [anon:.bss]
+map: start: e7845000 end: e8437000 offset: 0 load_bias: 2b000 flags: 5 name: /system/lib/libLLVM.so
+map: start: e8437000 end: e8438000 offset: 0 load_bias: 0 flags: 0 name: 
+map: start: e8438000 end: e848a000 offset: bf2000 load_bias: 0 flags: 1 name: /system/lib/libLLVM.so
+map: start: e848a000 end: e848b000 offset: c44000 load_bias: 0 flags: 3 name: /system/lib/libLLVM.so
+map: start: e848b000 end: e84a1000 offset: 0 load_bias: 0 flags: 3 name: [anon:.bss]
+map: start: e84eb000 end: e84f7000 offset: 0 load_bias: 0 flags: 5 name: /system/lib/libcutils.so
+map: start: e84f7000 end: e84f8000 offset: 0 load_bias: 0 flags: 0 name: 
+map: start: e84f8000 end: e84f9000 offset: c000 load_bias: 0 flags: 1 name: /system/lib/libcutils.so
+map: start: e84f9000 end: e84fa000 offset: d000 load_bias: 0 flags: 3 name: /system/lib/libcutils.so
+map: start: e852e000 end: e85b3000 offset: 0 load_bias: 2000 flags: 5 name: /system/lib/libc++.so
+map: start: e85b3000 end: e85b4000 offset: 0 load_bias: 0 flags: 0 name: 
+map: start: e85b4000 end: e85b8000 offset: 85000 load_bias: 0 flags: 1 name: /system/lib/libc++.so
+map: start: e85b8000 end: e85b9000 offset: 89000 load_bias: 0 flags: 3 name: /system/lib/libc++.so
+map: start: e85b9000 end: e85ba000 offset: 0 load_bias: 0 flags: 3 name: [anon:.bss]
+map: start: e85ce000 end: e85cf000 offset: 0 load_bias: 0 flags: 3 name: [anon:linker_alloc]
+map: start: e85e4000 end: e85e5000 offset: 0 load_bias: 0 flags: 3 name: [anon:linker_alloc_small_objects]
+map: start: e8607000 end: e8608000 offset: 0 load_bias: 0 flags: 1 name: [anon:linker_alloc]
+map: start: e8680000 end: e8700000 offset: 0 load_bias: 0 flags: 3 name: [anon:libc_malloc]
+map: start: e870d000 end: e8719000 offset: 0 load_bias: 0 flags: 3 name: 
+map: start: e8719000 end: e871b000 offset: 0 load_bias: 0 flags: 1 name: [anon:atexit handlers]
+map: start: e871b000 end: e873b000 offset: 0 load_bias: 0 flags: 32769 name: /dev/__properties__/u:object_r:default_prop:s0
+map: start: e873b000 end: e875b000 offset: 0 load_bias: 0 flags: 32769 name: /dev/__properties__/properties_serial
+map: start: e875b000 end: e875c000 offset: 0 load_bias: 0 flags: 3 name: [anon:linker_alloc_vector]
+map: start: e875c000 end: e875d000 offset: 0 load_bias: 0 flags: 3 name: [anon:arc4random data]
+map: start: e875d000 end: e875e000 offset: 0 load_bias: 0 flags: 3 name: [anon:linker_alloc]
+map: start: e875e000 end: e875f000 offset: 0 load_bias: 0 flags: 1 name: [anon:linker_alloc]
+map: start: e875f000 end: e877f000 offset: 0 load_bias: 0 flags: 32769 name: /dev/__properties__/u:object_r:debug_prop:s0
+map: start: e877f000 end: e879f000 offset: 0 load_bias: 0 flags: 32769 name: /dev/__properties__/properties_serial
+map: start: e879f000 end: e87a0000 offset: 0 load_bias: 0 flags: 3 name: [anon:linker_alloc_vector]
+map: start: e87a0000 end: e87a1000 offset: 0 load_bias: 0 flags: 3 name: [anon:linker_alloc_small_objects]
+map: start: e87a1000 end: e87a2000 offset: 0 load_bias: 0 flags: 3 name: [anon:linker_alloc_vector]
+map: start: e87a2000 end: e87a3000 offset: 0 load_bias: 0 flags: 0 name: 
+map: start: e87a3000 end: e87a4000 offset: 0 load_bias: 0 flags: 3 name: 
+map: start: e87a4000 end: e87a5000 offset: 0 load_bias: 0 flags: 0 name: 
+map: start: e87a5000 end: e87a6000 offset: 0 load_bias: 0 flags: 3 name: [anon:linker_alloc_lob]
+map: start: e87a6000 end: e87a7000 offset: 0 load_bias: 0 flags: 1 name: [anon:linker_alloc]
+map: start: e87a7000 end: e87a8000 offset: 0 load_bias: 0 flags: 3 name: [anon:linker_alloc_vector]
+map: start: e87a8000 end: e87a9000 offset: 0 load_bias: 0 flags: 3 name: [anon:linker_alloc_small_objects]
+map: start: e87a9000 end: e87aa000 offset: 0 load_bias: 0 flags: 3 name: [anon:linker_alloc_vector]
+map: start: e87aa000 end: e87ab000 offset: 0 load_bias: 0 flags: 3 name: [anon:linker_alloc_small_objects]
+map: start: e87ab000 end: e87ac000 offset: 0 load_bias: 0 flags: 1 name: [anon:atexit handlers]
+map: start: e87ac000 end: e87ad000 offset: 0 load_bias: 0 flags: 0 name: [anon:thread signal stack guard page]
+map: start: e87ad000 end: e87af000 offset: 0 load_bias: 0 flags: 3 name: [anon:thread signal stack]
+map: start: e87af000 end: e87b0000 offset: 0 load_bias: 0 flags: 3 name: [anon:arc4random data]
+map: start: e87b0000 end: e880d000 offset: 0 load_bias: 0 flags: 5 name: /system/bin/linker
+map: start: e880d000 end: e880f000 offset: 5c000 load_bias: 0 flags: 1 name: /system/bin/linker
+map: start: e880f000 end: e8810000 offset: 5e000 load_bias: 0 flags: 3 name: /system/bin/linker
+map: start: e8810000 end: e8812000 offset: 0 load_bias: 0 flags: 3 name: 
+map: start: e8812000 end: e8813000 offset: 0 load_bias: 0 flags: 1 name: 
+map: start: e8813000 end: e8815000 offset: 0 load_bias: 0 flags: 3 name: 
+map: start: ff886000 end: ff8a9000 offset: 0 load_bias: 0 flags: 3 name: [stack]
+map: start: ffff0000 end: ffff1000 offset: 0 load_bias: 0 flags: 5 name: [vectors]
+ucontext: 104 000000000000000000000000000000000000000000000000000000000000000034868affdc8871e8150000001c0000001c000000150000000e00000007000000e08771e834868aff2354d2aa24f9ffffdc8871e88c8771e875b86ee778ba6ee70000000000000000
 stack: start: e8715000 end: e8719000 size: 16384 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000dc8871e87dba6ee734868affdc8871e8dc8871e85dba6ee7070000000e000000150000001c000000dc8871e85dba6ee71c000000150000000e00000007000000100000000c0000000800000004000000ddb86ee75dba6ee7dc8871e804000000080000000c00000010000000dc8871e85dba6ee7100000000c000000080000000400000008000000060000000400000002000000288871e835b96ee75dba6ee7dc8871e802000000040000000600000008000000dc8871e85dba6ee70800000006000000040000000200000004000000030000000200000001000000708871e88db96ee75dba6ee7dc8871e801000000020000000300000004000000dc8871e85dba6ee70400000003000000020000000100000004000000208971e8208971e878000000e87d00003dba6ee75dba6ee7dc8871e878000000c5807ce7fc7183e734868aff78868aff78868aff34868aff34868aff78868affe0879437208971e84154d2aa0020000034868aff34868aff34868aff78000000c9b87ee7b1b87ee7a3f47be7288971e8b1b87ee7208971e800000000f83481e800000000e97d0000e87d000000000000005071e82039000000100000000000000000000000000000000000002354d2aa34868aff00000000002071e801000000000000000000000000000000708971e8208971e8000000000000000000000000e0879437000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
 function: start: 0 end: e76eb835 name: unknown_start
 function: start: e76eb835 end: e76eb88d name: test_level_four
diff --git a/libbacktrace/testdata/arm/offline_testdata_for_libGLESv2_adreno b/libbacktrace/testdata/arm/offline_testdata_for_libGLESv2_adreno
new file mode 100644
index 0000000..d7c186e
--- /dev/null
+++ b/libbacktrace/testdata/arm/offline_testdata_for_libGLESv2_adreno
@@ -0,0 +1,6 @@
+pid: 7288 tid: 31656
+ucontext: 104 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f017cc00000000356241cc0000000000000000
+map: start: cc361000 end: cc758000 offset: 0 load_bias: 9000 flags: 5 name: /vendor/lib/egl/libGLESv2_adreno.so
+stack: start: cc17f234 end: cc17f258 size: 36 0000000000000000000000000000000000000000000000000000000000000000b36141cc
+function: start: be1f0 end: be304 name: EsxContext::Clear(unsigned int, unsigned int, unsigned int, EsxClearValues*)
+function: start: be058 end: be1f0 name: EsxContext::ClearBuffersForDebug()
diff --git a/libbacktrace/testdata/arm/offline_testdata_for_libandroid_runtime b/libbacktrace/testdata/arm/offline_testdata_for_libandroid_runtime
new file mode 100644
index 0000000..54f3525
--- /dev/null
+++ b/libbacktrace/testdata/arm/offline_testdata_for_libandroid_runtime
@@ -0,0 +1,6 @@
+pid: 7288 tid: 31656
+ucontext: 104 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003069fed80000000049dcf6f10000000000000000
+map: start: f1f10000 end: f2049000 offset: 0 load_bias: 10000 flags: 5 name: /system/lib/libandroid_runtime.so
+stack: start: d8fe6948 end: d8fe6958 size: 16 000000000000000000000000e7dcf6f1
+function: start: 6dbf9 end: 6dce5 name: android::AndroidRuntime::javaThreadShell
+function: start: 6dce5 end: 6dd79 name: android::AndroidRuntime::javaCreateThreadEtc
diff --git a/libbacktrace/testdata/arm/offline_testdata_for_libart b/libbacktrace/testdata/arm/offline_testdata_for_libart
index 63f6a07..c1369ff 100644
--- a/libbacktrace/testdata/arm/offline_testdata_for_libart
+++ b/libbacktrace/testdata/arm/offline_testdata_for_libart
@@ -1,10 +1,10 @@
 pid: 32232 tid: 32233
-registers: 64 000000000000000000000000000000006473602451b3e2e700000000d82fd1ff5600000000908eec00000000d42dd1ff00000000c02dd1ff617171e9617171e9
-map: start: e9380000 end: e9766000 offset: 0 load_base: b000 flags: 5 name: /system/lib/libart.so
-stack: start: ffd12dc0 end: ffd16000 size: 12864 00000000000c5024070000000300000005070a0a0100000051b3e2e700000000d82fd1ff560000004c2ed1ff000000000000000081b771e9d82fd1ff000000004c2ed1ff0c2ed1ff40a8d27024bf76e900908eec000000000834d1ff0000000000000000000000000d000000050000000000000000000000080000000101d1ff44b8bfeb4b0000000000000000000000e8b8952400000000fc2ed1ff4fb3e2e7bc49ac6f00908eecb02ed1ffd82fd1ff040000008c908eec942fd1ffd5c141e9d82fd1ff4fb3e2e7542fd1ff336c68e940000000400000007030d1fff031d1ff00000000bc49ac6f5c30d1ff942fd1ff842fd1ffd82fd1ff00000000b8f1786f4fb3e2e7610d67e9d82fd1ff4fb3e2e77880adeb7980adeb7a80adeb7b80adeb7c80adeb7d80adeb7e80adeb7f80adeb000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007430d1ff02000000e8b89524e8d895240200000000908eec5c30d1ffbc49ac6f4fb3e2e74030d1ffe8d8952400000000b8f1786fbc49ac6f332367e94fb3e2e701000000637171e9637171e9000000005c30d1ff8430d1ffe0c08bec882fd1ff4fb3e2e70200000004000000942fd1ffe8b8952400908eec58d8952458d895247fbd69e90500000000400fe40100000000908eec58d89524060000009c86bd6f6b876fe900908eece0c08bec00008eec0000000000000000000000000000000044b8bfeb4b000000009be86f040000000038d1ff01000000c8e7446f060000000000000000908eec30d89524e8b895249c86bd6f7893476f00908eec00000000358c6fe970400fe4116e71e9a0285a6fa4d49c6f4489bd6f30d8952458d89524e8d8952400908eeca431d1ff2c31d1ffb75861e90100000000908eec30528bec409181e958d8952431abed6fac33576fb438d1ff030000007800502400000000a0005024060000007893476f00908eec000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004489bd6f78005024d00c5024a0005024a431d1ff2c31d1ff9b99aa71a4d49c6f30d8952400000000e8d895244489bd6fa8e5bc6fc8b895240100000000000000b033d1ff56000000637171e900000000d00c5024c8b89524000000000100000000000000b033d1ff560000006431d1ffa431d1ff000000009fb671e9b033d1ff00000000a431d1ff6431d1ffc431d1ff000000000000000081b771e9b033d1ff00000000c431d1ff8431d1ff01000000020000002429000001000000dc501b002033d1ff0100000018f9736f0100000000908eec58d8952440f180e9a8ec01245b215ce8a4d49c6f00908eec0832d1ffb033d1ff040000008c908eeca832d1ffabc141e9b033d1ff5b215ce82832d1ffb033d1ff080000008c908eec000000000035d1ff0834d1ffa832d1ffa4d49c6f04000000cca312e800908eec6832d1ffb033d1ff0834d1ff6bc354e9b033d1ff5b215ce8cca312e800908eec8832d1ffb033d1ff0834d1ff6bc354e900908eeca4d49c6f44b8bfeb1833d1ff000000006832d1ffb033d1ff478054e9b033d1ff1b8054e90834d1ffa4d49c6f0000000000000000000000000000000008000000000000000834d1ff0000000000000000000000000000000000000000000000000000000058d895240000000000000000000000000000000000000000000000000000000058d89524b17e54e98c56af6f00000000a4d49c6f288944e800908eec00000000d032d1ff0000000000000000000000000000000000000000000000007e8ea6c358a58cec00f580e90834d1ffa4d49c6f58d8952400908eecb033d1ffe9100000da8844e8833c70e9e9100000b033d1ff0200000058d8952408b1796f0200000000908eecda8844e82c34d1ff00908eece9100000005d70e9070000007d1300006034d1ff98d170e9b033d1ff0834d1ff148844e800908eecb033d1ffa034d1ffa833d1ff0100000044b8bfeb41f252e9e91fdeeaa491deea000000004700000001000000d9c4ddea0000000000000000b834d1ff00b051ff0834d1ff00908eecf833d1ffa034d1ff148844e800000000020000004d4c53e900000000000000000000000000908eec44b8bfeb0834d1ff3835d1ff148844e85035d1ffbb936fe90000000044b8bfebb033d1ffda8844e8148844e8000000000d0000005a0000007d137d13d00c502400000000600480240400000070048024f80c5024170000000100000002000000000000000040000000000000d0018024d00c502400000000600480240000000070048024f80c5024000000000000000000000000000000000000000000000000d001802465906fe97b2e5ce8000000000300000000000000881388131a00000001000000000000004cdd76e9010000007b2e5ce8020000009835d1ff5835d1ffc435d1ff010000000000000000000000010000000000000000dd76e90834d1ff0d0000000100000000000000000000005035d1ff9036d1ff00000000a435d1ff7e8ea6c3080000000000000000000000000000000000000038cb7e7044b8bfeb7d2e5ce800000000c037d1ff5600000000908eec00000000cc35d1ff55af71e9e0285a6f040000000800000001000000a437d1ff010000001c73d870000000000000000043000000339768e9040000006c36d1ff0e000000b436d1ff8cc97e706c36d1ff0e00000018eb01243173d870040000007d2e5ce800000000c037d1ff5600000000000000cc35d1ff637171e90000000018eb012402000000010000007d2e5ce800000000c037d1ff560000004436d1ff000000000000000081b771e9c037d1ff000000004436d1ff0436d1ff00e68dec0800000001000000a437d1ff010000001c73d870000000000000000043000000339768e9040000006c36d1ff0e000000b436d1ff8cc97e706c36d1ff0e000000adf861e918eb01243173d870040000007b2e5ce844b8bfeb00908eeca836d1ffc037d1ff040000008c908eec7c37d1ffd5c141e9c037d1ff7b2e5ce80000000000908eecd036d1ff00000000b038d1ff183ad1ff0000000044b8bfeb1038d1ff7c37d1ff6c37d1ffc037d1ff7b2e5ce8000000007b2e5ce8610d67e9c037d1ff7b2e5ce8280477e99835456f960300009a35456f10aa5e6f9a35456f9835456f68b85e6f881e77e9b30a47e9e81382e94c95b4ec7100000000908eec9c908eec30528bec1038d1ff7b2e5ce800000000c78469e91038d1ff0aeb3b52208989ec150645e9010000001038d1ff6c37d1ff44b8bfeb6c37d1ff00000000d837d1ff1038d1ff7b2e5ce8000000006c38d1ff8f0b67e97b2e5ce818eb012400000000000000000838d1ff7b2e5ce802000000040000007c37d1ff18eb01249835456f00000000901e77e9180000000300000000908eec480000004800000043000000640477e97669747954687265070000001a00000060eb0124000000000000000000000000a500000044b8bfeb1038d1ff00908eeceeac73e943000000640477e9901e77e9e6ac73e961705ce96c38d1ff18eb012400908eeceeac73e943000000640477e9000059008bc95ce900908eec30528bec409181e900908eec430000005900000000528bec409181e900004300710000000300000030528bec89c75ce944b8bfebe2050000103dd1ff03000000a3f6a7eb89c75ce96c38d1ff7e8ea6c389c75ce997f5a7eb710000000000000030528bec7e8ea6c3e83cd1ff2079002488beba6ff0ca726f5600000000908eec000000005439d1ff8b1db8aa803a89ec7e8ea6c3000000009173d870ec55af6f00000000010000004892796f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003801802488beba6ff0ca726f56000000000000005439d1ff9d3daa71cc55af6f7039d1ff0b0000006839d1ff7d2e5ce800000000483bd1ff637171e900000000207900240b00000058a58cec40f180e9010000007d2e5ce800000000483bd1ff56000000cc39d1ff000000000000000081b771e9483bd1ff00000000cc39d1ff8c39d1ff05000000050000000c000000040000000100000012000000958c00000100000074d73500483bd1ff01000000e880746f0100000000908eec903ad1ff40f180e97e02000000908eec2079002400000000383ad1ff7b2e5ce8cc55af6f00908eec303ad1ff483bd1ff040000008c908eec043bd1ffd5c141e9483bd1ff7b2e5ce840f180e900908eec583ad1ff00000000000000000000000000000000cc55af6f983bd1ff043bd1fff43ad1ff483bd1ff7b2e5ce8000000007b2e5ce8610d67e9483bd1ff7b2e5ce8280477e94892796f860100004e92796f00a088ec4e92796f4892796f18a688ec881e77e9b30a47e978e388ec4c95b4ec2100000000908eec9c908eec30528bec983bd1ff7b2e5ce800000000c78469e9983bd1ff06b005fdf0298aec150645e901000000983bd1fff43ad1ffcc55af6ff43ad1ff00000000603bd1ff983bd1ff7b2e5ce800000000f43bd1ff8f0b67e97b2e5ce8e00864e80000000000000000903bd1ff7b2e5ce80200000004000000043bd1ff207900249c908eec04000000583bd1ff603bd1ff4892796f04000000ac3bd1ff01000000901e77e917885ee9010000004d5cb1eb485cb1eb00908eec4892796f00000000000000000000000000004300cc55af6f983bd1ff00908eeceeac73e943000000640477e9901e77e9e6ac73e961705ce9f43bd1ff55000000ac3bd1ffeeac73e943000000640477e900005900e3225ce900908eec30528bec409181e900908eec430000005900000000528bec409181e9000043005500000078e388ec2100000009215ce901000000ce3fb8aae83cd1ff40420f00a3f6a7eb09215ce9f43bd1ff7e8ea6c309215ce9ed0ea8eb2100000075270000003289ec0000000030528becef665c74db0e42e911ac58e99daf58e9103dd1ff010000007e8ea6c31b000000385cd1ff385cd1ff02000000103dd1ff0300000087e26deae43cd1ff0200000001000000a31eb8aa020000007c3cd1ff18ac89ec1dac89ec0f000000fc94b4ec7c3cd1ff18ac89ec7e8ea6c3e83cd1ff884dd1ff741ab8aaa81ab8aa000000000700000004000000e43cd1ff3b19b8aa000000000000000000000000000000000000000000000000884dd1ff0000000001000000844dd1ff7e8ea6c3f065b4ec00fd0000205db8aa308789ec010000000000000004000000b8e78aec18ac89ec005db8aa2ceab2eb101082e935000000000000000800000001100000ba5bd1ff99000000b8e78aec205db8aa508789ec030000000000000004000000e2050000108789ec00000000d991aeece583aeec10d0acec10d0acec50d0acec6170705f70726f63657373333200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080a4ec00000000001000009dfe6feb00000000975673eb000000002516b8aa844dd1ff08000000a84dd1ff0000000000000000000000007c4dd1ff3b996feb00000000000000000000000000000000000000005015b8aad45cb8aadc5cb8aae85cb8aa804dd1ff0000000015b8aeec08000000ba5bd1ffd45bd1ffe05bd1ffee5bd1ff0f5cd1ff335cd1ff355cd1ff385cd1ff00000000535cd1ff6f5cd1ff825cd1ff9d5cd1ffbf5cd1ffd45cd1ffee5cd1ff015dd1ff1c5dd1ffe35ed1fffc5ed1ff465fd1ffc55fd1ff0000000010000000d6b0270006000000001000001100000064000000030000003400b8aa040000002000000005000000090000000700000000d0adec080000000000000009000000ec14b8aa0b000000752700000c000000752700000d000000752700000e000000752700001700000000000000190000007c4ed1ff1a0000001f0000001f000000de5fd1ff0f0000008c4ed1ff00000000000000000000000086da76325883c1a6b44d586d68c7843576386c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000636f6d2e6578616d706c652e7375646f67616d65000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005f3d2f73797374656d2f62696e2f6170705f70726f63657373333200414e44524f49445f444154413d2f6461746100444f574e4c4f41445f43414348453d2f646174612f636163686500414e44524f49445f534f434b45545f7a79676f74655f7365636f6e646172793d3900414e44524f49445f524f4f543d2f73797374656d00415345435f4d4f554e54504f494e543d2f6d6e742f6173656300414e44524f49445f424f4f544c4f474f3d3100414e44524f49445f4153534554533d2f73797374656d2f61707000424f4f54434c415353504154483d2f73797374656d2f6672616d65776f726b2f636f72652d6f6a2e6a61723a2f73797374656d2f6672616d65776f726b2f636f72652d6c69626172742e6a61723a2f73797374656d2f6672616d65776f726b2f636f6e7363727970742e6a61723a2f73797374656d2f6672616d65776f726b2f6f6b687474702e6a61723a2f73797374656d2f6672616d65776f726b2f6c65676163792d746573742e6a61723a2f73797374656d2f6672616d65776f726b2f626f756e6379636173746c652e6a61723a2f73797374656d2f6672616d65776f726b2f6578742e6a61723a2f73797374656d2f6672616d65776f726b2f6672616d65776f726b2e6a61723a2f73797374656d2f6672616d65776f726b2f74656c6570686f6e792d636f6d6d6f6e2e6a61723a2f73797374656d2f6672616d65776f726b2f766f69702d636f6d6d6f6e2e6a61723a2f73797374656d2f6672616d65776f726b2f696d732d636f6d6d6f6e2e6a61723a2f73797374656d2f6672616d65776f726b2f6170616368652d786d6c2e6a61723a2f73797374656d2f6672616d65776f726b2f6f72672e6170616368652e687474702e6c65676163792e626f6f742e6a617200414e44524f49445f53544f524147453d2f73746f7261676500504154483d2f7362696e3a2f73797374656d2f7362696e3a2f73797374656d2f62696e3a2f73797374656d2f7862696e3a2f76656e646f722f62696e3a2f76656e646f722f7862696e0053595354454d534552564552434c415353504154483d2f73797374656d2f6672616d65776f726b2f73657276696365732e6a61723a2f73797374656d2f6672616d65776f726b2f65746865726e65742d736572766963652e6a61723a2f73797374656d2f6672616d65776f726b2f776966692d736572766963652e6a61720045585445524e414c5f53544f524147453d2f736463617264002f73797374656d2f62696e2f6170705f70726f636573733332000000000000000000
+ucontext: 104 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006473602451b3e2e700000000d82fd1ff5600000000908eec00000000d42dd1ff00000000c02dd1ff617171e9617171e90000000000000000
+map: start: e9380000 end: e9766000 offset: 0 load_bias: b000 flags: 5 name: /system/lib/libart.so
+stack: start: ffd12dc0 end: ffd1306c size: 684 00000000000c5024070000000300000005070a0a0100000051b3e2e700000000d82fd1ff560000004c2ed1ff000000000000000081b771e9d82fd1ff000000004c2ed1ff0c2ed1ff40a8d27024bf76e900908eec000000000834d1ff0000000000000000000000000d000000050000000000000000000000080000000101d1ff44b8bfeb4b0000000000000000000000e8b8952400000000fc2ed1ff4fb3e2e7bc49ac6f00908eecb02ed1ffd82fd1ff040000008c908eec942fd1ffd5c141e9d82fd1ff4fb3e2e7542fd1ff336c68e940000000400000007030d1fff031d1ff00000000bc49ac6f5c30d1ff942fd1ff842fd1ffd82fd1ff00000000b8f1786f4fb3e2e7610d67e9d82fd1ff4fb3e2e77880adeb7980adeb7a80adeb7b80adeb7c80adeb7d80adeb7e80adeb7f80adeb000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007430d1ff02000000e8b89524e8d895240200000000908eec5c30d1ffbc49ac6f4fb3e2e74030d1ffe8d8952400000000b8f1786fbc49ac6f332367e94fb3e2e701000000637171e9637171e9000000005c30d1ff8430d1ffe0c08bec882fd1ff4fb3e2e70200000004000000942fd1ffe8b8952400908eec58d8952458d895247fbd69e90500000000400fe40100000000908eec58d89524060000009c86bd6f6b876fe900908eece0c08bec00008eec0000000000000000000000000000000044b8bfeb4b000000009be86f040000000038d1ff01000000c8e7446f060000000000000000908eec30d89524e8b895249c86bd6f7893476f00908eec00000000358c6fe970400fe4116e71e9a0285a6fa4d49c6f4489bd6f30d8952458d89524e8d8952400908eeca431d1ff2c31d1ffb75861e90100000000908eec30528bec409181e958d89524
 function: start: 3a2121 end: 3a217a name: art_quick_invoke_stub_internal
 function: start: 3a66a5 end: 3a6787 name: art_quick_invoke_static_stub
 function: start: a7129 end: a72f1 name: art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char const*)
 function: start: 2fbd35 end: 2fc789 name: art::InvokeWithArgArray(art::ScopedObjectAccessAlreadyRunnable const&, art::ArtMethod*, art::ArgArray*, art::JValue*, char const*)
 function: start: 2fcf75 end: 2fd88d name: art::InvokeMethod(art::ScopedObjectAccessAlreadyRunnable const&, _jobject*, _jobject*, _jobject*, unsigned int)
-function: start: 2a089d end: 2a08bb name: art::Method_invoke(_JNIEnv*, _jobject*, _jobject*, _jobject*)
\ No newline at end of file
+function: start: 2a089d end: 2a08bb name: art::Method_invoke(_JNIEnv*, _jobject*, _jobject*, _jobject*)
diff --git a/libbacktrace/testdata/arm64/eglSubDriverAndroid.so b/libbacktrace/testdata/arm64/eglSubDriverAndroid.so
new file mode 100644
index 0000000..10ce06b
--- /dev/null
+++ b/libbacktrace/testdata/arm64/eglSubDriverAndroid.so
Binary files differ
diff --git a/libbacktrace/testdata/aarch64/libbacktrace_test_eh_frame.so b/libbacktrace/testdata/arm64/libbacktrace_test_eh_frame.so
similarity index 100%
rename from libbacktrace/testdata/aarch64/libbacktrace_test_eh_frame.so
rename to libbacktrace/testdata/arm64/libbacktrace_test_eh_frame.so
Binary files differ
diff --git a/libbacktrace/testdata/arm64/libskia.so b/libbacktrace/testdata/arm64/libskia.so
new file mode 100644
index 0000000..ef1a6a1
--- /dev/null
+++ b/libbacktrace/testdata/arm64/libskia.so
Binary files differ
diff --git a/libbacktrace/testdata/arm64/offline_testdata b/libbacktrace/testdata/arm64/offline_testdata
new file mode 100644
index 0000000..cee9f72
--- /dev/null
+++ b/libbacktrace/testdata/arm64/offline_testdata
@@ -0,0 +1,107 @@
+pid: 32438 tid: 32439
+map: start: 557066e000 end: 55706ee000 offset: 0 load_bias: 0 flags: 5 name: /data/backtrace_test64
+map: start: 55706ef000 end: 55706f2000 offset: 80000 load_bias: 0 flags: 1 name: /data/backtrace_test64
+map: start: 55706f2000 end: 55706f3000 offset: 83000 load_bias: 0 flags: 3 name: /data/backtrace_test64
+map: start: 7014200000 end: 7014600000 offset: 0 load_bias: 0 flags: 3 name: [anon:libc_malloc]
+map: start: 701464c000 end: 701465c000 offset: 0 load_bias: 0 flags: 5 name: /system/lib64/libcutils.so
+map: start: 701465c000 end: 701465d000 offset: 0 load_bias: 0 flags: 0 name: 
+map: start: 701465d000 end: 701465e000 offset: 10000 load_bias: 0 flags: 1 name: /system/lib64/libcutils.so
+map: start: 701465e000 end: 701465f000 offset: 11000 load_bias: 0 flags: 3 name: /system/lib64/libcutils.so
+map: start: 7014691000 end: 70146b5000 offset: 0 load_bias: 0 flags: 5 name: /system/lib64/liblzma.so
+map: start: 70146b5000 end: 70146b6000 offset: 23000 load_bias: 0 flags: 1 name: /system/lib64/liblzma.so
+map: start: 70146b6000 end: 70146b7000 offset: 24000 load_bias: 0 flags: 3 name: /system/lib64/liblzma.so
+map: start: 70146b7000 end: 70146bc000 offset: 0 load_bias: 0 flags: 3 name: [anon:.bss]
+map: start: 70146c9000 end: 70158b5000 offset: 0 load_bias: af000 flags: 5 name: /system/lib64/libLLVM.so
+map: start: 70158b5000 end: 701596b000 offset: 11eb000 load_bias: 0 flags: 1 name: /system/lib64/libLLVM.so
+map: start: 701596b000 end: 701596c000 offset: 12a1000 load_bias: 0 flags: 3 name: /system/lib64/libLLVM.so
+map: start: 701596c000 end: 701599f000 offset: 0 load_bias: 0 flags: 3 name: [anon:.bss]
+map: start: 70159c2000 end: 70159f9000 offset: 0 load_bias: 0 flags: 5 name: /system/lib64/libm.so
+map: start: 70159f9000 end: 70159fa000 offset: 36000 load_bias: 0 flags: 1 name: /system/lib64/libm.so
+map: start: 70159fa000 end: 70159fb000 offset: 37000 load_bias: 0 flags: 3 name: /system/lib64/libm.so
+map: start: 7015a1e000 end: 7015a2e000 offset: 0 load_bias: 0 flags: 5 name: /system/lib64/libbacktrace.so
+map: start: 7015a2e000 end: 7015a2f000 offset: f000 load_bias: 0 flags: 1 name: /system/lib64/libbacktrace.so
+map: start: 7015a2f000 end: 7015a30000 offset: 10000 load_bias: 0 flags: 3 name: /system/lib64/libbacktrace.so
+map: start: 7015a5e000 end: 7015a7d000 offset: 0 load_bias: 1000 flags: 5 name: /system/lib64/libutils.so
+map: start: 7015a7d000 end: 7015a7e000 offset: 0 load_bias: 0 flags: 0 name: 
+map: start: 7015a7e000 end: 7015a7f000 offset: 1f000 load_bias: 0 flags: 1 name: /system/lib64/libutils.so
+map: start: 7015a7f000 end: 7015a80000 offset: 20000 load_bias: 0 flags: 3 name: /system/lib64/libutils.so
+map: start: 7015a99000 end: 7015b6d000 offset: 0 load_bias: 9000 flags: 5 name: /system/lib64/libc++.so
+map: start: 7015b6d000 end: 7015b6e000 offset: 0 load_bias: 0 flags: 0 name: 
+map: start: 7015b6e000 end: 7015b76000 offset: d4000 load_bias: 0 flags: 1 name: /system/lib64/libc++.so
+map: start: 7015b76000 end: 7015b77000 offset: dc000 load_bias: 0 flags: 3 name: /system/lib64/libc++.so
+map: start: 7015b77000 end: 7015b7a000 offset: 0 load_bias: 0 flags: 3 name: [anon:.bss]
+map: start: 7015b81000 end: 7015b92000 offset: 0 load_bias: 0 flags: 5 name: /system/lib64/liblog.so
+map: start: 7015b92000 end: 7015b93000 offset: 10000 load_bias: 0 flags: 1 name: /system/lib64/liblog.so
+map: start: 7015b93000 end: 7015b94000 offset: 11000 load_bias: 0 flags: 3 name: /system/lib64/liblog.so
+map: start: 7015be3000 end: 7015ca3000 offset: 0 load_bias: 0 flags: 5 name: /system/lib64/libc.so
+map: start: 7015ca3000 end: 7015ca9000 offset: bf000 load_bias: 0 flags: 1 name: /system/lib64/libc.so
+map: start: 7015ca9000 end: 7015cab000 offset: c5000 load_bias: 0 flags: 3 name: /system/lib64/libc.so
+map: start: 7015cab000 end: 7015cac000 offset: 0 load_bias: 0 flags: 3 name: [anon:.bss]
+map: start: 7015cac000 end: 7015cad000 offset: 0 load_bias: 0 flags: 1 name: [anon:.bss]
+map: start: 7015cad000 end: 7015cb4000 offset: 0 load_bias: 0 flags: 3 name: [anon:.bss]
+map: start: 7015cf5000 end: 7015cf6000 offset: 0 load_bias: 0 flags: 5 name: /data/libbacktrace_test.so
+map: start: 7015cf6000 end: 7015cf7000 offset: 0 load_bias: 0 flags: 1 name: /data/libbacktrace_test.so
+map: start: 7015cf7000 end: 7015cf8000 offset: 1000 load_bias: 0 flags: 3 name: /data/libbacktrace_test.so
+map: start: 7015d1f000 end: 7015d39000 offset: 0 load_bias: 0 flags: 5 name: /system/lib64/libunwind.so
+map: start: 7015d39000 end: 7015d3a000 offset: 19000 load_bias: 0 flags: 1 name: /system/lib64/libunwind.so
+map: start: 7015d3a000 end: 7015d3b000 offset: 1a000 load_bias: 0 flags: 3 name: /system/lib64/libunwind.so
+map: start: 7015d3b000 end: 7015da4000 offset: 0 load_bias: 0 flags: 3 name: [anon:.bss]
+map: start: 7015de8000 end: 7015df7000 offset: 0 load_bias: 0 flags: 5 name: /system/lib64/libbase.so
+map: start: 7015df7000 end: 7015df8000 offset: 0 load_bias: 0 flags: 0 name: 
+map: start: 7015df8000 end: 7015df9000 offset: f000 load_bias: 0 flags: 1 name: /system/lib64/libbase.so
+map: start: 7015df9000 end: 7015dfa000 offset: 10000 load_bias: 0 flags: 3 name: /system/lib64/libbase.so
+map: start: 7015e35000 end: 7015e36000 offset: 0 load_bias: 0 flags: 3 name: [anon:linker_alloc]
+map: start: 7015e4f000 end: 7015e50000 offset: 0 load_bias: 0 flags: 3 name: [anon:linker_alloc_small_objects]
+map: start: 7015f11000 end: 7015f13000 offset: 0 load_bias: 0 flags: 5 name: /system/lib64/libnetd_client.so
+map: start: 7015f13000 end: 7015f14000 offset: 1000 load_bias: 0 flags: 1 name: /system/lib64/libnetd_client.so
+map: start: 7015f14000 end: 7015f15000 offset: 2000 load_bias: 0 flags: 3 name: /system/lib64/libnetd_client.so
+map: start: 7015f6c000 end: 7015f79000 offset: 0 load_bias: 0 flags: 3 name: 
+map: start: 7015f79000 end: 7015f99000 offset: 0 load_bias: 0 flags: 32769 name: /dev/__properties__/u:object_r:default_prop:s0
+map: start: 7015f99000 end: 7015f9a000 offset: 0 load_bias: 0 flags: 3 name: [anon:linker_alloc_vector]
+map: start: 7015f9a000 end: 7015f9b000 offset: 0 load_bias: 0 flags: 3 name: [anon:linker_alloc_small_objects]
+map: start: 7015fa6000 end: 7015fa7000 offset: 0 load_bias: 0 flags: 3 name: [anon:linker_alloc_vector]
+map: start: 7015fa8000 end: 7015fa9000 offset: 0 load_bias: 0 flags: 1 name: [anon:linker_alloc]
+map: start: 7015faf000 end: 7015fcf000 offset: 0 load_bias: 0 flags: 32769 name: /dev/__properties__/properties_serial
+map: start: 7015fcf000 end: 7015fd0000 offset: 0 load_bias: 0 flags: 3 name: [anon:linker_alloc_vector]
+map: start: 7015fd0000 end: 7015fd1000 offset: 0 load_bias: 0 flags: 3 name: [anon:linker_alloc_small_objects]
+map: start: 7015fd1000 end: 7015fd2000 offset: 0 load_bias: 0 flags: 3 name: [anon:linker_alloc_vector]
+map: start: 7015fd4000 end: 7015fd7000 offset: 0 load_bias: 0 flags: 3 name: 
+map: start: 7015fd7000 end: 7015fdb000 offset: 0 load_bias: 0 flags: 1 name: [anon:atexit handlers]
+map: start: 7015fdb000 end: 7015fdc000 offset: 0 load_bias: 0 flags: 3 name: [anon:linker_alloc]
+map: start: 7015fdc000 end: 7015fdd000 offset: 0 load_bias: 0 flags: 3 name: 
+map: start: 7015fdd000 end: 7015fde000 offset: 0 load_bias: 0 flags: 1 name: [anon:linker_alloc]
+map: start: 7015fde000 end: 7015ffe000 offset: 0 load_bias: 0 flags: 32769 name: /dev/__properties__/u:object_r:debug_prop:s0
+map: start: 7015ffe000 end: 701601e000 offset: 0 load_bias: 0 flags: 32769 name: /dev/__properties__/properties_serial
+map: start: 701601e000 end: 701601f000 offset: 0 load_bias: 0 flags: 3 name: [anon:linker_alloc_vector]
+map: start: 701601f000 end: 7016020000 offset: 0 load_bias: 0 flags: 0 name: 
+map: start: 7016020000 end: 7016021000 offset: 0 load_bias: 0 flags: 3 name: 
+map: start: 7016021000 end: 7016022000 offset: 0 load_bias: 0 flags: 0 name: 
+map: start: 7016022000 end: 7016023000 offset: 0 load_bias: 0 flags: 3 name: [anon:linker_alloc_lob]
+map: start: 7016023000 end: 7016025000 offset: 0 load_bias: 0 flags: 1 name: [anon:linker_alloc]
+map: start: 7016025000 end: 7016026000 offset: 0 load_bias: 0 flags: 3 name: [anon:linker_alloc_small_objects]
+map: start: 7016026000 end: 7016027000 offset: 0 load_bias: 0 flags: 3 name: [anon:linker_alloc_vector]
+map: start: 7016027000 end: 7016028000 offset: 0 load_bias: 0 flags: 3 name: [anon:linker_alloc_small_objects]
+map: start: 7016028000 end: 7016029000 offset: 0 load_bias: 0 flags: 3 name: [anon:arc4random data]
+map: start: 7016029000 end: 701602a000 offset: 0 load_bias: 0 flags: 3 name: [anon:linker_alloc_small_objects]
+map: start: 701602a000 end: 701602b000 offset: 0 load_bias: 0 flags: 1 name: [anon:atexit handlers]
+map: start: 701602b000 end: 701602c000 offset: 0 load_bias: 0 flags: 0 name: [anon:thread signal stack guard page]
+map: start: 701602c000 end: 7016030000 offset: 0 load_bias: 0 flags: 3 name: [anon:thread signal stack]
+map: start: 7016030000 end: 7016031000 offset: 0 load_bias: 0 flags: 3 name: [anon:arc4random data]
+map: start: 7016031000 end: 7016033000 offset: 0 load_bias: 0 flags: 5 name: [vdso]
+map: start: 7016033000 end: 70160dd000 offset: 0 load_bias: 0 flags: 5 name: /system/bin/linker64
+map: start: 70160dd000 end: 70160e0000 offset: a9000 load_bias: 0 flags: 1 name: /system/bin/linker64
+map: start: 70160e0000 end: 70160e1000 offset: ac000 load_bias: 0 flags: 3 name: /system/bin/linker64
+map: start: 70160e1000 end: 70160e4000 offset: 0 load_bias: 0 flags: 3 name: 
+map: start: 70160e4000 end: 70160e5000 offset: 0 load_bias: 0 flags: 1 name: 
+map: start: 70160e5000 end: 70160e8000 offset: 0 load_bias: 0 flags: 3 name: 
+map: start: 7fd8baf000 end: 7fd8be6000 offset: 0 load_bias: 0 flags: 3 name: [stack]
+ucontext: 464 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f838bed87f0000000e0000000000000015000000000000001c00000000000000ec59cf1570000000b863fd15700000005064fd15700000000000000000000000ec59cf15700000000200000000000000b863fd1570000000144abed87f0000006064fd15700000005064fd157000000000010000000000005826bed87f000000d86fcf15700000006057cf157000000000000000000000005064fd15700000005064fd15700000005064fd1570000000b67e00000000000040fd677055000000d064fd15700000000030fd157000000002000000000000000100000000000000fcb58a56000000000063fd15700000009857cf1570000000c062fd15700000001c5acf157000000000000000000000000000000000000000
+stack: start: 7015fd3000 end: 7015fd7000 size: 16384 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f838bed87f0000004038bed87f000000b863fd1570000000b863fd1570000000b863fd1570000000ec59cf15700000001c000000150000000e000000070000003063fd15700000001c58cf1570000000b863fd1570000000ec59cf1570000000100000000c00000008000000040000006063fd15700000007c58cf1570000000b863fd1570000000ec59cf1570000000080000000600000004000000020000009063fd1570000000dc58cf1570000000b863fd1570000000ec59cf157000000004000000030000000200000001000000d063fd1570000000c459cf15700000000100000000000000144abed87f0000004038bed87f0000004038bed87f000000144abed87f000000d3aec914588f4bcd1064fd157000000074fd6770550000004038bed87f0000004038bed87f000000ec84c41570000000e484c41570000000c484c4157000000000000000000000004064fd15700000004015c01570000000b67e0000000000000000000000000000705a0e1670000000185b0e167000000000000000000000000000000000000000705a0e16700000000000000000000000b77e0000b67e000000000000550000000030fd157000000050340000000000000010000000000000000000000000000000b222147000000000102a14700000000000000000000000000000000000000040fd6770550000004038bed87f000000000000000000000000a0fa1570000000010000000000000000000000000000000000000000000000e864fd15700000005064fd1570000000000000000000000000000000000000000000000000000000d3aec914588f4bcd0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
+function: start: 0 end: 7015cf5760 name: unknown_start
+function: start: 7015cf5760 end: 7015cf57cc name: test_level_four
+function: start: 7015cf57cc end: 7015cf582c name: test_level_three
+function: start: 7015cf582c end: 7015cf588c name: test_level_two
+function: start: 7015cf588c end: 7015cf58ec name: test_level_one
+function: start: 7015cf58ec end: 7015cf5968 name: test_recursive_call
+function: start: 7015cf5968 end: ffffffffffffffff name: test_get_context_and_wait
+function: start: ffffffffffffffff end: ffffffffffffffff name: unknown_end
diff --git a/libbacktrace/testdata/arm64/offline_testdata_for_eglSubDriverAndroid b/libbacktrace/testdata/arm64/offline_testdata_for_eglSubDriverAndroid
new file mode 100644
index 0000000..673e30e
--- /dev/null
+++ b/libbacktrace/testdata/arm64/offline_testdata_for_eglSubDriverAndroid
@@ -0,0 +1,6 @@
+pid: 12276 tid: 12303
+map: start: 7b8c01e000 end: 7b8c030000 offset: 0 load_bias: 0 flags: 5 name: /vendor/lib64/egl/eglSubDriverAndroid.so
+ucontext: 464 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004070158c7b00000000000000000000001070158c7b000000647f028c7b00000000000000000000000000000000000000
+stack: start: 7b8c157020 end: 7b8c157050 size: 48 00000000000000000000000000000000000000000000000000000000000000000000000000000000547e028c7b000000
+function: start: 9ed8 end: a1b0 name: EglAndroidWindowSurface::Initialize(EglAndroidConfig*, int const*)
+function: start: 9dcc end: 9ed8 name: EglAndroidWindowSurface::Create(ANativeWindow*, EglAndroidConfig*, EglAndroidWindowSurface**, int const*)
diff --git a/libbacktrace/testdata/arm64/offline_testdata_for_libskia b/libbacktrace/testdata/arm64/offline_testdata_for_libskia
new file mode 100644
index 0000000..da820c0
--- /dev/null
+++ b/libbacktrace/testdata/arm64/offline_testdata_for_libskia
@@ -0,0 +1,6 @@
+pid: 32232 tid: 32233
+map: start: 7c24c80000 end: 7c25413000 offset: 0 load_bias: 5f000 flags: 5 name: /system/lib64/libskia.so
+ucontext: 464 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b04b158c7b0000000000000000000000504b158c7b0000000c9a18257c00000000000000000000000000000000000000
+stack: start: 7b8c154b80 end: 7b8c154bc0 size: 64 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ec43f2247c000000
+function: start: 568970 end: 568c08 name: SkScalerContext_FreeType::generateImage(SkGlyph const&)
+function: start: 30330c end: 3044b0 name: SkScalerContext::getImage(SkGlyph const&)
diff --git a/libbacktrace/testdata/x86/offline_testdata b/libbacktrace/testdata/x86/offline_testdata
index 3e4e06c..920b338 100644
--- a/libbacktrace/testdata/x86/offline_testdata
+++ b/libbacktrace/testdata/x86/offline_testdata
@@ -1,76 +1,76 @@
 pid: 34545 tid: 34546
-map: start: f705a000 end: f705c000 offset: 0 load_base: 0 flags: 3 name: 
-map: start: f705c000 end: f707f000 offset: 0 load_base: 0 flags: 5 name: /ssd/android/aosp_master/out/host/linux-x86/lib/liblzma.so
-map: start: f707f000 end: f7080000 offset: 22000 load_base: 0 flags: 1 name: /ssd/android/aosp_master/out/host/linux-x86/lib/liblzma.so
-map: start: f7080000 end: f7081000 offset: 23000 load_base: 0 flags: 3 name: /ssd/android/aosp_master/out/host/linux-x86/lib/liblzma.so
-map: start: f7081000 end: f7088000 offset: 0 load_base: 0 flags: 3 name: 
-map: start: f7088000 end: f7230000 offset: 0 load_base: 0 flags: 5 name: /lib/i386-linux-gnu/libc-2.19.so
-map: start: f7230000 end: f7231000 offset: 1a8000 load_base: 0 flags: 0 name: /lib/i386-linux-gnu/libc-2.19.so
-map: start: f7231000 end: f7233000 offset: 1a8000 load_base: 0 flags: 1 name: /lib/i386-linux-gnu/libc-2.19.so
-map: start: f7233000 end: f7234000 offset: 1aa000 load_base: 0 flags: 3 name: /lib/i386-linux-gnu/libc-2.19.so
-map: start: f7234000 end: f7237000 offset: 0 load_base: 0 flags: 3 name: 
-map: start: f7237000 end: f727b000 offset: 0 load_base: 0 flags: 5 name: /lib/i386-linux-gnu/libm-2.19.so
-map: start: f727b000 end: f727c000 offset: 43000 load_base: 0 flags: 1 name: /lib/i386-linux-gnu/libm-2.19.so
-map: start: f727c000 end: f727d000 offset: 44000 load_base: 0 flags: 3 name: /lib/i386-linux-gnu/libm-2.19.so
-map: start: f727d000 end: f7299000 offset: 0 load_base: 0 flags: 5 name: /lib/i386-linux-gnu/libgcc_s.so.1
-map: start: f7299000 end: f729a000 offset: 1b000 load_base: 0 flags: 3 name: /lib/i386-linux-gnu/libgcc_s.so.1
-map: start: f729a000 end: f72b8000 offset: 0 load_base: 0 flags: 5 name: /lib/i386-linux-gnu/libtinfo.so.5.9
-map: start: f72b8000 end: f72b9000 offset: 1e000 load_base: 0 flags: 0 name: /lib/i386-linux-gnu/libtinfo.so.5.9
-map: start: f72b9000 end: f72bb000 offset: 1e000 load_base: 0 flags: 1 name: /lib/i386-linux-gnu/libtinfo.so.5.9
-map: start: f72bb000 end: f72bc000 offset: 20000 load_base: 0 flags: 3 name: /lib/i386-linux-gnu/libtinfo.so.5.9
-map: start: f72bc000 end: f72bd000 offset: 0 load_base: 0 flags: 3 name: 
-map: start: f72bd000 end: f72e0000 offset: 0 load_base: 0 flags: 5 name: /lib/i386-linux-gnu/libncurses.so.5.9
-map: start: f72e0000 end: f72e1000 offset: 22000 load_base: 0 flags: 1 name: /lib/i386-linux-gnu/libncurses.so.5.9
-map: start: f72e1000 end: f72e2000 offset: 23000 load_base: 0 flags: 3 name: /lib/i386-linux-gnu/libncurses.so.5.9
-map: start: f72e2000 end: f72e5000 offset: 0 load_base: 0 flags: 5 name: /lib/i386-linux-gnu/libdl-2.19.so
-map: start: f72e5000 end: f72e6000 offset: 2000 load_base: 0 flags: 1 name: /lib/i386-linux-gnu/libdl-2.19.so
-map: start: f72e6000 end: f72e7000 offset: 3000 load_base: 0 flags: 3 name: /lib/i386-linux-gnu/libdl-2.19.so
-map: start: f72e7000 end: f72ee000 offset: 0 load_base: 0 flags: 5 name: /lib/i386-linux-gnu/librt-2.19.so
-map: start: f72ee000 end: f72ef000 offset: 6000 load_base: 0 flags: 1 name: /lib/i386-linux-gnu/librt-2.19.so
-map: start: f72ef000 end: f72f0000 offset: 7000 load_base: 0 flags: 3 name: /lib/i386-linux-gnu/librt-2.19.so
-map: start: f72f0000 end: f7308000 offset: 0 load_base: 0 flags: 5 name: /lib/i386-linux-gnu/libpthread-2.19.so
-map: start: f7308000 end: f7309000 offset: 18000 load_base: 0 flags: 1 name: /lib/i386-linux-gnu/libpthread-2.19.so
-map: start: f7309000 end: f730a000 offset: 19000 load_base: 0 flags: 3 name: /lib/i386-linux-gnu/libpthread-2.19.so
-map: start: f730a000 end: f730c000 offset: 0 load_base: 0 flags: 3 name: 
-map: start: f732f000 end: f7331000 offset: 0 load_base: 0 flags: 3 name: 
-map: start: f7331000 end: f7425000 offset: 0 load_base: 0 flags: 5 name: /ssd/android/aosp_master/out/host/linux-x86/lib/libc++.so
-map: start: f7425000 end: f7426000 offset: f4000 load_base: 0 flags: 0 name: /ssd/android/aosp_master/out/host/linux-x86/lib/libc++.so
-map: start: f7426000 end: f742a000 offset: f4000 load_base: 0 flags: 1 name: /ssd/android/aosp_master/out/host/linux-x86/lib/libc++.so
-map: start: f742a000 end: f742b000 offset: f8000 load_base: 0 flags: 3 name: /ssd/android/aosp_master/out/host/linux-x86/lib/libc++.so
-map: start: f742b000 end: f742d000 offset: 0 load_base: 0 flags: 3 name: 
-map: start: f742d000 end: f7446000 offset: 0 load_base: 0 flags: 5 name: /ssd/android/aosp_master/out/host/linux-x86/lib/libunwind.so
-map: start: f7446000 end: f7447000 offset: 18000 load_base: 0 flags: 1 name: /ssd/android/aosp_master/out/host/linux-x86/lib/libunwind.so
-map: start: f7447000 end: f7448000 offset: 19000 load_base: 0 flags: 3 name: /ssd/android/aosp_master/out/host/linux-x86/lib/libunwind.so
-map: start: f7448000 end: f7457000 offset: 0 load_base: 0 flags: 3 name: 
-map: start: f7457000 end: f745c000 offset: 0 load_base: 0 flags: 5 name: /ssd/android/aosp_master/out/host/linux-x86/lib/liblog.so
-map: start: f745c000 end: f745d000 offset: 4000 load_base: 0 flags: 1 name: /ssd/android/aosp_master/out/host/linux-x86/lib/liblog.so
-map: start: f745d000 end: f745e000 offset: 5000 load_base: 0 flags: 3 name: /ssd/android/aosp_master/out/host/linux-x86/lib/liblog.so
-map: start: f745e000 end: f7467000 offset: 0 load_base: 0 flags: 5 name: /ssd/android/aosp_master/out/host/linux-x86/lib/libcutils.so
-map: start: f7467000 end: f7468000 offset: 9000 load_base: 0 flags: 0 name: /ssd/android/aosp_master/out/host/linux-x86/lib/libcutils.so
-map: start: f7468000 end: f7469000 offset: 9000 load_base: 0 flags: 1 name: /ssd/android/aosp_master/out/host/linux-x86/lib/libcutils.so
-map: start: f7469000 end: f746a000 offset: a000 load_base: 0 flags: 3 name: /ssd/android/aosp_master/out/host/linux-x86/lib/libcutils.so
-map: start: f746a000 end: f7477000 offset: 0 load_base: 0 flags: 5 name: /ssd/android/aosp_master/out/host/linux-x86/lib/libbase.so
-map: start: f7477000 end: f7478000 offset: c000 load_base: 0 flags: 1 name: /ssd/android/aosp_master/out/host/linux-x86/lib/libbase.so
-map: start: f7478000 end: f7479000 offset: d000 load_base: 0 flags: 3 name: /ssd/android/aosp_master/out/host/linux-x86/lib/libbase.so
-map: start: f7479000 end: f7489000 offset: 0 load_base: 0 flags: 5 name: /ssd/android/aosp_master/out/host/linux-x86/lib/libbacktrace.so
-map: start: f7489000 end: f748a000 offset: f000 load_base: 0 flags: 1 name: /ssd/android/aosp_master/out/host/linux-x86/lib/libbacktrace.so
-map: start: f748a000 end: f748b000 offset: 10000 load_base: 0 flags: 3 name: /ssd/android/aosp_master/out/host/linux-x86/lib/libbacktrace.so
-map: start: f748b000 end: f748c000 offset: 0 load_base: 0 flags: 3 name: 
-map: start: f748c000 end: f748d000 offset: 0 load_base: 0 flags: 5 name: /ssd/android/aosp_master/out/host/linux-x86/lib/libbacktrace_test.so
-map: start: f748d000 end: f748e000 offset: 0 load_base: 0 flags: 1 name: /ssd/android/aosp_master/out/host/linux-x86/lib/libbacktrace_test.so
-map: start: f748e000 end: f748f000 offset: 1000 load_base: 0 flags: 3 name: /ssd/android/aosp_master/out/host/linux-x86/lib/libbacktrace_test.so
-map: start: f748f000 end: f7491000 offset: 0 load_base: 0 flags: 3 name: 
-map: start: f7491000 end: f74b1000 offset: 0 load_base: 0 flags: 5 name: /lib/i386-linux-gnu/ld-2.19.so
-map: start: f74b1000 end: f74b2000 offset: 1f000 load_base: 0 flags: 1 name: /lib/i386-linux-gnu/ld-2.19.so
-map: start: f74b2000 end: f74b3000 offset: 20000 load_base: 0 flags: 3 name: /lib/i386-linux-gnu/ld-2.19.so
-map: start: f74b3000 end: f77c6000 offset: 0 load_base: 0 flags: 5 name: /ssd/android/aosp_master/out/host/linux-x86/nativetest/backtrace_test/backtrace_test32
-map: start: f77c6000 end: f77c7000 offset: 0 load_base: ffffe000 flags: 5 name: [vdso]
-map: start: f77c7000 end: f77d4000 offset: 313000 load_base: 0 flags: 1 name: /ssd/android/aosp_master/out/host/linux-x86/nativetest/backtrace_test/backtrace_test32
-map: start: f77d4000 end: f77d5000 offset: 320000 load_base: 0 flags: 3 name: /ssd/android/aosp_master/out/host/linux-x86/nativetest/backtrace_test/backtrace_test32
-map: start: f77d5000 end: f77d6000 offset: 0 load_base: 0 flags: 3 name: 
-map: start: f7ec6000 end: f7ee7000 offset: 0 load_base: 0 flags: 3 name: [heap]
-map: start: ffe4e000 end: ffe70000 offset: 0 load_base: 0 flags: 3 name: [stack]
-registers: 348 00000000abdae6fff83b7df704000000a6ec77f7abdae6ff00000000afdae6ff78dae6ff150000001c000000b8f132f7a0f132f7d0df48f7a0ca48f728d9e6ff000000008c8decf78c8decf7ceca48f7a8dae6ff8d8decf70a0000000000000014dae6ff8c8decf78c8decf78c8decf78c8decf78c8decf7a9dae6ff06000000c03a23f78c8decf78c8decf78c8decf78c8decf78c8decf78c8decf78c8decf78d8decf78d8decf7c03a23f7543b23f7000033f75f5f0ff7c03a23f7503423f77000000098000000020000000f2700006c0000000e00000080000000000000008c8decf7000000008c8decf77f03ffff0000ffffffffffff0000000000000000000000000000ffff040000000f27000008000000003023f78d8decf7f069ec000046bdaa308decf715537df7f83b7df78c8decf74c1c71f78c8decf715537df70000000000000000f069ecf7f83b7df75064ecf792e671f7f069ecf7
+map: start: f705a000 end: f705c000 offset: 0 load_bias: 0 flags: 3 name: 
+map: start: f705c000 end: f707f000 offset: 0 load_bias: 0 flags: 5 name: /ssd/android/aosp_master/out/host/linux-x86/lib/liblzma.so
+map: start: f707f000 end: f7080000 offset: 22000 load_bias: 0 flags: 1 name: /ssd/android/aosp_master/out/host/linux-x86/lib/liblzma.so
+map: start: f7080000 end: f7081000 offset: 23000 load_bias: 0 flags: 3 name: /ssd/android/aosp_master/out/host/linux-x86/lib/liblzma.so
+map: start: f7081000 end: f7088000 offset: 0 load_bias: 0 flags: 3 name: 
+map: start: f7088000 end: f7230000 offset: 0 load_bias: 0 flags: 5 name: /lib/i386-linux-gnu/libc-2.19.so
+map: start: f7230000 end: f7231000 offset: 1a8000 load_bias: 0 flags: 0 name: /lib/i386-linux-gnu/libc-2.19.so
+map: start: f7231000 end: f7233000 offset: 1a8000 load_bias: 0 flags: 1 name: /lib/i386-linux-gnu/libc-2.19.so
+map: start: f7233000 end: f7234000 offset: 1aa000 load_bias: 0 flags: 3 name: /lib/i386-linux-gnu/libc-2.19.so
+map: start: f7234000 end: f7237000 offset: 0 load_bias: 0 flags: 3 name: 
+map: start: f7237000 end: f727b000 offset: 0 load_bias: 0 flags: 5 name: /lib/i386-linux-gnu/libm-2.19.so
+map: start: f727b000 end: f727c000 offset: 43000 load_bias: 0 flags: 1 name: /lib/i386-linux-gnu/libm-2.19.so
+map: start: f727c000 end: f727d000 offset: 44000 load_bias: 0 flags: 3 name: /lib/i386-linux-gnu/libm-2.19.so
+map: start: f727d000 end: f7299000 offset: 0 load_bias: 0 flags: 5 name: /lib/i386-linux-gnu/libgcc_s.so.1
+map: start: f7299000 end: f729a000 offset: 1b000 load_bias: 0 flags: 3 name: /lib/i386-linux-gnu/libgcc_s.so.1
+map: start: f729a000 end: f72b8000 offset: 0 load_bias: 0 flags: 5 name: /lib/i386-linux-gnu/libtinfo.so.5.9
+map: start: f72b8000 end: f72b9000 offset: 1e000 load_bias: 0 flags: 0 name: /lib/i386-linux-gnu/libtinfo.so.5.9
+map: start: f72b9000 end: f72bb000 offset: 1e000 load_bias: 0 flags: 1 name: /lib/i386-linux-gnu/libtinfo.so.5.9
+map: start: f72bb000 end: f72bc000 offset: 20000 load_bias: 0 flags: 3 name: /lib/i386-linux-gnu/libtinfo.so.5.9
+map: start: f72bc000 end: f72bd000 offset: 0 load_bias: 0 flags: 3 name: 
+map: start: f72bd000 end: f72e0000 offset: 0 load_bias: 0 flags: 5 name: /lib/i386-linux-gnu/libncurses.so.5.9
+map: start: f72e0000 end: f72e1000 offset: 22000 load_bias: 0 flags: 1 name: /lib/i386-linux-gnu/libncurses.so.5.9
+map: start: f72e1000 end: f72e2000 offset: 23000 load_bias: 0 flags: 3 name: /lib/i386-linux-gnu/libncurses.so.5.9
+map: start: f72e2000 end: f72e5000 offset: 0 load_bias: 0 flags: 5 name: /lib/i386-linux-gnu/libdl-2.19.so
+map: start: f72e5000 end: f72e6000 offset: 2000 load_bias: 0 flags: 1 name: /lib/i386-linux-gnu/libdl-2.19.so
+map: start: f72e6000 end: f72e7000 offset: 3000 load_bias: 0 flags: 3 name: /lib/i386-linux-gnu/libdl-2.19.so
+map: start: f72e7000 end: f72ee000 offset: 0 load_bias: 0 flags: 5 name: /lib/i386-linux-gnu/librt-2.19.so
+map: start: f72ee000 end: f72ef000 offset: 6000 load_bias: 0 flags: 1 name: /lib/i386-linux-gnu/librt-2.19.so
+map: start: f72ef000 end: f72f0000 offset: 7000 load_bias: 0 flags: 3 name: /lib/i386-linux-gnu/librt-2.19.so
+map: start: f72f0000 end: f7308000 offset: 0 load_bias: 0 flags: 5 name: /lib/i386-linux-gnu/libpthread-2.19.so
+map: start: f7308000 end: f7309000 offset: 18000 load_bias: 0 flags: 1 name: /lib/i386-linux-gnu/libpthread-2.19.so
+map: start: f7309000 end: f730a000 offset: 19000 load_bias: 0 flags: 3 name: /lib/i386-linux-gnu/libpthread-2.19.so
+map: start: f730a000 end: f730c000 offset: 0 load_bias: 0 flags: 3 name: 
+map: start: f732f000 end: f7331000 offset: 0 load_bias: 0 flags: 3 name: 
+map: start: f7331000 end: f7425000 offset: 0 load_bias: 0 flags: 5 name: /ssd/android/aosp_master/out/host/linux-x86/lib/libc++.so
+map: start: f7425000 end: f7426000 offset: f4000 load_bias: 0 flags: 0 name: /ssd/android/aosp_master/out/host/linux-x86/lib/libc++.so
+map: start: f7426000 end: f742a000 offset: f4000 load_bias: 0 flags: 1 name: /ssd/android/aosp_master/out/host/linux-x86/lib/libc++.so
+map: start: f742a000 end: f742b000 offset: f8000 load_bias: 0 flags: 3 name: /ssd/android/aosp_master/out/host/linux-x86/lib/libc++.so
+map: start: f742b000 end: f742d000 offset: 0 load_bias: 0 flags: 3 name: 
+map: start: f742d000 end: f7446000 offset: 0 load_bias: 0 flags: 5 name: /ssd/android/aosp_master/out/host/linux-x86/lib/libunwind.so
+map: start: f7446000 end: f7447000 offset: 18000 load_bias: 0 flags: 1 name: /ssd/android/aosp_master/out/host/linux-x86/lib/libunwind.so
+map: start: f7447000 end: f7448000 offset: 19000 load_bias: 0 flags: 3 name: /ssd/android/aosp_master/out/host/linux-x86/lib/libunwind.so
+map: start: f7448000 end: f7457000 offset: 0 load_bias: 0 flags: 3 name: 
+map: start: f7457000 end: f745c000 offset: 0 load_bias: 0 flags: 5 name: /ssd/android/aosp_master/out/host/linux-x86/lib/liblog.so
+map: start: f745c000 end: f745d000 offset: 4000 load_bias: 0 flags: 1 name: /ssd/android/aosp_master/out/host/linux-x86/lib/liblog.so
+map: start: f745d000 end: f745e000 offset: 5000 load_bias: 0 flags: 3 name: /ssd/android/aosp_master/out/host/linux-x86/lib/liblog.so
+map: start: f745e000 end: f7467000 offset: 0 load_bias: 0 flags: 5 name: /ssd/android/aosp_master/out/host/linux-x86/lib/libcutils.so
+map: start: f7467000 end: f7468000 offset: 9000 load_bias: 0 flags: 0 name: /ssd/android/aosp_master/out/host/linux-x86/lib/libcutils.so
+map: start: f7468000 end: f7469000 offset: 9000 load_bias: 0 flags: 1 name: /ssd/android/aosp_master/out/host/linux-x86/lib/libcutils.so
+map: start: f7469000 end: f746a000 offset: a000 load_bias: 0 flags: 3 name: /ssd/android/aosp_master/out/host/linux-x86/lib/libcutils.so
+map: start: f746a000 end: f7477000 offset: 0 load_bias: 0 flags: 5 name: /ssd/android/aosp_master/out/host/linux-x86/lib/libbase.so
+map: start: f7477000 end: f7478000 offset: c000 load_bias: 0 flags: 1 name: /ssd/android/aosp_master/out/host/linux-x86/lib/libbase.so
+map: start: f7478000 end: f7479000 offset: d000 load_bias: 0 flags: 3 name: /ssd/android/aosp_master/out/host/linux-x86/lib/libbase.so
+map: start: f7479000 end: f7489000 offset: 0 load_bias: 0 flags: 5 name: /ssd/android/aosp_master/out/host/linux-x86/lib/libbacktrace.so
+map: start: f7489000 end: f748a000 offset: f000 load_bias: 0 flags: 1 name: /ssd/android/aosp_master/out/host/linux-x86/lib/libbacktrace.so
+map: start: f748a000 end: f748b000 offset: 10000 load_bias: 0 flags: 3 name: /ssd/android/aosp_master/out/host/linux-x86/lib/libbacktrace.so
+map: start: f748b000 end: f748c000 offset: 0 load_bias: 0 flags: 3 name: 
+map: start: f748c000 end: f748d000 offset: 0 load_bias: 0 flags: 5 name: /ssd/android/aosp_master/out/host/linux-x86/lib/libbacktrace_test.so
+map: start: f748d000 end: f748e000 offset: 0 load_bias: 0 flags: 1 name: /ssd/android/aosp_master/out/host/linux-x86/lib/libbacktrace_test.so
+map: start: f748e000 end: f748f000 offset: 1000 load_bias: 0 flags: 3 name: /ssd/android/aosp_master/out/host/linux-x86/lib/libbacktrace_test.so
+map: start: f748f000 end: f7491000 offset: 0 load_bias: 0 flags: 3 name: 
+map: start: f7491000 end: f74b1000 offset: 0 load_bias: 0 flags: 5 name: /lib/i386-linux-gnu/ld-2.19.so
+map: start: f74b1000 end: f74b2000 offset: 1f000 load_bias: 0 flags: 1 name: /lib/i386-linux-gnu/ld-2.19.so
+map: start: f74b2000 end: f74b3000 offset: 20000 load_bias: 0 flags: 3 name: /lib/i386-linux-gnu/ld-2.19.so
+map: start: f74b3000 end: f77c6000 offset: 0 load_bias: 0 flags: 5 name: /ssd/android/aosp_master/out/host/linux-x86/nativetest/backtrace_test/backtrace_test32
+map: start: f77c6000 end: f77c7000 offset: 0 load_bias: ffffe000 flags: 5 name: [vdso]
+map: start: f77c7000 end: f77d4000 offset: 313000 load_bias: 0 flags: 1 name: /ssd/android/aosp_master/out/host/linux-x86/nativetest/backtrace_test/backtrace_test32
+map: start: f77d4000 end: f77d5000 offset: 320000 load_bias: 0 flags: 3 name: /ssd/android/aosp_master/out/host/linux-x86/nativetest/backtrace_test/backtrace_test32
+map: start: f77d5000 end: f77d6000 offset: 0 load_bias: 0 flags: 3 name: 
+map: start: f7ec6000 end: f7ee7000 offset: 0 load_bias: 0 flags: 3 name: [heap]
+map: start: ffe4e000 end: ffe70000 offset: 0 load_bias: 0 flags: 3 name: [stack]
+ucontext: 96 0000000000000000000000000000000000000000abdae6ff00000000afdae6ff78dae6ff150000001c000000b8f132f7a0f132f7d0df48f7a0ca48f728d9e6ff000000000000000000000000ceca48f7a8dae6ff000000000000000000000000
 stack: start: f732c000 end: f7330000 size: 16384 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009d9d49f761b009f71e382ff7000000000000000000000000000000000000000002000000f6372ff704c82bf70000000000204bf7f0a908f7ceca48f728d9e6fff4a449f70000000020f332f720f332f7d0df48f7f8f132f794c748f720f332f70c144205978142a8d4be08f7d0df48f720f332f7a0ca48f71c000000150000000e000000070000001c000000a0ca48f7d0df48f748f232f739c848f7070000000e000000150000001c000000a0ca48f720f332f70000000000000000d0df48f720f332f7a0ca48f7100000000c000000080000000400000010000000a0ca48f7d0df48f798f232f7c9c848f704000000080000000c00000010000000a0ca48f720f332f70000000000000000d0df48f720f332f7a0ca48f70800000006000000040000000200000008000000a0ca48f7d0df48f7e8f232f759c948f702000000040000000600000008000000a0ca48f720f332f70000000000000000d0df48f720f332f7a0ca48f7040000000300000002000000010000000046bdaa00000000d0df48f738f332f77cca48f701000000020000000300000004000000a0ca48f720f332f700000000f83b7df7696d4df7d0df48f788dae6ff28d9e6ff28d9e6ff88dae6ff58f332f70046bdaa40fb32f7f83b7df758f332f78d6d4df728d9e6ff88dae6fff83b7df728d9e6ff28d9e6ff009030f728f432f7726f2ff728d9e6ff40fb32f740fb32f740fb32f790f332f700000000000000000000000000000000000000000000000000000000009030f740fb32f7000f3d0028f432f703b12c75032f144e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a06e2ff700000000000f3d00000000008e3f17f740fb32f7000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a03823f7c4fd32f700000000e0341df7e02e1df7e03d1df70000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040fb32f7188eecf740fb32f70100000030647cf70046bdaa28658876000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000060a705f7a4b130f7f2860000f1860000b0fb32f7ecffffff000000000000000090f332f7000000000800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ccfb32f70000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000c2638cfa7f3e1d0000000000000000000000000000000000406d4df728d9e6ff0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c032f7004000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
 function: start: 0 end: f748c740 name: unknown_start
 function: start: f748c740 end: f748c7c0 name: test_level_four
diff --git a/libbacktrace/testdata/x86_64/offline_testdata b/libbacktrace/testdata/x86_64/offline_testdata
index baf6450..c6bb241 100644
--- a/libbacktrace/testdata/x86_64/offline_testdata
+++ b/libbacktrace/testdata/x86_64/offline_testdata
@@ -1,87 +1,87 @@
 pid: 25683 tid: 25692
-map: start: 7fd5aa784000 end: 7fd5aa93e000 offset: 0 load_base: 0 flags: 5 name: /lib/x86_64-linux-gnu/libc-2.19.so
-map: start: 7fd5aa93e000 end: 7fd5aab3e000 offset: 1ba000 load_base: 0 flags: 0 name: /lib/x86_64-linux-gnu/libc-2.19.so
-map: start: 7fd5aab3e000 end: 7fd5aab42000 offset: 1ba000 load_base: 0 flags: 1 name: /lib/x86_64-linux-gnu/libc-2.19.so
-map: start: 7fd5aab42000 end: 7fd5aab44000 offset: 1be000 load_base: 0 flags: 3 name: /lib/x86_64-linux-gnu/libc-2.19.so
-map: start: 7fd5aab44000 end: 7fd5aab49000 offset: 0 load_base: 0 flags: 3 name: 
-map: start: 7fd5aab49000 end: 7fd5aac4e000 offset: 0 load_base: 0 flags: 5 name: /lib/x86_64-linux-gnu/libm-2.19.so
-map: start: 7fd5aac4e000 end: 7fd5aae4d000 offset: 105000 load_base: 0 flags: 0 name: /lib/x86_64-linux-gnu/libm-2.19.so
-map: start: 7fd5aae4d000 end: 7fd5aae4e000 offset: 104000 load_base: 0 flags: 1 name: /lib/x86_64-linux-gnu/libm-2.19.so
-map: start: 7fd5aae4e000 end: 7fd5aae4f000 offset: 105000 load_base: 0 flags: 3 name: /lib/x86_64-linux-gnu/libm-2.19.so
-map: start: 7fd5aae4f000 end: 7fd5aae65000 offset: 0 load_base: 0 flags: 5 name: /lib/x86_64-linux-gnu/libgcc_s.so.1
-map: start: 7fd5aae65000 end: 7fd5ab064000 offset: 16000 load_base: 0 flags: 0 name: /lib/x86_64-linux-gnu/libgcc_s.so.1
-map: start: 7fd5ab064000 end: 7fd5ab065000 offset: 15000 load_base: 0 flags: 3 name: /lib/x86_64-linux-gnu/libgcc_s.so.1
-map: start: 7fd5ab065000 end: 7fd5ab08a000 offset: 0 load_base: 0 flags: 5 name: /lib/x86_64-linux-gnu/libtinfo.so.5.9
-map: start: 7fd5ab08a000 end: 7fd5ab289000 offset: 25000 load_base: 0 flags: 0 name: /lib/x86_64-linux-gnu/libtinfo.so.5.9
-map: start: 7fd5ab289000 end: 7fd5ab28d000 offset: 24000 load_base: 0 flags: 1 name: /lib/x86_64-linux-gnu/libtinfo.so.5.9
-map: start: 7fd5ab28d000 end: 7fd5ab28e000 offset: 28000 load_base: 0 flags: 3 name: /lib/x86_64-linux-gnu/libtinfo.so.5.9
-map: start: 7fd5ab28e000 end: 7fd5ab2b0000 offset: 0 load_base: 0 flags: 5 name: /lib/x86_64-linux-gnu/libncurses.so.5.9
-map: start: 7fd5ab2b0000 end: 7fd5ab4af000 offset: 22000 load_base: 0 flags: 0 name: /lib/x86_64-linux-gnu/libncurses.so.5.9
-map: start: 7fd5ab4af000 end: 7fd5ab4b0000 offset: 21000 load_base: 0 flags: 1 name: /lib/x86_64-linux-gnu/libncurses.so.5.9
-map: start: 7fd5ab4b0000 end: 7fd5ab4b1000 offset: 22000 load_base: 0 flags: 3 name: /lib/x86_64-linux-gnu/libncurses.so.5.9
-map: start: 7fd5ab4b1000 end: 7fd5ab4b4000 offset: 0 load_base: 0 flags: 5 name: /lib/x86_64-linux-gnu/libdl-2.19.so
-map: start: 7fd5ab4b4000 end: 7fd5ab6b3000 offset: 3000 load_base: 0 flags: 0 name: /lib/x86_64-linux-gnu/libdl-2.19.so
-map: start: 7fd5ab6b3000 end: 7fd5ab6b4000 offset: 2000 load_base: 0 flags: 1 name: /lib/x86_64-linux-gnu/libdl-2.19.so
-map: start: 7fd5ab6b4000 end: 7fd5ab6b5000 offset: 3000 load_base: 0 flags: 3 name: /lib/x86_64-linux-gnu/libdl-2.19.so
-map: start: 7fd5ab6b5000 end: 7fd5ab6bc000 offset: 0 load_base: 0 flags: 5 name: /lib/x86_64-linux-gnu/librt-2.19.so
-map: start: 7fd5ab6bc000 end: 7fd5ab8bb000 offset: 7000 load_base: 0 flags: 0 name: /lib/x86_64-linux-gnu/librt-2.19.so
-map: start: 7fd5ab8bb000 end: 7fd5ab8bc000 offset: 6000 load_base: 0 flags: 1 name: /lib/x86_64-linux-gnu/librt-2.19.so
-map: start: 7fd5ab8bc000 end: 7fd5ab8bd000 offset: 7000 load_base: 0 flags: 3 name: /lib/x86_64-linux-gnu/librt-2.19.so
-map: start: 7fd5ab8bd000 end: 7fd5ab8d6000 offset: 0 load_base: 0 flags: 5 name: /lib/x86_64-linux-gnu/libpthread-2.19.so
-map: start: 7fd5ab8d6000 end: 7fd5abad5000 offset: 19000 load_base: 0 flags: 0 name: /lib/x86_64-linux-gnu/libpthread-2.19.so
-map: start: 7fd5abad5000 end: 7fd5abad6000 offset: 18000 load_base: 0 flags: 1 name: /lib/x86_64-linux-gnu/libpthread-2.19.so
-map: start: 7fd5abad6000 end: 7fd5abad7000 offset: 19000 load_base: 0 flags: 3 name: /lib/x86_64-linux-gnu/libpthread-2.19.so
-map: start: 7fd5abad7000 end: 7fd5abadb000 offset: 0 load_base: 0 flags: 3 name: 
-map: start: 7fd5abadb000 end: 7fd5abafe000 offset: 0 load_base: 0 flags: 5 name: /lib/x86_64-linux-gnu/ld-2.19.so
-map: start: 7fd5abb17000 end: 7fd5abb1a000 offset: 0 load_base: 0 flags: 3 name: 
-map: start: 7fd5abb1a000 end: 7fd5abb40000 offset: 0 load_base: 0 flags: 5 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/liblzma.so
-map: start: 7fd5abb40000 end: 7fd5abb41000 offset: 25000 load_base: 0 flags: 1 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/liblzma.so
-map: start: 7fd5abb41000 end: 7fd5abb42000 offset: 26000 load_base: 0 flags: 3 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/liblzma.so
-map: start: 7fd5abb42000 end: 7fd5abb4b000 offset: 0 load_base: 0 flags: 3 name: 
-map: start: 7fd5abb6a000 end: 7fd5abb70000 offset: 0 load_base: 0 flags: 3 name: 
-map: start: 7fd5abb70000 end: 7fd5abc62000 offset: 0 load_base: 0 flags: 5 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libc++.so
-map: start: 7fd5abc62000 end: 7fd5abc63000 offset: f2000 load_base: 0 flags: 0 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libc++.so
-map: start: 7fd5abc63000 end: 7fd5abc6b000 offset: f2000 load_base: 0 flags: 1 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libc++.so
-map: start: 7fd5abc6b000 end: 7fd5abc6c000 offset: fa000 load_base: 0 flags: 3 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libc++.so
-map: start: 7fd5abc6c000 end: 7fd5abc70000 offset: 0 load_base: 0 flags: 3 name: 
-map: start: 7fd5abc70000 end: 7fd5abc8d000 offset: 0 load_base: 0 flags: 5 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libunwind.so
-map: start: 7fd5abc8d000 end: 7fd5abc8e000 offset: 1c000 load_base: 0 flags: 1 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libunwind.so
-map: start: 7fd5abc8e000 end: 7fd5abc8f000 offset: 1d000 load_base: 0 flags: 3 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libunwind.so
-map: start: 7fd5abc8f000 end: 7fd5abcb8000 offset: 0 load_base: 0 flags: 3 name: 
-map: start: 7fd5abcb8000 end: 7fd5abcbe000 offset: 0 load_base: 0 flags: 5 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/liblog.so
-map: start: 7fd5abcbe000 end: 7fd5abcbf000 offset: 6000 load_base: 0 flags: 0 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/liblog.so
-map: start: 7fd5abcbf000 end: 7fd5abcc0000 offset: 6000 load_base: 0 flags: 1 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/liblog.so
-map: start: 7fd5abcc0000 end: 7fd5abcc1000 offset: 7000 load_base: 0 flags: 3 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/liblog.so
-map: start: 7fd5abcc1000 end: 7fd5abcc2000 offset: 0 load_base: 0 flags: 3 name: 
-map: start: 7fd5abcc2000 end: 7fd5abccd000 offset: 0 load_base: 0 flags: 5 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libcutils.so
-map: start: 7fd5abccd000 end: 7fd5abcce000 offset: b000 load_base: 0 flags: 0 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libcutils.so
-map: start: 7fd5abcce000 end: 7fd5abccf000 offset: b000 load_base: 0 flags: 1 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libcutils.so
-map: start: 7fd5abccf000 end: 7fd5abcd0000 offset: c000 load_base: 0 flags: 3 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libcutils.so
-map: start: 7fd5abcd0000 end: 7fd5abcdf000 offset: 0 load_base: 0 flags: 5 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libbase.so
-map: start: 7fd5abcdf000 end: 7fd5abce0000 offset: f000 load_base: 0 flags: 0 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libbase.so
-map: start: 7fd5abce0000 end: 7fd5abce1000 offset: f000 load_base: 0 flags: 1 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libbase.so
-map: start: 7fd5abce1000 end: 7fd5abce2000 offset: 10000 load_base: 0 flags: 3 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libbase.so
-map: start: 7fd5abce2000 end: 7fd5abcf5000 offset: 0 load_base: 0 flags: 5 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libbacktrace.so
-map: start: 7fd5abcf5000 end: 7fd5abcf6000 offset: 12000 load_base: 0 flags: 1 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libbacktrace.so
-map: start: 7fd5abcf6000 end: 7fd5abcf7000 offset: 13000 load_base: 0 flags: 3 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libbacktrace.so
-map: start: 7fd5abcf7000 end: 7fd5abcf8000 offset: 0 load_base: 0 flags: 5 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libbacktrace_test.so
-map: start: 7fd5abcf8000 end: 7fd5abcf9000 offset: 1000 load_base: 0 flags: 0 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libbacktrace_test.so
-map: start: 7fd5abcf9000 end: 7fd5abcfa000 offset: 1000 load_base: 0 flags: 1 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libbacktrace_test.so
-map: start: 7fd5abcfa000 end: 7fd5abcfb000 offset: 2000 load_base: 0 flags: 3 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libbacktrace_test.so
-map: start: 7fd5abcfb000 end: 7fd5abcfd000 offset: 0 load_base: 0 flags: 3 name: 
-map: start: 7fd5abcfd000 end: 7fd5abcfe000 offset: 22000 load_base: 0 flags: 1 name: /lib/x86_64-linux-gnu/ld-2.19.so
-map: start: 7fd5abcfe000 end: 7fd5abcff000 offset: 23000 load_base: 0 flags: 3 name: /lib/x86_64-linux-gnu/ld-2.19.so
-map: start: 7fd5abcff000 end: 7fd5abd00000 offset: 0 load_base: 0 flags: 3 name: 
-map: start: 7fd5abd00000 end: 7fd5ac053000 offset: 0 load_base: 0 flags: 5 name: /ssd/android/aosp_master/out/host/linux-x86/nativetest64/backtrace_test/backtrace_test64
-map: start: 7fd5ac053000 end: 7fd5ac054000 offset: 0 load_base: 0 flags: 3 name: 
-map: start: 7fd5ac054000 end: 7fd5ac06f000 offset: 353000 load_base: 0 flags: 1 name: /ssd/android/aosp_master/out/host/linux-x86/nativetest64/backtrace_test/backtrace_test64
-map: start: 7fd5ac06f000 end: 7fd5ac070000 offset: 36e000 load_base: 0 flags: 3 name: /ssd/android/aosp_master/out/host/linux-x86/nativetest64/backtrace_test/backtrace_test64
-map: start: 7fd5ac070000 end: 7fd5ac071000 offset: 0 load_base: 0 flags: 3 name: 
-map: start: 7fd5ad54e000 end: 7fd5ad56f000 offset: 0 load_base: 0 flags: 3 name: [heap]
-map: start: 7ffcf47ed000 end: 7ffcf480f000 offset: 0 load_base: 0 flags: 3 name: [stack]
-map: start: 7ffcf48d5000 end: 7ffcf48d7000 offset: 0 load_base: ffffffffff700000 flags: 5 name: [vdso]
-map: start: ffffffffff600000 end: ffffffffff601000 offset: 0 load_base: 0 flags: 5 name: [vsyscall]
-registers: 936 010000000000000028b480f4fc7f000028b480f4fc7f000028b480f4fc7f0000b92455add57f0000b07bcfabd57f000098deb6abd57f0000b82455add57f0000010000000000000000000000000000000000000000000000c0e354add57f000000e7b6abd57f0000c8b080f4fc7f00000e0000000000000080ddb6abd57f000000000000000000001500000000000000b07bcfabd57f00001c0000000000000060ddb6abd57f0000d07bcfabd57f0000a0b180f4fc7f000028b480f4fc7f000028b480f4fc7f000028b480f4fc7f000078b480f4fc7f000078b480f4fc7f00007f03ffff0000ffffffffffff000000000000000000000000801f0000fc7f000078b480f4fc7f000060b280f4fc7f000000006a80f3f73cf110b480f4fc7f0000de66d6abd57f00000000000000000000000000000000000000006a80f3f73cf128b480f4fc7f0000782455add57f0000fd96d6abd57f0000092555add57f0000000000000000000057b480f4fc7f0000092555add57f000060b480f4fc7f00006994d6abd57f0000b0e0c6abd57f000071b280f4fc7f000070b280f4fc7f0000256c640000000000a8b180f4fc7f000090b480f4fc7f0000082555add57f0000092555add57f0000092555add57f0000082555add57f00001700000000000000082555add57f0000082555add57f0000f93cfcabd57f0000092555add57f000016000000000000000000000000000000090c07acd57f0000082555add57f0000082555add57f0000082555add57f0000082555add57f0000082555add57f000010b380f4fc7f000078b480f4fc7f00000000000000000000082555add57f0000082555add57f0000082555add57f0000082555add57f0000082555add57f0000082555add57f0000082555add57f0000092555add57f0000092555add57f0000b92455add57f000028b480f4fc7f0000c800000000000000014c7f0b0380ffff0d000000fc7f0000030000000000000033000000d57f000000b480f4fc7f00000000000000000000a0e154ad5b000000000000000000000000000000000000006e000000770000000000000000000000082555add57f00000000000000000000082555add57f0000082555add57f0000082555add57f0000082555add57f00000000000000000000082555add57f0000082555add57f0000082555add57f00006027b4aad57f0000c800000000000000a0e154add57f0000c0e354add57f0000092555add57f000000006a80f3f73cf1e0e054add57f0000e0e554add57f0000d14df6abd57f0000
+map: start: 7fd5aa784000 end: 7fd5aa93e000 offset: 0 load_bias: 0 flags: 5 name: /lib/x86_64-linux-gnu/libc-2.19.so
+map: start: 7fd5aa93e000 end: 7fd5aab3e000 offset: 1ba000 load_bias: 0 flags: 0 name: /lib/x86_64-linux-gnu/libc-2.19.so
+map: start: 7fd5aab3e000 end: 7fd5aab42000 offset: 1ba000 load_bias: 0 flags: 1 name: /lib/x86_64-linux-gnu/libc-2.19.so
+map: start: 7fd5aab42000 end: 7fd5aab44000 offset: 1be000 load_bias: 0 flags: 3 name: /lib/x86_64-linux-gnu/libc-2.19.so
+map: start: 7fd5aab44000 end: 7fd5aab49000 offset: 0 load_bias: 0 flags: 3 name: 
+map: start: 7fd5aab49000 end: 7fd5aac4e000 offset: 0 load_bias: 0 flags: 5 name: /lib/x86_64-linux-gnu/libm-2.19.so
+map: start: 7fd5aac4e000 end: 7fd5aae4d000 offset: 105000 load_bias: 0 flags: 0 name: /lib/x86_64-linux-gnu/libm-2.19.so
+map: start: 7fd5aae4d000 end: 7fd5aae4e000 offset: 104000 load_bias: 0 flags: 1 name: /lib/x86_64-linux-gnu/libm-2.19.so
+map: start: 7fd5aae4e000 end: 7fd5aae4f000 offset: 105000 load_bias: 0 flags: 3 name: /lib/x86_64-linux-gnu/libm-2.19.so
+map: start: 7fd5aae4f000 end: 7fd5aae65000 offset: 0 load_bias: 0 flags: 5 name: /lib/x86_64-linux-gnu/libgcc_s.so.1
+map: start: 7fd5aae65000 end: 7fd5ab064000 offset: 16000 load_bias: 0 flags: 0 name: /lib/x86_64-linux-gnu/libgcc_s.so.1
+map: start: 7fd5ab064000 end: 7fd5ab065000 offset: 15000 load_bias: 0 flags: 3 name: /lib/x86_64-linux-gnu/libgcc_s.so.1
+map: start: 7fd5ab065000 end: 7fd5ab08a000 offset: 0 load_bias: 0 flags: 5 name: /lib/x86_64-linux-gnu/libtinfo.so.5.9
+map: start: 7fd5ab08a000 end: 7fd5ab289000 offset: 25000 load_bias: 0 flags: 0 name: /lib/x86_64-linux-gnu/libtinfo.so.5.9
+map: start: 7fd5ab289000 end: 7fd5ab28d000 offset: 24000 load_bias: 0 flags: 1 name: /lib/x86_64-linux-gnu/libtinfo.so.5.9
+map: start: 7fd5ab28d000 end: 7fd5ab28e000 offset: 28000 load_bias: 0 flags: 3 name: /lib/x86_64-linux-gnu/libtinfo.so.5.9
+map: start: 7fd5ab28e000 end: 7fd5ab2b0000 offset: 0 load_bias: 0 flags: 5 name: /lib/x86_64-linux-gnu/libncurses.so.5.9
+map: start: 7fd5ab2b0000 end: 7fd5ab4af000 offset: 22000 load_bias: 0 flags: 0 name: /lib/x86_64-linux-gnu/libncurses.so.5.9
+map: start: 7fd5ab4af000 end: 7fd5ab4b0000 offset: 21000 load_bias: 0 flags: 1 name: /lib/x86_64-linux-gnu/libncurses.so.5.9
+map: start: 7fd5ab4b0000 end: 7fd5ab4b1000 offset: 22000 load_bias: 0 flags: 3 name: /lib/x86_64-linux-gnu/libncurses.so.5.9
+map: start: 7fd5ab4b1000 end: 7fd5ab4b4000 offset: 0 load_bias: 0 flags: 5 name: /lib/x86_64-linux-gnu/libdl-2.19.so
+map: start: 7fd5ab4b4000 end: 7fd5ab6b3000 offset: 3000 load_bias: 0 flags: 0 name: /lib/x86_64-linux-gnu/libdl-2.19.so
+map: start: 7fd5ab6b3000 end: 7fd5ab6b4000 offset: 2000 load_bias: 0 flags: 1 name: /lib/x86_64-linux-gnu/libdl-2.19.so
+map: start: 7fd5ab6b4000 end: 7fd5ab6b5000 offset: 3000 load_bias: 0 flags: 3 name: /lib/x86_64-linux-gnu/libdl-2.19.so
+map: start: 7fd5ab6b5000 end: 7fd5ab6bc000 offset: 0 load_bias: 0 flags: 5 name: /lib/x86_64-linux-gnu/librt-2.19.so
+map: start: 7fd5ab6bc000 end: 7fd5ab8bb000 offset: 7000 load_bias: 0 flags: 0 name: /lib/x86_64-linux-gnu/librt-2.19.so
+map: start: 7fd5ab8bb000 end: 7fd5ab8bc000 offset: 6000 load_bias: 0 flags: 1 name: /lib/x86_64-linux-gnu/librt-2.19.so
+map: start: 7fd5ab8bc000 end: 7fd5ab8bd000 offset: 7000 load_bias: 0 flags: 3 name: /lib/x86_64-linux-gnu/librt-2.19.so
+map: start: 7fd5ab8bd000 end: 7fd5ab8d6000 offset: 0 load_bias: 0 flags: 5 name: /lib/x86_64-linux-gnu/libpthread-2.19.so
+map: start: 7fd5ab8d6000 end: 7fd5abad5000 offset: 19000 load_bias: 0 flags: 0 name: /lib/x86_64-linux-gnu/libpthread-2.19.so
+map: start: 7fd5abad5000 end: 7fd5abad6000 offset: 18000 load_bias: 0 flags: 1 name: /lib/x86_64-linux-gnu/libpthread-2.19.so
+map: start: 7fd5abad6000 end: 7fd5abad7000 offset: 19000 load_bias: 0 flags: 3 name: /lib/x86_64-linux-gnu/libpthread-2.19.so
+map: start: 7fd5abad7000 end: 7fd5abadb000 offset: 0 load_bias: 0 flags: 3 name: 
+map: start: 7fd5abadb000 end: 7fd5abafe000 offset: 0 load_bias: 0 flags: 5 name: /lib/x86_64-linux-gnu/ld-2.19.so
+map: start: 7fd5abb17000 end: 7fd5abb1a000 offset: 0 load_bias: 0 flags: 3 name: 
+map: start: 7fd5abb1a000 end: 7fd5abb40000 offset: 0 load_bias: 0 flags: 5 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/liblzma.so
+map: start: 7fd5abb40000 end: 7fd5abb41000 offset: 25000 load_bias: 0 flags: 1 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/liblzma.so
+map: start: 7fd5abb41000 end: 7fd5abb42000 offset: 26000 load_bias: 0 flags: 3 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/liblzma.so
+map: start: 7fd5abb42000 end: 7fd5abb4b000 offset: 0 load_bias: 0 flags: 3 name: 
+map: start: 7fd5abb6a000 end: 7fd5abb70000 offset: 0 load_bias: 0 flags: 3 name: 
+map: start: 7fd5abb70000 end: 7fd5abc62000 offset: 0 load_bias: 0 flags: 5 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libc++.so
+map: start: 7fd5abc62000 end: 7fd5abc63000 offset: f2000 load_bias: 0 flags: 0 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libc++.so
+map: start: 7fd5abc63000 end: 7fd5abc6b000 offset: f2000 load_bias: 0 flags: 1 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libc++.so
+map: start: 7fd5abc6b000 end: 7fd5abc6c000 offset: fa000 load_bias: 0 flags: 3 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libc++.so
+map: start: 7fd5abc6c000 end: 7fd5abc70000 offset: 0 load_bias: 0 flags: 3 name: 
+map: start: 7fd5abc70000 end: 7fd5abc8d000 offset: 0 load_bias: 0 flags: 5 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libunwind.so
+map: start: 7fd5abc8d000 end: 7fd5abc8e000 offset: 1c000 load_bias: 0 flags: 1 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libunwind.so
+map: start: 7fd5abc8e000 end: 7fd5abc8f000 offset: 1d000 load_bias: 0 flags: 3 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libunwind.so
+map: start: 7fd5abc8f000 end: 7fd5abcb8000 offset: 0 load_bias: 0 flags: 3 name: 
+map: start: 7fd5abcb8000 end: 7fd5abcbe000 offset: 0 load_bias: 0 flags: 5 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/liblog.so
+map: start: 7fd5abcbe000 end: 7fd5abcbf000 offset: 6000 load_bias: 0 flags: 0 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/liblog.so
+map: start: 7fd5abcbf000 end: 7fd5abcc0000 offset: 6000 load_bias: 0 flags: 1 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/liblog.so
+map: start: 7fd5abcc0000 end: 7fd5abcc1000 offset: 7000 load_bias: 0 flags: 3 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/liblog.so
+map: start: 7fd5abcc1000 end: 7fd5abcc2000 offset: 0 load_bias: 0 flags: 3 name: 
+map: start: 7fd5abcc2000 end: 7fd5abccd000 offset: 0 load_bias: 0 flags: 5 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libcutils.so
+map: start: 7fd5abccd000 end: 7fd5abcce000 offset: b000 load_bias: 0 flags: 0 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libcutils.so
+map: start: 7fd5abcce000 end: 7fd5abccf000 offset: b000 load_bias: 0 flags: 1 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libcutils.so
+map: start: 7fd5abccf000 end: 7fd5abcd0000 offset: c000 load_bias: 0 flags: 3 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libcutils.so
+map: start: 7fd5abcd0000 end: 7fd5abcdf000 offset: 0 load_bias: 0 flags: 5 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libbase.so
+map: start: 7fd5abcdf000 end: 7fd5abce0000 offset: f000 load_bias: 0 flags: 0 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libbase.so
+map: start: 7fd5abce0000 end: 7fd5abce1000 offset: f000 load_bias: 0 flags: 1 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libbase.so
+map: start: 7fd5abce1000 end: 7fd5abce2000 offset: 10000 load_bias: 0 flags: 3 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libbase.so
+map: start: 7fd5abce2000 end: 7fd5abcf5000 offset: 0 load_bias: 0 flags: 5 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libbacktrace.so
+map: start: 7fd5abcf5000 end: 7fd5abcf6000 offset: 12000 load_bias: 0 flags: 1 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libbacktrace.so
+map: start: 7fd5abcf6000 end: 7fd5abcf7000 offset: 13000 load_bias: 0 flags: 3 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libbacktrace.so
+map: start: 7fd5abcf7000 end: 7fd5abcf8000 offset: 0 load_bias: 0 flags: 5 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libbacktrace_test.so
+map: start: 7fd5abcf8000 end: 7fd5abcf9000 offset: 1000 load_bias: 0 flags: 0 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libbacktrace_test.so
+map: start: 7fd5abcf9000 end: 7fd5abcfa000 offset: 1000 load_bias: 0 flags: 1 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libbacktrace_test.so
+map: start: 7fd5abcfa000 end: 7fd5abcfb000 offset: 2000 load_bias: 0 flags: 3 name: /ssd/android/aosp_master/out/host/linux-x86/lib64/libbacktrace_test.so
+map: start: 7fd5abcfb000 end: 7fd5abcfd000 offset: 0 load_bias: 0 flags: 3 name: 
+map: start: 7fd5abcfd000 end: 7fd5abcfe000 offset: 22000 load_bias: 0 flags: 1 name: /lib/x86_64-linux-gnu/ld-2.19.so
+map: start: 7fd5abcfe000 end: 7fd5abcff000 offset: 23000 load_bias: 0 flags: 3 name: /lib/x86_64-linux-gnu/ld-2.19.so
+map: start: 7fd5abcff000 end: 7fd5abd00000 offset: 0 load_bias: 0 flags: 3 name: 
+map: start: 7fd5abd00000 end: 7fd5ac053000 offset: 0 load_bias: 0 flags: 5 name: /ssd/android/aosp_master/out/host/linux-x86/nativetest64/backtrace_test/backtrace_test64
+map: start: 7fd5ac053000 end: 7fd5ac054000 offset: 0 load_bias: 0 flags: 3 name: 
+map: start: 7fd5ac054000 end: 7fd5ac06f000 offset: 353000 load_bias: 0 flags: 1 name: /ssd/android/aosp_master/out/host/linux-x86/nativetest64/backtrace_test/backtrace_test64
+map: start: 7fd5ac06f000 end: 7fd5ac070000 offset: 36e000 load_bias: 0 flags: 3 name: /ssd/android/aosp_master/out/host/linux-x86/nativetest64/backtrace_test/backtrace_test64
+map: start: 7fd5ac070000 end: 7fd5ac071000 offset: 0 load_bias: 0 flags: 3 name: 
+map: start: 7fd5ad54e000 end: 7fd5ad56f000 offset: 0 load_bias: 0 flags: 3 name: [heap]
+map: start: 7ffcf47ed000 end: 7ffcf480f000 offset: 0 load_bias: 0 flags: 3 name: [stack]
+map: start: 7ffcf48d5000 end: 7ffcf48d7000 offset: 0 load_bias: ffffffffff700000 flags: 5 name: [vdso]
+map: start: ffffffffff600000 end: ffffffffff601000 offset: 0 load_bias: 0 flags: 5 name: [vsyscall]
+ucontext: 224 00000000000000000000000000000000000000000000000000000000000000000000000000000000b07bcfabd57f000098deb6abd57f0000b82455add57f0000010000000000000000000000000000000000000000000000c0e354add57f000000e7b6abd57f0000c8b080f4fc7f00000e0000000000000080ddb6abd57f000000000000000000001500000000000000b07bcfabd57f00001c0000000000000060ddb6abd57f0000d07bcfabd57f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
 stack: start: 7fd5abb6b000 end: 7fd5abb6f000 size: 16384 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006c4eaeabd57f00000000000000000000978142a8000000000f0000000000000012000000000000003888b4abd57f0000e657aeabd57f00000000000000000000a0dcb6abd57f0000307d78aad57f0000b0ddb6abd57f000028d978aad57f0000060aa10200000000a0ddb6abd57f0000000000000000000000000000000000002091b1abd57f0000e094b4abd57f000017008cabd57f0000c84d79aad57f000028e28babd57f00000000000005000000d503000001000000000000000000000068deb6abd57f000040deb6abd57f00002091b1abd57f00000100000000000000e0f3c6abd57f000088f0c6abd57f00006159aeabd57f000000000000000000002091b1abd57f000005000000000000000000000000000000010000000000000088f0c6abd57f00000000000000000000d07bcfabd57f00000000000000000000000000000000000098deb6abd57f000098deb6abd57f0000b0ddb6abd57f00006179cfabd57f000098deb6abd57f0000b07bcfabd57f00001c000000150000000e00000007000000f0ddb6abd57f0000e179cfabd57f00000000000000000000150000001c00000098deb6abd57f0000b07bcfabd57f0000100000000c000000080000000400000030deb6abd57f0000417acfabd57f000000000000000000000c0000001000000098deb6abd57f0000b07bcfabd57f00000800000006000000040000000200000070deb6abd57f0000a17acfabd57f00000000000000000000060000000800000098deb6abd57f0000b07bcfabd57f000004000000030000000200000001000000b0deb6abd57f0000817bcfabd57f0000000000000000000074b480f4fc7f0000c8b080f4fc7f0000c8b080f4fc7f000074b480f4fc7f000000006a80f3f73cf1d0deb6abd57f00002a52d5abd57f0000c8b080f4fc7f0000c8b080f4fc7f0000000000000000000084518cabd57f0000000000000000000000e7b6abd57f000000e7b6abd57f00008f990b1e3bad5a6700000000000000000000000000000000c0e354add57f000000e7b6abd57f00008f99cba356faf1988f9991bc23faf1980000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e7b6abd57f00007de387aad57f000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006030b4aad57f0000b8edb6abd57f00000000000000000000000000000000000080b88eaad57f0000000000000000000080b28eaad57f0000000000000000000080c18eaad57f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e7b6abd57f0000402555add57f000000e7b6abd57f00000100000000000000000000000000000000006a80f3f73cf1058f9d56adb3c7cc000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000407ab1abd57f000030a3adabd57f00005c64000053640000e0e9b6abd57f0000e0e9b6abd57f0000e0ffffffffffffff00000000000000000000000000000000f0deb6abd57f00000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010eab6abd57f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000c46ad90f52391d00000000000000000000000000000000000000000000000000f051d5abd57f0000c8b080f4fc7f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b0b6abd57f000000400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
 function: start: 0 end: 7fd5abcf7930 name: unknown_start
 function: start: 7fd5abcf7930 end: 7fd5abcf7990 name: test_level_four
diff --git a/libbacktrace/thread_utils.c b/libbacktrace/thread_utils.c
deleted file mode 100644
index e75f56e..0000000
--- a/libbacktrace/thread_utils.c
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Copyright (C) 2013 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 "thread_utils.h"
-
-#if !defined(__BIONIC__)
-
-// glibc doesn't implement or export tgkill.
-#include <unistd.h>
-#include <sys/syscall.h>
-
-int tgkill(int tgid, int tid, int sig) {
-  return syscall(__NR_tgkill, tgid, tid, sig);
-}
-
-#endif
diff --git a/libbacktrace/thread_utils.h b/libbacktrace/thread_utils.h
deleted file mode 100644
index 9590657..0000000
--- a/libbacktrace/thread_utils.h
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright (C) 2013 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 _LIBBACKTRACE_THREAD_UTILS_H
-#define _LIBBACKTRACE_THREAD_UTILS_H
-
-#include <unistd.h>
-
-#if !defined(__ANDROID__)
-#include <cutils/threads.h>
-#endif
-
-__BEGIN_DECLS
-
-int tgkill(int tgid, int tid, int sig);
-
-__END_DECLS
-
-#endif /* _LIBBACKTRACE_THREAD_UTILS_H */
diff --git a/libbinderwrapper/Android.bp b/libbinderwrapper/Android.bp
index 6fac0d8..d2487e2 100644
--- a/libbinderwrapper/Android.bp
+++ b/libbinderwrapper/Android.bp
@@ -38,6 +38,7 @@
 cc_library_shared {
     name: "libbinderwrapper",
     defaults: ["libbinderwrapper_defaults"],
+    vendor_available: true,
 
     srcs: [
         "binder_wrapper.cc",
diff --git a/libcrypto_utils/Android.bp b/libcrypto_utils/Android.bp
index f2560e6..e47560f 100644
--- a/libcrypto_utils/Android.bp
+++ b/libcrypto_utils/Android.bp
@@ -16,6 +16,11 @@
 
 cc_library {
     name: "libcrypto_utils",
+    vendor_available: true,
+    recovery_available: true,
+    vndk: {
+        enabled: true,
+    },
     host_supported: true,
     srcs: [
         "android_pubkey.c",
diff --git a/libcutils/Android.bp b/libcutils/Android.bp
index 245deb1..37afb98 100644
--- a/libcutils/Android.bp
+++ b/libcutils/Android.bp
@@ -19,24 +19,26 @@
 // which are also hard or even impossible to port to native Win32
 libcutils_nonwindows_sources = [
     "android_get_control_file.cpp",
-    "fs.c",
-    "multiuser.c",
-    "socket_inaddr_any_server_unix.c",
-    "socket_local_client_unix.c",
-    "socket_local_server_unix.c",
-    "socket_network_client_unix.c",
+    "fs.cpp",
+    "hashmap.cpp",
+    "multiuser.cpp",
+    "socket_inaddr_any_server_unix.cpp",
+    "socket_local_client_unix.cpp",
+    "socket_local_server_unix.cpp",
+    "socket_network_client_unix.cpp",
     "sockets_unix.cpp",
-    "str_parms.c",
+    "str_parms.cpp",
 ]
 
 cc_library_headers {
     name: "libcutils_headers",
     vendor_available: true,
+    recovery_available: true,
     host_supported: true,
     export_include_dirs: ["include"],
     target: {
         vendor: {
-            export_include_dirs: ["include_vndk"],
+            override_export_include_dirs: ["include_vndk"],
         },
         linux_bionic: {
             enabled: true,
@@ -50,43 +52,42 @@
 cc_library {
     name: "libcutils",
     vendor_available: true,
+    vndk: {
+        enabled: true,
+        support_system_process: true,
+    },
+    recovery_available: true,
     host_supported: true,
     srcs: [
-        "config_utils.c",
+        "config_utils.cpp",
         "fs_config.cpp",
-        "canned_fs_config.c",
-        "hashmap.c",
-        "iosched_policy.c",
-        "load_file.c",
-        "native_handle.c",
-        "open_memstream.c",
-        "record_stream.c",
+        "canned_fs_config.cpp",
+        "iosched_policy.cpp",
+        "load_file.cpp",
+        "native_handle.cpp",
+        "record_stream.cpp",
         "sched_policy.cpp",
         "sockets.cpp",
-        "strdup16to8.c",
-        "strdup8to16.c",
+        "strdup16to8.cpp",
+        "strdup8to16.cpp",
         "strlcpy.c",
-        "threads.c",
+        "threads.cpp",
     ],
 
     target: {
-        host: {
-            srcs: ["dlmalloc_stubs.c"],
-        },
         linux_bionic: {
             enabled: true,
-            exclude_srcs: ["dlmalloc_stubs.c"],
         },
         not_windows: {
             srcs: libcutils_nonwindows_sources + [
-                "ashmem-host.c",
-                "trace-host.c",
+                "ashmem-host.cpp",
+                "trace-host.cpp",
             ],
         },
         windows: {
             srcs: [
-                "socket_inaddr_any_server_windows.c",
-                "socket_network_client_windows.c",
+                "socket_inaddr_any_server_windows.cpp",
+                "socket_network_client_windows.cpp",
                 "sockets_windows.cpp",
             ],
 
@@ -101,32 +102,41 @@
 
         android: {
             srcs: libcutils_nonwindows_sources + [
-                "android_reboot.c",
-                "ashmem-dev.c",
+                "android_reboot.cpp",
+                "ashmem-dev.cpp",
                 "klog.cpp",
-                "partition_utils.c",
+                "partition_utils.cpp",
                 "properties.cpp",
-                "qtaguid.c",
-                "trace-dev.c",
-                "uevent.c",
+                "qtaguid.cpp",
+                "trace-dev.cpp",
+                "uevent.cpp",
             ],
+        },
+
+        android_arm: {
+            srcs: ["arch-arm/memset32.S"],
+            sanitize: {
+                misc_undefined: ["integer"],
+            },
+        },
+        android_arm64: {
+            srcs: ["arch-arm64/android_memset.S"],
             sanitize: {
                 misc_undefined: ["integer"],
             },
         },
 
-        android_arm: {
-            srcs: ["arch-arm/memset32.S"],
-        },
-        android_arm64: {
-            srcs: ["arch-arm64/android_memset.S"],
-        },
-
         android_mips: {
             srcs: ["arch-mips/android_memset.c"],
+            sanitize: {
+                misc_undefined: ["integer"],
+            },
         },
         android_mips64: {
             srcs: ["arch-mips/android_memset.c"],
+            sanitize: {
+                misc_undefined: ["integer"],
+            },
         },
 
         android_x86: {
@@ -134,6 +144,12 @@
                 "arch-x86/android_memset16.S",
                 "arch-x86/android_memset32.S",
             ],
+            // TODO: This is to work around b/29412086.
+            // Remove once __mulodi4 is available and move the "sanitize" block
+            // to the android target.
+            sanitize: {
+                misc_undefined: [],
+            },
         },
 
         android_x86_64: {
@@ -141,23 +157,35 @@
                 "arch-x86_64/android_memset16.S",
                 "arch-x86_64/android_memset32.S",
             ],
+            sanitize: {
+                misc_undefined: ["integer"],
+            },
         },
+
+        vendor: {
+            exclude_srcs: [
+                // qtaguid.cpp loads libnetd_client.so with dlopen().  Since
+                // the interface of libnetd_client.so may vary between AOSP
+                // releases, exclude qtaguid.cpp from the VNDK-SP variant.
+                "qtaguid.cpp",
+            ],
+        }
     },
 
     shared_libs: ["liblog"],
     header_libs: [
+        "libbase_headers",
         "libcutils_headers",
         "libutils_headers",
     ],
     export_header_lib_headers: ["libcutils_headers"],
+    local_include_dirs: ["include"],
 
     cflags: [
         "-Werror",
         "-Wall",
         "-Wextra",
     ],
-
-    clang: true,
 }
 
 subdirs = ["tests"]
diff --git a/libcutils/OWNERS b/libcutils/OWNERS
new file mode 100644
index 0000000..c18ed51
--- /dev/null
+++ b/libcutils/OWNERS
@@ -0,0 +1,4 @@
+cferris@google.com
+enh@google.com
+jmgao@google.com
+tomcherry@google.com
diff --git a/libcutils/android_get_control_file.cpp b/libcutils/android_get_control_file.cpp
index 780d9f1..d8121f5 100644
--- a/libcutils/android_get_control_file.cpp
+++ b/libcutils/android_get_control_file.cpp
@@ -25,6 +25,9 @@
  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  */
+
+#include <cutils/android_get_control_file.h>
+
 #include <ctype.h>
 #include <errno.h>
 #include <fcntl.h>
@@ -36,8 +39,6 @@
 #include <sys/types.h>
 #include <unistd.h>
 
-#include <cutils/android_get_control_file.h>
-
 #include "android_get_control_env.h"
 
 #ifndef TEMP_FAILURE_RETRY
diff --git a/libcutils/android_reboot.c b/libcutils/android_reboot.c
deleted file mode 100644
index a33e45f..0000000
--- a/libcutils/android_reboot.c
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright 2011, 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 <stdio.h>
-#include <stdlib.h>
-
-#include <cutils/android_reboot.h>
-#include <cutils/properties.h>
-
-#define TAG "android_reboot"
-
-int android_reboot(int cmd, int flags __unused, const char* arg) {
-    int ret;
-    const char* restart_cmd = NULL;
-    char* prop_value;
-
-    switch (cmd) {
-        case ANDROID_RB_RESTART:  // deprecated
-        case ANDROID_RB_RESTART2:
-            restart_cmd = "reboot";
-            break;
-        case ANDROID_RB_POWEROFF:
-            restart_cmd = "shutdown";
-            break;
-        case ANDROID_RB_THERMOFF:
-            restart_cmd = "thermal-shutdown";
-            break;
-    }
-    if (!restart_cmd) return -1;
-    if (arg) {
-        ret = asprintf(&prop_value, "%s,%s", restart_cmd, arg);
-    } else {
-        ret = asprintf(&prop_value, "%s", restart_cmd);
-    }
-    if (ret < 0) return -1;
-    ret = property_set(ANDROID_RB_PROPERTY, prop_value);
-    free(prop_value);
-    return ret;
-}
diff --git a/libcutils/android_reboot.cpp b/libcutils/android_reboot.cpp
new file mode 100644
index 0000000..ce41cd3
--- /dev/null
+++ b/libcutils/android_reboot.cpp
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2011, 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 <cutils/android_reboot.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <cutils/properties.h>
+
+#define TAG "android_reboot"
+
+int android_reboot(int cmd, int /*flags*/, const char* arg) {
+    int ret;
+    const char* restart_cmd = NULL;
+    char* prop_value;
+
+    switch (static_cast<unsigned>(cmd)) {
+        case ANDROID_RB_RESTART:  // deprecated
+        case ANDROID_RB_RESTART2:
+            restart_cmd = "reboot";
+            break;
+        case ANDROID_RB_POWEROFF:
+            restart_cmd = "shutdown";
+            break;
+        case ANDROID_RB_THERMOFF:
+            restart_cmd = "shutdown,thermal";
+            break;
+    }
+    if (!restart_cmd) return -1;
+    if (arg && arg[0]) {
+        ret = asprintf(&prop_value, "%s,%s", restart_cmd, arg);
+    } else {
+        ret = asprintf(&prop_value, "%s", restart_cmd);
+    }
+    if (ret < 0) return -1;
+    ret = property_set(ANDROID_RB_PROPERTY, prop_value);
+    free(prop_value);
+    return ret;
+}
diff --git a/libcutils/ashmem-dev.c b/libcutils/ashmem-dev.c
deleted file mode 100644
index b4abb79..0000000
--- a/libcutils/ashmem-dev.c
+++ /dev/null
@@ -1,225 +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.
- */
-
-/*
- * Implementation of the user-space ashmem API for devices, which have our
- * ashmem-enabled kernel. See ashmem-sim.c for the "fake" tmp-based version,
- * used by the simulator.
- */
-#define LOG_TAG "ashmem"
-
-#include <errno.h>
-#include <fcntl.h>
-#include <linux/ashmem.h>
-#include <pthread.h>
-#include <string.h>
-#include <sys/ioctl.h>
-#include <sys/stat.h>
-#include <sys/sysmacros.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include <cutils/ashmem.h>
-#include <log/log.h>
-
-#define ASHMEM_DEVICE "/dev/ashmem"
-
-/* ashmem identity */
-static dev_t __ashmem_rdev;
-/*
- * If we trigger a signal handler in the middle of locked activity and the
- * signal handler calls ashmem, we could get into a deadlock state.
- */
-static pthread_mutex_t __ashmem_lock = PTHREAD_MUTEX_INITIALIZER;
-
-/* logistics of getting file descriptor for ashmem */
-static int __ashmem_open_locked()
-{
-    int ret;
-    struct stat st;
-
-    int fd = TEMP_FAILURE_RETRY(open(ASHMEM_DEVICE, O_RDWR));
-    if (fd < 0) {
-        return fd;
-    }
-
-    ret = TEMP_FAILURE_RETRY(fstat(fd, &st));
-    if (ret < 0) {
-        int save_errno = errno;
-        close(fd);
-        errno = save_errno;
-        return ret;
-    }
-    if (!S_ISCHR(st.st_mode) || !st.st_rdev) {
-        close(fd);
-        errno = ENOTTY;
-        return -1;
-    }
-
-    __ashmem_rdev = st.st_rdev;
-    return fd;
-}
-
-static int __ashmem_open()
-{
-    int fd;
-
-    pthread_mutex_lock(&__ashmem_lock);
-    fd = __ashmem_open_locked();
-    pthread_mutex_unlock(&__ashmem_lock);
-
-    return fd;
-}
-
-/* Make sure file descriptor references ashmem, negative number means false */
-static int __ashmem_is_ashmem(int fd, int fatal)
-{
-    dev_t rdev;
-    struct stat st;
-
-    if (TEMP_FAILURE_RETRY(fstat(fd, &st)) < 0) {
-        return -1;
-    }
-
-    rdev = 0; /* Too much complexity to sniff __ashmem_rdev */
-    if (S_ISCHR(st.st_mode) && st.st_rdev) {
-        pthread_mutex_lock(&__ashmem_lock);
-        rdev = __ashmem_rdev;
-        if (rdev) {
-            pthread_mutex_unlock(&__ashmem_lock);
-        } else {
-            int fd = __ashmem_open_locked();
-            if (fd < 0) {
-                pthread_mutex_unlock(&__ashmem_lock);
-                return -1;
-            }
-            rdev = __ashmem_rdev;
-            pthread_mutex_unlock(&__ashmem_lock);
-
-            close(fd);
-        }
-
-        if (st.st_rdev == rdev) {
-            return 0;
-        }
-    }
-
-    if (fatal) {
-        if (rdev) {
-            LOG_ALWAYS_FATAL("illegal fd=%d mode=0%o rdev=%d:%d expected 0%o %d:%d",
-              fd, st.st_mode, major(st.st_rdev), minor(st.st_rdev),
-              S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IRGRP,
-              major(rdev), minor(rdev));
-        } else {
-            LOG_ALWAYS_FATAL("illegal fd=%d mode=0%o rdev=%d:%d expected 0%o",
-              fd, st.st_mode, major(st.st_rdev), minor(st.st_rdev),
-              S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IRGRP);
-        }
-        /* NOTREACHED */
-    }
-
-    errno = ENOTTY;
-    return -1;
-}
-
-int ashmem_valid(int fd)
-{
-    return __ashmem_is_ashmem(fd, 0) >= 0;
-}
-
-/*
- * ashmem_create_region - creates a new ashmem region and returns the file
- * descriptor, or <0 on error
- *
- * `name' is an optional label to give the region (visible in /proc/pid/maps)
- * `size' is the size of the region, in page-aligned bytes
- */
-int ashmem_create_region(const char *name, size_t size)
-{
-    int ret, save_errno;
-
-    int fd = __ashmem_open();
-    if (fd < 0) {
-        return fd;
-    }
-
-    if (name) {
-        char buf[ASHMEM_NAME_LEN] = {0};
-
-        strlcpy(buf, name, sizeof(buf));
-        ret = TEMP_FAILURE_RETRY(ioctl(fd, ASHMEM_SET_NAME, buf));
-        if (ret < 0) {
-            goto error;
-        }
-    }
-
-    ret = TEMP_FAILURE_RETRY(ioctl(fd, ASHMEM_SET_SIZE, size));
-    if (ret < 0) {
-        goto error;
-    }
-
-    return fd;
-
-error:
-    save_errno = errno;
-    close(fd);
-    errno = save_errno;
-    return ret;
-}
-
-int ashmem_set_prot_region(int fd, int prot)
-{
-    int ret = __ashmem_is_ashmem(fd, 1);
-    if (ret < 0) {
-        return ret;
-    }
-
-    return TEMP_FAILURE_RETRY(ioctl(fd, ASHMEM_SET_PROT_MASK, prot));
-}
-
-int ashmem_pin_region(int fd, size_t offset, size_t len)
-{
-    struct ashmem_pin pin = { offset, len };
-
-    int ret = __ashmem_is_ashmem(fd, 1);
-    if (ret < 0) {
-        return ret;
-    }
-
-    return TEMP_FAILURE_RETRY(ioctl(fd, ASHMEM_PIN, &pin));
-}
-
-int ashmem_unpin_region(int fd, size_t offset, size_t len)
-{
-    struct ashmem_pin pin = { offset, len };
-
-    int ret = __ashmem_is_ashmem(fd, 1);
-    if (ret < 0) {
-        return ret;
-    }
-
-    return TEMP_FAILURE_RETRY(ioctl(fd, ASHMEM_UNPIN, &pin));
-}
-
-int ashmem_get_size_region(int fd)
-{
-    int ret = __ashmem_is_ashmem(fd, 1);
-    if (ret < 0) {
-        return ret;
-    }
-
-    return TEMP_FAILURE_RETRY(ioctl(fd, ASHMEM_GET_SIZE, NULL));
-}
diff --git a/libcutils/ashmem-dev.cpp b/libcutils/ashmem-dev.cpp
new file mode 100644
index 0000000..0cc4fc0
--- /dev/null
+++ b/libcutils/ashmem-dev.cpp
@@ -0,0 +1,213 @@
+/*
+ * 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.
+ */
+
+#include <cutils/ashmem.h>
+
+/*
+ * Implementation of the user-space ashmem API for devices, which have our
+ * ashmem-enabled kernel. See ashmem-sim.c for the "fake" tmp-based version,
+ * used by the simulator.
+ */
+#define LOG_TAG "ashmem"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <linux/ashmem.h>
+#include <pthread.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/sysmacros.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <log/log.h>
+
+#define ASHMEM_DEVICE "/dev/ashmem"
+
+/* ashmem identity */
+static dev_t __ashmem_rdev;
+/*
+ * If we trigger a signal handler in the middle of locked activity and the
+ * signal handler calls ashmem, we could get into a deadlock state.
+ */
+static pthread_mutex_t __ashmem_lock = PTHREAD_MUTEX_INITIALIZER;
+
+/* logistics of getting file descriptor for ashmem */
+static int __ashmem_open_locked()
+{
+    int ret;
+    struct stat st;
+
+    int fd = TEMP_FAILURE_RETRY(open(ASHMEM_DEVICE, O_RDWR | O_CLOEXEC));
+    if (fd < 0) {
+        return fd;
+    }
+
+    ret = TEMP_FAILURE_RETRY(fstat(fd, &st));
+    if (ret < 0) {
+        int save_errno = errno;
+        close(fd);
+        errno = save_errno;
+        return ret;
+    }
+    if (!S_ISCHR(st.st_mode) || !st.st_rdev) {
+        close(fd);
+        errno = ENOTTY;
+        return -1;
+    }
+
+    __ashmem_rdev = st.st_rdev;
+    return fd;
+}
+
+static int __ashmem_open()
+{
+    int fd;
+
+    pthread_mutex_lock(&__ashmem_lock);
+    fd = __ashmem_open_locked();
+    pthread_mutex_unlock(&__ashmem_lock);
+
+    return fd;
+}
+
+/* Make sure file descriptor references ashmem, negative number means false */
+static int __ashmem_is_ashmem(int fd, int fatal)
+{
+    dev_t rdev;
+    struct stat st;
+
+    if (fstat(fd, &st) < 0) {
+        return -1;
+    }
+
+    rdev = 0; /* Too much complexity to sniff __ashmem_rdev */
+    if (S_ISCHR(st.st_mode) && st.st_rdev) {
+        pthread_mutex_lock(&__ashmem_lock);
+        rdev = __ashmem_rdev;
+        if (rdev) {
+            pthread_mutex_unlock(&__ashmem_lock);
+        } else {
+            int fd = __ashmem_open_locked();
+            if (fd < 0) {
+                pthread_mutex_unlock(&__ashmem_lock);
+                return -1;
+            }
+            rdev = __ashmem_rdev;
+            pthread_mutex_unlock(&__ashmem_lock);
+
+            close(fd);
+        }
+
+        if (st.st_rdev == rdev) {
+            return 0;
+        }
+    }
+
+    if (fatal) {
+        if (rdev) {
+            LOG_ALWAYS_FATAL("illegal fd=%d mode=0%o rdev=%d:%d expected 0%o %d:%d",
+              fd, st.st_mode, major(st.st_rdev), minor(st.st_rdev),
+              S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IRGRP,
+              major(rdev), minor(rdev));
+        } else {
+            LOG_ALWAYS_FATAL("illegal fd=%d mode=0%o rdev=%d:%d expected 0%o",
+              fd, st.st_mode, major(st.st_rdev), minor(st.st_rdev),
+              S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IRGRP);
+        }
+        /* NOTREACHED */
+    }
+
+    errno = ENOTTY;
+    return -1;
+}
+
+static int __ashmem_check_failure(int fd, int result)
+{
+    if (result == -1 && errno == ENOTTY) __ashmem_is_ashmem(fd, 1);
+    return result;
+}
+
+int ashmem_valid(int fd)
+{
+    return __ashmem_is_ashmem(fd, 0) >= 0;
+}
+
+/*
+ * ashmem_create_region - creates a new ashmem region and returns the file
+ * descriptor, or <0 on error
+ *
+ * `name' is an optional label to give the region (visible in /proc/pid/maps)
+ * `size' is the size of the region, in page-aligned bytes
+ */
+int ashmem_create_region(const char *name, size_t size)
+{
+    int ret, save_errno;
+
+    int fd = __ashmem_open();
+    if (fd < 0) {
+        return fd;
+    }
+
+    if (name) {
+        char buf[ASHMEM_NAME_LEN] = {0};
+
+        strlcpy(buf, name, sizeof(buf));
+        ret = TEMP_FAILURE_RETRY(ioctl(fd, ASHMEM_SET_NAME, buf));
+        if (ret < 0) {
+            goto error;
+        }
+    }
+
+    ret = TEMP_FAILURE_RETRY(ioctl(fd, ASHMEM_SET_SIZE, size));
+    if (ret < 0) {
+        goto error;
+    }
+
+    return fd;
+
+error:
+    save_errno = errno;
+    close(fd);
+    errno = save_errno;
+    return ret;
+}
+
+int ashmem_set_prot_region(int fd, int prot)
+{
+    return __ashmem_check_failure(fd, TEMP_FAILURE_RETRY(ioctl(fd, ASHMEM_SET_PROT_MASK, prot)));
+}
+
+int ashmem_pin_region(int fd, size_t offset, size_t len)
+{
+    // TODO: should LP64 reject too-large offset/len?
+    ashmem_pin pin = { static_cast<uint32_t>(offset), static_cast<uint32_t>(len) };
+
+    return __ashmem_check_failure(fd, TEMP_FAILURE_RETRY(ioctl(fd, ASHMEM_PIN, &pin)));
+}
+
+int ashmem_unpin_region(int fd, size_t offset, size_t len)
+{
+    // TODO: should LP64 reject too-large offset/len?
+    ashmem_pin pin = { static_cast<uint32_t>(offset), static_cast<uint32_t>(len) };
+
+    return __ashmem_check_failure(fd, TEMP_FAILURE_RETRY(ioctl(fd, ASHMEM_UNPIN, &pin)));
+}
+
+int ashmem_get_size_region(int fd)
+{
+    return __ashmem_check_failure(fd, TEMP_FAILURE_RETRY(ioctl(fd, ASHMEM_GET_SIZE, NULL)));
+}
diff --git a/libcutils/ashmem-host.c b/libcutils/ashmem-host.c
deleted file mode 100644
index 1f9f753..0000000
--- a/libcutils/ashmem-host.c
+++ /dev/null
@@ -1,92 +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.
- */
-
-/*
- * Implementation of the user-space ashmem API for the simulator, which lacks
- * an ashmem-enabled kernel. See ashmem-dev.c for the real ashmem-based version.
- */
-
-#include <errno.h>
-#include <fcntl.h>
-#include <limits.h>
-#include <stdbool.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <time.h>
-#include <unistd.h>
-
-#include <cutils/ashmem.h>
-#include <utils/Compat.h>
-
-#ifndef __unused
-#define __unused __attribute__((__unused__))
-#endif
-
-int ashmem_create_region(const char *ignored __unused, size_t size)
-{
-    char template[PATH_MAX];
-    snprintf(template, sizeof(template), "/tmp/android-ashmem-%d-XXXXXXXXX", getpid());
-    int fd = mkstemp(template);
-    if (fd == -1) return -1;
-
-    unlink(template);
-
-    if (TEMP_FAILURE_RETRY(ftruncate(fd, size)) == -1) {
-      close(fd);
-      return -1;
-    }
-
-    return fd;
-}
-
-int ashmem_set_prot_region(int fd __unused, int prot __unused)
-{
-    return 0;
-}
-
-int ashmem_pin_region(int fd __unused, size_t offset __unused, size_t len __unused)
-{
-    return 0 /*ASHMEM_NOT_PURGED*/;
-}
-
-int ashmem_unpin_region(int fd __unused, size_t offset __unused, size_t len __unused)
-{
-    return 0 /*ASHMEM_IS_UNPINNED*/;
-}
-
-int ashmem_get_size_region(int fd)
-{
-    struct stat buf;
-    int result = fstat(fd, &buf);
-    if (result == -1) {
-        return -1;
-    }
-
-    /*
-     * Check if this is an "ashmem" region.
-     * TODO: This is very hacky, and can easily break.
-     * We need some reliable indicator.
-     */
-    if (!(buf.st_nlink == 0 && S_ISREG(buf.st_mode))) {
-        errno = ENOTTY;
-        return -1;
-    }
-
-    return buf.st_size;
-}
diff --git a/libcutils/ashmem-host.cpp b/libcutils/ashmem-host.cpp
new file mode 100644
index 0000000..bb990d5
--- /dev/null
+++ b/libcutils/ashmem-host.cpp
@@ -0,0 +1,84 @@
+/*
+ * 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.
+ */
+
+#include <cutils/ashmem.h>
+
+/*
+ * Implementation of the user-space ashmem API for the simulator, which lacks
+ * an ashmem-enabled kernel. See ashmem-dev.c for the real ashmem-based version.
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <utils/Compat.h>
+
+int ashmem_create_region(const char* /*ignored*/, size_t size) {
+    char pattern[PATH_MAX];
+    snprintf(pattern, sizeof(pattern), "/tmp/android-ashmem-%d-XXXXXXXXX", getpid());
+    int fd = mkstemp(pattern);
+    if (fd == -1) return -1;
+
+    unlink(pattern);
+
+    if (TEMP_FAILURE_RETRY(ftruncate(fd, size)) == -1) {
+      close(fd);
+      return -1;
+    }
+
+    return fd;
+}
+
+int ashmem_set_prot_region(int /*fd*/, int /*prot*/) {
+    return 0;
+}
+
+int ashmem_pin_region(int /*fd*/, size_t /*offset*/, size_t /*len*/) {
+    return 0 /*ASHMEM_NOT_PURGED*/;
+}
+
+int ashmem_unpin_region(int /*fd*/, size_t /*offset*/, size_t /*len*/) {
+    return 0 /*ASHMEM_IS_UNPINNED*/;
+}
+
+int ashmem_get_size_region(int fd)
+{
+    struct stat buf;
+    int result = fstat(fd, &buf);
+    if (result == -1) {
+        return -1;
+    }
+
+    /*
+     * Check if this is an "ashmem" region.
+     * TODO: This is very hacky, and can easily break.
+     * We need some reliable indicator.
+     */
+    if (!(buf.st_nlink == 0 && S_ISREG(buf.st_mode))) {
+        errno = ENOTTY;
+        return -1;
+    }
+
+    return buf.st_size;
+}
diff --git a/libcutils/canned_fs_config.c b/libcutils/canned_fs_config.c
deleted file mode 100644
index 96ca566..0000000
--- a/libcutils/canned_fs_config.c
+++ /dev/null
@@ -1,128 +0,0 @@
-/*
- * Copyright (C) 2014 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 <errno.h>
-#include <inttypes.h>
-#include <limits.h>
-#include <stdbool.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include <private/android_filesystem_config.h>
-#include <private/canned_fs_config.h>
-
-typedef struct {
-    const char* path;
-    unsigned uid;
-    unsigned gid;
-    unsigned mode;
-    uint64_t capabilities;
-} Path;
-
-static Path* canned_data = NULL;
-static int canned_alloc = 0;
-static int canned_used = 0;
-
-static int path_compare(const void* a, const void* b) {
-    return strcmp(((Path*)a)->path, ((Path*)b)->path);
-}
-
-int load_canned_fs_config(const char* fn) {
-    char buf[PATH_MAX + 200];
-    FILE* f;
-
-    f = fopen(fn, "r");
-    if (f == NULL) {
-        fprintf(stderr, "failed to open %s: %s\n", fn, strerror(errno));
-        return -1;
-    }
-
-    while (fgets(buf, sizeof(buf), f)) {
-        Path* p;
-        char* token;
-        char* line = buf;
-        bool rootdir;
-
-        while (canned_used >= canned_alloc) {
-            canned_alloc = (canned_alloc+1) * 2;
-            canned_data = (Path*) realloc(canned_data, canned_alloc * sizeof(Path));
-        }
-        p = canned_data + canned_used;
-        if (line[0] == '/') line++;
-        rootdir = line[0] == ' ';
-        p->path = strdup(rootdir ? "" : strtok(line, " "));
-        p->uid = atoi(strtok(rootdir ? line : NULL, " "));
-        p->gid = atoi(strtok(NULL, " "));
-        p->mode = strtol(strtok(NULL, " "), NULL, 8);   // mode is in octal
-        p->capabilities = 0;
-
-        do {
-            token = strtok(NULL, " ");
-            if (token && strncmp(token, "capabilities=", 13) == 0) {
-                p->capabilities = strtoll(token+13, NULL, 0);
-                break;
-            }
-        } while (token);
-
-        canned_used++;
-    }
-
-    fclose(f);
-
-    qsort(canned_data, canned_used, sizeof(Path), path_compare);
-    printf("loaded %d fs_config entries\n", canned_used);
-
-    return 0;
-}
-
-static const int kDebugCannedFsConfig = 0;
-
-void canned_fs_config(const char* path, int dir, const char* target_out_path,
-                      unsigned* uid, unsigned* gid, unsigned* mode, uint64_t* capabilities) {
-    Path key, *p;
-
-    key.path = path;
-    if (path[0] == '/') key.path++; // canned paths lack the leading '/'
-    p = (Path*) bsearch(&key, canned_data, canned_used, sizeof(Path), path_compare);
-    if (p == NULL) {
-        fprintf(stderr, "failed to find [%s] in canned fs_config\n", path);
-        exit(1);
-    }
-    *uid = p->uid;
-    *gid = p->gid;
-    *mode = p->mode;
-    *capabilities = p->capabilities;
-
-    if (kDebugCannedFsConfig) {
-        // for debugging, run the built-in fs_config and compare the results.
-
-        unsigned c_uid, c_gid, c_mode;
-        uint64_t c_capabilities;
-
-        fs_config(path, dir, target_out_path, &c_uid, &c_gid, &c_mode, &c_capabilities);
-
-        if (c_uid != *uid) printf("%s uid %d %d\n", path, *uid, c_uid);
-        if (c_gid != *gid) printf("%s gid %d %d\n", path, *gid, c_gid);
-        if (c_mode != *mode) printf("%s mode 0%o 0%o\n", path, *mode, c_mode);
-        if (c_capabilities != *capabilities) {
-            printf("%s capabilities %" PRIx64 " %" PRIx64 "\n",
-                path,
-                *capabilities,
-                c_capabilities);
-        }
-    }
-}
diff --git a/libcutils/canned_fs_config.cpp b/libcutils/canned_fs_config.cpp
new file mode 100644
index 0000000..2772ef0
--- /dev/null
+++ b/libcutils/canned_fs_config.cpp
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2014 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 <private/android_filesystem_config.h>
+#include <private/canned_fs_config.h>
+#include <private/fs_config.h>
+
+#include <errno.h>
+#include <inttypes.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+typedef struct {
+    const char* path;
+    unsigned uid;
+    unsigned gid;
+    unsigned mode;
+    uint64_t capabilities;
+} Path;
+
+static Path* canned_data = NULL;
+static int canned_alloc = 0;
+static int canned_used = 0;
+
+static int path_compare(const void* a, const void* b) {
+    return strcmp(((Path*)a)->path, ((Path*)b)->path);
+}
+
+int load_canned_fs_config(const char* fn) {
+    char buf[PATH_MAX + 200];
+    FILE* f;
+
+    f = fopen(fn, "r");
+    if (f == NULL) {
+        fprintf(stderr, "failed to open %s: %s\n", fn, strerror(errno));
+        return -1;
+    }
+
+    while (fgets(buf, sizeof(buf), f)) {
+        Path* p;
+        char* token;
+        char* line = buf;
+        bool rootdir;
+
+        while (canned_used >= canned_alloc) {
+            canned_alloc = (canned_alloc+1) * 2;
+            canned_data = (Path*) realloc(canned_data, canned_alloc * sizeof(Path));
+        }
+        p = canned_data + canned_used;
+        if (line[0] == '/') line++;
+        rootdir = line[0] == ' ';
+        p->path = strdup(rootdir ? "" : strtok(line, " "));
+        p->uid = atoi(strtok(rootdir ? line : NULL, " "));
+        p->gid = atoi(strtok(NULL, " "));
+        p->mode = strtol(strtok(NULL, " "), NULL, 8);   // mode is in octal
+        p->capabilities = 0;
+
+        do {
+            token = strtok(NULL, " ");
+            if (token && strncmp(token, "capabilities=", 13) == 0) {
+                p->capabilities = strtoll(token+13, NULL, 0);
+                break;
+            }
+        } while (token);
+
+        canned_used++;
+    }
+
+    fclose(f);
+
+    qsort(canned_data, canned_used, sizeof(Path), path_compare);
+    printf("loaded %d fs_config entries\n", canned_used);
+
+    return 0;
+}
+
+static const int kDebugCannedFsConfig = 0;
+
+void canned_fs_config(const char* path, int dir, const char* target_out_path,
+                      unsigned* uid, unsigned* gid, unsigned* mode, uint64_t* capabilities) {
+    Path key, *p;
+
+    key.path = path;
+    if (path[0] == '/') key.path++; // canned paths lack the leading '/'
+    p = (Path*) bsearch(&key, canned_data, canned_used, sizeof(Path), path_compare);
+    if (p == NULL) {
+        fprintf(stderr, "failed to find [%s] in canned fs_config\n", path);
+        exit(1);
+    }
+    *uid = p->uid;
+    *gid = p->gid;
+    *mode = p->mode;
+    *capabilities = p->capabilities;
+
+    if (kDebugCannedFsConfig) {
+        // for debugging, run the built-in fs_config and compare the results.
+
+        unsigned c_uid, c_gid, c_mode;
+        uint64_t c_capabilities;
+
+        fs_config(path, dir, target_out_path, &c_uid, &c_gid, &c_mode, &c_capabilities);
+
+        if (c_uid != *uid) printf("%s uid %d %d\n", path, *uid, c_uid);
+        if (c_gid != *gid) printf("%s gid %d %d\n", path, *gid, c_gid);
+        if (c_mode != *mode) printf("%s mode 0%o 0%o\n", path, *mode, c_mode);
+        if (c_capabilities != *capabilities) {
+            printf("%s capabilities %" PRIx64 " %" PRIx64 "\n",
+                path,
+                *capabilities,
+                c_capabilities);
+        }
+    }
+}
diff --git a/libcutils/config_utils.c b/libcutils/config_utils.c
deleted file mode 100644
index fc5ca78..0000000
--- a/libcutils/config_utils.c
+++ /dev/null
@@ -1,329 +0,0 @@
-/*
- * Copyright (C) 2007 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 <string.h>
-#include <ctype.h>
-#include <stdlib.h>
-#include <fcntl.h>
-#include <unistd.h>
-
-#include <cutils/config_utils.h>
-#include <cutils/misc.h>
-
-cnode* config_node(const char *name, const char *value)
-{
-    cnode *node;
-
-    node = calloc(sizeof(cnode), 1);
-    if(node) {
-        node->name = name ? name : "";
-        node->value = value ? value : "";
-    }
-
-    return node;
-}
-
-cnode* config_find(cnode *root, const char *name)
-{
-    cnode *node, *match = NULL;
-
-    /* we walk the whole list, as we need to return the last (newest) entry */
-    for(node = root->first_child; node; node = node->next)
-        if(!strcmp(node->name, name))
-            match = node;
-
-    return match;
-}
-
-static cnode* _config_create(cnode *root, const char *name)
-{
-    cnode *node;
-
-    node = config_node(name, NULL);
-
-    if(root->last_child)
-        root->last_child->next = node;
-    else
-        root->first_child = node;
-
-    root->last_child = node;
-
-    return node;
-}
-
-int config_bool(cnode *root, const char *name, int _default)
-{
-    cnode *node;
-        
-    node = config_find(root, name);
-    if(!node)
-        return _default;
-
-    switch(node->value[0]) {
-    case 'y':
-    case 'Y':
-    case '1':
-        return 1;
-    default:
-        return 0;
-    }
-}
-
-const char* config_str(cnode *root, const char *name, const char *_default)
-{
-    cnode *node;
-
-    node = config_find(root, name);
-    if(!node)
-        return _default;
-    return node->value;
-}
-
-void config_set(cnode *root, const char *name, const char *value)
-{
-    cnode *node;
-
-    node = config_find(root, name);
-    if(node)
-        node->value = value;
-    else {
-        node = _config_create(root, name);
-        node->value = value;
-    }
-}
-
-#define T_EOF 0
-#define T_TEXT 1
-#define T_DOT 2
-#define T_OBRACE 3
-#define T_CBRACE 4
-
-typedef struct
-{
-    char *data;
-    char *text;
-    int len;
-    char next;
-} cstate;
-
-static int _lex(cstate *cs, int value)
-{
-    char c;
-    char *s;
-    char *data;
-
-    data = cs->data;
-
-    if(cs->next != 0) {
-        c = cs->next;
-        cs->next = 0;
-        goto got_c;
-    }
-
-restart:
-    for(;;) {
-        c = *data++;
-    got_c:
-        if(isspace(c))
-            continue;
-
-        switch(c) {
-        case 0:
-            return T_EOF;
-
-        case '#':
-            for(;;) {
-                switch(*data) {
-                case 0:
-                    cs->data = data;
-                    return T_EOF;
-                case '\n':
-                    cs->data = data + 1;
-                    goto restart;
-                default:
-                    data++;
-                }
-            }
-            break;
-            
-        case '.':
-            cs->data = data;
-            return T_DOT;
-
-        case '{':
-            cs->data = data;
-            return T_OBRACE;
-
-        case '}':
-            cs->data = data;
-            return T_CBRACE;
-
-        default:
-            s = data - 1;
-
-            if(value) {
-                for(;;) {
-                    if(*data == 0) {
-                        cs->data = data;
-                        break;
-                    }
-                    if(*data == '\n') {
-                        cs->data = data + 1;
-                        *data-- = 0;
-                        break;
-                    }
-                    data++;
-                }
-
-                    /* strip trailing whitespace */
-                while(data > s){
-                    if(!isspace(*data)) break;
-                    *data-- = 0;
-                }
-
-                goto got_text;                
-            } else {
-                for(;;) {
-                    if(isspace(*data)) {
-                        *data = 0;
-                        cs->data = data + 1;
-                        goto got_text;
-                    }
-                    switch(*data) {
-                    case 0:
-                        cs->data = data;
-                        goto got_text;
-                    case '.':
-                    case '{':
-                    case '}':
-                        cs->next = *data;
-                        *data = 0;
-                        cs->data = data + 1;
-                        goto got_text;
-                    default:
-                        data++;
-                    }
-                }
-            }
-        }
-    }
-
-got_text:
-    cs->text = s;
-    return T_TEXT;
-}
-
-#if 0
-char *TOKENNAMES[] = { "EOF", "TEXT", "DOT", "OBRACE", "CBRACE" };
-
-static int lex(cstate *cs, int value)
-{
-    int tok = _lex(cs, value);
-    printf("TOKEN(%d) %s %s\n", value, TOKENNAMES[tok],
-           tok == T_TEXT ? cs->text : "");
-    return tok;
-}
-#else
-#define lex(cs,v) _lex(cs,v)
-#endif
-
-static int parse_expr(cstate *cs, cnode *node);
-
-static int parse_block(cstate *cs, cnode *node)
-{
-    for(;;){
-        switch(lex(cs, 0)){
-        case T_TEXT:
-            if(parse_expr(cs, node)) return -1;
-            continue;
-
-        case T_CBRACE:
-            return 0;
-
-        default:
-            return -1;
-        }
-    }
-}
-
-static int parse_expr(cstate *cs, cnode *root)
-{
-    cnode *node;
-
-        /* last token was T_TEXT */
-    node = config_find(root, cs->text);
-    if(!node || *node->value)
-        node = _config_create(root, cs->text);
-
-    for(;;) {
-        switch(lex(cs, 1)) {
-        case T_DOT:
-            if(lex(cs, 0) != T_TEXT)
-                return -1;
-            node = _config_create(node, cs->text);
-            continue;
-
-        case T_TEXT:
-            node->value = cs->text;
-            return 0;
-
-        case T_OBRACE:
-            return parse_block(cs, node);
-
-        default:
-            return -1;
-        }
-    }
-}
-
-void config_load(cnode *root, char *data)
-{
-    if(data != 0) {
-        cstate cs;
-        cs.data = data;
-        cs.next = 0;
-
-        for(;;) {
-            switch(lex(&cs, 0)) {
-            case T_TEXT:
-                if(parse_expr(&cs, root))
-                    return;
-                break;
-            default:
-                return;
-            }
-        }
-    }
-}
-
-void config_load_file(cnode *root, const char *fn)
-{
-    char *data;
-    data = load_file(fn, 0);
-    config_load(root, data);
-}
-
-void config_free(cnode *root)
-{
-    cnode *cur = root->first_child;
-
-    while (cur) {
-        cnode *prev = cur;
-        config_free(cur);
-        cur = cur->next;
-        free(prev);
-    }
-}
diff --git a/libcutils/config_utils.cpp b/libcutils/config_utils.cpp
new file mode 100644
index 0000000..a3af01a
--- /dev/null
+++ b/libcutils/config_utils.cpp
@@ -0,0 +1,328 @@
+/*
+ * Copyright (C) 2007 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 <cutils/config_utils.h>
+
+#include <string.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include <cutils/misc.h>
+
+cnode* config_node(const char *name, const char *value)
+{
+    cnode* node = static_cast<cnode*>(calloc(sizeof(cnode), 1));
+    if(node) {
+        node->name = name ? name : "";
+        node->value = value ? value : "";
+    }
+
+    return node;
+}
+
+cnode* config_find(cnode *root, const char *name)
+{
+    cnode *node, *match = NULL;
+
+    /* we walk the whole list, as we need to return the last (newest) entry */
+    for(node = root->first_child; node; node = node->next)
+        if(!strcmp(node->name, name))
+            match = node;
+
+    return match;
+}
+
+static cnode* _config_create(cnode *root, const char *name)
+{
+    cnode *node;
+
+    node = config_node(name, NULL);
+
+    if(root->last_child)
+        root->last_child->next = node;
+    else
+        root->first_child = node;
+
+    root->last_child = node;
+
+    return node;
+}
+
+int config_bool(cnode *root, const char *name, int _default)
+{
+    cnode *node;
+        
+    node = config_find(root, name);
+    if(!node)
+        return _default;
+
+    switch(node->value[0]) {
+    case 'y':
+    case 'Y':
+    case '1':
+        return 1;
+    default:
+        return 0;
+    }
+}
+
+const char* config_str(cnode *root, const char *name, const char *_default)
+{
+    cnode *node;
+
+    node = config_find(root, name);
+    if(!node)
+        return _default;
+    return node->value;
+}
+
+void config_set(cnode *root, const char *name, const char *value)
+{
+    cnode *node;
+
+    node = config_find(root, name);
+    if(node)
+        node->value = value;
+    else {
+        node = _config_create(root, name);
+        node->value = value;
+    }
+}
+
+#define T_EOF 0
+#define T_TEXT 1
+#define T_DOT 2
+#define T_OBRACE 3
+#define T_CBRACE 4
+
+typedef struct
+{
+    char *data;
+    char *text;
+    int len;
+    char next;
+} cstate;
+
+static int _lex(cstate *cs, int value)
+{
+    char c;
+    char *s;
+    char *data;
+
+    data = cs->data;
+
+    if(cs->next != 0) {
+        c = cs->next;
+        cs->next = 0;
+        goto got_c;
+    }
+
+restart:
+    for(;;) {
+        c = *data++;
+    got_c:
+        if(isspace(c))
+            continue;
+
+        switch(c) {
+        case 0:
+            return T_EOF;
+
+        case '#':
+            for(;;) {
+                switch(*data) {
+                case 0:
+                    cs->data = data;
+                    return T_EOF;
+                case '\n':
+                    cs->data = data + 1;
+                    goto restart;
+                default:
+                    data++;
+                }
+            }
+            break;
+            
+        case '.':
+            cs->data = data;
+            return T_DOT;
+
+        case '{':
+            cs->data = data;
+            return T_OBRACE;
+
+        case '}':
+            cs->data = data;
+            return T_CBRACE;
+
+        default:
+            s = data - 1;
+
+            if(value) {
+                for(;;) {
+                    if(*data == 0) {
+                        cs->data = data;
+                        break;
+                    }
+                    if(*data == '\n') {
+                        cs->data = data + 1;
+                        *data-- = 0;
+                        break;
+                    }
+                    data++;
+                }
+
+                    /* strip trailing whitespace */
+                while(data > s){
+                    if(!isspace(*data)) break;
+                    *data-- = 0;
+                }
+
+                goto got_text;                
+            } else {
+                for(;;) {
+                    if(isspace(*data)) {
+                        *data = 0;
+                        cs->data = data + 1;
+                        goto got_text;
+                    }
+                    switch(*data) {
+                    case 0:
+                        cs->data = data;
+                        goto got_text;
+                    case '.':
+                    case '{':
+                    case '}':
+                        cs->next = *data;
+                        *data = 0;
+                        cs->data = data + 1;
+                        goto got_text;
+                    default:
+                        data++;
+                    }
+                }
+            }
+        }
+    }
+
+got_text:
+    cs->text = s;
+    return T_TEXT;
+}
+
+#if 0
+char *TOKENNAMES[] = { "EOF", "TEXT", "DOT", "OBRACE", "CBRACE" };
+
+static int lex(cstate *cs, int value)
+{
+    int tok = _lex(cs, value);
+    printf("TOKEN(%d) %s %s\n", value, TOKENNAMES[tok],
+           tok == T_TEXT ? cs->text : "");
+    return tok;
+}
+#else
+#define lex(cs,v) _lex(cs,v)
+#endif
+
+static int parse_expr(cstate *cs, cnode *node);
+
+static int parse_block(cstate *cs, cnode *node)
+{
+    for(;;){
+        switch(lex(cs, 0)){
+        case T_TEXT:
+            if(parse_expr(cs, node)) return -1;
+            continue;
+
+        case T_CBRACE:
+            return 0;
+
+        default:
+            return -1;
+        }
+    }
+}
+
+static int parse_expr(cstate *cs, cnode *root)
+{
+    cnode *node;
+
+        /* last token was T_TEXT */
+    node = config_find(root, cs->text);
+    if(!node || *node->value)
+        node = _config_create(root, cs->text);
+
+    for(;;) {
+        switch(lex(cs, 1)) {
+        case T_DOT:
+            if(lex(cs, 0) != T_TEXT)
+                return -1;
+            node = _config_create(node, cs->text);
+            continue;
+
+        case T_TEXT:
+            node->value = cs->text;
+            return 0;
+
+        case T_OBRACE:
+            return parse_block(cs, node);
+
+        default:
+            return -1;
+        }
+    }
+}
+
+void config_load(cnode *root, char *data)
+{
+    if(data != 0) {
+        cstate cs;
+        cs.data = data;
+        cs.next = 0;
+
+        for(;;) {
+            switch(lex(&cs, 0)) {
+            case T_TEXT:
+                if(parse_expr(&cs, root))
+                    return;
+                break;
+            default:
+                return;
+            }
+        }
+    }
+}
+
+void config_load_file(cnode *root, const char *fn)
+{
+    char* data = static_cast<char*>(load_file(fn, nullptr));
+    config_load(root, data);
+    // TODO: deliberate leak :-/
+}
+
+void config_free(cnode *root)
+{
+    cnode *cur = root->first_child;
+
+    while (cur) {
+        cnode *prev = cur;
+        config_free(cur);
+        cur = cur->next;
+        free(prev);
+    }
+}
diff --git a/libcutils/dlmalloc_stubs.c b/libcutils/dlmalloc_stubs.c
deleted file mode 100644
index 2cff9dd..0000000
--- a/libcutils/dlmalloc_stubs.c
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright (C) 2007 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.
- */
-
-#define LOG_TAG "dlmalloc-stubs"
-
-#include "log/log.h"
-
-#define UNUSED __attribute__((__unused__))
-
-/*
- * Stubs for functions defined in bionic/libc/bionic/dlmalloc.c. These
- * are used in host builds, as the host libc will not contain these
- * functions.
- */
-void dlmalloc_inspect_all(void(*handler)(void*, void *, size_t, void*) UNUSED,
-                          void* arg UNUSED)
-{
-  ALOGW("Called host unimplemented stub: dlmalloc_inspect_all");
-}
-
-int dlmalloc_trim(size_t unused UNUSED)
-{
-  ALOGW("Called host unimplemented stub: dlmalloc_trim");
-  return 0;
-}
diff --git a/libcutils/fs.c b/libcutils/fs.c
deleted file mode 100644
index b253b1c..0000000
--- a/libcutils/fs.c
+++ /dev/null
@@ -1,274 +0,0 @@
-/*
- * Copyright (C) 2012 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.
- */
-
-#define LOG_TAG "cutils"
-
-/* These defines are only needed because prebuilt headers are out of date */
-#define __USE_XOPEN2K8 1
-#define _ATFILE_SOURCE 1
-#define _GNU_SOURCE 1
-
-#include <dirent.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <limits.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include <cutils/fs.h>
-#include <log/log.h>
-
-#define ALL_PERMS (S_ISUID | S_ISGID | S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO)
-#define BUF_SIZE 64
-
-static int fs_prepare_path_impl(const char* path, mode_t mode, uid_t uid, gid_t gid,
-        int allow_fixup, int prepare_as_dir) {
-    // Check if path needs to be created
-    struct stat sb;
-    int create_result = -1;
-    if (TEMP_FAILURE_RETRY(lstat(path, &sb)) == -1) {
-        if (errno == ENOENT) {
-            goto create;
-        } else {
-            ALOGE("Failed to lstat(%s): %s", path, strerror(errno));
-            return -1;
-        }
-    }
-
-    // Exists, verify status
-    int type_ok = prepare_as_dir ? S_ISDIR(sb.st_mode) : S_ISREG(sb.st_mode);
-    if (!type_ok) {
-        ALOGE("Not a %s: %s", (prepare_as_dir ? "directory" : "regular file"), path);
-        return -1;
-    }
-
-    int owner_match = ((sb.st_uid == uid) && (sb.st_gid == gid));
-    int mode_match = ((sb.st_mode & ALL_PERMS) == mode);
-    if (owner_match && mode_match) {
-        return 0;
-    } else if (allow_fixup) {
-        goto fixup;
-    } else {
-        if (!owner_match) {
-            ALOGE("Expected path %s with owner %d:%d but found %d:%d",
-                    path, uid, gid, sb.st_uid, sb.st_gid);
-            return -1;
-        } else {
-            ALOGW("Expected path %s with mode %o but found %o",
-                    path, mode, (sb.st_mode & ALL_PERMS));
-            return 0;
-        }
-    }
-
-create:
-    create_result = prepare_as_dir
-        ? TEMP_FAILURE_RETRY(mkdir(path, mode))
-        : TEMP_FAILURE_RETRY(open(path, O_CREAT | O_CLOEXEC | O_NOFOLLOW | O_RDONLY, 0644));
-    if (create_result == -1) {
-        if (errno != EEXIST) {
-            ALOGE("Failed to %s(%s): %s",
-                    (prepare_as_dir ? "mkdir" : "open"), path, strerror(errno));
-            return -1;
-        }
-    } else if (!prepare_as_dir) {
-        // For regular files we need to make sure we close the descriptor
-        if (close(create_result) == -1) {
-            ALOGW("Failed to close file after create %s: %s", path, strerror(errno));
-        }
-    }
-fixup:
-    if (TEMP_FAILURE_RETRY(chmod(path, mode)) == -1) {
-        ALOGE("Failed to chmod(%s, %d): %s", path, mode, strerror(errno));
-        return -1;
-    }
-    if (TEMP_FAILURE_RETRY(chown(path, uid, gid)) == -1) {
-        ALOGE("Failed to chown(%s, %d, %d): %s", path, uid, gid, strerror(errno));
-        return -1;
-    }
-
-    return 0;
-}
-
-int fs_prepare_dir(const char* path, mode_t mode, uid_t uid, gid_t gid) {
-    return fs_prepare_path_impl(path, mode, uid, gid, /*allow_fixup*/ 1, /*prepare_as_dir*/ 1);
-}
-
-int fs_prepare_dir_strict(const char* path, mode_t mode, uid_t uid, gid_t gid) {
-    return fs_prepare_path_impl(path, mode, uid, gid, /*allow_fixup*/ 0, /*prepare_as_dir*/ 1);
-}
-
-int fs_prepare_file_strict(const char* path, mode_t mode, uid_t uid, gid_t gid) {
-    return fs_prepare_path_impl(path, mode, uid, gid, /*allow_fixup*/ 0, /*prepare_as_dir*/ 0);
-}
-
-int fs_read_atomic_int(const char* path, int* out_value) {
-    int fd = TEMP_FAILURE_RETRY(open(path, O_RDONLY));
-    if (fd == -1) {
-        ALOGE("Failed to read %s: %s", path, strerror(errno));
-        return -1;
-    }
-
-    char buf[BUF_SIZE];
-    if (TEMP_FAILURE_RETRY(read(fd, buf, BUF_SIZE)) == -1) {
-        ALOGE("Failed to read %s: %s", path, strerror(errno));
-        goto fail;
-    }
-    if (sscanf(buf, "%d", out_value) != 1) {
-        ALOGE("Failed to parse %s: %s", path, strerror(errno));
-        goto fail;
-    }
-    close(fd);
-    return 0;
-
-fail:
-    close(fd);
-    *out_value = -1;
-    return -1;
-}
-
-int fs_write_atomic_int(const char* path, int value) {
-    char temp[PATH_MAX];
-    if (snprintf(temp, PATH_MAX, "%s.XXXXXX", path) >= PATH_MAX) {
-        ALOGE("Path too long");
-        return -1;
-    }
-
-    int fd = TEMP_FAILURE_RETRY(mkstemp(temp));
-    if (fd == -1) {
-        ALOGE("Failed to open %s: %s", temp, strerror(errno));
-        return -1;
-    }
-
-    char buf[BUF_SIZE];
-    int len = snprintf(buf, BUF_SIZE, "%d", value) + 1;
-    if (len > BUF_SIZE) {
-        ALOGE("Value %d too large: %s", value, strerror(errno));
-        goto fail;
-    }
-    if (TEMP_FAILURE_RETRY(write(fd, buf, len)) < len) {
-        ALOGE("Failed to write %s: %s", temp, strerror(errno));
-        goto fail;
-    }
-    if (close(fd) == -1) {
-        ALOGE("Failed to close %s: %s", temp, strerror(errno));
-        goto fail_closed;
-    }
-
-    if (rename(temp, path) == -1) {
-        ALOGE("Failed to rename %s to %s: %s", temp, path, strerror(errno));
-        goto fail_closed;
-    }
-
-    return 0;
-
-fail:
-    close(fd);
-fail_closed:
-    unlink(temp);
-    return -1;
-}
-
-#ifndef __APPLE__
-
-int fs_mkdirs(const char* path, mode_t mode) {
-    int res = 0;
-    int fd = 0;
-    struct stat sb;
-    char* buf = strdup(path);
-
-    if (*buf != '/') {
-        ALOGE("Relative paths are not allowed: %s", buf);
-        res = -EINVAL;
-        goto done;
-    }
-
-    if ((fd = open("/", 0)) == -1) {
-        ALOGE("Failed to open(/): %s", strerror(errno));
-        res = -errno;
-        goto done;
-    }
-
-    char* segment = buf + 1;
-    char* p = segment;
-    while (*p != '\0') {
-        if (*p == '/') {
-            *p = '\0';
-
-            if (!strcmp(segment, "..") || !strcmp(segment, ".") || !strcmp(segment, "")) {
-                ALOGE("Invalid path: %s", buf);
-                res = -EINVAL;
-                goto done_close;
-            }
-
-            if (fstatat(fd, segment, &sb, AT_SYMLINK_NOFOLLOW) != 0) {
-                if (errno == ENOENT) {
-                    /* Nothing there yet; let's create it! */
-                    if (mkdirat(fd, segment, mode) != 0) {
-                        if (errno == EEXIST) {
-                            /* We raced with someone; ignore */
-                        } else {
-                            ALOGE("Failed to mkdirat(%s): %s", buf, strerror(errno));
-                            res = -errno;
-                            goto done_close;
-                        }
-                    }
-                } else {
-                    ALOGE("Failed to fstatat(%s): %s", buf, strerror(errno));
-                    res = -errno;
-                    goto done_close;
-                }
-            } else {
-                if (S_ISLNK(sb.st_mode)) {
-                    ALOGE("Symbolic links are not allowed: %s", buf);
-                    res = -ELOOP;
-                    goto done_close;
-                }
-                if (!S_ISDIR(sb.st_mode)) {
-                    ALOGE("Existing segment not a directory: %s", buf);
-                    res = -ENOTDIR;
-                    goto done_close;
-                }
-            }
-
-            /* Yay, segment is ready for us to step into */
-            int next_fd;
-            if ((next_fd = openat(fd, segment, O_NOFOLLOW | O_CLOEXEC)) == -1) {
-                ALOGE("Failed to openat(%s): %s", buf, strerror(errno));
-                res = -errno;
-                goto done_close;
-            }
-
-            close(fd);
-            fd = next_fd;
-
-            *p = '/';
-            segment = p + 1;
-        }
-        p++;
-    }
-
-done_close:
-    close(fd);
-done:
-    free(buf);
-    return res;
-}
-
-#endif
diff --git a/libcutils/fs.cpp b/libcutils/fs.cpp
new file mode 100644
index 0000000..ef85acc
--- /dev/null
+++ b/libcutils/fs.cpp
@@ -0,0 +1,276 @@
+/*
+ * Copyright (C) 2012 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 <cutils/fs.h>
+
+#define LOG_TAG "cutils"
+
+/* These defines are only needed because prebuilt headers are out of date */
+#define __USE_XOPEN2K8 1
+#define _ATFILE_SOURCE 1
+#define _GNU_SOURCE 1
+
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <log/log.h>
+
+#define ALL_PERMS (S_ISUID | S_ISGID | S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO)
+#define BUF_SIZE 64
+
+static int fs_prepare_path_impl(const char* path, mode_t mode, uid_t uid, gid_t gid,
+        int allow_fixup, int prepare_as_dir) {
+    // TODO: fix the goto hell below.
+    int type_ok;
+    int owner_match;
+    int mode_match;
+
+    // Check if path needs to be created
+    struct stat sb;
+    int create_result = -1;
+    if (TEMP_FAILURE_RETRY(lstat(path, &sb)) == -1) {
+        if (errno == ENOENT) {
+            goto create;
+        } else {
+            ALOGE("Failed to lstat(%s): %s", path, strerror(errno));
+            return -1;
+        }
+    }
+
+    // Exists, verify status
+    type_ok = prepare_as_dir ? S_ISDIR(sb.st_mode) : S_ISREG(sb.st_mode);
+    if (!type_ok) {
+        ALOGE("Not a %s: %s", (prepare_as_dir ? "directory" : "regular file"), path);
+        return -1;
+    }
+
+    owner_match = ((sb.st_uid == uid) && (sb.st_gid == gid));
+    mode_match = ((sb.st_mode & ALL_PERMS) == mode);
+    if (owner_match && mode_match) {
+        return 0;
+    } else if (allow_fixup) {
+        goto fixup;
+    } else {
+        if (!owner_match) {
+            ALOGE("Expected path %s with owner %d:%d but found %d:%d",
+                    path, uid, gid, sb.st_uid, sb.st_gid);
+            return -1;
+        } else {
+            ALOGW("Expected path %s with mode %o but found %o",
+                    path, mode, (sb.st_mode & ALL_PERMS));
+            return 0;
+        }
+    }
+
+create:
+    create_result = prepare_as_dir
+        ? TEMP_FAILURE_RETRY(mkdir(path, mode))
+        : TEMP_FAILURE_RETRY(open(path, O_CREAT | O_CLOEXEC | O_NOFOLLOW | O_RDONLY, 0644));
+    if (create_result == -1) {
+        if (errno != EEXIST) {
+            ALOGE("Failed to %s(%s): %s",
+                    (prepare_as_dir ? "mkdir" : "open"), path, strerror(errno));
+            return -1;
+        }
+    } else if (!prepare_as_dir) {
+        // For regular files we need to make sure we close the descriptor
+        if (close(create_result) == -1) {
+            ALOGW("Failed to close file after create %s: %s", path, strerror(errno));
+        }
+    }
+fixup:
+    if (TEMP_FAILURE_RETRY(chmod(path, mode)) == -1) {
+        ALOGE("Failed to chmod(%s, %d): %s", path, mode, strerror(errno));
+        return -1;
+    }
+    if (TEMP_FAILURE_RETRY(chown(path, uid, gid)) == -1) {
+        ALOGE("Failed to chown(%s, %d, %d): %s", path, uid, gid, strerror(errno));
+        return -1;
+    }
+
+    return 0;
+}
+
+int fs_prepare_dir(const char* path, mode_t mode, uid_t uid, gid_t gid) {
+    return fs_prepare_path_impl(path, mode, uid, gid, /*allow_fixup*/ 1, /*prepare_as_dir*/ 1);
+}
+
+int fs_prepare_dir_strict(const char* path, mode_t mode, uid_t uid, gid_t gid) {
+    return fs_prepare_path_impl(path, mode, uid, gid, /*allow_fixup*/ 0, /*prepare_as_dir*/ 1);
+}
+
+int fs_prepare_file_strict(const char* path, mode_t mode, uid_t uid, gid_t gid) {
+    return fs_prepare_path_impl(path, mode, uid, gid, /*allow_fixup*/ 0, /*prepare_as_dir*/ 0);
+}
+
+int fs_read_atomic_int(const char* path, int* out_value) {
+    int fd = TEMP_FAILURE_RETRY(open(path, O_RDONLY));
+    if (fd == -1) {
+        ALOGE("Failed to read %s: %s", path, strerror(errno));
+        return -1;
+    }
+
+    char buf[BUF_SIZE];
+    if (TEMP_FAILURE_RETRY(read(fd, buf, BUF_SIZE)) == -1) {
+        ALOGE("Failed to read %s: %s", path, strerror(errno));
+        goto fail;
+    }
+    if (sscanf(buf, "%d", out_value) != 1) {
+        ALOGE("Failed to parse %s: %s", path, strerror(errno));
+        goto fail;
+    }
+    close(fd);
+    return 0;
+
+fail:
+    close(fd);
+    *out_value = -1;
+    return -1;
+}
+
+int fs_write_atomic_int(const char* path, int value) {
+    char temp[PATH_MAX];
+    if (snprintf(temp, PATH_MAX, "%s.XXXXXX", path) >= PATH_MAX) {
+        ALOGE("Path too long");
+        return -1;
+    }
+
+    int fd = TEMP_FAILURE_RETRY(mkstemp(temp));
+    if (fd == -1) {
+        ALOGE("Failed to open %s: %s", temp, strerror(errno));
+        return -1;
+    }
+
+    char buf[BUF_SIZE];
+    int len = snprintf(buf, BUF_SIZE, "%d", value) + 1;
+    if (len > BUF_SIZE) {
+        ALOGE("Value %d too large: %s", value, strerror(errno));
+        goto fail;
+    }
+    if (TEMP_FAILURE_RETRY(write(fd, buf, len)) < len) {
+        ALOGE("Failed to write %s: %s", temp, strerror(errno));
+        goto fail;
+    }
+    if (close(fd) == -1) {
+        ALOGE("Failed to close %s: %s", temp, strerror(errno));
+        goto fail_closed;
+    }
+
+    if (rename(temp, path) == -1) {
+        ALOGE("Failed to rename %s to %s: %s", temp, path, strerror(errno));
+        goto fail_closed;
+    }
+
+    return 0;
+
+fail:
+    close(fd);
+fail_closed:
+    unlink(temp);
+    return -1;
+}
+
+#ifndef __APPLE__
+
+int fs_mkdirs(const char* path, mode_t mode) {
+    if (*path != '/') {
+        ALOGE("Relative paths are not allowed: %s", path);
+        return -EINVAL;
+    }
+
+    int fd = open("/", 0);
+    if (fd == -1) {
+        ALOGE("Failed to open(/): %s", strerror(errno));
+        return -errno;
+    }
+
+    struct stat sb;
+    int res = 0;
+    char* buf = strdup(path);
+    char* segment = buf + 1;
+    char* p = segment;
+    while (*p != '\0') {
+        if (*p == '/') {
+            *p = '\0';
+
+            if (!strcmp(segment, "..") || !strcmp(segment, ".") || !strcmp(segment, "")) {
+                ALOGE("Invalid path: %s", buf);
+                res = -EINVAL;
+                goto done_close;
+            }
+
+            if (fstatat(fd, segment, &sb, AT_SYMLINK_NOFOLLOW) != 0) {
+                if (errno == ENOENT) {
+                    /* Nothing there yet; let's create it! */
+                    if (mkdirat(fd, segment, mode) != 0) {
+                        if (errno == EEXIST) {
+                            /* We raced with someone; ignore */
+                        } else {
+                            ALOGE("Failed to mkdirat(%s): %s", buf, strerror(errno));
+                            res = -errno;
+                            goto done_close;
+                        }
+                    }
+                } else {
+                    ALOGE("Failed to fstatat(%s): %s", buf, strerror(errno));
+                    res = -errno;
+                    goto done_close;
+                }
+            } else {
+                if (S_ISLNK(sb.st_mode)) {
+                    ALOGE("Symbolic links are not allowed: %s", buf);
+                    res = -ELOOP;
+                    goto done_close;
+                }
+                if (!S_ISDIR(sb.st_mode)) {
+                    ALOGE("Existing segment not a directory: %s", buf);
+                    res = -ENOTDIR;
+                    goto done_close;
+                }
+            }
+
+            /* Yay, segment is ready for us to step into */
+            int next_fd;
+            if ((next_fd = openat(fd, segment, O_NOFOLLOW | O_CLOEXEC)) == -1) {
+                ALOGE("Failed to openat(%s): %s", buf, strerror(errno));
+                res = -errno;
+                goto done_close;
+            }
+
+            close(fd);
+            fd = next_fd;
+
+            *p = '/';
+            segment = p + 1;
+        }
+        p++;
+    }
+
+done_close:
+    close(fd);
+    free(buf);
+    return res;
+}
+
+#endif
diff --git a/libcutils/fs_config.cpp b/libcutils/fs_config.cpp
index 13c4abf..48def6f 100644
--- a/libcutils/fs_config.cpp
+++ b/libcutils/fs_config.cpp
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#include <private/fs_config.h>
+
 // This file is used to define the properties of the filesystem
 // images generated by build tools (mkbootfs and mkyaffs2image) and
 // by the device side of adb.
@@ -22,7 +24,6 @@
 
 #include <errno.h>
 #include <fcntl.h>
-#include <stdbool.h>
 #include <stdint.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -32,7 +33,6 @@
 
 #include <log/log.h>
 #include <private/android_filesystem_config.h>
-#include <private/fs_config.h>
 #include <utils/Compat.h>
 
 #ifndef O_BINARY
@@ -80,7 +80,7 @@
     { 00775, AID_ROOT,         AID_ROOT,         0, "data/preloads" },
     { 00771, AID_SYSTEM,       AID_SYSTEM,       0, "data" },
     { 00755, AID_ROOT,         AID_SYSTEM,       0, "mnt" },
-    { 00755, AID_ROOT,         AID_ROOT,         0, "root" },
+    { 00755, AID_ROOT,         AID_SHELL,        0, "product/bin" },
     { 00750, AID_ROOT,         AID_SHELL,        0, "sbin" },
     { 00777, AID_ROOT,         AID_ROOT,         0, "sdcard" },
     { 00751, AID_ROOT,         AID_SDCARD_R,     0, "storage" },
@@ -123,8 +123,14 @@
     {odm_conf_file, odm_conf_dir},
 };
 
+// Do not use android_files to grant Linux capabilities.  Use ambient capabilities in their
+// associated init.rc file instead.  See https://source.android.com/devices/tech/config/ambient.
+
+// Do not place any new vendor/, data/vendor/, etc entries in android_files.
+// Vendor entries should be done via a vendor or device specific config.fs.
+// See https://source.android.com/devices/tech/config/filesystem#using-file-system-capabilities
 static const struct fs_path_config android_files[] = {
-    // clang-format off
+        // clang-format off
     { 00644, AID_SYSTEM,    AID_SYSTEM,    0, "data/app/*" },
     { 00644, AID_SYSTEM,    AID_SYSTEM,    0, "data/app-ephemeral/*" },
     { 00644, AID_SYSTEM,    AID_SYSTEM,    0, "data/app-private/*" },
@@ -134,18 +140,22 @@
     { 00640, AID_ROOT,      AID_SHELL,     0, "data/nativetest64/tests.txt" },
     { 00750, AID_ROOT,      AID_SHELL,     0, "data/nativetest/*" },
     { 00750, AID_ROOT,      AID_SHELL,     0, "data/nativetest64/*" },
-    { 00600, AID_ROOT,      AID_ROOT,      0, "default.prop" },
+    { 00600, AID_ROOT,      AID_ROOT,      0, "default.prop" }, // legacy
+    { 00600, AID_ROOT,      AID_ROOT,      0, "system/etc/prop.default" },
     { 00600, AID_ROOT,      AID_ROOT,      0, "odm/build.prop" },
     { 00600, AID_ROOT,      AID_ROOT,      0, "odm/default.prop" },
     { 00444, AID_ROOT,      AID_ROOT,      0, odm_conf_dir + 1 },
     { 00444, AID_ROOT,      AID_ROOT,      0, odm_conf_file + 1 },
     { 00444, AID_ROOT,      AID_ROOT,      0, oem_conf_dir + 1 },
     { 00444, AID_ROOT,      AID_ROOT,      0, oem_conf_file + 1 },
+    { 00600, AID_ROOT,      AID_ROOT,      0, "product/build.prop" },
+    { 00600, AID_ROOT,      AID_ROOT,      0, "product-services/build.prop" },
     { 00750, AID_ROOT,      AID_SHELL,     0, "sbin/fs_mgr" },
     { 00755, AID_ROOT,      AID_SHELL,     0, "system/bin/crash_dump32" },
     { 00755, AID_ROOT,      AID_SHELL,     0, "system/bin/crash_dump64" },
     { 00755, AID_ROOT,      AID_SHELL,     0, "system/bin/debuggerd" },
     { 00750, AID_ROOT,      AID_ROOT,      0, "system/bin/install-recovery.sh" },
+    { 00550, AID_LOGD,      AID_LOGD,      0, "system/bin/logd" },
     { 00700, AID_ROOT,      AID_ROOT,      0, "system/bin/secilc" },
     { 00750, AID_ROOT,      AID_ROOT,      0, "system/bin/uncrypt" },
     { 00600, AID_ROOT,      AID_ROOT,      0, "system/build.prop" },
@@ -171,10 +181,6 @@
     // in user builds.
     { 00700, AID_SYSTEM,    AID_SHELL,     CAP_MASK_LONG(CAP_BLOCK_SUSPEND),
                                               "system/bin/inputflinger" },
-    { 00550, AID_LOGD,      AID_LOGD,      CAP_MASK_LONG(CAP_SYSLOG) |
-                                           CAP_MASK_LONG(CAP_AUDIT_CONTROL) |
-                                           CAP_MASK_LONG(CAP_SETGID),
-                                              "system/bin/logd" },
     { 00750, AID_ROOT,      AID_SHELL,     CAP_MASK_LONG(CAP_SETUID) |
                                            CAP_MASK_LONG(CAP_SETGID),
                                               "system/bin/run-as" },
@@ -182,37 +188,11 @@
     // Support FIFO scheduling mode in SurfaceFlinger.
     { 00755, AID_SYSTEM,    AID_GRAPHICS,  CAP_MASK_LONG(CAP_SYS_NICE),
                                               "system/bin/surfaceflinger" },
-
-    // Support hostapd administering a network interface.
-    { 00755, AID_WIFI,      AID_WIFI,      CAP_MASK_LONG(CAP_NET_ADMIN) |
-                                           CAP_MASK_LONG(CAP_NET_RAW),
-                                              "system/bin/hostapd" },
-
-    // Support Bluetooth legacy hal accessing /sys/class/rfkill
-    // Support RT scheduling in Bluetooth
-    { 00700, AID_BLUETOOTH, AID_BLUETOOTH, CAP_MASK_LONG(CAP_NET_ADMIN) |
-                                           CAP_MASK_LONG(CAP_SYS_NICE),
-                                              "vendor/bin/hw/android.hardware.bluetooth@1.0-service" },
-
-    // Support wifi_hal_legacy administering a network interface.
-    { 00755, AID_WIFI,      AID_WIFI,      CAP_MASK_LONG(CAP_NET_ADMIN) |
-                                           CAP_MASK_LONG(CAP_NET_RAW),
-                                              "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.*" },
     { 00750, AID_ROOT,      AID_SHELL,     0, "init*" },
+    { 00755, AID_ROOT,      AID_SHELL,     0, "product/bin/*" },
     { 00750, AID_ROOT,      AID_SHELL,     0, "sbin/*" },
     { 00755, AID_ROOT,      AID_SHELL,     0, "system/bin/*" },
     { 00755, AID_ROOT,      AID_ROOT,      0, "system/lib/valgrind/*" },
@@ -221,7 +201,7 @@
     { 00755, AID_ROOT,      AID_SHELL,     0, "vendor/bin/*" },
     { 00755, AID_ROOT,      AID_SHELL,     0, "vendor/xbin/*" },
     { 00644, AID_ROOT,      AID_ROOT,      0, 0 },
-    // clang-format on
+        // clang-format on
 };
 #ifndef __ANDROID_VNDK__
 auto __for_testing_only__android_files = android_files;
@@ -255,9 +235,10 @@
     return fd;
 }
 
-// if path is "vendor/<stuff>", "oem/<stuff>" or "odm/<stuff>"
+// if path is "odm/<stuff>", "oem/<stuff>", "product/<stuff>" or
+// "vendor/<stuff>"
 static bool is_partition(const char* path, size_t len) {
-    static const char* partitions[] = {"vendor/", "oem/", "odm/"};
+    static const char* partitions[] = {"odm/", "oem/", "product/", "vendor/"};
     for (size_t i = 0; i < (sizeof(partitions) / sizeof(partitions[0])); ++i) {
         size_t plen = strlen(partitions[i]);
         if (len <= plen) continue;
diff --git a/libcutils/hashmap.c b/libcutils/hashmap.c
deleted file mode 100644
index ede3b98..0000000
--- a/libcutils/hashmap.c
+++ /dev/null
@@ -1,358 +0,0 @@
-/*
- * Copyright (C) 2007 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 <cutils/hashmap.h>
-#include <assert.h>
-#include <errno.h>
-#include <cutils/threads.h>
-#include <stdlib.h>
-#include <string.h>
-#include <stdbool.h>
-#include <sys/types.h>
-
-typedef struct Entry Entry;
-struct Entry {
-    void* key;
-    int hash;
-    void* value;
-    Entry* next;
-};
-
-struct Hashmap {
-    Entry** buckets;
-    size_t bucketCount;
-    int (*hash)(void* key);
-    bool (*equals)(void* keyA, void* keyB);
-    mutex_t lock; 
-    size_t size;
-};
-
-Hashmap* hashmapCreate(size_t initialCapacity,
-        int (*hash)(void* key), bool (*equals)(void* keyA, void* keyB)) {
-    assert(hash != NULL);
-    assert(equals != NULL);
-    
-    Hashmap* map = malloc(sizeof(Hashmap));
-    if (map == NULL) {
-        return NULL;
-    }
-    
-    // 0.75 load factor.
-    size_t minimumBucketCount = initialCapacity * 4 / 3;
-    map->bucketCount = 1;
-    while (map->bucketCount <= minimumBucketCount) {
-        // Bucket count must be power of 2.
-        map->bucketCount <<= 1; 
-    }
-
-    map->buckets = calloc(map->bucketCount, sizeof(Entry*));
-    if (map->buckets == NULL) {
-        free(map);
-        return NULL;
-    }
-    
-    map->size = 0;
-
-    map->hash = hash;
-    map->equals = equals;
-    
-    mutex_init(&map->lock);
-    
-    return map;
-}
-
-/**
- * Hashes the given key.
- */
-#ifdef __clang__
-__attribute__((no_sanitize("integer")))
-#endif
-static inline int hashKey(Hashmap* map, void* key) {
-    int h = map->hash(key);
-
-    // We apply this secondary hashing discovered by Doug Lea to defend
-    // against bad hashes.
-    h += ~(h << 9);
-    h ^= (((unsigned int) h) >> 14);
-    h += (h << 4);
-    h ^= (((unsigned int) h) >> 10);
-       
-    return h;
-}
-
-size_t hashmapSize(Hashmap* map) {
-    return map->size;
-}
-
-static inline size_t calculateIndex(size_t bucketCount, int hash) {
-    return ((size_t) hash) & (bucketCount - 1);
-}
-
-static void expandIfNecessary(Hashmap* map) {
-    // If the load factor exceeds 0.75...
-    if (map->size > (map->bucketCount * 3 / 4)) {
-        // Start off with a 0.33 load factor.
-        size_t newBucketCount = map->bucketCount << 1;
-        Entry** newBuckets = calloc(newBucketCount, sizeof(Entry*));
-        if (newBuckets == NULL) {
-            // Abort expansion.
-            return;
-        }
-        
-        // Move over existing entries.
-        size_t i;
-        for (i = 0; i < map->bucketCount; i++) {
-            Entry* entry = map->buckets[i];
-            while (entry != NULL) {
-                Entry* next = entry->next;
-                size_t index = calculateIndex(newBucketCount, entry->hash);
-                entry->next = newBuckets[index];
-                newBuckets[index] = entry;
-                entry = next;
-            }
-        }
-
-        // Copy over internals.
-        free(map->buckets);
-        map->buckets = newBuckets;
-        map->bucketCount = newBucketCount;
-    }
-}
-
-void hashmapLock(Hashmap* map) {
-    mutex_lock(&map->lock);
-}
-
-void hashmapUnlock(Hashmap* map) {
-    mutex_unlock(&map->lock);
-}
-
-void hashmapFree(Hashmap* map) {
-    size_t i;
-    for (i = 0; i < map->bucketCount; i++) {
-        Entry* entry = map->buckets[i];
-        while (entry != NULL) {
-            Entry* next = entry->next;
-            free(entry);
-            entry = next;
-        }
-    }
-    free(map->buckets);
-    mutex_destroy(&map->lock);
-    free(map);
-}
-
-#ifdef __clang__
-__attribute__((no_sanitize("integer")))
-#endif
-/* FIXME: relies on signed integer overflow, which is undefined behavior */
-int hashmapHash(void* key, size_t keySize) {
-    int h = keySize;
-    char* data = (char*) key;
-    size_t i;
-    for (i = 0; i < keySize; i++) {
-        h = h * 31 + *data;
-        data++;
-    }
-    return h;
-}
-
-static Entry* createEntry(void* key, int hash, void* value) {
-    Entry* entry = malloc(sizeof(Entry));
-    if (entry == NULL) {
-        return NULL;
-    }
-    entry->key = key;
-    entry->hash = hash;
-    entry->value = value;
-    entry->next = NULL;
-    return entry;
-}
-
-static inline bool equalKeys(void* keyA, int hashA, void* keyB, int hashB,
-        bool (*equals)(void*, void*)) {
-    if (keyA == keyB) {
-        return true;
-    }
-    if (hashA != hashB) {
-        return false;
-    }
-    return equals(keyA, keyB);
-}
-
-void* hashmapPut(Hashmap* map, void* key, void* value) {
-    int hash = hashKey(map, key);
-    size_t index = calculateIndex(map->bucketCount, hash);
-
-    Entry** p = &(map->buckets[index]);
-    while (true) {
-        Entry* current = *p;
-
-        // Add a new entry.
-        if (current == NULL) {
-            *p = createEntry(key, hash, value);
-            if (*p == NULL) {
-                errno = ENOMEM;
-                return NULL;
-            }
-            map->size++;
-            expandIfNecessary(map);
-            return NULL;
-        }
-
-        // Replace existing entry.
-        if (equalKeys(current->key, current->hash, key, hash, map->equals)) {
-            void* oldValue = current->value;
-            current->value = value;
-            return oldValue;
-        }
-
-        // Move to next entry.
-        p = &current->next;
-    }
-}
-
-void* hashmapGet(Hashmap* map, void* key) {
-    int hash = hashKey(map, key);
-    size_t index = calculateIndex(map->bucketCount, hash);
-
-    Entry* entry = map->buckets[index];
-    while (entry != NULL) {
-        if (equalKeys(entry->key, entry->hash, key, hash, map->equals)) {
-            return entry->value;
-        }
-        entry = entry->next;
-    }
-
-    return NULL;
-}
-
-bool hashmapContainsKey(Hashmap* map, void* key) {
-    int hash = hashKey(map, key);
-    size_t index = calculateIndex(map->bucketCount, hash);
-
-    Entry* entry = map->buckets[index];
-    while (entry != NULL) {
-        if (equalKeys(entry->key, entry->hash, key, hash, map->equals)) {
-            return true;
-        }
-        entry = entry->next;
-    }
-
-    return false;
-}
-
-void* hashmapMemoize(Hashmap* map, void* key, 
-        void* (*initialValue)(void* key, void* context), void* context) {
-    int hash = hashKey(map, key);
-    size_t index = calculateIndex(map->bucketCount, hash);
-
-    Entry** p = &(map->buckets[index]);
-    while (true) {
-        Entry* current = *p;
-
-        // Add a new entry.
-        if (current == NULL) {
-            *p = createEntry(key, hash, NULL);
-            if (*p == NULL) {
-                errno = ENOMEM;
-                return NULL;
-            }
-            void* value = initialValue(key, context);
-            (*p)->value = value;
-            map->size++;
-            expandIfNecessary(map);
-            return value;
-        }
-
-        // Return existing value.
-        if (equalKeys(current->key, current->hash, key, hash, map->equals)) {
-            return current->value;
-        }
-
-        // Move to next entry.
-        p = &current->next;
-    }
-}
-
-void* hashmapRemove(Hashmap* map, void* key) {
-    int hash = hashKey(map, key);
-    size_t index = calculateIndex(map->bucketCount, hash);
-
-    // Pointer to the current entry.
-    Entry** p = &(map->buckets[index]);
-    Entry* current;
-    while ((current = *p) != NULL) {
-        if (equalKeys(current->key, current->hash, key, hash, map->equals)) {
-            void* value = current->value;
-            *p = current->next;
-            free(current);
-            map->size--;
-            return value;
-        }
-
-        p = &current->next;
-    }
-
-    return NULL;
-}
-
-void hashmapForEach(Hashmap* map, 
-        bool (*callback)(void* key, void* value, void* context),
-        void* context) {
-    size_t i;
-    for (i = 0; i < map->bucketCount; i++) {
-        Entry* entry = map->buckets[i];
-        while (entry != NULL) {
-            Entry *next = entry->next;
-            if (!callback(entry->key, entry->value, context)) {
-                return;
-            }
-            entry = next;
-        }
-    }
-}
-
-size_t hashmapCurrentCapacity(Hashmap* map) {
-    size_t bucketCount = map->bucketCount;
-    return bucketCount * 3 / 4;
-}
-
-size_t hashmapCountCollisions(Hashmap* map) {
-    size_t collisions = 0;
-    size_t i;
-    for (i = 0; i < map->bucketCount; i++) {
-        Entry* entry = map->buckets[i];
-        while (entry != NULL) {
-            if (entry->next != NULL) {
-                collisions++;
-            }
-            entry = entry->next;
-        }
-    }
-    return collisions;
-}
-
-int hashmapIntHash(void* key) {
-    // Return the key value itself.
-    return *((int*) key);
-}
-
-bool hashmapIntEquals(void* keyA, void* keyB) {
-    int a = *((int*) keyA);
-    int b = *((int*) keyB);
-    return a == b;
-}
diff --git a/libcutils/hashmap.cpp b/libcutils/hashmap.cpp
new file mode 100644
index 0000000..57d6006
--- /dev/null
+++ b/libcutils/hashmap.cpp
@@ -0,0 +1,274 @@
+/*
+ * Copyright (C) 2007 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 <cutils/hashmap.h>
+
+#include <assert.h>
+#include <errno.h>
+#include <pthread.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+
+typedef struct Entry Entry;
+struct Entry {
+    void* key;
+    int hash;
+    void* value;
+    Entry* next;
+};
+
+struct Hashmap {
+    Entry** buckets;
+    size_t bucketCount;
+    int (*hash)(void* key);
+    bool (*equals)(void* keyA, void* keyB);
+    pthread_mutex_t lock;
+    size_t size;
+};
+
+Hashmap* hashmapCreate(size_t initialCapacity,
+        int (*hash)(void* key), bool (*equals)(void* keyA, void* keyB)) {
+    assert(hash != NULL);
+    assert(equals != NULL);
+
+    Hashmap* map = static_cast<Hashmap*>(malloc(sizeof(Hashmap)));
+    if (map == NULL) {
+        return NULL;
+    }
+
+    // 0.75 load factor.
+    size_t minimumBucketCount = initialCapacity * 4 / 3;
+    map->bucketCount = 1;
+    while (map->bucketCount <= minimumBucketCount) {
+        // Bucket count must be power of 2.
+        map->bucketCount <<= 1;
+    }
+
+    map->buckets = static_cast<Entry**>(calloc(map->bucketCount, sizeof(Entry*)));
+    if (map->buckets == NULL) {
+        free(map);
+        return NULL;
+    }
+
+    map->size = 0;
+
+    map->hash = hash;
+    map->equals = equals;
+
+    pthread_mutex_init(&map->lock, nullptr);
+
+    return map;
+}
+
+/**
+ * Hashes the given key.
+ */
+#ifdef __clang__
+__attribute__((no_sanitize("integer")))
+#endif
+static inline int hashKey(Hashmap* map, void* key) {
+    int h = map->hash(key);
+
+    // We apply this secondary hashing discovered by Doug Lea to defend
+    // against bad hashes.
+    h += ~(h << 9);
+    h ^= (((unsigned int) h) >> 14);
+    h += (h << 4);
+    h ^= (((unsigned int) h) >> 10);
+
+    return h;
+}
+
+static inline size_t calculateIndex(size_t bucketCount, int hash) {
+    return ((size_t) hash) & (bucketCount - 1);
+}
+
+static void expandIfNecessary(Hashmap* map) {
+    // If the load factor exceeds 0.75...
+    if (map->size > (map->bucketCount * 3 / 4)) {
+        // Start off with a 0.33 load factor.
+        size_t newBucketCount = map->bucketCount << 1;
+        Entry** newBuckets = static_cast<Entry**>(calloc(newBucketCount, sizeof(Entry*)));
+        if (newBuckets == NULL) {
+            // Abort expansion.
+            return;
+        }
+
+        // Move over existing entries.
+        size_t i;
+        for (i = 0; i < map->bucketCount; i++) {
+            Entry* entry = map->buckets[i];
+            while (entry != NULL) {
+                Entry* next = entry->next;
+                size_t index = calculateIndex(newBucketCount, entry->hash);
+                entry->next = newBuckets[index];
+                newBuckets[index] = entry;
+                entry = next;
+            }
+        }
+
+        // Copy over internals.
+        free(map->buckets);
+        map->buckets = newBuckets;
+        map->bucketCount = newBucketCount;
+    }
+}
+
+void hashmapLock(Hashmap* map) {
+    pthread_mutex_lock(&map->lock);
+}
+
+void hashmapUnlock(Hashmap* map) {
+    pthread_mutex_unlock(&map->lock);
+}
+
+void hashmapFree(Hashmap* map) {
+    size_t i;
+    for (i = 0; i < map->bucketCount; i++) {
+        Entry* entry = map->buckets[i];
+        while (entry != NULL) {
+            Entry* next = entry->next;
+            free(entry);
+            entry = next;
+        }
+    }
+    free(map->buckets);
+    pthread_mutex_destroy(&map->lock);
+    free(map);
+}
+
+#ifdef __clang__
+__attribute__((no_sanitize("integer")))
+#endif
+/* FIXME: relies on signed integer overflow, which is undefined behavior */
+int hashmapHash(void* key, size_t keySize) {
+    int h = keySize;
+    char* data = (char*) key;
+    size_t i;
+    for (i = 0; i < keySize; i++) {
+        h = h * 31 + *data;
+        data++;
+    }
+    return h;
+}
+
+static Entry* createEntry(void* key, int hash, void* value) {
+    Entry* entry = static_cast<Entry*>(malloc(sizeof(Entry)));
+    if (entry == NULL) {
+        return NULL;
+    }
+    entry->key = key;
+    entry->hash = hash;
+    entry->value = value;
+    entry->next = NULL;
+    return entry;
+}
+
+static inline bool equalKeys(void* keyA, int hashA, void* keyB, int hashB,
+        bool (*equals)(void*, void*)) {
+    if (keyA == keyB) {
+        return true;
+    }
+    if (hashA != hashB) {
+        return false;
+    }
+    return equals(keyA, keyB);
+}
+
+void* hashmapPut(Hashmap* map, void* key, void* value) {
+    int hash = hashKey(map, key);
+    size_t index = calculateIndex(map->bucketCount, hash);
+
+    Entry** p = &(map->buckets[index]);
+    while (true) {
+        Entry* current = *p;
+
+        // Add a new entry.
+        if (current == NULL) {
+            *p = createEntry(key, hash, value);
+            if (*p == NULL) {
+                errno = ENOMEM;
+                return NULL;
+            }
+            map->size++;
+            expandIfNecessary(map);
+            return NULL;
+        }
+
+        // Replace existing entry.
+        if (equalKeys(current->key, current->hash, key, hash, map->equals)) {
+            void* oldValue = current->value;
+            current->value = value;
+            return oldValue;
+        }
+
+        // Move to next entry.
+        p = &current->next;
+    }
+}
+
+void* hashmapGet(Hashmap* map, void* key) {
+    int hash = hashKey(map, key);
+    size_t index = calculateIndex(map->bucketCount, hash);
+
+    Entry* entry = map->buckets[index];
+    while (entry != NULL) {
+        if (equalKeys(entry->key, entry->hash, key, hash, map->equals)) {
+            return entry->value;
+        }
+        entry = entry->next;
+    }
+
+    return NULL;
+}
+
+void* hashmapRemove(Hashmap* map, void* key) {
+    int hash = hashKey(map, key);
+    size_t index = calculateIndex(map->bucketCount, hash);
+
+    // Pointer to the current entry.
+    Entry** p = &(map->buckets[index]);
+    Entry* current;
+    while ((current = *p) != NULL) {
+        if (equalKeys(current->key, current->hash, key, hash, map->equals)) {
+            void* value = current->value;
+            *p = current->next;
+            free(current);
+            map->size--;
+            return value;
+        }
+
+        p = &current->next;
+    }
+
+    return NULL;
+}
+
+void hashmapForEach(Hashmap* map, bool (*callback)(void* key, void* value, void* context),
+                    void* context) {
+    size_t i;
+    for (i = 0; i < map->bucketCount; i++) {
+        Entry* entry = map->buckets[i];
+        while (entry != NULL) {
+            Entry *next = entry->next;
+            if (!callback(entry->key, entry->value, context)) {
+                return;
+            }
+            entry = next;
+        }
+    }
+}
diff --git a/libcutils/include/cutils/android_filesystem_config.h b/libcutils/include/cutils/android_filesystem_config.h
new file mode 120000
index 0000000..d2a92fe
--- /dev/null
+++ b/libcutils/include/cutils/android_filesystem_config.h
@@ -0,0 +1 @@
+../private/android_filesystem_config.h
\ No newline at end of file
diff --git a/libcutils/include/cutils/android_reboot.h b/libcutils/include/cutils/android_reboot.h
index 716567a..99030ed 100644
--- a/libcutils/include/cutils/android_reboot.h
+++ b/libcutils/include/cutils/android_reboot.h
@@ -17,6 +17,7 @@
 #ifndef __CUTILS_ANDROID_REBOOT_H__
 #define __CUTILS_ANDROID_REBOOT_H__
 
+#include <sys/cdefs.h>
 
 __BEGIN_DECLS
 
@@ -29,8 +30,8 @@
 /* Properties */
 #define ANDROID_RB_PROPERTY "sys.powerctl"
 
-/* Android reboot reason stored in this file */
-#define LAST_REBOOT_REASON_FILE "/data/misc/reboot/last_reboot_reason"
+/* Android reboot reason stored in this property */
+#define LAST_REBOOT_REASON_PROPERTY "persist.sys.boot.reason"
 
 /* Reboot or shutdown the system.
  * This call uses ANDROID_RB_PROPERTY to request reboot to init process.
diff --git a/libcutils/include/cutils/hashmap.h b/libcutils/include/cutils/hashmap.h
index 5cb344c..9cfd669 100644
--- a/libcutils/include/cutils/hashmap.h
+++ b/libcutils/include/cutils/hashmap.h
@@ -16,6 +16,9 @@
 
 /**
  * Hash map.
+ *
+ * Use std::map or std::unordered_map instead.
+ * https://en.cppreference.com/w/cpp/container
  */
 
 #ifndef __HASHMAP_H
@@ -68,38 +71,17 @@
 void* hashmapGet(Hashmap* map, void* key);
 
 /**
- * Returns true if the map contains an entry for the given key.
- */
-bool hashmapContainsKey(Hashmap* map, void* key);
-
-/**
- * Gets the value for a key. If a value is not found, this function gets a 
- * value and creates an entry using the given callback.
- *
- * If memory allocation fails, the callback is not called, this function
- * returns NULL, and errno is set to ENOMEM.
- */
-void* hashmapMemoize(Hashmap* map, void* key, 
-        void* (*initialValue)(void* key, void* context), void* context);
-
-/**
  * Removes an entry from the map. Returns the removed value or NULL if no
  * entry was present.
  */
 void* hashmapRemove(Hashmap* map, void* key);
 
 /**
- * Gets the number of entries in this map.
- */
-size_t hashmapSize(Hashmap* map);
-
-/**
  * Invokes the given callback on each entry in the map. Stops iterating if
  * the callback returns false.
  */
-void hashmapForEach(Hashmap* map, 
-        bool (*callback)(void* key, void* value, void* context),
-        void* context);
+void hashmapForEach(Hashmap* map, bool (*callback)(void* key, void* value, void* context),
+                    void* context);
 
 /**
  * Concurrency support.
@@ -115,36 +97,8 @@
  */
 void hashmapUnlock(Hashmap* map);
 
-/**
- * Key utilities.
- */
-
-/**
- * Hashes int keys. 'key' is a pointer to int.
- */
-int hashmapIntHash(void* key);
-
-/**
- * Compares two int keys for equality.
- */
-bool hashmapIntEquals(void* keyA, void* keyB);
-
-/**
- * For debugging.
- */
-
-/**
- * Gets current capacity.
- */
-size_t hashmapCurrentCapacity(Hashmap* map);
-
-/**
- * Counts the number of entry collisions.
- */
-size_t hashmapCountCollisions(Hashmap* map);
-
 #ifdef __cplusplus
 }
 #endif
 
-#endif /* __HASHMAP_H */ 
+#endif /* __HASHMAP_H */
diff --git a/libcutils/include/cutils/list.h b/libcutils/include/cutils/list.h
index 4ba2cfd..dfdc53b 100644
--- a/libcutils/include/cutils/list.h
+++ b/libcutils/include/cutils/list.h
@@ -34,20 +34,20 @@
 
 #define list_declare(name) \
     struct listnode name = { \
-        .next = &name, \
-        .prev = &name, \
+        .next = &(name), \
+        .prev = &(name), \
     }
 
 #define list_for_each(node, list) \
-    for (node = (list)->next; node != (list); node = node->next)
+    for ((node) = (list)->next; (node) != (list); (node) = (node)->next)
 
 #define list_for_each_reverse(node, list) \
-    for (node = (list)->prev; node != (list); node = node->prev)
+    for ((node) = (list)->prev; (node) != (list); (node) = (node)->prev)
 
 #define list_for_each_safe(node, n, list) \
-    for (node = (list)->next, n = node->next; \
-         node != (list); \
-         node = n, n = node->next)
+    for ((node) = (list)->next, (n) = (node)->next; \
+         (node) != (list); \
+         (node) = (n), (n) = (node)->next)
 
 static inline void list_init(struct listnode *node)
 {
diff --git a/libcutils/include/cutils/native_handle.h b/libcutils/include/cutils/native_handle.h
index 7d6a988..10f5bc0 100644
--- a/libcutils/include/cutils/native_handle.h
+++ b/libcutils/include/cutils/native_handle.h
@@ -25,8 +25,8 @@
 
 /* Declare a char array for use with native_handle_init */
 #define NATIVE_HANDLE_DECLARE_STORAGE(name, maxFds, maxInts) \
-    alignas(native_handle_t) char name[                            \
-      sizeof(native_handle_t) + sizeof(int) * (maxFds + maxInts)]
+    alignas(native_handle_t) char (name)[                            \
+      sizeof(native_handle_t) + sizeof(int) * ((maxFds) + (maxInts))]
 
 typedef struct native_handle
 {
@@ -43,6 +43,8 @@
 #endif
 } native_handle_t;
 
+typedef const native_handle_t* buffer_handle_t;
+
 /*
  * native_handle_close
  * 
diff --git a/libcutils/include/cutils/open_memstream.h b/libcutils/include/cutils/open_memstream.h
deleted file mode 100644
index c1a81eb..0000000
--- a/libcutils/include/cutils/open_memstream.h
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright (C) 2010 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 __CUTILS_OPEN_MEMSTREAM_H__
-#define __CUTILS_OPEN_MEMSTREAM_H__
-
-#include <stdio.h>
-
-#if defined(__APPLE__)
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-FILE* open_memstream(char** bufp, size_t* sizep);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* __APPLE__ */
-
-#endif /*__CUTILS_OPEN_MEMSTREAM_H__*/
diff --git a/libcutils/include/cutils/partition_utils.h b/libcutils/include/cutils/partition_utils.h
index 72ca80d..7518559 100644
--- a/libcutils/include/cutils/partition_utils.h
+++ b/libcutils/include/cutils/partition_utils.h
@@ -17,6 +17,8 @@
 #ifndef __CUTILS_PARTITION_WIPED_H__
 #define __CUTILS_PARTITION_WIPED_H__
 
+#include <sys/cdefs.h>
+
 __BEGIN_DECLS
 
 int partition_wiped(char *source);
diff --git a/libcutils/include/cutils/properties.h b/libcutils/include/cutils/properties.h
index b45f58f..d2e0871 100644
--- a/libcutils/include/cutils/properties.h
+++ b/libcutils/include/cutils/properties.h
@@ -43,12 +43,7 @@
 ** If the property read fails or returns an empty value, the default
 ** value is used (if nonnull).
 */
-int property_get(const char *key, char *value, const char *default_value)
-/* Sometimes we use not-Bionic with this, so we need this check. */
-#if defined(__BIONIC_FORTIFY)
-        __overloadable __RENAME_CLANG(property_get)
-#endif
-        ;
+int property_get(const char* key, char* value, const char* default_value);
 
 /* property_get_bool: returns the value of key coerced into a
 ** boolean. If the property is not set, then the default value is returned.
@@ -119,27 +114,15 @@
 
 #if defined(__clang__)
 
-/* Some projects use -Weverything; enable_if is clang-specific.
-** FIXME: This is marked used because we'll otherwise get complaints about an
-** unused static function. This is more robust than marking it unused, since
-** -Wused-but-marked-unused is a thing that will complain if this function is
-** actually used, thus making FORTIFY noisier when an error happens. It's going
-** to go away anyway during our FORTIFY cleanup.
-**/
+/* Some projects use -Weverything; diagnose_if is clang-specific. */
 #pragma clang diagnostic push
 #pragma clang diagnostic ignored "-Wgcc-compat"
-__BIONIC_ERROR_FUNCTION_VISIBILITY
-int property_get(const char *key, char *value, const char *default_value)
-        __overloadable
-        __enable_if(__bos(value) != __BIONIC_FORTIFY_UNKNOWN_SIZE &&
-                    __bos(value) < PROPERTY_VALUE_MAX, __property_get_err_str)
-        __errorattr(__property_get_err_str)
-        __attribute__((used));
+int property_get(const char* key, char* value, const char* default_value)
+    __clang_error_if(__bos(value) != __BIONIC_FORTIFY_UNKNOWN_SIZE &&
+                         __bos(value) < PROPERTY_VALUE_MAX,
+                     __property_get_err_str);
 #pragma clang diagnostic pop
 
-/* No object size? No FORTIFY.
-*/
-
 #else /* defined(__clang__) */
 
 extern int __property_get_real(const char *, char *, const char *)
diff --git a/libcutils/include/cutils/qtaguid.h b/libcutils/include/cutils/qtaguid.h
index 803fe0d..3f5e41f 100644
--- a/libcutils/include/cutils/qtaguid.h
+++ b/libcutils/include/cutils/qtaguid.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2011 The Android Open Source Project
+ * Copyright (C) 2017 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.
@@ -17,18 +17,14 @@
 #ifndef __CUTILS_QTAGUID_H
 #define __CUTILS_QTAGUID_H
 
-#include <stdint.h>
 #include <sys/types.h>
-#include <unistd.h>
 
 #ifdef __cplusplus
 extern "C" {
 #endif
 
 /*
- * Set tags (and owning UIDs) for network sockets. The socket must be untagged
- * by calling qtaguid_untagSocket() before closing it, otherwise the qtaguid
- * module will keep a reference to it even after close.
+ * Set tags (and owning UIDs) for network sockets.
  */
 extern int qtaguid_tagSocket(int sockfd, int tag, uid_t uid);
 
@@ -46,8 +42,8 @@
 
 /*
  * Delete all tag info that relates to the given tag an uid.
- * If the tag is 0, then ALL info about the uid is freeded.
- * The delete data also affects active tagged socketd, which are
+ * If the tag is 0, then ALL info about the uid is freed.
+ * The delete data also affects active tagged sockets, which are
  * then untagged.
  * The calling process can only operate on its own tags.
  * Unless it is part of the happy AID_NET_BW_ACCT group.
diff --git a/libcutils/include/cutils/record_stream.h b/libcutils/include/cutils/record_stream.h
index bfac87a..bcfc80d 100644
--- a/libcutils/include/cutils/record_stream.h
+++ b/libcutils/include/cutils/record_stream.h
@@ -25,6 +25,7 @@
 extern "C" {
 #endif
 
+#include <stddef.h>
 
 typedef struct RecordStream RecordStream;
 
diff --git a/libcutils/include/cutils/sched_policy.h b/libcutils/include/cutils/sched_policy.h
index cf8f5c3..cf91b76 100644
--- a/libcutils/include/cutils/sched_policy.h
+++ b/libcutils/include/cutils/sched_policy.h
@@ -40,15 +40,17 @@
 
 /* Keep in sync with THREAD_GROUP_* in frameworks/base/core/java/android/os/Process.java */
 typedef enum {
-    SP_DEFAULT    = -1,
+    SP_DEFAULT = -1,
     SP_BACKGROUND = 0,
     SP_FOREGROUND = 1,
-    SP_SYSTEM     = 2,  // can't be used with set_sched_policy()
-    SP_AUDIO_APP  = 3,
-    SP_AUDIO_SYS  = 4,
-    SP_TOP_APP    = 5,
+    SP_SYSTEM = 2,  // can't be used with set_sched_policy()
+    SP_AUDIO_APP = 3,
+    SP_AUDIO_SYS = 4,
+    SP_TOP_APP = 5,
+    SP_RT_APP = 6,
+    SP_RESTRICTED = 7,
     SP_CNT,
-    SP_MAX        = SP_CNT - 1,
+    SP_MAX = SP_CNT - 1,
     SP_SYSTEM_DEFAULT = SP_FOREGROUND,
 } SchedPolicy;
 
diff --git a/libcutils/include/cutils/threads.h b/libcutils/include/cutils/threads.h
index 5727494..ba4846e 100644
--- a/libcutils/include/cutils/threads.h
+++ b/libcutils/include/cutils/threads.h
@@ -29,16 +29,16 @@
 extern "C" {
 #endif
 
-/***********************************************************************/
-/***********************************************************************/
-/*****                                                             *****/
-/*****         local thread storage                                *****/
-/*****                                                             *****/
-/***********************************************************************/
-/***********************************************************************/
+//
+// Deprecated: use android::base::GetThreadId instead, which doesn't truncate on Mac/Windows.
+//
 
 extern pid_t gettid();
 
+//
+// Deprecated: use `_Thread_local` in C or `thread_local` in C++.
+//
+
 #if !defined(_WIN32)
 
 typedef struct {
@@ -70,77 +70,6 @@
                                void*                    value,
                                thread_store_destruct_t  destroy);
 
-/***********************************************************************/
-/***********************************************************************/
-/*****                                                             *****/
-/*****         mutexes                                             *****/
-/*****                                                             *****/
-/***********************************************************************/
-/***********************************************************************/
-
-#if !defined(_WIN32)
-
-typedef pthread_mutex_t   mutex_t;
-
-#define  MUTEX_INITIALIZER  PTHREAD_MUTEX_INITIALIZER
-
-static __inline__ void  mutex_lock(mutex_t*  lock)
-{
-    pthread_mutex_lock(lock);
-}
-static __inline__ void  mutex_unlock(mutex_t*  lock)
-{
-    pthread_mutex_unlock(lock);
-}
-static __inline__ int  mutex_init(mutex_t*  lock)
-{
-    return pthread_mutex_init(lock, NULL);
-}
-static __inline__ void mutex_destroy(mutex_t*  lock)
-{
-    pthread_mutex_destroy(lock);
-}
-
-#else // !defined(_WIN32)
-
-typedef struct {
-    int                init;
-    CRITICAL_SECTION   lock[1];
-} mutex_t;
-
-#define  MUTEX_INITIALIZER  { 0, {{ NULL, 0, 0, NULL, NULL, 0 }} }
-
-static __inline__ void  mutex_lock(mutex_t*  lock)
-{
-    if (!lock->init) {
-        lock->init = 1;
-        InitializeCriticalSection( lock->lock );
-        lock->init = 2;
-    } else while (lock->init != 2)
-        Sleep(10);
-
-    EnterCriticalSection(lock->lock);
-}
-
-static __inline__ void  mutex_unlock(mutex_t*  lock)
-{
-    LeaveCriticalSection(lock->lock);
-}
-static __inline__ int  mutex_init(mutex_t*  lock)
-{
-    InitializeCriticalSection(lock->lock);
-    lock->init = 2;
-    return 0;
-}
-static __inline__ void  mutex_destroy(mutex_t*  lock)
-{
-    if (lock->init) {
-        lock->init = 0;
-        DeleteCriticalSection(lock->lock);
-    }
-}
-#endif // !defined(_WIN32)
-
 #ifdef __cplusplus
 }
 #endif
diff --git a/libcutils/include/cutils/trace.h b/libcutils/include/cutils/trace.h
index fcbdc9b..58b9f09 100644
--- a/libcutils/include/cutils/trace.h
+++ b/libcutils/include/cutils/trace.h
@@ -72,7 +72,10 @@
 #define ATRACE_TAG_DATABASE         (1<<20)
 #define ATRACE_TAG_NETWORK          (1<<21)
 #define ATRACE_TAG_ADB              (1<<22)
-#define ATRACE_TAG_LAST             ATRACE_TAG_ADB
+#define ATRACE_TAG_VIBRATOR         (1<<23)
+#define ATRACE_TAG_AIDL             (1<<24)
+#define ATRACE_TAG_NNAPI            (1<<25)
+#define ATRACE_TAG_LAST             ATRACE_TAG_NNAPI
 
 // Reserved for initialization.
 #define ATRACE_TAG_NOT_READY        (1ULL<<63)
diff --git a/libcutils/include/private/android_filesystem_config.h b/libcutils/include/private/android_filesystem_config.h
index 02141d6..3be8ad0 100644
--- a/libcutils/include/private/android_filesystem_config.h
+++ b/libcutils/include/private/android_filesystem_config.h
@@ -14,11 +14,6 @@
  * limitations under the License.
  */
 
-/* This file is used to define the properties of the filesystem
-** images generated by build tools (mkbootfs and mkyaffs2image) and
-** by the device side of adb.
-*/
-
 /*
  * This file is consumed by build/tools/fs_config and is used
  * for generating various files. Anything #define AID_<name>
@@ -49,23 +44,20 @@
 #ifndef _ANDROID_FILESYSTEM_CONFIG_H_
 #define _ANDROID_FILESYSTEM_CONFIG_H_
 
-#include <stdint.h>
-#include <sys/cdefs.h>
 #include <sys/types.h>
 
-#if defined(__BIONIC__)
-#include <linux/capability.h>
-#else
-#include "android_filesystem_capability.h"
+#if !defined(__ANDROID_VNDK__) && !defined(EXCLUDE_FS_CONFIG_STRUCTURES)
+#include <private/fs_config.h>
 #endif
 
-#define CAP_MASK_LONG(cap_name) (1ULL << (cap_name))
-
 /* This is the master Users and Groups config for the platform.
  * DO NOT EVER RENUMBER
  */
 
 #define AID_ROOT 0 /* traditional unix root user */
+/* The following are for LTP and should only be used for testing */
+#define AID_DAEMON 1 /* traditional unix daemon owner */
+#define AID_BIN 2    /* traditional unix binaries owner */
 
 #define AID_SYSTEM 1000 /* system server */
 
@@ -131,6 +123,14 @@
 #define AID_ESE 1060             /* embedded secure element (eSE) subsystem */
 #define AID_OTA_UPDATE 1061      /* resource tracking UID for OTA updates */
 #define AID_AUTOMOTIVE_EVS 1062  /* Automotive rear and surround view system */
+#define AID_LOWPAN 1063          /* LoWPAN subsystem */
+#define AID_HSM 1064             /* hardware security module subsystem */
+#define AID_RESERVED_DISK 1065   /* GID that has access to reserved disk space */
+#define AID_STATSD 1066          /* statsd daemon */
+#define AID_INCIDENTD 1067       /* incidentd daemon */
+#define AID_SECURE_ELEMENT 1068  /* secure element subsystem */
+#define AID_LMKD 1069            /* low memory killer daemon */
+#define AID_LLKD 1070            /* live lock daemon */
 /* Changes to this file must be made in AOSP, *not* in internal branches. */
 
 #define AID_SHELL 2000 /* adb and debug shell user */
@@ -179,6 +179,14 @@
 #define AID_SHARED_GID_START 50000 /* start of gids for apps in each user to share */
 #define AID_SHARED_GID_END 59999   /* end of gids for apps in each user to share */
 
+/*
+ * This is a magic number in the kernel and not something that was picked
+ * arbitrarily. This value is returned whenever a uid that has no mapping in the
+ * user namespace is returned to userspace:
+ * https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/include/linux/highuid.h?h=v4.4#n40
+ */
+#define AID_OVERFLOWUID 65534 /* unmapped user in the user namespace */
+
 #define AID_ISOLATED_START 99000 /* start of uids for fully isolated sandboxed processes */
 #define AID_ISOLATED_END 99999   /* end of uids for fully isolated sandboxed processes */
 
@@ -193,36 +201,4 @@
  * Also see build/tools/fs_config for more details.
  */
 
-#if !defined(EXCLUDE_FS_CONFIG_STRUCTURES)
-
-struct fs_path_config {
-    unsigned mode;
-    unsigned uid;
-    unsigned gid;
-    uint64_t capabilities;
-    const char* prefix;
-};
-
-/* Rules for directories and files has moved to system/code/libcutils/fs_config.c */
-
-__BEGIN_DECLS
-
-/*
- * Used in:
- *  build/tools/fs_config/fs_config.c
- *  build/tools/fs_get_stats/fs_get_stats.c
- *  system/extras/ext4_utils/make_ext4fs_main.c
- *  external/squashfs-tools/squashfs-tools/android.c
- *  system/core/cpio/mkbootfs.c
- *  system/core/adb/file_sync_service.cpp
- *  system/extras/ext4_utils/canned_fs_config.c
- */
-void fs_config(const char* path, int dir, const char* target_out_path, unsigned* uid, unsigned* gid,
-               unsigned* mode, uint64_t* capabilities);
-
-ssize_t fs_config_generate(char* buffer, size_t length, const struct fs_path_config* pc);
-
-__END_DECLS
-
-#endif
 #endif
diff --git a/libcutils/include/private/canned_fs_config.h b/libcutils/include/private/canned_fs_config.h
index 71e1537..135b91c 100644
--- a/libcutils/include/private/canned_fs_config.h
+++ b/libcutils/include/private/canned_fs_config.h
@@ -19,8 +19,12 @@
 
 #include <inttypes.h>
 
+__BEGIN_DECLS
+
 int load_canned_fs_config(const char* fn);
 void canned_fs_config(const char* path, int dir, const char* target_out_path, unsigned* uid,
                       unsigned* gid, unsigned* mode, uint64_t* capabilities);
 
+__END_DECLS
+
 #endif
diff --git a/libcutils/include/private/fs_config.h b/libcutils/include/private/fs_config.h
index 7dad668..8926491 100644
--- a/libcutils/include/private/fs_config.h
+++ b/libcutils/include/private/fs_config.h
@@ -14,10 +14,25 @@
  * limitations under the License.
  */
 
+/* This file is used to define the properties of the filesystem
+** images generated by build tools (mkbootfs and mkyaffs2image) and
+** by the device side of adb.
+*/
+
 #ifndef _LIBS_CUTILS_PRIVATE_FS_CONFIG_H
 #define _LIBS_CUTILS_PRIVATE_FS_CONFIG_H
 
 #include <stdint.h>
+#include <sys/cdefs.h>
+#include <sys/types.h>
+
+#if defined(__BIONIC__)
+#include <linux/capability.h>
+#else  // defined(__BIONIC__)
+#include "android_filesystem_capability.h"
+#endif  // defined(__BIONIC__)
+
+#define CAP_MASK_LONG(cap_name) (1ULL << (cap_name))
 
 /*
  * binary format for the runtime <partition>/etc/fs_config_(dirs|files)
@@ -34,4 +49,33 @@
     char prefix[];
 } __attribute__((__aligned__(sizeof(uint64_t))));
 
+struct fs_path_config {
+    unsigned mode;
+    unsigned uid;
+    unsigned gid;
+    uint64_t capabilities;
+    const char* prefix;
+};
+
+/* Rules for directories and files has moved to system/code/libcutils/fs_config.c */
+
+__BEGIN_DECLS
+
+/*
+ * Used in:
+ *  build/tools/fs_config/fs_config.c
+ *  build/tools/fs_get_stats/fs_get_stats.c
+ *  system/extras/ext4_utils/make_ext4fs_main.c
+ *  external/squashfs-tools/squashfs-tools/android.c
+ *  system/core/cpio/mkbootfs.c
+ *  system/core/adb/file_sync_service.cpp
+ *  system/extras/ext4_utils/canned_fs_config.c
+ */
+void fs_config(const char* path, int dir, const char* target_out_path, unsigned* uid, unsigned* gid,
+               unsigned* mode, uint64_t* capabilities);
+
+ssize_t fs_config_generate(char* buffer, size_t length, const struct fs_path_config* pc);
+
+__END_DECLS
+
 #endif /* _LIBS_CUTILS_PRIVATE_FS_CONFIG_H */
diff --git a/libcutils/include_vndk/cutils/android_filesystem_config.h b/libcutils/include_vndk/cutils/android_filesystem_config.h
new file mode 120000
index 0000000..13a5a08
--- /dev/null
+++ b/libcutils/include_vndk/cutils/android_filesystem_config.h
@@ -0,0 +1 @@
+../../include/private/android_filesystem_config.h
\ No newline at end of file
diff --git a/libcutils/include_vndk/cutils/open_memstream.h b/libcutils/include_vndk/cutils/open_memstream.h
deleted file mode 120000
index c894084..0000000
--- a/libcutils/include_vndk/cutils/open_memstream.h
+++ /dev/null
@@ -1 +0,0 @@
-../../include/cutils/open_memstream.h
\ No newline at end of file
diff --git a/libcutils/include_vndk/private b/libcutils/include_vndk/private
deleted file mode 120000
index 2245a85..0000000
--- a/libcutils/include_vndk/private
+++ /dev/null
@@ -1 +0,0 @@
-../include/private
\ No newline at end of file
diff --git a/libcutils/iosched_policy.c b/libcutils/iosched_policy.c
deleted file mode 100644
index 13c2ceb..0000000
--- a/libcutils/iosched_policy.c
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
-** Copyright 2007, 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 <errno.h>
-#include <fcntl.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-#include <cutils/iosched_policy.h>
-
-#if defined(__ANDROID__)
-#define IOPRIO_WHO_PROCESS (1)
-#define IOPRIO_CLASS_SHIFT (13)
-#include <sys/syscall.h>
-#define __android_unused
-#else
-#define __android_unused __attribute__((__unused__))
-#endif
-
-int android_set_ioprio(int pid __android_unused, IoSchedClass clazz __android_unused, int ioprio __android_unused) {
-#if defined(__ANDROID__)
-    if (syscall(SYS_ioprio_set, IOPRIO_WHO_PROCESS, pid, ioprio | (clazz << IOPRIO_CLASS_SHIFT))) {
-        return -1;
-    }
-#endif
-    return 0;
-}
-
-int android_get_ioprio(int pid __android_unused, IoSchedClass *clazz, int *ioprio) {
-#if defined(__ANDROID__)
-    int rc;
-
-    if ((rc = syscall(SYS_ioprio_get, IOPRIO_WHO_PROCESS, pid)) < 0) {
-        return -1;
-    }
-
-    *clazz = (rc >> IOPRIO_CLASS_SHIFT);
-    *ioprio = (rc & 0xff);
-#else
-    *clazz = IoSchedClass_NONE;
-    *ioprio = 0;
-#endif
-    return 0;
-}
diff --git a/libcutils/iosched_policy.cpp b/libcutils/iosched_policy.cpp
new file mode 100644
index 0000000..012c537
--- /dev/null
+++ b/libcutils/iosched_policy.cpp
@@ -0,0 +1,59 @@
+/*
+** Copyright 2007, 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 <cutils/iosched_policy.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#if defined(__ANDROID__)
+#define IOPRIO_WHO_PROCESS (1)
+#define IOPRIO_CLASS_SHIFT (13)
+#include <sys/syscall.h>
+#define __android_unused
+#else
+#define __android_unused __attribute__((__unused__))
+#endif
+
+int android_set_ioprio(int pid __android_unused, IoSchedClass clazz __android_unused, int ioprio __android_unused) {
+#if defined(__ANDROID__)
+    if (syscall(SYS_ioprio_set, IOPRIO_WHO_PROCESS, pid, ioprio | (clazz << IOPRIO_CLASS_SHIFT))) {
+        return -1;
+    }
+#endif
+    return 0;
+}
+
+int android_get_ioprio(int pid __android_unused, IoSchedClass *clazz, int *ioprio) {
+#if defined(__ANDROID__)
+    int rc;
+
+    if ((rc = syscall(SYS_ioprio_get, IOPRIO_WHO_PROCESS, pid)) < 0) {
+        return -1;
+    }
+
+    *clazz = static_cast<IoSchedClass>(rc >> IOPRIO_CLASS_SHIFT);
+    *ioprio = (rc & 0xff);
+#else
+    *clazz = IoSchedClass_NONE;
+    *ioprio = 0;
+#endif
+    return 0;
+}
diff --git a/libcutils/klog.cpp b/libcutils/klog.cpp
index d301276..6a9f4df 100644
--- a/libcutils/klog.cpp
+++ b/libcutils/klog.cpp
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#include <cutils/klog.h>
+
 #include <errno.h>
 #include <fcntl.h>
 #include <stdarg.h>
@@ -25,7 +27,6 @@
 #include <unistd.h>
 
 #include <cutils/android_get_control_file.h>
-#include <cutils/klog.h>
 
 static int klog_level = KLOG_INFO_LEVEL;
 
diff --git a/libcutils/load_file.c b/libcutils/load_file.c
deleted file mode 100644
index 99f2965..0000000
--- a/libcutils/load_file.c
+++ /dev/null
@@ -1,51 +0,0 @@
-/* libs/cutils/load_file.c
-**
-** Copyright 2006, 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 <stdlib.h>
-#include <unistd.h>
-#include <fcntl.h>
-
-void *load_file(const char *fn, unsigned *_sz)
-{
-    char *data;
-    int sz;
-    int fd;
-
-    data = 0;
-    fd = open(fn, O_RDONLY);
-    if(fd < 0) return 0;
-
-    sz = lseek(fd, 0, SEEK_END);
-    if(sz < 0) goto oops;
-
-    if(lseek(fd, 0, SEEK_SET) != 0) goto oops;
-
-    data = (char*) malloc(sz + 1);
-    if(data == 0) goto oops;
-
-    if(read(fd, data, sz) != sz) goto oops;
-    close(fd);
-    data[sz] = 0;
-
-    if(_sz) *_sz = sz;
-    return data;
-
-oops:
-    close(fd);
-    if(data != 0) free(data);
-    return 0;
-}
diff --git a/libcutils/load_file.cpp b/libcutils/load_file.cpp
new file mode 100644
index 0000000..346105c
--- /dev/null
+++ b/libcutils/load_file.cpp
@@ -0,0 +1,53 @@
+/* libs/cutils/load_file.c
+**
+** Copyright 2006, 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 <cutils/misc.h>
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+void *load_file(const char *fn, unsigned *_sz)
+{
+    char *data;
+    int sz;
+    int fd;
+
+    data = 0;
+    fd = open(fn, O_RDONLY);
+    if(fd < 0) return 0;
+
+    sz = lseek(fd, 0, SEEK_END);
+    if(sz < 0) goto oops;
+
+    if(lseek(fd, 0, SEEK_SET) != 0) goto oops;
+
+    data = (char*) malloc(sz + 1);
+    if(data == 0) goto oops;
+
+    if(read(fd, data, sz) != sz) goto oops;
+    close(fd);
+    data[sz] = 0;
+
+    if(_sz) *_sz = sz;
+    return data;
+
+oops:
+    close(fd);
+    if(data != 0) free(data);
+    return 0;
+}
diff --git a/libcutils/multiuser.c b/libcutils/multiuser.c
deleted file mode 100644
index 61403f4..0000000
--- a/libcutils/multiuser.c
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright (C) 2012 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 <cutils/multiuser.h>
-#include <private/android_filesystem_config.h>
-
-userid_t multiuser_get_user_id(uid_t uid) {
-    return uid / AID_USER_OFFSET;
-}
-
-appid_t multiuser_get_app_id(uid_t uid) {
-    return uid % AID_USER_OFFSET;
-}
-
-uid_t multiuser_get_uid(userid_t user_id, appid_t app_id) {
-    return (user_id * AID_USER_OFFSET) + (app_id % AID_USER_OFFSET);
-}
-
-gid_t multiuser_get_cache_gid(userid_t user_id, appid_t app_id) {
-    if (app_id >= AID_APP_START && app_id <= AID_APP_END) {
-        return multiuser_get_uid(user_id, (app_id - AID_APP_START) + AID_CACHE_GID_START);
-    } else {
-        return -1;
-    }
-}
-
-gid_t multiuser_get_ext_gid(userid_t user_id, appid_t app_id) {
-    if (app_id >= AID_APP_START && app_id <= AID_APP_END) {
-        return multiuser_get_uid(user_id, (app_id - AID_APP_START) + AID_EXT_GID_START);
-    } else {
-        return -1;
-    }
-}
-
-gid_t multiuser_get_ext_cache_gid(userid_t user_id, appid_t app_id) {
-    if (app_id >= AID_APP_START && app_id <= AID_APP_END) {
-        return multiuser_get_uid(user_id, (app_id - AID_APP_START) + AID_EXT_CACHE_GID_START);
-    } else {
-        return -1;
-    }
-}
-
-gid_t multiuser_get_shared_gid(userid_t user_id, appid_t app_id) {
-    if (app_id >= AID_APP_START && app_id <= AID_APP_END) {
-        return multiuser_get_uid(user_id, (app_id - AID_APP_START) + AID_SHARED_GID_START);
-    } else {
-        return -1;
-    }
-}
-
-gid_t multiuser_get_shared_app_gid(uid_t uid) {
-    return multiuser_get_shared_gid(multiuser_get_user_id(uid), multiuser_get_app_id(uid));
-}
diff --git a/libcutils/multiuser.cpp b/libcutils/multiuser.cpp
new file mode 100644
index 0000000..0fd3d0c
--- /dev/null
+++ b/libcutils/multiuser.cpp
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2012 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 <cutils/multiuser.h>
+#include <private/android_filesystem_config.h>
+
+userid_t multiuser_get_user_id(uid_t uid) {
+    return uid / AID_USER_OFFSET;
+}
+
+appid_t multiuser_get_app_id(uid_t uid) {
+    return uid % AID_USER_OFFSET;
+}
+
+uid_t multiuser_get_uid(userid_t user_id, appid_t app_id) {
+    return (user_id * AID_USER_OFFSET) + (app_id % AID_USER_OFFSET);
+}
+
+gid_t multiuser_get_cache_gid(userid_t user_id, appid_t app_id) {
+    if (app_id >= AID_APP_START && app_id <= AID_APP_END) {
+        return multiuser_get_uid(user_id, (app_id - AID_APP_START) + AID_CACHE_GID_START);
+    } else {
+        return -1;
+    }
+}
+
+gid_t multiuser_get_ext_gid(userid_t user_id, appid_t app_id) {
+    if (app_id >= AID_APP_START && app_id <= AID_APP_END) {
+        return multiuser_get_uid(user_id, (app_id - AID_APP_START) + AID_EXT_GID_START);
+    } else {
+        return -1;
+    }
+}
+
+gid_t multiuser_get_ext_cache_gid(userid_t user_id, appid_t app_id) {
+    if (app_id >= AID_APP_START && app_id <= AID_APP_END) {
+        return multiuser_get_uid(user_id, (app_id - AID_APP_START) + AID_EXT_CACHE_GID_START);
+    } else {
+        return -1;
+    }
+}
+
+gid_t multiuser_get_shared_gid(userid_t, appid_t app_id) {
+    if (app_id >= AID_APP_START && app_id <= AID_APP_END) {
+        return (app_id - AID_APP_START) + AID_SHARED_GID_START;
+    } else if (app_id >= AID_ROOT && app_id <= AID_APP_START) {
+        return app_id;
+    } else {
+        return -1;
+    }
+}
+
+gid_t multiuser_get_shared_app_gid(uid_t uid) {
+    return multiuser_get_shared_gid(multiuser_get_user_id(uid), multiuser_get_app_id(uid));
+}
diff --git a/libcutils/native_handle.c b/libcutils/native_handle.c
deleted file mode 100644
index 95bbc41..0000000
--- a/libcutils/native_handle.c
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * Copyright (C) 2007 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 <cutils/native_handle.h>
-
-#include <errno.h>
-#include <stdint.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-static const int kMaxNativeFds = 1024;
-static const int kMaxNativeInts = 1024;
-
-native_handle_t* native_handle_init(char* storage, int numFds, int numInts) {
-    if ((uintptr_t) storage % alignof(native_handle_t)) {
-        errno = EINVAL;
-        return NULL;
-    }
-
-    native_handle_t* handle = (native_handle_t*) storage;
-    handle->version = sizeof(native_handle_t);
-    handle->numFds = numFds;
-    handle->numInts = numInts;
-    return handle;
-}
-
-native_handle_t* native_handle_create(int numFds, int numInts) {
-    if (numFds < 0 || numInts < 0 || numFds > kMaxNativeFds || numInts > kMaxNativeInts) {
-        errno = EINVAL;
-        return NULL;
-    }
-
-    size_t mallocSize = sizeof(native_handle_t) + (sizeof(int) * (numFds + numInts));
-    native_handle_t* h = malloc(mallocSize);
-    if (h) {
-        h->version = sizeof(native_handle_t);
-        h->numFds = numFds;
-        h->numInts = numInts;
-    }
-    return h;
-}
-
-native_handle_t* native_handle_clone(const native_handle_t* handle) {
-    native_handle_t* clone = native_handle_create(handle->numFds, handle->numInts);
-    if (clone == NULL) return NULL;
-
-    for (int i = 0; i < handle->numFds; i++) {
-        clone->data[i] = dup(handle->data[i]);
-        if (clone->data[i] == -1) {
-            clone->numFds = i;
-            native_handle_close(clone);
-            native_handle_delete(clone);
-            return NULL;
-        }
-    }
-
-    memcpy(&clone->data[handle->numFds], &handle->data[handle->numFds],
-           sizeof(int) * handle->numInts);
-
-    return clone;
-}
-
-int native_handle_delete(native_handle_t* h) {
-    if (h) {
-        if (h->version != sizeof(native_handle_t)) return -EINVAL;
-        free(h);
-    }
-    return 0;
-}
-
-int native_handle_close(const native_handle_t* h) {
-    if (h->version != sizeof(native_handle_t)) return -EINVAL;
-
-    int saved_errno = errno;
-    const int numFds = h->numFds;
-    for (int i = 0; i < numFds; ++i) {
-        close(h->data[i]);
-    }
-    errno = saved_errno;
-    return 0;
-}
diff --git a/libcutils/native_handle.cpp b/libcutils/native_handle.cpp
new file mode 100644
index 0000000..66f7a3d
--- /dev/null
+++ b/libcutils/native_handle.cpp
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2007 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 <cutils/native_handle.h>
+
+#include <errno.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+static const int kMaxNativeFds = 1024;
+static const int kMaxNativeInts = 1024;
+
+native_handle_t* native_handle_init(char* storage, int numFds, int numInts) {
+    if ((uintptr_t) storage % alignof(native_handle_t)) {
+        errno = EINVAL;
+        return NULL;
+    }
+
+    native_handle_t* handle = (native_handle_t*) storage;
+    handle->version = sizeof(native_handle_t);
+    handle->numFds = numFds;
+    handle->numInts = numInts;
+    return handle;
+}
+
+native_handle_t* native_handle_create(int numFds, int numInts) {
+    if (numFds < 0 || numInts < 0 || numFds > kMaxNativeFds || numInts > kMaxNativeInts) {
+        errno = EINVAL;
+        return NULL;
+    }
+
+    size_t mallocSize = sizeof(native_handle_t) + (sizeof(int) * (numFds + numInts));
+    native_handle_t* h = static_cast<native_handle_t*>(malloc(mallocSize));
+    if (h) {
+        h->version = sizeof(native_handle_t);
+        h->numFds = numFds;
+        h->numInts = numInts;
+    }
+    return h;
+}
+
+native_handle_t* native_handle_clone(const native_handle_t* handle) {
+    native_handle_t* clone = native_handle_create(handle->numFds, handle->numInts);
+    if (clone == NULL) return NULL;
+
+    for (int i = 0; i < handle->numFds; i++) {
+        clone->data[i] = dup(handle->data[i]);
+        if (clone->data[i] == -1) {
+            clone->numFds = i;
+            native_handle_close(clone);
+            native_handle_delete(clone);
+            return NULL;
+        }
+    }
+
+    memcpy(&clone->data[handle->numFds], &handle->data[handle->numFds],
+           sizeof(int) * handle->numInts);
+
+    return clone;
+}
+
+int native_handle_delete(native_handle_t* h) {
+    if (h) {
+        if (h->version != sizeof(native_handle_t)) return -EINVAL;
+        free(h);
+    }
+    return 0;
+}
+
+int native_handle_close(const native_handle_t* h) {
+    if (h->version != sizeof(native_handle_t)) return -EINVAL;
+
+    int saved_errno = errno;
+    const int numFds = h->numFds;
+    for (int i = 0; i < numFds; ++i) {
+        close(h->data[i]);
+    }
+    errno = saved_errno;
+    return 0;
+}
diff --git a/libcutils/open_memstream.c b/libcutils/open_memstream.c
deleted file mode 100644
index 9183266..0000000
--- a/libcutils/open_memstream.c
+++ /dev/null
@@ -1,373 +0,0 @@
-/*
- * Copyright (C) 2010 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.
- */
-
-#if defined(__APPLE__)
-
-/*
- * Implementation of the POSIX open_memstream() function, which Linux has
- * but BSD lacks.
- *
- * Summary:
- * - Works like a file-backed FILE* opened with fopen(name, "w"), but the
- *   backing is a chunk of memory rather than a file.
- * - The buffer expands as you write more data.  Seeking past the end
- *   of the file and then writing to it zero-fills the gap.
- * - The values at "*bufp" and "*sizep" should be considered read-only,
- *   and are only valid immediately after an fflush() or fclose().
- * - A '\0' is maintained just past the end of the file. This is not included
- *   in "*sizep".  (The behavior w.r.t. fseek() is not clearly defined.
- *   The spec says the null byte is written when a write() advances EOF,
- *   but it looks like glibc ensures the null byte is always found at EOF,
- *   even if you just seeked backwards.  The example on the opengroup.org
- *   page suggests that this is the expected behavior.  The null must be
- *   present after a no-op fflush(), which we can't see, so we have to save
- *   and restore it.  Annoying, but allows file truncation.)
- * - After fclose(), the caller must eventually free(*bufp).
- *
- * This is built out of funopen(), which BSD has but Linux lacks.  There is
- * no flush() operator, so we need to keep the user pointers up to date
- * after each operation.
- *
- * I don't think Windows has any of the above, but we don't need to use
- * them there, so we just supply a stub.
- */
-#include <cutils/open_memstream.h>
-#include <stdlib.h>
-#include <sys/types.h>
-#include <unistd.h>
-#include <stdio.h>
-#include <string.h>
-#include <errno.h>
-#include <assert.h>
-
-#if 0
-# define DBUG(x) printf x
-#else
-# define DBUG(x) ((void)0)
-#endif
-
-/*
- * Definition of a seekable, write-only memory stream.
- */
-typedef struct {
-    char**      bufp;       /* pointer to buffer pointer */
-    size_t*     sizep;      /* pointer to eof */
-
-    size_t      allocSize;  /* size of buffer */
-    size_t      eof;        /* furthest point we've written to */
-    size_t      offset;     /* current write offset */
-    char        saved;      /* required by NUL handling */
-} MemStream;
-
-#define kInitialSize    1024
-
-/*
- * Ensure that we have enough storage to write "size" bytes at the
- * current offset.  We also have to take into account the extra '\0'
- * that we maintain just past EOF.
- *
- * Returns 0 on success.
- */
-static int ensureCapacity(MemStream* stream, int writeSize)
-{
-    DBUG(("+++ ensureCap off=%d size=%d\n", stream->offset, writeSize));
-
-    size_t neededSize = stream->offset + writeSize + 1;
-    if (neededSize <= stream->allocSize)
-        return 0;
-
-    size_t newSize;
-
-    if (stream->allocSize == 0) {
-        newSize = kInitialSize;
-    } else {
-        newSize = stream->allocSize;
-        newSize += newSize / 2;             /* expand by 3/2 */
-    }
-
-    if (newSize < neededSize)
-        newSize = neededSize;
-    DBUG(("+++ realloc %p->%p to size=%d\n",
-        stream->bufp, *stream->bufp, newSize));
-    char* newBuf = (char*) realloc(*stream->bufp, newSize);
-    if (newBuf == NULL)
-        return -1;
-
-    *stream->bufp = newBuf;
-    stream->allocSize = newSize;
-    return 0;
-}
-
-/*
- * Write data to a memstream, expanding the buffer if necessary.
- *
- * If we previously seeked beyond EOF, zero-fill the gap.
- *
- * Returns the number of bytes written.
- */
-static int write_memstream(void* cookie, const char* buf, int size)
-{
-    MemStream* stream = (MemStream*) cookie;
-
-    if (ensureCapacity(stream, size) < 0)
-        return -1;
-
-    /* seeked past EOF earlier? */
-    if (stream->eof < stream->offset) {
-        DBUG(("+++ zero-fill gap from %d to %d\n",
-            stream->eof, stream->offset-1));
-        memset(*stream->bufp + stream->eof, '\0',
-            stream->offset - stream->eof);
-    }
-
-    /* copy data, advance write pointer */
-    memcpy(*stream->bufp + stream->offset, buf, size);
-    stream->offset += size;
-
-    if (stream->offset > stream->eof) {
-        /* EOF has advanced, update it and append null byte */
-        DBUG(("+++ EOF advanced to %d, appending nul\n", stream->offset));
-        assert(stream->offset < stream->allocSize);
-        stream->eof = stream->offset;
-    } else {
-        /* within previously-written area; save char we're about to stomp */
-        DBUG(("+++ within written area, saving '%c' at %d\n",
-            *(*stream->bufp + stream->offset), stream->offset));
-        stream->saved = *(*stream->bufp + stream->offset);
-    }
-    *(*stream->bufp + stream->offset) = '\0';
-    *stream->sizep = stream->offset;
-
-    return size;
-}
-
-/*
- * Seek within a memstream.
- *
- * Returns the new offset, or -1 on failure.
- */
-static fpos_t seek_memstream(void* cookie, fpos_t offset, int whence)
-{
-    MemStream* stream = (MemStream*) cookie;
-    off_t newPosn = (off_t) offset;
-
-    if (whence == SEEK_CUR) {
-        newPosn += stream->offset;
-    } else if (whence == SEEK_END) {
-        newPosn += stream->eof;
-    }
-
-    if (newPosn < 0 || ((fpos_t)((size_t) newPosn)) != newPosn) {
-        /* bad offset - negative or huge */
-        DBUG(("+++ bogus seek offset %ld\n", (long) newPosn));
-        errno = EINVAL;
-        return (fpos_t) -1;
-    }
-
-    if (stream->offset < stream->eof) {
-        /*
-         * We were pointing to an area we'd already written to, which means
-         * we stomped on a character and must now restore it.
-         */
-        DBUG(("+++ restoring char '%c' at %d\n",
-            stream->saved, stream->offset));
-        *(*stream->bufp + stream->offset) = stream->saved;
-    }
-
-    stream->offset = (size_t) newPosn;
-
-    if (stream->offset < stream->eof) {
-        /*
-         * We're seeked backward into the stream.  Preserve the character
-         * at EOF and stomp it with a NUL.
-         */
-        stream->saved = *(*stream->bufp + stream->offset);
-        *(*stream->bufp + stream->offset) = '\0';
-        *stream->sizep = stream->offset;
-    } else {
-        /*
-         * We're positioned at, or possibly beyond, the EOF.  We want to
-         * publish the current EOF, not the current position.
-         */
-        *stream->sizep = stream->eof;
-    }
-
-    return newPosn;
-}
-
-/*
- * Close the memstream.  We free everything but the data buffer.
- */
-static int close_memstream(void* cookie)
-{
-    free(cookie);
-    return 0;
-}
-
-/*
- * Prepare a memstream.
- */
-FILE* open_memstream(char** bufp, size_t* sizep)
-{
-    FILE* fp;
-    MemStream* stream;
-
-    if (bufp == NULL || sizep == NULL) {
-        errno = EINVAL;
-        return NULL;
-    }
-
-    stream = (MemStream*) calloc(1, sizeof(MemStream));
-    if (stream == NULL)
-        return NULL;
-
-    fp = funopen(stream,
-        NULL, write_memstream, seek_memstream, close_memstream);
-    if (fp == NULL) {
-        free(stream);
-        return NULL;
-    }
-
-    *sizep = 0;
-    *bufp = NULL;
-    stream->bufp = bufp;
-    stream->sizep = sizep;
-
-    return fp;
-}
-
-
-
-
-#if 0
-#define _GNU_SOURCE
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-/*
- * Simple regression test.
- *
- * To test on desktop Linux with valgrind, it's possible to make a simple
- * change to open_memstream() to use fopencookie instead:
- *
- *  cookie_io_functions_t iofuncs =
- *      { NULL, write_memstream, seek_memstream, close_memstream };
- *  fp = fopencookie(stream, "w", iofuncs);
- *
- * (Some tweaks to seek_memstream are also required, as that takes a
- * pointer to an offset rather than an offset, and returns 0 or -1.)
- */
-int testMemStream(void)
-{
-    FILE *stream;
-    char *buf;
-    size_t len;
-    off_t eob;
-
-    printf("Test1\n");
-
-    /* std example */
-    stream = open_memstream(&buf, &len);
-    fprintf(stream, "hello my world");
-    fflush(stream);
-    printf("buf=%s, len=%zu\n", buf, len);
-    eob = ftello(stream);
-    fseeko(stream, 0, SEEK_SET);
-    fprintf(stream, "good-bye");
-    fseeko(stream, eob, SEEK_SET);
-    fclose(stream);
-    printf("buf=%s, len=%zu\n", buf, len);
-    free(buf);
-
-    printf("Test2\n");
-
-    /* std example without final seek-to-end */
-    stream = open_memstream(&buf, &len);
-    fprintf(stream, "hello my world");
-    fflush(stream);
-    printf("buf=%s, len=%zu\n", buf, len);
-    eob = ftello(stream);
-    fseeko(stream, 0, SEEK_SET);
-    fprintf(stream, "good-bye");
-    //fseeko(stream, eob, SEEK_SET);
-    fclose(stream);
-    printf("buf=%s, len=%zu\n", buf, len);
-    free(buf);
-
-    printf("Test3\n");
-
-    /* fancy example; should expand buffer with writes */
-    static const int kCmpLen = 1024 + 128;
-    char* cmp = malloc(kCmpLen);
-    memset(cmp, 0, 1024);
-    memset(cmp+1024, 0xff, kCmpLen-1024);
-    sprintf(cmp, "This-is-a-tes1234");
-    sprintf(cmp + 1022, "abcdef");
-
-    stream = open_memstream (&buf, &len);
-    setvbuf(stream, NULL, _IONBF, 0);   /* note: crashes in glibc with this */
-    fprintf(stream, "This-is-a-test");
-    fseek(stream, -1, SEEK_CUR);    /* broken in glibc; can use {13,SEEK_SET} */
-    fprintf(stream, "1234");
-    fseek(stream, 1022, SEEK_SET);
-    fputc('a', stream);
-    fputc('b', stream);
-    fputc('c', stream);
-    fputc('d', stream);
-    fputc('e', stream);
-    fputc('f', stream);
-    fflush(stream);
-
-    if (memcmp(buf, cmp, len+1) != 0) {
-        printf("mismatch\n");
-    } else {
-        printf("match\n");
-    }
-
-    printf("Test4\n");
-    stream = open_memstream (&buf, &len);
-    fseek(stream, 5000, SEEK_SET);
-    fseek(stream, 4096, SEEK_SET);
-    fseek(stream, -1, SEEK_SET);        /* should have no effect */
-    fputc('x', stream);
-    if (ftell(stream) == 4097)
-        printf("good\n");
-    else
-        printf("BAD: offset is %ld\n", ftell(stream));
-
-    printf("DONE\n");
-
-    return 0;
-}
-
-/* expected output:
-Test1
-buf=hello my world, len=14
-buf=good-bye world, len=14
-Test2
-buf=hello my world, len=14
-buf=good-bye, len=8
-Test3
-match
-Test4
-good
-DONE
-*/
-
-#endif
-
-#endif /* __APPLE__ */
diff --git a/libcutils/partition_utils.c b/libcutils/partition_utils.c
deleted file mode 100644
index 823b162..0000000
--- a/libcutils/partition_utils.c
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright 2011, 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 <fcntl.h>
-#include <sys/ioctl.h>
-#include <sys/mount.h> /* for BLKGETSIZE */
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include <cutils/properties.h>
-
-static int only_one_char(char *buf, int len, char c)
-{
-    int i, ret;
-
-    ret = 1;
-    for (i=0; i<len; i++) {
-        if (buf[i] != c) {
-            ret = 0;
-            break;
-        }
-    }
-    return ret;
-}
-
-int partition_wiped(char *source)
-{
-    char buf[4096];
-    int fd, ret;
-
-    if ((fd = open(source, O_RDONLY)) < 0) {
-        return 0;
-    }
-
-    ret = read(fd, buf, sizeof(buf));
-    close(fd);
-
-    if (ret != sizeof(buf)) {
-        return 0;
-    }
-
-    /* Check for all zeros */
-    if (only_one_char(buf, sizeof(buf), 0)) {
-       return 1;
-    }
-
-    /* Check for all ones */
-    if (only_one_char(buf, sizeof(buf), 0xff)) {
-       return 1;
-    }
-
-    return 0;
-}
-
diff --git a/libcutils/partition_utils.cpp b/libcutils/partition_utils.cpp
new file mode 100644
index 0000000..6735d6c
--- /dev/null
+++ b/libcutils/partition_utils.cpp
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2011, 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 <cutils/partition_utils.h>
+
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/mount.h> /* for BLKGETSIZE */
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <cutils/properties.h>
+
+static int only_one_char(char *buf, int len, char c)
+{
+    int i, ret;
+
+    ret = 1;
+    for (i=0; i<len; i++) {
+        if (buf[i] != c) {
+            ret = 0;
+            break;
+        }
+    }
+    return ret;
+}
+
+int partition_wiped(char *source)
+{
+    char buf[4096];
+    int fd, ret;
+
+    if ((fd = open(source, O_RDONLY)) < 0) {
+        return 0;
+    }
+
+    ret = read(fd, buf, sizeof(buf));
+    close(fd);
+
+    if (ret != sizeof(buf)) {
+        return 0;
+    }
+
+    /* Check for all zeros */
+    if (only_one_char(buf, sizeof(buf), 0)) {
+       return 1;
+    }
+
+    /* Check for all ones */
+    if (only_one_char(buf, sizeof(buf), 0xff)) {
+       return 1;
+    }
+
+    return 0;
+}
+
diff --git a/libcutils/properties.cpp b/libcutils/properties.cpp
index d2645e6..5dbbeba 100644
--- a/libcutils/properties.cpp
+++ b/libcutils/properties.cpp
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#include <cutils/properties.h>
+
 #define LOG_TAG "properties"
 // #define LOG_NDEBUG 0
 
@@ -21,12 +23,10 @@
 #include <ctype.h>
 #include <errno.h>
 #include <inttypes.h>
-#include <stdbool.h>
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
 
-#include <cutils/properties.h>
 #include <cutils/sockets.h>
 #include <log/log.h>
 
diff --git a/libcutils/qtaguid.c b/libcutils/qtaguid.c
deleted file mode 100644
index 22b8325..0000000
--- a/libcutils/qtaguid.c
+++ /dev/null
@@ -1,174 +0,0 @@
-/*
-** Copyright 2011, 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.
-*/
-
-// #define LOG_NDEBUG 0
-
-#define LOG_TAG "qtaguid"
-
-#include <errno.h>
-#include <fcntl.h>
-#include <inttypes.h>
-#include <pthread.h>
-#include <stdio.h>
-#include <string.h>
-#include <unistd.h>
-
-#include <log/log.h>
-#include <cutils/qtaguid.h>
-
-static const char* CTRL_PROCPATH = "/proc/net/xt_qtaguid/ctrl";
-static const int CTRL_MAX_INPUT_LEN = 128;
-static const char *GLOBAL_PACIFIER_PARAM = "/sys/module/xt_qtaguid/parameters/passive";
-static const char *TAG_PACIFIER_PARAM = "/sys/module/xt_qtaguid/parameters/tag_tracking_passive";
-
-/*
- * One per proccess.
- * Once the device is open, this process will have its socket tags tracked.
- * And on exit or untimely death, all socket tags will be removed.
- * A process can only open /dev/xt_qtaguid once.
- * It should not close it unless it is really done with all the socket tags.
- * Failure to open it will be visible when socket tagging will be attempted.
- */
-static int resTrackFd = -1;
-pthread_once_t resTrackInitDone = PTHREAD_ONCE_INIT;
-
-/* Only call once per process. */
-void qtaguid_resTrack(void) {
-    resTrackFd = TEMP_FAILURE_RETRY(open("/dev/xt_qtaguid", O_RDONLY | O_CLOEXEC));
-}
-
-/*
- * Returns:
- *   0 on success.
- *   -errno on failure.
- */
-static int write_ctrl(const char *cmd) {
-    int fd, res, savedErrno;
-
-    ALOGV("write_ctrl(%s)", cmd);
-
-    fd = TEMP_FAILURE_RETRY(open(CTRL_PROCPATH, O_WRONLY | O_CLOEXEC));
-    if (fd < 0) {
-        return -errno;
-    }
-
-    res = TEMP_FAILURE_RETRY(write(fd, cmd, strlen(cmd)));
-    if (res < 0) {
-        savedErrno = errno;
-    } else {
-        savedErrno = 0;
-    }
-    if (res < 0) {
-        // ALOGV is enough because all the callers also log failures
-        ALOGV("Failed write_ctrl(%s) res=%d errno=%d", cmd, res, savedErrno);
-    }
-    close(fd);
-    return -savedErrno;
-}
-
-static int write_param(const char *param_path, const char *value) {
-    int param_fd;
-    int res;
-
-    param_fd = TEMP_FAILURE_RETRY(open(param_path, O_WRONLY | O_CLOEXEC));
-    if (param_fd < 0) {
-        return -errno;
-    }
-    res = TEMP_FAILURE_RETRY(write(param_fd, value, strlen(value)));
-    if (res < 0) {
-        return -errno;
-    }
-    close(param_fd);
-    return 0;
-}
-
-int qtaguid_tagSocket(int sockfd, int tag, uid_t uid) {
-    char lineBuf[CTRL_MAX_INPUT_LEN];
-    int res;
-    uint64_t kTag = ((uint64_t)tag << 32);
-
-    pthread_once(&resTrackInitDone, qtaguid_resTrack);
-
-    snprintf(lineBuf, sizeof(lineBuf), "t %d %" PRIu64 " %d", sockfd, kTag, uid);
-
-    ALOGV("Tagging socket %d with tag %" PRIx64 "{%u,0} for uid %d", sockfd, kTag, tag, uid);
-
-    res = write_ctrl(lineBuf);
-    if (res < 0) {
-        ALOGI("Tagging socket %d with tag %" PRIx64 "(%d) for uid %d failed errno=%d",
-             sockfd, kTag, tag, uid, res);
-    }
-
-    return res;
-}
-
-int qtaguid_untagSocket(int sockfd) {
-    char lineBuf[CTRL_MAX_INPUT_LEN];
-    int res;
-
-    ALOGV("Untagging socket %d", sockfd);
-
-    snprintf(lineBuf, sizeof(lineBuf), "u %d", sockfd);
-    res = write_ctrl(lineBuf);
-    if (res < 0) {
-        ALOGI("Untagging socket %d failed errno=%d", sockfd, res);
-    }
-
-    return res;
-}
-
-int qtaguid_setCounterSet(int counterSetNum, uid_t uid) {
-    char lineBuf[CTRL_MAX_INPUT_LEN];
-    int res;
-
-    ALOGV("Setting counters to set %d for uid %d", counterSetNum, uid);
-
-    snprintf(lineBuf, sizeof(lineBuf), "s %d %d", counterSetNum, uid);
-    res = write_ctrl(lineBuf);
-    return res;
-}
-
-int qtaguid_deleteTagData(int tag, uid_t uid) {
-    char lineBuf[CTRL_MAX_INPUT_LEN];
-    int cnt = 0, res = 0;
-    uint64_t kTag = (uint64_t)tag << 32;
-
-    ALOGV("Deleting tag data with tag %" PRIx64 "{%d,0} for uid %d", kTag, tag, uid);
-
-    pthread_once(&resTrackInitDone, qtaguid_resTrack);
-
-    snprintf(lineBuf, sizeof(lineBuf), "d %" PRIu64 " %d", kTag, uid);
-    res = write_ctrl(lineBuf);
-    if (res < 0) {
-        ALOGI("Deleting tag data with tag %" PRIx64 "/%d for uid %d failed with cnt=%d errno=%d",
-             kTag, tag, uid, cnt, errno);
-    }
-
-    return res;
-}
-
-int qtaguid_setPacifier(int on) {
-    const char *value;
-
-    value = on ? "Y" : "N";
-    if (write_param(GLOBAL_PACIFIER_PARAM, value) < 0) {
-        return -errno;
-    }
-    if (write_param(TAG_PACIFIER_PARAM, value) < 0) {
-        return -errno;
-    }
-    return 0;
-}
diff --git a/libcutils/qtaguid.cpp b/libcutils/qtaguid.cpp
new file mode 100644
index 0000000..b94d134
--- /dev/null
+++ b/libcutils/qtaguid.cpp
@@ -0,0 +1,126 @@
+/*
+ * Copyright 2017, 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 <cutils/qtaguid.h>
+
+// #define LOG_NDEBUG 0
+
+#define LOG_TAG "qtaguid"
+
+#include <dlfcn.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <log/log.h>
+
+class netdHandler {
+  public:
+    int (*netdTagSocket)(int, uint32_t, uid_t);
+    int (*netdUntagSocket)(int);
+    int (*netdSetCounterSet)(uint32_t, uid_t);
+    int (*netdDeleteTagData)(uint32_t, uid_t);
+};
+
+int dummyTagSocket(int, uint32_t, uid_t) {
+    return -EREMOTEIO;
+}
+
+int dummyUntagSocket(int) {
+    return -EREMOTEIO;
+}
+
+int dummySetCounterSet(uint32_t, uid_t) {
+    return -EREMOTEIO;
+}
+
+int dummyDeleteTagData(uint32_t, uid_t) {
+    return -EREMOTEIO;
+}
+
+netdHandler initHandler(void) {
+    netdHandler handler = {dummyTagSocket, dummyUntagSocket, dummySetCounterSet, dummyDeleteTagData};
+
+    void* netdClientHandle = dlopen("libnetd_client.so", RTLD_NOW);
+    if (!netdClientHandle) {
+        ALOGE("Failed to open libnetd_client.so: %s", dlerror());
+        return handler;
+    }
+
+    handler.netdTagSocket = (int (*)(int, uint32_t, uid_t))dlsym(netdClientHandle, "tagSocket");
+    if (!handler.netdTagSocket) {
+        ALOGE("load netdTagSocket handler failed: %s", dlerror());
+    }
+
+    handler.netdUntagSocket = (int (*)(int))dlsym(netdClientHandle, "untagSocket");
+    if (!handler.netdUntagSocket) {
+        ALOGE("load netdUntagSocket handler failed: %s", dlerror());
+    }
+
+    handler.netdSetCounterSet = (int (*)(uint32_t, uid_t))dlsym(netdClientHandle, "setCounterSet");
+    if (!handler.netdSetCounterSet) {
+        ALOGE("load netdSetCounterSet handler failed: %s", dlerror());
+    }
+
+    handler.netdDeleteTagData = (int (*)(uint32_t, uid_t))dlsym(netdClientHandle, "deleteTagData");
+    if (!handler.netdDeleteTagData) {
+        ALOGE("load netdDeleteTagData handler failed: %s", dlerror());
+    }
+    return handler;
+}
+
+// The language guarantees that this object will be initialized in a thread-safe way.
+static netdHandler& getHandler() {
+    static netdHandler instance = initHandler();
+    return instance;
+}
+
+int qtaguid_tagSocket(int sockfd, int tag, uid_t uid) {
+    // Check the socket fd passed to us is still valid before we load the netd
+    // client. Pass a already closed socket fd to netd client may let netd open
+    // the unix socket with the same fd number and pass it to server for
+    // tagging.
+    // TODO: move the check into netdTagSocket.
+    int res = fcntl(sockfd, F_GETFD);
+    if (res < 0) return res;
+
+    ALOGV("Tagging socket %d with tag %u for uid %d", sockfd, tag, uid);
+    return getHandler().netdTagSocket(sockfd, tag, uid);
+}
+
+int qtaguid_untagSocket(int sockfd) {
+    // Similiar to tag socket. We need a check before untag to make sure untag a closed socket fail
+    // as expected.
+    // TODO: move the check into netdTagSocket.
+    int res = fcntl(sockfd, F_GETFD);
+    if (res < 0) return res;
+
+    ALOGV("Untagging socket %d", sockfd);
+    return getHandler().netdUntagSocket(sockfd);
+}
+
+int qtaguid_setCounterSet(int counterSetNum, uid_t uid) {
+    ALOGV("Setting counters to set %d for uid %d", counterSetNum, uid);
+    return getHandler().netdSetCounterSet(counterSetNum, uid);
+}
+
+int qtaguid_deleteTagData(int tag, uid_t uid) {
+    ALOGV("Deleting tag data with tag %u for uid %d", tag, uid);
+    return getHandler().netdDeleteTagData(tag, uid);
+}
diff --git a/libcutils/record_stream.c b/libcutils/record_stream.c
deleted file mode 100644
index 2bc4226..0000000
--- a/libcutils/record_stream.c
+++ /dev/null
@@ -1,186 +0,0 @@
-/* libs/cutils/record_stream.c
-**
-** Copyright 2006, 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 <stdlib.h>
-#include <unistd.h>
-#include <assert.h>
-#include <errno.h>
-#include <cutils/record_stream.h>
-#include <string.h>
-#include <stdint.h>
-#if defined(_WIN32)
-#include <winsock2.h>   /* for ntohl */
-#else
-#include <netinet/in.h>
-#endif
-
-#define HEADER_SIZE 4
-
-struct RecordStream {
-    int fd;
-    size_t maxRecordLen;
-
-    unsigned char *buffer;
-
-    unsigned char *unconsumed;
-    unsigned char *read_end;
-    unsigned char *buffer_end;
-};
-
-
-extern RecordStream *record_stream_new(int fd, size_t maxRecordLen)
-{
-    RecordStream *ret;
-
-    assert (maxRecordLen <= 0xffff);
-
-    ret = (RecordStream *)calloc(1, sizeof(RecordStream));
-
-    ret->fd = fd;
-    ret->maxRecordLen = maxRecordLen;
-    ret->buffer = (unsigned char *)malloc (maxRecordLen + HEADER_SIZE);
-    
-    ret->unconsumed = ret->buffer;
-    ret->read_end = ret->buffer;
-    ret->buffer_end = ret->buffer + maxRecordLen + HEADER_SIZE;
-
-    return ret;
-}
-
-
-extern void record_stream_free(RecordStream *rs)
-{
-    free(rs->buffer);
-    free(rs);
-}
-
-
-/* returns NULL; if there isn't a full record in the buffer */
-static unsigned char * getEndOfRecord (unsigned char *p_begin,
-                                            unsigned char *p_end)
-{
-    size_t len;
-    unsigned char * p_ret;
-
-    if (p_end < p_begin + HEADER_SIZE) {
-        return NULL;
-    }
-
-    //First four bytes are length
-    len = ntohl(*((uint32_t *)p_begin));
-
-    p_ret = p_begin + HEADER_SIZE + len;
-
-    if (p_end < p_ret) {
-        return NULL;
-    }
-
-    return p_ret;
-}
-
-static void *getNextRecord (RecordStream *p_rs, size_t *p_outRecordLen)
-{
-    unsigned char *record_start, *record_end;
-
-    record_end = getEndOfRecord (p_rs->unconsumed, p_rs->read_end);
-
-    if (record_end != NULL) {
-        /* one full line in the buffer */
-        record_start = p_rs->unconsumed + HEADER_SIZE;
-        p_rs->unconsumed = record_end;
-
-        *p_outRecordLen = record_end - record_start;
-
-        return record_start;
-    }
-
-    return NULL;
-}
-
-/**
- * Reads the next record from stream fd
- * Records are prefixed by a 16-bit big endian length value
- * Records may not be larger than maxRecordLen
- *
- * Doesn't guard against EINTR
- *
- * p_outRecord and p_outRecordLen may not be NULL
- *
- * Return 0 on success, -1 on fail
- * Returns 0 with *p_outRecord set to NULL on end of stream
- * Returns -1 / errno = EAGAIN if it needs to read again
- */
-int record_stream_get_next (RecordStream *p_rs, void ** p_outRecord, 
-                                    size_t *p_outRecordLen)
-{
-    void *ret;
-
-    ssize_t countRead;
-
-    /* is there one record already in the buffer? */
-    ret = getNextRecord (p_rs, p_outRecordLen);
-
-    if (ret != NULL) {
-        *p_outRecord = ret;
-        return 0;
-    }
-
-    // if the buffer is full and we don't have a full record
-    if (p_rs->unconsumed == p_rs->buffer 
-        && p_rs->read_end == p_rs->buffer_end
-    ) {
-        // this should never happen
-        //ALOGE("max record length exceeded\n");
-        assert (0);
-        errno = EFBIG;
-        return -1;
-    }
-
-    if (p_rs->unconsumed != p_rs->buffer) {
-        // move remainder to the beginning of the buffer
-        size_t toMove;
-
-        toMove = p_rs->read_end - p_rs->unconsumed;
-        if (toMove) {
-            memmove(p_rs->buffer, p_rs->unconsumed, toMove);
-        }
-
-        p_rs->read_end = p_rs->buffer + toMove;
-        p_rs->unconsumed = p_rs->buffer;
-    }
-
-    countRead = read (p_rs->fd, p_rs->read_end, p_rs->buffer_end - p_rs->read_end);
-
-    if (countRead <= 0) {
-        /* note: end-of-stream drops through here too */
-        *p_outRecord = NULL;
-        return countRead;
-    }
-
-    p_rs->read_end += countRead;
-
-    ret = getNextRecord (p_rs, p_outRecordLen);
-
-    if (ret == NULL) {
-        /* not enough of a buffer to for a whole command */
-        errno = EAGAIN;
-        return -1;
-    }
-
-    *p_outRecord = ret;        
-    return 0;
-}
diff --git a/libcutils/record_stream.cpp b/libcutils/record_stream.cpp
new file mode 100644
index 0000000..5a86b83
--- /dev/null
+++ b/libcutils/record_stream.cpp
@@ -0,0 +1,187 @@
+/* libs/cutils/record_stream.c
+**
+** Copyright 2006, 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 <cutils/record_stream.h>
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <assert.h>
+#include <errno.h>
+#include <string.h>
+#include <stdint.h>
+#if defined(_WIN32)
+#include <winsock2.h>   /* for ntohl */
+#else
+#include <netinet/in.h>
+#endif
+
+#define HEADER_SIZE 4
+
+struct RecordStream {
+    int fd;
+    size_t maxRecordLen;
+
+    unsigned char *buffer;
+
+    unsigned char *unconsumed;
+    unsigned char *read_end;
+    unsigned char *buffer_end;
+};
+
+
+extern RecordStream *record_stream_new(int fd, size_t maxRecordLen)
+{
+    RecordStream *ret;
+
+    assert (maxRecordLen <= 0xffff);
+
+    ret = (RecordStream *)calloc(1, sizeof(RecordStream));
+
+    ret->fd = fd;
+    ret->maxRecordLen = maxRecordLen;
+    ret->buffer = (unsigned char *)malloc (maxRecordLen + HEADER_SIZE);
+    
+    ret->unconsumed = ret->buffer;
+    ret->read_end = ret->buffer;
+    ret->buffer_end = ret->buffer + maxRecordLen + HEADER_SIZE;
+
+    return ret;
+}
+
+
+extern void record_stream_free(RecordStream *rs)
+{
+    free(rs->buffer);
+    free(rs);
+}
+
+
+/* returns NULL; if there isn't a full record in the buffer */
+static unsigned char * getEndOfRecord (unsigned char *p_begin,
+                                            unsigned char *p_end)
+{
+    size_t len;
+    unsigned char * p_ret;
+
+    if (p_end < p_begin + HEADER_SIZE) {
+        return NULL;
+    }
+
+    //First four bytes are length
+    len = ntohl(*((uint32_t *)p_begin));
+
+    p_ret = p_begin + HEADER_SIZE + len;
+
+    if (p_end < p_ret) {
+        return NULL;
+    }
+
+    return p_ret;
+}
+
+static void *getNextRecord (RecordStream *p_rs, size_t *p_outRecordLen)
+{
+    unsigned char *record_start, *record_end;
+
+    record_end = getEndOfRecord (p_rs->unconsumed, p_rs->read_end);
+
+    if (record_end != NULL) {
+        /* one full line in the buffer */
+        record_start = p_rs->unconsumed + HEADER_SIZE;
+        p_rs->unconsumed = record_end;
+
+        *p_outRecordLen = record_end - record_start;
+
+        return record_start;
+    }
+
+    return NULL;
+}
+
+/**
+ * Reads the next record from stream fd
+ * Records are prefixed by a 16-bit big endian length value
+ * Records may not be larger than maxRecordLen
+ *
+ * Doesn't guard against EINTR
+ *
+ * p_outRecord and p_outRecordLen may not be NULL
+ *
+ * Return 0 on success, -1 on fail
+ * Returns 0 with *p_outRecord set to NULL on end of stream
+ * Returns -1 / errno = EAGAIN if it needs to read again
+ */
+int record_stream_get_next (RecordStream *p_rs, void ** p_outRecord, 
+                                    size_t *p_outRecordLen)
+{
+    void *ret;
+
+    ssize_t countRead;
+
+    /* is there one record already in the buffer? */
+    ret = getNextRecord (p_rs, p_outRecordLen);
+
+    if (ret != NULL) {
+        *p_outRecord = ret;
+        return 0;
+    }
+
+    // if the buffer is full and we don't have a full record
+    if (p_rs->unconsumed == p_rs->buffer 
+        && p_rs->read_end == p_rs->buffer_end
+    ) {
+        // this should never happen
+        //ALOGE("max record length exceeded\n");
+        assert (0);
+        errno = EFBIG;
+        return -1;
+    }
+
+    if (p_rs->unconsumed != p_rs->buffer) {
+        // move remainder to the beginning of the buffer
+        size_t toMove;
+
+        toMove = p_rs->read_end - p_rs->unconsumed;
+        if (toMove) {
+            memmove(p_rs->buffer, p_rs->unconsumed, toMove);
+        }
+
+        p_rs->read_end = p_rs->buffer + toMove;
+        p_rs->unconsumed = p_rs->buffer;
+    }
+
+    countRead = read (p_rs->fd, p_rs->read_end, p_rs->buffer_end - p_rs->read_end);
+
+    if (countRead <= 0) {
+        /* note: end-of-stream drops through here too */
+        *p_outRecord = NULL;
+        return countRead;
+    }
+
+    p_rs->read_end += countRead;
+
+    ret = getNextRecord (p_rs, p_outRecordLen);
+
+    if (ret == NULL) {
+        /* not enough of a buffer to for a whole command */
+        errno = EAGAIN;
+        return -1;
+    }
+
+    *p_outRecord = ret;        
+    return 0;
+}
diff --git a/libcutils/sched_policy.cpp b/libcutils/sched_policy.cpp
index f733e90..3fa548f 100644
--- a/libcutils/sched_policy.cpp
+++ b/libcutils/sched_policy.cpp
@@ -14,6 +14,8 @@
 ** limitations under the License.
 */
 
+#include <cutils/sched_policy.h>
+
 #define LOG_TAG "SchedPolicy"
 
 #include <errno.h>
@@ -23,10 +25,8 @@
 #include <string.h>
 #include <unistd.h>
 
+#include <android-base/macros.h>
 #include <log/log.h>
-#include <cutils/sched_policy.h>
-
-#define UNUSED __attribute__((__unused__))
 
 /* Re-map SP_DEFAULT to the system default policy, and leave other values unchanged.
  * Call this any place a SchedPolicy is used as an input parameter.
@@ -58,11 +58,13 @@
 static int bg_cpuset_fd = -1;
 static int fg_cpuset_fd = -1;
 static int ta_cpuset_fd = -1; // special cpuset for top app
+static int rs_cpuset_fd = -1;  // special cpuset for screen off restrictions
 
 // File descriptors open to /dev/stune/../tasks, setup by initialize, or -1 on error
 static int bg_schedboost_fd = -1;
 static int fg_schedboost_fd = -1;
 static int ta_schedboost_fd = -1;
+static int rt_schedboost_fd = -1;
 
 /* Add tid to the scheduling group defined by the policy */
 static int add_tid_to_cgroup(int tid, int fd)
@@ -151,6 +153,8 @@
             system_bg_cpuset_fd = open(filename, O_WRONLY | O_CLOEXEC);
             filename = "/dev/cpuset/top-app/tasks";
             ta_cpuset_fd = open(filename, O_WRONLY | O_CLOEXEC);
+            filename = "/dev/cpuset/restricted/tasks";
+            rs_cpuset_fd = open(filename, O_WRONLY | O_CLOEXEC);
 
             if (schedboost_enabled()) {
                 filename = "/dev/stune/top-app/tasks";
@@ -159,6 +163,8 @@
                 fg_schedboost_fd = open(filename, O_WRONLY | O_CLOEXEC);
                 filename = "/dev/stune/background/tasks";
                 bg_schedboost_fd = open(filename, O_WRONLY | O_CLOEXEC);
+                filename = "/dev/stune/rt/tasks";
+                rt_schedboost_fd = open(filename, O_WRONLY | O_CLOEXEC);
             }
         }
     }
@@ -306,6 +312,9 @@
     case SP_SYSTEM:
         fd = system_bg_cpuset_fd;
         break;
+    case SP_RESTRICTED:
+        fd = rs_cpuset_fd;
+        break;
     default:
         boost_fd = fd = -1;
         break;
@@ -394,6 +403,9 @@
     case SP_SYSTEM:
         SLOGD("/// tid %d (%s)", tid, thread_name);
         break;
+    case SP_RT_APP:
+	SLOGD("RT  tid %d (%s)", tid, thread_name);
+	break;
     default:
         SLOGD("??? tid %d (%s)", tid, thread_name);
         break;
@@ -414,6 +426,9 @@
         case SP_TOP_APP:
             boost_fd = ta_schedboost_fd;
             break;
+        case SP_RT_APP:
+	    boost_fd = rt_schedboost_fd;
+	    break;
         default:
             boost_fd = -1;
             break;
@@ -435,32 +450,27 @@
 
 /* Stubs for non-Android targets. */
 
-int set_sched_policy(int tid UNUSED, SchedPolicy policy UNUSED)
-{
+int set_sched_policy(int /*tid*/, SchedPolicy /*policy*/) {
     return 0;
 }
 
-int get_sched_policy(int tid UNUSED, SchedPolicy *policy)
-{
+int get_sched_policy(int /*tid*/, SchedPolicy* policy) {
     *policy = SP_SYSTEM_DEFAULT;
     return 0;
 }
 
 #endif
 
-const char *get_sched_policy_name(SchedPolicy policy)
-{
+const char* get_sched_policy_name(SchedPolicy policy) {
     policy = _policy(policy);
-    static const char * const strings[SP_CNT] = {
-       [SP_BACKGROUND] = "bg",
-       [SP_FOREGROUND] = "fg",
-       [SP_SYSTEM]     = "  ",
-       [SP_AUDIO_APP]  = "aa",
-       [SP_AUDIO_SYS]  = "as",
-       [SP_TOP_APP]    = "ta",
+    static const char* const kSchedPolicyNames[] = {
+            [SP_BACKGROUND] = "bg", [SP_FOREGROUND] = "fg", [SP_SYSTEM] = "  ",
+            [SP_AUDIO_APP] = "aa",  [SP_AUDIO_SYS] = "as",  [SP_TOP_APP] = "ta",
+            [SP_RT_APP] = "rt",     [SP_RESTRICTED] = "rs",
     };
-    if ((policy < SP_CNT) && (strings[policy] != NULL))
-        return strings[policy];
-    else
+    static_assert(arraysize(kSchedPolicyNames) == SP_CNT, "missing name");
+    if (policy < SP_BACKGROUND || policy >= SP_CNT) {
         return "error";
+    }
+    return kSchedPolicyNames[policy];
 }
diff --git a/libcutils/socket_inaddr_any_server_unix.c b/libcutils/socket_inaddr_any_server_unix.c
deleted file mode 100644
index 387258f..0000000
--- a/libcutils/socket_inaddr_any_server_unix.c
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
-** Copyright 2006, 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 <errno.h>
-#include <stddef.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-#include <sys/socket.h>
-#include <sys/select.h>
-#include <sys/types.h>
-#include <netinet/in.h>
-
-#include <cutils/sockets.h>
-
-#define LISTEN_BACKLOG 4
-
-/* open listen() port on any interface */
-int socket_inaddr_any_server(int port, int type)
-{
-    struct sockaddr_in6 addr;
-    int s, n;
-
-    memset(&addr, 0, sizeof(addr));
-    addr.sin6_family = AF_INET6;
-    addr.sin6_port = htons(port);
-    addr.sin6_addr = in6addr_any;
-
-    s = socket(AF_INET6, type, 0);
-    if (s < 0) return -1;
-
-    n = 1;
-    setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (const char *) &n, sizeof(n));
-
-    if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
-        close(s);
-        return -1;
-    }
-
-    if (type == SOCK_STREAM) {
-        int ret;
-
-        ret = listen(s, LISTEN_BACKLOG);
-
-        if (ret < 0) {
-            close(s);
-            return -1; 
-        }
-    }
-
-    return s;
-}
diff --git a/libcutils/socket_inaddr_any_server_unix.cpp b/libcutils/socket_inaddr_any_server_unix.cpp
new file mode 100644
index 0000000..27c5333
--- /dev/null
+++ b/libcutils/socket_inaddr_any_server_unix.cpp
@@ -0,0 +1,66 @@
+/*
+** Copyright 2006, 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 <cutils/sockets.h>
+
+#include <errno.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <sys/socket.h>
+#include <sys/select.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+
+#define LISTEN_BACKLOG 4
+
+/* open listen() port on any interface */
+int socket_inaddr_any_server(int port, int type)
+{
+    struct sockaddr_in6 addr;
+    int s, n;
+
+    memset(&addr, 0, sizeof(addr));
+    addr.sin6_family = AF_INET6;
+    addr.sin6_port = htons(port);
+    addr.sin6_addr = in6addr_any;
+
+    s = socket(AF_INET6, type, 0);
+    if (s < 0) return -1;
+
+    n = 1;
+    setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (const char *) &n, sizeof(n));
+
+    if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+        close(s);
+        return -1;
+    }
+
+    if (type == SOCK_STREAM) {
+        int ret;
+
+        ret = listen(s, LISTEN_BACKLOG);
+
+        if (ret < 0) {
+            close(s);
+            return -1; 
+        }
+    }
+
+    return s;
+}
diff --git a/libcutils/socket_inaddr_any_server_windows.c b/libcutils/socket_inaddr_any_server_windows.c
deleted file mode 100644
index c15200a..0000000
--- a/libcutils/socket_inaddr_any_server_windows.c
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *  * Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- *  * Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in
- *    the documentation and/or other materials provided with the
- *    distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
- * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
- * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
- * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
- * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
- * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#include <errno.h>
-
-#include <cutils/sockets.h>
-
-#define LISTEN_BACKLOG 4
-
-extern bool initialize_windows_sockets();
-
-SOCKET socket_inaddr_any_server(int port, int type) {
-    if (!initialize_windows_sockets()) {
-      return INVALID_SOCKET;
-    }
-
-    SOCKET sock = socket(AF_INET6, type, 0);
-    if (sock == INVALID_SOCKET) {
-        return INVALID_SOCKET;
-    }
-
-    // Enforce exclusive addresses so nobody can steal the port from us (1),
-    // and enable dual-stack so both IPv4 and IPv6 work (2).
-    // (1) https://msdn.microsoft.com/en-us/library/windows/desktop/ms740621(v=vs.85).aspx.
-    // (2) https://msdn.microsoft.com/en-us/library/windows/desktop/bb513665(v=vs.85).aspx.
-    int exclusive = 1;
-    DWORD v6_only = 0;
-    if (setsockopt(sock, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, (char*)&exclusive,
-                   sizeof(exclusive)) == SOCKET_ERROR ||
-        setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, (char*)&v6_only,
-                   sizeof(v6_only)) == SOCKET_ERROR) {
-        closesocket(sock);
-        return INVALID_SOCKET;
-    }
-
-    // Bind the socket to our local port.
-    struct sockaddr_in6 addr;
-    memset(&addr, 0, sizeof(addr));
-    addr.sin6_family = AF_INET6;
-    addr.sin6_port = htons(port);
-    addr.sin6_addr = in6addr_any;
-    if (bind(sock, (struct sockaddr*)&addr, sizeof(addr)) == SOCKET_ERROR) {
-        closesocket(sock);
-        return INVALID_SOCKET;
-    }
-
-    // Start listening for connections if this is a TCP socket.
-    if (type == SOCK_STREAM && listen(sock, LISTEN_BACKLOG) == SOCKET_ERROR) {
-        closesocket(sock);
-        return INVALID_SOCKET;
-    }
-
-    return sock;
-}
diff --git a/libcutils/socket_inaddr_any_server_windows.cpp b/libcutils/socket_inaddr_any_server_windows.cpp
new file mode 100644
index 0000000..1d73206
--- /dev/null
+++ b/libcutils/socket_inaddr_any_server_windows.cpp
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <cutils/sockets.h>
+
+#include <errno.h>
+
+#define LISTEN_BACKLOG 4
+
+extern bool initialize_windows_sockets();
+
+SOCKET socket_inaddr_any_server(int port, int type) {
+    if (!initialize_windows_sockets()) {
+      return INVALID_SOCKET;
+    }
+
+    SOCKET sock = socket(AF_INET6, type, 0);
+    if (sock == INVALID_SOCKET) {
+        return INVALID_SOCKET;
+    }
+
+    // Enforce exclusive addresses so nobody can steal the port from us (1),
+    // and enable dual-stack so both IPv4 and IPv6 work (2).
+    // (1) https://msdn.microsoft.com/en-us/library/windows/desktop/ms740621(v=vs.85).aspx.
+    // (2) https://msdn.microsoft.com/en-us/library/windows/desktop/bb513665(v=vs.85).aspx.
+    int exclusive = 1;
+    DWORD v6_only = 0;
+    if (setsockopt(sock, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, (char*)&exclusive,
+                   sizeof(exclusive)) == SOCKET_ERROR ||
+        setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, (char*)&v6_only,
+                   sizeof(v6_only)) == SOCKET_ERROR) {
+        closesocket(sock);
+        return INVALID_SOCKET;
+    }
+
+    // Bind the socket to our local port.
+    struct sockaddr_in6 addr;
+    memset(&addr, 0, sizeof(addr));
+    addr.sin6_family = AF_INET6;
+    addr.sin6_port = htons(port);
+    addr.sin6_addr = in6addr_any;
+    if (bind(sock, (struct sockaddr*)&addr, sizeof(addr)) == SOCKET_ERROR) {
+        closesocket(sock);
+        return INVALID_SOCKET;
+    }
+
+    // Start listening for connections if this is a TCP socket.
+    if (type == SOCK_STREAM && listen(sock, LISTEN_BACKLOG) == SOCKET_ERROR) {
+        closesocket(sock);
+        return INVALID_SOCKET;
+    }
+
+    return sock;
+}
diff --git a/libcutils/socket_local_client_unix.c b/libcutils/socket_local_client_unix.c
deleted file mode 100644
index 92fb9f1..0000000
--- a/libcutils/socket_local_client_unix.c
+++ /dev/null
@@ -1,168 +0,0 @@
-/*
- * Copyright (C) 2006 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 <errno.h>
-#include <stddef.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-#include <cutils/sockets.h>
-
-#if defined(_WIN32)
-
-int socket_local_client(const char *name, int namespaceId, int type)
-{
-    errno = ENOSYS;
-    return -1;
-}
-
-#else /* !_WIN32 */
-
-#include <sys/socket.h>
-#include <sys/un.h>
-#include <sys/select.h>
-#include <sys/types.h>
-
-#include "socket_local_unix.h"
-
-#define UNUSED __attribute__((unused))
-
-#define LISTEN_BACKLOG 4
-
-/* Documented in header file. */
-int socket_make_sockaddr_un(const char *name, int namespaceId, 
-        struct sockaddr_un *p_addr, socklen_t *alen)
-{
-    memset (p_addr, 0, sizeof (*p_addr));
-    size_t namelen;
-
-    switch (namespaceId) {
-        case ANDROID_SOCKET_NAMESPACE_ABSTRACT:
-#if defined(__linux__)
-            namelen  = strlen(name);
-
-            // Test with length +1 for the *initial* '\0'.
-            if ((namelen + 1) > sizeof(p_addr->sun_path)) {
-                goto error;
-            }
-
-            /*
-             * Note: The path in this case is *not* supposed to be
-             * '\0'-terminated. ("man 7 unix" for the gory details.)
-             */
-            
-            p_addr->sun_path[0] = 0;
-            memcpy(p_addr->sun_path + 1, name, namelen);
-#else
-            /* this OS doesn't have the Linux abstract namespace */
-
-            namelen = strlen(name) + strlen(FILESYSTEM_SOCKET_PREFIX);
-            /* unix_path_max appears to be missing on linux */
-            if (namelen > sizeof(*p_addr) 
-                    - offsetof(struct sockaddr_un, sun_path) - 1) {
-                goto error;
-            }
-
-            strcpy(p_addr->sun_path, FILESYSTEM_SOCKET_PREFIX);
-            strcat(p_addr->sun_path, name);
-#endif
-        break;
-
-        case ANDROID_SOCKET_NAMESPACE_RESERVED:
-            namelen = strlen(name) + strlen(ANDROID_RESERVED_SOCKET_PREFIX);
-            /* unix_path_max appears to be missing on linux */
-            if (namelen > sizeof(*p_addr) 
-                    - offsetof(struct sockaddr_un, sun_path) - 1) {
-                goto error;
-            }
-
-            strcpy(p_addr->sun_path, ANDROID_RESERVED_SOCKET_PREFIX);
-            strcat(p_addr->sun_path, name);
-        break;
-
-        case ANDROID_SOCKET_NAMESPACE_FILESYSTEM:
-            namelen = strlen(name);
-            /* unix_path_max appears to be missing on linux */
-            if (namelen > sizeof(*p_addr) 
-                    - offsetof(struct sockaddr_un, sun_path) - 1) {
-                goto error;
-            }
-
-            strcpy(p_addr->sun_path, name);
-        break;
-        default:
-            // invalid namespace id
-            return -1;
-    }
-
-    p_addr->sun_family = AF_LOCAL;
-    *alen = namelen + offsetof(struct sockaddr_un, sun_path) + 1;
-    return 0;
-error:
-    return -1;
-}
-
-/**
- * connect to peer named "name" on fd
- * returns same fd or -1 on error.
- * fd is not closed on error. that's your job.
- * 
- * Used by AndroidSocketImpl
- */
-int socket_local_client_connect(int fd, const char *name, int namespaceId, 
-        int type UNUSED)
-{
-    struct sockaddr_un addr;
-    socklen_t alen;
-    int err;
-
-    err = socket_make_sockaddr_un(name, namespaceId, &addr, &alen);
-
-    if (err < 0) {
-        goto error;
-    }
-
-    if(connect(fd, (struct sockaddr *) &addr, alen) < 0) {
-        goto error;
-    }
-
-    return fd;
-
-error:
-    return -1;
-}
-
-/** 
- * connect to peer named "name"
- * returns fd or -1 on error
- */
-int socket_local_client(const char *name, int namespaceId, int type)
-{
-    int s;
-
-    s = socket(AF_LOCAL, type, 0);
-    if(s < 0) return -1;
-
-    if ( 0 > socket_local_client_connect(s, name, namespaceId, type)) {
-        close(s);
-        return -1;
-    }
-
-    return s;
-}
-
-#endif /* !_WIN32 */
diff --git a/libcutils/socket_local_client_unix.cpp b/libcutils/socket_local_client_unix.cpp
new file mode 100644
index 0000000..d2b4909
--- /dev/null
+++ b/libcutils/socket_local_client_unix.cpp
@@ -0,0 +1,164 @@
+/*
+ * Copyright (C) 2006 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 <cutils/sockets.h>
+
+#include <errno.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#if defined(_WIN32)
+
+int socket_local_client(const char *name, int namespaceId, int type)
+{
+    errno = ENOSYS;
+    return -1;
+}
+
+#else /* !_WIN32 */
+
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/select.h>
+#include <sys/types.h>
+
+#include "socket_local_unix.h"
+
+#define LISTEN_BACKLOG 4
+
+/* Documented in header file. */
+int socket_make_sockaddr_un(const char *name, int namespaceId, 
+        struct sockaddr_un *p_addr, socklen_t *alen)
+{
+    memset (p_addr, 0, sizeof (*p_addr));
+    size_t namelen;
+
+    switch (namespaceId) {
+        case ANDROID_SOCKET_NAMESPACE_ABSTRACT:
+#if defined(__linux__)
+            namelen  = strlen(name);
+
+            // Test with length +1 for the *initial* '\0'.
+            if ((namelen + 1) > sizeof(p_addr->sun_path)) {
+                goto error;
+            }
+
+            /*
+             * Note: The path in this case is *not* supposed to be
+             * '\0'-terminated. ("man 7 unix" for the gory details.)
+             */
+            
+            p_addr->sun_path[0] = 0;
+            memcpy(p_addr->sun_path + 1, name, namelen);
+#else
+            /* this OS doesn't have the Linux abstract namespace */
+
+            namelen = strlen(name) + strlen(FILESYSTEM_SOCKET_PREFIX);
+            /* unix_path_max appears to be missing on linux */
+            if (namelen > sizeof(*p_addr) 
+                    - offsetof(struct sockaddr_un, sun_path) - 1) {
+                goto error;
+            }
+
+            strcpy(p_addr->sun_path, FILESYSTEM_SOCKET_PREFIX);
+            strcat(p_addr->sun_path, name);
+#endif
+        break;
+
+        case ANDROID_SOCKET_NAMESPACE_RESERVED:
+            namelen = strlen(name) + strlen(ANDROID_RESERVED_SOCKET_PREFIX);
+            /* unix_path_max appears to be missing on linux */
+            if (namelen > sizeof(*p_addr) 
+                    - offsetof(struct sockaddr_un, sun_path) - 1) {
+                goto error;
+            }
+
+            strcpy(p_addr->sun_path, ANDROID_RESERVED_SOCKET_PREFIX);
+            strcat(p_addr->sun_path, name);
+        break;
+
+        case ANDROID_SOCKET_NAMESPACE_FILESYSTEM:
+            namelen = strlen(name);
+            /* unix_path_max appears to be missing on linux */
+            if (namelen > sizeof(*p_addr) 
+                    - offsetof(struct sockaddr_un, sun_path) - 1) {
+                goto error;
+            }
+
+            strcpy(p_addr->sun_path, name);
+        break;
+        default:
+            // invalid namespace id
+            return -1;
+    }
+
+    p_addr->sun_family = AF_LOCAL;
+    *alen = namelen + offsetof(struct sockaddr_un, sun_path) + 1;
+    return 0;
+error:
+    return -1;
+}
+
+/**
+ * connect to peer named "name" on fd
+ * returns same fd or -1 on error.
+ * fd is not closed on error. that's your job.
+ * 
+ * Used by AndroidSocketImpl
+ */
+int socket_local_client_connect(int fd, const char* name, int namespaceId, int /*type*/) {
+    struct sockaddr_un addr;
+    socklen_t alen;
+    int err;
+
+    err = socket_make_sockaddr_un(name, namespaceId, &addr, &alen);
+
+    if (err < 0) {
+        goto error;
+    }
+
+    if(connect(fd, (struct sockaddr *) &addr, alen) < 0) {
+        goto error;
+    }
+
+    return fd;
+
+error:
+    return -1;
+}
+
+/** 
+ * connect to peer named "name"
+ * returns fd or -1 on error
+ */
+int socket_local_client(const char *name, int namespaceId, int type)
+{
+    int s;
+
+    s = socket(AF_LOCAL, type, 0);
+    if(s < 0) return -1;
+
+    if ( 0 > socket_local_client_connect(s, name, namespaceId, type)) {
+        close(s);
+        return -1;
+    }
+
+    return s;
+}
+
+#endif /* !_WIN32 */
diff --git a/libcutils/socket_local_server_unix.c b/libcutils/socket_local_server_unix.c
deleted file mode 100644
index db9e1e0..0000000
--- a/libcutils/socket_local_server_unix.c
+++ /dev/null
@@ -1,126 +0,0 @@
-/* libs/cutils/socket_local_server.c
-**
-** Copyright 2006, 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 <cutils/sockets.h>
-
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <errno.h>
-#include <stddef.h>
-
-#if defined(_WIN32)
-
-int socket_local_server(const char *name, int namespaceId, int type)
-{
-    errno = ENOSYS;
-    return -1;
-}
-
-#else /* !_WIN32 */
-
-#include <sys/socket.h>
-#include <sys/un.h>
-#include <sys/select.h>
-#include <sys/types.h>
-#include <netinet/in.h>
-
-#include "socket_local_unix.h"
-
-#define LISTEN_BACKLOG 4
-
-/* Only the bottom bits are really the socket type; there are flags too. */
-#define SOCK_TYPE_MASK 0xf
-
-/**
- * Binds a pre-created socket(AF_LOCAL) 's' to 'name'
- * returns 's' on success, -1 on fail
- *
- * Does not call listen()
- */
-int socket_local_server_bind(int s, const char *name, int namespaceId)
-{
-    struct sockaddr_un addr;
-    socklen_t alen;
-    int n;
-    int err;
-
-    err = socket_make_sockaddr_un(name, namespaceId, &addr, &alen);
-
-    if (err < 0) {
-        return -1;
-    }
-
-    /* basically: if this is a filesystem path, unlink first */
-#if !defined(__linux__)
-    if (1) {
-#else
-    if (namespaceId == ANDROID_SOCKET_NAMESPACE_RESERVED
-        || namespaceId == ANDROID_SOCKET_NAMESPACE_FILESYSTEM) {
-#endif
-        /*ignore ENOENT*/
-        unlink(addr.sun_path);
-    }
-
-    n = 1;
-    setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &n, sizeof(n));
-
-    if(bind(s, (struct sockaddr *) &addr, alen) < 0) {
-        return -1;
-    }
-
-    return s;
-
-}
-
-
-/** Open a server-side UNIX domain datagram socket in the Linux non-filesystem 
- *  namespace
- *
- *  Returns fd on success, -1 on fail
- */
-
-int socket_local_server(const char *name, int namespace, int type)
-{
-    int err;
-    int s;
-    
-    s = socket(AF_LOCAL, type, 0);
-    if (s < 0) return -1;
-
-    err = socket_local_server_bind(s, name, namespace);
-
-    if (err < 0) {
-        close(s);
-        return -1;
-    }
-
-    if ((type & SOCK_TYPE_MASK) == SOCK_STREAM) {
-        int ret;
-
-        ret = listen(s, LISTEN_BACKLOG);
-
-        if (ret < 0) {
-            close(s);
-            return -1;
-        }
-    }
-
-    return s;
-}
-
-#endif /* !_WIN32 */
diff --git a/libcutils/socket_local_server_unix.cpp b/libcutils/socket_local_server_unix.cpp
new file mode 100644
index 0000000..855e5da
--- /dev/null
+++ b/libcutils/socket_local_server_unix.cpp
@@ -0,0 +1,126 @@
+/* libs/cutils/socket_local_server.c
+**
+** Copyright 2006, 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 <cutils/sockets.h>
+
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stddef.h>
+
+#if defined(_WIN32)
+
+int socket_local_server(const char *name, int namespaceId, int type)
+{
+    errno = ENOSYS;
+    return -1;
+}
+
+#else /* !_WIN32 */
+
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/select.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+
+#include "socket_local_unix.h"
+
+#define LISTEN_BACKLOG 4
+
+/* Only the bottom bits are really the socket type; there are flags too. */
+#define SOCK_TYPE_MASK 0xf
+
+/**
+ * Binds a pre-created socket(AF_LOCAL) 's' to 'name'
+ * returns 's' on success, -1 on fail
+ *
+ * Does not call listen()
+ */
+int socket_local_server_bind(int s, const char *name, int namespaceId)
+{
+    struct sockaddr_un addr;
+    socklen_t alen;
+    int n;
+    int err;
+
+    err = socket_make_sockaddr_un(name, namespaceId, &addr, &alen);
+
+    if (err < 0) {
+        return -1;
+    }
+
+    /* basically: if this is a filesystem path, unlink first */
+#if !defined(__linux__)
+    if (1) {
+#else
+    if (namespaceId == ANDROID_SOCKET_NAMESPACE_RESERVED
+        || namespaceId == ANDROID_SOCKET_NAMESPACE_FILESYSTEM) {
+#endif
+        /*ignore ENOENT*/
+        unlink(addr.sun_path);
+    }
+
+    n = 1;
+    setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &n, sizeof(n));
+
+    if(bind(s, (struct sockaddr *) &addr, alen) < 0) {
+        return -1;
+    }
+
+    return s;
+
+}
+
+
+/** Open a server-side UNIX domain datagram socket in the Linux non-filesystem 
+ *  namespace
+ *
+ *  Returns fd on success, -1 on fail
+ */
+
+int socket_local_server(const char *name, int namespaceId, int type)
+{
+    int err;
+    int s;
+    
+    s = socket(AF_LOCAL, type, 0);
+    if (s < 0) return -1;
+
+    err = socket_local_server_bind(s, name, namespaceId);
+
+    if (err < 0) {
+        close(s);
+        return -1;
+    }
+
+    if ((type & SOCK_TYPE_MASK) == SOCK_STREAM) {
+        int ret;
+
+        ret = listen(s, LISTEN_BACKLOG);
+
+        if (ret < 0) {
+            close(s);
+            return -1;
+        }
+    }
+
+    return s;
+}
+
+#endif /* !_WIN32 */
diff --git a/libcutils/socket_network_client_unix.c b/libcutils/socket_network_client_unix.c
deleted file mode 100644
index 37851b1..0000000
--- a/libcutils/socket_network_client_unix.c
+++ /dev/null
@@ -1,125 +0,0 @@
-/*
-** Copyright 2006, 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 <errno.h>
-#include <fcntl.h>
-#include <stddef.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-#include <sys/socket.h>
-#include <sys/select.h>
-#include <sys/types.h>
-#include <netinet/in.h>
-#include <netdb.h>
-
-#include <cutils/sockets.h>
-
-static int toggle_O_NONBLOCK(int s) {
-    int flags = fcntl(s, F_GETFL);
-    if (flags == -1 || fcntl(s, F_SETFL, flags ^ O_NONBLOCK) == -1) {
-        close(s);
-        return -1;
-    }
-    return s;
-}
-
-// Connect to the given host and port.
-// 'timeout' is in seconds (0 for no timeout).
-// Returns a file descriptor or -1 on error.
-// On error, check *getaddrinfo_error (for use with gai_strerror) first;
-// if that's 0, use errno instead.
-int socket_network_client_timeout(const char* host, int port, int type, int timeout,
-                                  int* getaddrinfo_error) {
-    struct addrinfo hints;
-    memset(&hints, 0, sizeof(hints));
-    hints.ai_family = AF_UNSPEC;
-    hints.ai_socktype = type;
-
-    char port_str[16];
-    snprintf(port_str, sizeof(port_str), "%d", port);
-
-    struct addrinfo* addrs;
-    *getaddrinfo_error = getaddrinfo(host, port_str, &hints, &addrs);
-    if (*getaddrinfo_error != 0) {
-        return -1;
-    }
-
-    int result = -1;
-    for (struct addrinfo* addr = addrs; addr != NULL; addr = addr->ai_next) {
-        // The Mac doesn't have SOCK_NONBLOCK.
-        int s = socket(addr->ai_family, type, addr->ai_protocol);
-        if (s == -1 || toggle_O_NONBLOCK(s) == -1) return -1;
-
-        int rc = connect(s, addr->ai_addr, addr->ai_addrlen);
-        if (rc == 0) {
-            result = toggle_O_NONBLOCK(s);
-            break;
-        } else if (rc == -1 && errno != EINPROGRESS) {
-            close(s);
-            continue;
-        }
-
-        fd_set r_set;
-        FD_ZERO(&r_set);
-        FD_SET(s, &r_set);
-        fd_set w_set = r_set;
-
-        struct timeval ts;
-        ts.tv_sec = timeout;
-        ts.tv_usec = 0;
-        if ((rc = select(s + 1, &r_set, &w_set, NULL, (timeout != 0) ? &ts : NULL)) == -1) {
-            close(s);
-            break;
-        }
-        if (rc == 0) {  // we had a timeout
-            errno = ETIMEDOUT;
-            close(s);
-            break;
-        }
-
-        int error = 0;
-        socklen_t len = sizeof(error);
-        if (FD_ISSET(s, &r_set) || FD_ISSET(s, &w_set)) {
-            if (getsockopt(s, SOL_SOCKET, SO_ERROR, &error, &len) < 0) {
-                close(s);
-                break;
-            }
-        } else {
-            close(s);
-            break;
-        }
-
-        if (error) {  // check if we had a socket error
-            // TODO: Update the timeout.
-            errno = error;
-            close(s);
-            continue;
-        }
-
-        result = toggle_O_NONBLOCK(s);
-        break;
-    }
-
-    freeaddrinfo(addrs);
-    return result;
-}
-
-int socket_network_client(const char* host, int port, int type) {
-    int getaddrinfo_error;
-    return socket_network_client_timeout(host, port, type, 0, &getaddrinfo_error);
-}
diff --git a/libcutils/socket_network_client_unix.cpp b/libcutils/socket_network_client_unix.cpp
new file mode 100644
index 0000000..be3c535
--- /dev/null
+++ b/libcutils/socket_network_client_unix.cpp
@@ -0,0 +1,125 @@
+/*
+** Copyright 2006, 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 <cutils/sockets.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <sys/socket.h>
+#include <sys/select.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <netdb.h>
+
+static int toggle_O_NONBLOCK(int s) {
+    int flags = fcntl(s, F_GETFL);
+    if (flags == -1 || fcntl(s, F_SETFL, flags ^ O_NONBLOCK) == -1) {
+        close(s);
+        return -1;
+    }
+    return s;
+}
+
+// Connect to the given host and port.
+// 'timeout' is in seconds (0 for no timeout).
+// Returns a file descriptor or -1 on error.
+// On error, check *getaddrinfo_error (for use with gai_strerror) first;
+// if that's 0, use errno instead.
+int socket_network_client_timeout(const char* host, int port, int type, int timeout,
+                                  int* getaddrinfo_error) {
+    struct addrinfo hints;
+    memset(&hints, 0, sizeof(hints));
+    hints.ai_family = AF_UNSPEC;
+    hints.ai_socktype = type;
+
+    char port_str[16];
+    snprintf(port_str, sizeof(port_str), "%d", port);
+
+    struct addrinfo* addrs;
+    *getaddrinfo_error = getaddrinfo(host, port_str, &hints, &addrs);
+    if (*getaddrinfo_error != 0) {
+        return -1;
+    }
+
+    int result = -1;
+    for (struct addrinfo* addr = addrs; addr != NULL; addr = addr->ai_next) {
+        // The Mac doesn't have SOCK_NONBLOCK.
+        int s = socket(addr->ai_family, type, addr->ai_protocol);
+        if (s == -1 || toggle_O_NONBLOCK(s) == -1) break;
+
+        int rc = connect(s, addr->ai_addr, addr->ai_addrlen);
+        if (rc == 0) {
+            result = toggle_O_NONBLOCK(s);
+            break;
+        } else if (rc == -1 && errno != EINPROGRESS) {
+            close(s);
+            continue;
+        }
+
+        fd_set r_set;
+        FD_ZERO(&r_set);
+        FD_SET(s, &r_set);
+        fd_set w_set = r_set;
+
+        struct timeval ts;
+        ts.tv_sec = timeout;
+        ts.tv_usec = 0;
+        if ((rc = select(s + 1, &r_set, &w_set, NULL, (timeout != 0) ? &ts : NULL)) == -1) {
+            close(s);
+            break;
+        }
+        if (rc == 0) {  // we had a timeout
+            errno = ETIMEDOUT;
+            close(s);
+            break;
+        }
+
+        int error = 0;
+        socklen_t len = sizeof(error);
+        if (FD_ISSET(s, &r_set) || FD_ISSET(s, &w_set)) {
+            if (getsockopt(s, SOL_SOCKET, SO_ERROR, &error, &len) < 0) {
+                close(s);
+                break;
+            }
+        } else {
+            close(s);
+            break;
+        }
+
+        if (error) {  // check if we had a socket error
+            // TODO: Update the timeout.
+            errno = error;
+            close(s);
+            continue;
+        }
+
+        result = toggle_O_NONBLOCK(s);
+        break;
+    }
+
+    freeaddrinfo(addrs);
+    return result;
+}
+
+int socket_network_client(const char* host, int port, int type) {
+    int getaddrinfo_error;
+    return socket_network_client_timeout(host, port, type, 0, &getaddrinfo_error);
+}
diff --git a/libcutils/socket_network_client_windows.c b/libcutils/socket_network_client_windows.cpp
similarity index 100%
rename from libcutils/socket_network_client_windows.c
rename to libcutils/socket_network_client_windows.cpp
diff --git a/libcutils/sockets_unix.cpp b/libcutils/sockets_unix.cpp
index e91f358..0cb8a4d 100644
--- a/libcutils/sockets_unix.cpp
+++ b/libcutils/sockets_unix.cpp
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#include <cutils/sockets.h>
+
 #define LOG_TAG "socket-unix"
 
 #include <stdio.h>
@@ -26,15 +28,10 @@
 #include <unistd.h>
 
 #include <cutils/android_get_control_file.h>
-#include <cutils/sockets.h>
 #include <log/log.h>
 
 #include "android_get_control_env.h"
 
-#ifndef TEMP_FAILURE_RETRY
-#define TEMP_FAILURE_RETRY(exp) (exp) // KISS implementation
-#endif
-
 #if defined(__ANDROID__)
 /* For the socket trust (credentials) check */
 #include <private/android_filesystem_config.h>
@@ -101,15 +98,15 @@
     // Compare to UNIX domain socket name, must match!
     struct sockaddr_un addr;
     socklen_t addrlen = sizeof(addr);
-    int ret = TEMP_FAILURE_RETRY(getsockname(fd, (struct sockaddr *)&addr, &addrlen));
+    int ret = getsockname(fd, (struct sockaddr*)&addr, &addrlen);
     if (ret < 0) return -1;
-    char *path = NULL;
-    if (asprintf(&path, ANDROID_SOCKET_DIR "/%s", name) < 0) return -1;
-    if (!path) return -1;
-    int cmp = strcmp(addr.sun_path, path);
-    free(path);
-    if (cmp != 0) return -1;
 
-    // It is what we think it is
-    return fd;
+    constexpr char prefix[] = ANDROID_SOCKET_DIR "/";
+    constexpr size_t prefix_size = sizeof(prefix) - sizeof('\0');
+    if ((strncmp(addr.sun_path, prefix, prefix_size) == 0) &&
+        (strcmp(addr.sun_path + prefix_size, name) == 0)) {
+        // It is what we think it is
+        return fd;
+    }
+    return -1;
 }
diff --git a/libcutils/sockets_windows.cpp b/libcutils/sockets_windows.cpp
index 3064c70..df14712 100644
--- a/libcutils/sockets_windows.cpp
+++ b/libcutils/sockets_windows.cpp
@@ -37,7 +37,7 @@
 // Both adb (1) and Chrome (2) purposefully avoid WSACleanup() with no issues.
 // (1) https://android.googlesource.com/platform/system/core.git/+/master/adb/sysdeps_win32.cpp
 // (2) https://code.google.com/p/chromium/codesearch#chromium/src/net/base/winsock_init.cc
-extern "C" bool initialize_windows_sockets() {
+bool initialize_windows_sockets() {
     // There's no harm in calling WSAStartup() multiple times but no benefit
     // either, we may as well skip it after the first.
     static bool init_success = false;
@@ -85,6 +85,6 @@
     return -1;
 }
 
-int android_get_control_socket(const char* name) {
+int android_get_control_socket(const char*) {
     return -1;
 }
diff --git a/libcutils/str_parms.c b/libcutils/str_parms.c
deleted file mode 100644
index 8dafded..0000000
--- a/libcutils/str_parms.c
+++ /dev/null
@@ -1,382 +0,0 @@
-/*
- * Copyright (C) 2011 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.
- */
-
-#define LOG_TAG "str_params"
-//#define LOG_NDEBUG 0
-
-#define _GNU_SOURCE 1
-#include <errno.h>
-#include <stdint.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include <cutils/hashmap.h>
-#include <cutils/memory.h>
-#include <cutils/str_parms.h>
-#include <log/log.h>
-
-#define UNUSED __attribute__((unused))
-
-/* When an object is allocated but not freed in a function,
- * because its ownership is released to other object like a hashmap,
- * call RELEASE_OWNERSHIP to tell the clang analyzer and avoid
- * false warnings about potential memory leak.
- * For now, a "temporary" assignment to global variables
- * is enough to confuse the clang static analyzer.
- */
-#ifdef __clang_analyzer__
-static void *released_pointer;
-#define RELEASE_OWNERSHIP(x) { released_pointer = x; released_pointer = 0; }
-#else
-#define RELEASE_OWNERSHIP(x)
-#endif
-
-struct str_parms {
-    Hashmap *map;
-};
-
-
-static bool str_eq(void *key_a, void *key_b)
-{
-    return !strcmp((const char *)key_a, (const char *)key_b);
-}
-
-/* use djb hash unless we find it inadequate */
-#ifdef __clang__
-__attribute__((no_sanitize("integer")))
-#endif
-static int str_hash_fn(void *str)
-{
-    uint32_t hash = 5381;
-    char *p;
-
-    for (p = str; p && *p; p++)
-        hash = ((hash << 5) + hash) + *p;
-    return (int)hash;
-}
-
-struct str_parms *str_parms_create(void)
-{
-    struct str_parms *str_parms;
-
-    str_parms = calloc(1, sizeof(struct str_parms));
-    if (!str_parms)
-        return NULL;
-
-    str_parms->map = hashmapCreate(5, str_hash_fn, str_eq);
-    if (!str_parms->map)
-        goto err;
-
-    return str_parms;
-
-err:
-    free(str_parms);
-    return NULL;
-}
-
-struct remove_ctxt {
-    struct str_parms *str_parms;
-    const char *key;
-};
-
-static bool remove_pair(void *key, void *value, void *context)
-{
-    struct remove_ctxt *ctxt = context;
-    bool should_continue;
-
-    /*
-     * - if key is not supplied, then we are removing all entries,
-     *   so remove key and continue (i.e. return true)
-     * - if key is supplied and matches, then remove it and don't
-     *   continue (return false). Otherwise, return true and keep searching
-     *   for key.
-     *
-     */
-    if (!ctxt->key) {
-        should_continue = true;
-        goto do_remove;
-    } else if (!strcmp(ctxt->key, key)) {
-        should_continue = false;
-        goto do_remove;
-    }
-
-    return true;
-
-do_remove:
-    hashmapRemove(ctxt->str_parms->map, key);
-    free(key);
-    free(value);
-    return should_continue;
-}
-
-void str_parms_del(struct str_parms *str_parms, const char *key)
-{
-    struct remove_ctxt ctxt = {
-        .str_parms = str_parms,
-        .key = key,
-    };
-    hashmapForEach(str_parms->map, remove_pair, &ctxt);
-}
-
-void str_parms_destroy(struct str_parms *str_parms)
-{
-    struct remove_ctxt ctxt = {
-        .str_parms = str_parms,
-    };
-
-    hashmapForEach(str_parms->map, remove_pair, &ctxt);
-    hashmapFree(str_parms->map);
-    free(str_parms);
-}
-
-struct str_parms *str_parms_create_str(const char *_string)
-{
-    struct str_parms *str_parms;
-    char *str;
-    char *kvpair;
-    char *tmpstr;
-    int items = 0;
-
-    str_parms = str_parms_create();
-    if (!str_parms)
-        goto err_create_str_parms;
-
-    str = strdup(_string);
-    if (!str)
-        goto err_strdup;
-
-    ALOGV("%s: source string == '%s'\n", __func__, _string);
-
-    kvpair = strtok_r(str, ";", &tmpstr);
-    while (kvpair && *kvpair) {
-        char *eq = strchr(kvpair, '='); /* would love strchrnul */
-        char *value;
-        char *key;
-        void *old_val;
-
-        if (eq == kvpair)
-            goto next_pair;
-
-        if (eq) {
-            key = strndup(kvpair, eq - kvpair);
-            if (*(++eq))
-                value = strdup(eq);
-            else
-                value = strdup("");
-        } else {
-            key = strdup(kvpair);
-            value = strdup("");
-        }
-
-        /* if we replaced a value, free it */
-        old_val = hashmapPut(str_parms->map, key, value);
-        RELEASE_OWNERSHIP(value);
-        if (old_val) {
-            free(old_val);
-            free(key);
-        } else {
-            RELEASE_OWNERSHIP(key);
-        }
-
-        items++;
-next_pair:
-        kvpair = strtok_r(NULL, ";", &tmpstr);
-    }
-
-    if (!items)
-        ALOGV("%s: no items found in string\n", __func__);
-
-    free(str);
-
-    return str_parms;
-
-err_strdup:
-    str_parms_destroy(str_parms);
-err_create_str_parms:
-    return NULL;
-}
-
-int str_parms_add_str(struct str_parms *str_parms, const char *key,
-                      const char *value)
-{
-    void *tmp_key = NULL;
-    void *tmp_val = NULL;
-    void *old_val = NULL;
-
-    // strdup and hashmapPut both set errno on failure.
-    // Set errno to 0 so we can recognize whether anything went wrong.
-    int saved_errno = errno;
-    errno = 0;
-
-    tmp_key = strdup(key);
-    if (tmp_key == NULL) {
-        goto clean_up;
-    }
-
-    tmp_val = strdup(value);
-    if (tmp_val == NULL) {
-        goto clean_up;
-    }
-
-    old_val = hashmapPut(str_parms->map, tmp_key, tmp_val);
-    if (old_val == NULL) {
-        // Did hashmapPut fail?
-        if (errno == ENOMEM) {
-            goto clean_up;
-        }
-        // For new keys, hashmap takes ownership of tmp_key and tmp_val.
-        RELEASE_OWNERSHIP(tmp_key);
-        RELEASE_OWNERSHIP(tmp_val);
-        tmp_key = tmp_val = NULL;
-    } else {
-        // For existing keys, hashmap takes ownership of tmp_val.
-        // (It also gives up ownership of old_val.)
-        RELEASE_OWNERSHIP(tmp_val);
-        tmp_val = NULL;
-    }
-
-clean_up:
-    free(tmp_key);
-    free(tmp_val);
-    free(old_val);
-    int result = -errno;
-    errno = saved_errno;
-    return result;
-}
-
-int str_parms_add_int(struct str_parms *str_parms, const char *key, int value)
-{
-    char val_str[12];
-    int ret;
-
-    ret = snprintf(val_str, sizeof(val_str), "%d", value);
-    if (ret < 0)
-        return -EINVAL;
-
-    ret = str_parms_add_str(str_parms, key, val_str);
-    return ret;
-}
-
-int str_parms_add_float(struct str_parms *str_parms, const char *key,
-                        float value)
-{
-    char val_str[23];
-    int ret;
-
-    ret = snprintf(val_str, sizeof(val_str), "%.10f", value);
-    if (ret < 0)
-        return -EINVAL;
-
-    ret = str_parms_add_str(str_parms, key, val_str);
-    return ret;
-}
-
-int str_parms_has_key(struct str_parms *str_parms, const char *key) {
-    return hashmapGet(str_parms->map, (void *)key) != NULL;
-}
-
-int str_parms_get_str(struct str_parms *str_parms, const char *key, char *val,
-                      int len)
-{
-    char *value;
-
-    value = hashmapGet(str_parms->map, (void *)key);
-    if (value)
-        return strlcpy(val, value, len);
-
-    return -ENOENT;
-}
-
-int str_parms_get_int(struct str_parms *str_parms, const char *key, int *val)
-{
-    char *value;
-    char *end;
-
-    value = hashmapGet(str_parms->map, (void *)key);
-    if (!value)
-        return -ENOENT;
-
-    *val = (int)strtol(value, &end, 0);
-    if (*value != '\0' && *end == '\0')
-        return 0;
-
-    return -EINVAL;
-}
-
-int str_parms_get_float(struct str_parms *str_parms, const char *key,
-                        float *val)
-{
-    float out;
-    char *value;
-    char *end;
-
-    value = hashmapGet(str_parms->map, (void *)key);
-    if (!value)
-        return -ENOENT;
-
-    out = strtof(value, &end);
-    if (*value == '\0' || *end != '\0')
-        return -EINVAL;
-
-    *val = out;
-    return 0;
-}
-
-static bool combine_strings(void *key, void *value, void *context)
-{
-    char **old_str = context;
-    char *new_str;
-    int ret;
-
-    ret = asprintf(&new_str, "%s%s%s=%s",
-                   *old_str ? *old_str : "",
-                   *old_str ? ";" : "",
-                   (char *)key,
-                   (char *)value);
-    if (*old_str)
-        free(*old_str);
-
-    if (ret >= 0) {
-        *old_str = new_str;
-        return true;
-    }
-
-    *old_str = NULL;
-    return false;
-}
-
-char *str_parms_to_str(struct str_parms *str_parms)
-{
-    char *str = NULL;
-
-    if (hashmapSize(str_parms->map) > 0)
-        hashmapForEach(str_parms->map, combine_strings, &str);
-    else
-        str = strdup("");
-    return str;
-}
-
-static bool dump_entry(void *key, void *value, void *context UNUSED)
-{
-    ALOGI("key: '%s' value: '%s'\n", (char *)key, (char *)value);
-    return true;
-}
-
-void str_parms_dump(struct str_parms *str_parms)
-{
-    hashmapForEach(str_parms->map, dump_entry, str_parms);
-}
diff --git a/libcutils/str_parms.cpp b/libcutils/str_parms.cpp
new file mode 100644
index 0000000..d818c51
--- /dev/null
+++ b/libcutils/str_parms.cpp
@@ -0,0 +1,369 @@
+/*
+ * Copyright (C) 2011 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 <cutils/str_parms.h>
+
+#define LOG_TAG "str_params"
+//#define LOG_NDEBUG 0
+
+#define _GNU_SOURCE 1
+#include <errno.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <cutils/hashmap.h>
+#include <cutils/memory.h>
+#include <log/log.h>
+
+/* When an object is allocated but not freed in a function,
+ * because its ownership is released to other object like a hashmap,
+ * call RELEASE_OWNERSHIP to tell the clang analyzer and avoid
+ * false warnings about potential memory leak.
+ * For now, a "temporary" assignment to global variables
+ * is enough to confuse the clang static analyzer.
+ */
+#ifdef __clang_analyzer__
+static void *released_pointer;
+#define RELEASE_OWNERSHIP(x) { released_pointer = x; released_pointer = 0; }
+#else
+#define RELEASE_OWNERSHIP(x)
+#endif
+
+struct str_parms {
+    Hashmap *map;
+};
+
+
+static bool str_eq(void *key_a, void *key_b)
+{
+    return !strcmp((const char *)key_a, (const char *)key_b);
+}
+
+/* use djb hash unless we find it inadequate */
+#ifdef __clang__
+__attribute__((no_sanitize("integer")))
+#endif
+static int str_hash_fn(void *str)
+{
+    uint32_t hash = 5381;
+
+    for (char* p = static_cast<char*>(str); p && *p; p++)
+        hash = ((hash << 5) + hash) + *p;
+    return (int)hash;
+}
+
+struct str_parms *str_parms_create(void)
+{
+    str_parms* s = static_cast<str_parms*>(calloc(1, sizeof(str_parms)));
+    if (!s) return NULL;
+
+    s->map = hashmapCreate(5, str_hash_fn, str_eq);
+    if (!s->map) {
+        free(s);
+        return NULL;
+    }
+
+    return s;
+}
+
+struct remove_ctxt {
+    struct str_parms *str_parms;
+    const char *key;
+};
+
+static bool remove_pair(void *key, void *value, void *context)
+{
+    remove_ctxt* ctxt = static_cast<remove_ctxt*>(context);
+    bool should_continue;
+
+    /*
+     * - if key is not supplied, then we are removing all entries,
+     *   so remove key and continue (i.e. return true)
+     * - if key is supplied and matches, then remove it and don't
+     *   continue (return false). Otherwise, return true and keep searching
+     *   for key.
+     *
+     */
+    if (!ctxt->key) {
+        should_continue = true;
+        goto do_remove;
+    } else if (!strcmp(ctxt->key, static_cast<const char*>(key))) {
+        should_continue = false;
+        goto do_remove;
+    }
+
+    return true;
+
+do_remove:
+    hashmapRemove(ctxt->str_parms->map, key);
+    free(key);
+    free(value);
+    return should_continue;
+}
+
+void str_parms_del(struct str_parms *str_parms, const char *key)
+{
+    struct remove_ctxt ctxt = {
+        .str_parms = str_parms,
+        .key = key,
+    };
+    hashmapForEach(str_parms->map, remove_pair, &ctxt);
+}
+
+void str_parms_destroy(struct str_parms *str_parms)
+{
+    struct remove_ctxt ctxt = {
+        .str_parms = str_parms,
+    };
+
+    hashmapForEach(str_parms->map, remove_pair, &ctxt);
+    hashmapFree(str_parms->map);
+    free(str_parms);
+}
+
+struct str_parms *str_parms_create_str(const char *_string)
+{
+    struct str_parms *str_parms;
+    char *str;
+    char *kvpair;
+    char *tmpstr;
+    int items = 0;
+
+    str_parms = str_parms_create();
+    if (!str_parms)
+        goto err_create_str_parms;
+
+    str = strdup(_string);
+    if (!str)
+        goto err_strdup;
+
+    ALOGV("%s: source string == '%s'\n", __func__, _string);
+
+    kvpair = strtok_r(str, ";", &tmpstr);
+    while (kvpair && *kvpair) {
+        char *eq = strchr(kvpair, '='); /* would love strchrnul */
+        char *value;
+        char *key;
+        void *old_val;
+
+        if (eq == kvpair)
+            goto next_pair;
+
+        if (eq) {
+            key = strndup(kvpair, eq - kvpair);
+            if (*(++eq))
+                value = strdup(eq);
+            else
+                value = strdup("");
+        } else {
+            key = strdup(kvpair);
+            value = strdup("");
+        }
+
+        /* if we replaced a value, free it */
+        old_val = hashmapPut(str_parms->map, key, value);
+        RELEASE_OWNERSHIP(value);
+        if (old_val) {
+            free(old_val);
+            free(key);
+        } else {
+            RELEASE_OWNERSHIP(key);
+        }
+
+        items++;
+next_pair:
+        kvpair = strtok_r(NULL, ";", &tmpstr);
+    }
+
+    if (!items)
+        ALOGV("%s: no items found in string\n", __func__);
+
+    free(str);
+
+    return str_parms;
+
+err_strdup:
+    str_parms_destroy(str_parms);
+err_create_str_parms:
+    return NULL;
+}
+
+int str_parms_add_str(struct str_parms *str_parms, const char *key,
+                      const char *value)
+{
+    void *tmp_key = NULL;
+    void *tmp_val = NULL;
+    void *old_val = NULL;
+
+    // strdup and hashmapPut both set errno on failure.
+    // Set errno to 0 so we can recognize whether anything went wrong.
+    int saved_errno = errno;
+    errno = 0;
+
+    tmp_key = strdup(key);
+    if (tmp_key == NULL) {
+        goto clean_up;
+    }
+
+    tmp_val = strdup(value);
+    if (tmp_val == NULL) {
+        goto clean_up;
+    }
+
+    old_val = hashmapPut(str_parms->map, tmp_key, tmp_val);
+    if (old_val == NULL) {
+        // Did hashmapPut fail?
+        if (errno == ENOMEM) {
+            goto clean_up;
+        }
+        // For new keys, hashmap takes ownership of tmp_key and tmp_val.
+        RELEASE_OWNERSHIP(tmp_key);
+        RELEASE_OWNERSHIP(tmp_val);
+        tmp_key = tmp_val = NULL;
+    } else {
+        // For existing keys, hashmap takes ownership of tmp_val.
+        // (It also gives up ownership of old_val.)
+        RELEASE_OWNERSHIP(tmp_val);
+        tmp_val = NULL;
+    }
+
+clean_up:
+    free(tmp_key);
+    free(tmp_val);
+    free(old_val);
+    int result = -errno;
+    errno = saved_errno;
+    return result;
+}
+
+int str_parms_add_int(struct str_parms *str_parms, const char *key, int value)
+{
+    char val_str[12];
+    int ret;
+
+    ret = snprintf(val_str, sizeof(val_str), "%d", value);
+    if (ret < 0)
+        return -EINVAL;
+
+    ret = str_parms_add_str(str_parms, key, val_str);
+    return ret;
+}
+
+int str_parms_add_float(struct str_parms *str_parms, const char *key,
+                        float value)
+{
+    char val_str[23];
+    int ret;
+
+    ret = snprintf(val_str, sizeof(val_str), "%.10f", value);
+    if (ret < 0)
+        return -EINVAL;
+
+    ret = str_parms_add_str(str_parms, key, val_str);
+    return ret;
+}
+
+int str_parms_has_key(struct str_parms *str_parms, const char *key) {
+    return hashmapGet(str_parms->map, (void *)key) != NULL;
+}
+
+int str_parms_get_str(struct str_parms *str_parms, const char *key, char *val,
+                      int len)
+{
+    // TODO: hashmapGet should take a const* key.
+    char* value = static_cast<char*>(hashmapGet(str_parms->map, (void*)key));
+    if (value)
+        return strlcpy(val, value, len);
+
+    return -ENOENT;
+}
+
+int str_parms_get_int(struct str_parms *str_parms, const char *key, int *val)
+{
+    char *end;
+
+    // TODO: hashmapGet should take a const* key.
+    char* value = static_cast<char*>(hashmapGet(str_parms->map, (void*)key));
+    if (!value)
+        return -ENOENT;
+
+    *val = (int)strtol(value, &end, 0);
+    if (*value != '\0' && *end == '\0')
+        return 0;
+
+    return -EINVAL;
+}
+
+int str_parms_get_float(struct str_parms *str_parms, const char *key,
+                        float *val)
+{
+    float out;
+    char *end;
+
+    // TODO: hashmapGet should take a const* key.
+    char* value = static_cast<char*>(hashmapGet(str_parms->map, (void*)(key)));
+    if (!value)
+        return -ENOENT;
+
+    out = strtof(value, &end);
+    if (*value == '\0' || *end != '\0')
+        return -EINVAL;
+
+    *val = out;
+    return 0;
+}
+
+static bool combine_strings(void *key, void *value, void *context)
+{
+    char** old_str = static_cast<char**>(context);
+    char *new_str;
+    int ret;
+
+    ret = asprintf(&new_str, "%s%s%s=%s",
+                   *old_str ? *old_str : "",
+                   *old_str ? ";" : "",
+                   (char *)key,
+                   (char *)value);
+    if (*old_str)
+        free(*old_str);
+
+    if (ret >= 0) {
+        *old_str = new_str;
+        return true;
+    }
+
+    *old_str = NULL;
+    return false;
+}
+
+char *str_parms_to_str(struct str_parms *str_parms)
+{
+    char *str = NULL;
+    hashmapForEach(str_parms->map, combine_strings, &str);
+    return (str != NULL) ? str : strdup("");
+}
+
+static bool dump_entry(void* key, void* value, void* /*context*/) {
+    ALOGI("key: '%s' value: '%s'\n", (char *)key, (char *)value);
+    return true;
+}
+
+void str_parms_dump(struct str_parms *str_parms)
+{
+    hashmapForEach(str_parms->map, dump_entry, str_parms);
+}
diff --git a/libcutils/strdup16to8.c b/libcutils/strdup16to8.c
deleted file mode 100644
index 4dc987e..0000000
--- a/libcutils/strdup16to8.c
+++ /dev/null
@@ -1,171 +0,0 @@
-/* libs/cutils/strdup16to8.c
-**
-** Copyright 2006, 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 <limits.h>  /* for SIZE_MAX */
-
-#include <cutils/jstring.h>
-#include <assert.h>
-#include <stdlib.h>
-
-
-/**
- * Given a UTF-16 string, compute the length of the corresponding UTF-8
- * string in bytes.
- */
-extern size_t strnlen16to8(const char16_t* utf16Str, size_t len)
-{
-    size_t utf8Len = 0;
-
-    /* A small note on integer overflow. The result can
-     * potentially be as big as 3*len, which will overflow
-     * for len > SIZE_MAX/3.
-     *
-     * Moreover, the result of a strnlen16to8 is typically used
-     * to allocate a destination buffer to strncpy16to8 which
-     * requires one more byte to terminate the UTF-8 copy, and
-     * this is generally done by careless users by incrementing
-     * the result without checking for integer overflows, e.g.:
-     *
-     *   dst = malloc(strnlen16to8(utf16,len)+1)
-     *
-     * Due to this, the following code will try to detect
-     * overflows, and never return more than (SIZE_MAX-1)
-     * when it detects one. A careless user will try to malloc
-     * SIZE_MAX bytes, which will return NULL which can at least
-     * be detected appropriately.
-     *
-     * As far as I know, this function is only used by strndup16(),
-     * but better be safe than sorry.
-     */
-
-    /* Fast path for the usual case where 3*len is < SIZE_MAX-1.
-     */
-    if (len < (SIZE_MAX-1)/3) {
-        while (len != 0) {
-            len--;
-            unsigned int uic = *utf16Str++;
-
-            if (uic > 0x07ff)
-                utf8Len += 3;
-            else if (uic > 0x7f || uic == 0)
-                utf8Len += 2;
-            else
-                utf8Len++;
-        }
-        return utf8Len;
-    }
-
-    /* The slower but paranoid version */
-    while (len != 0) {
-        len--;
-        unsigned int  uic     = *utf16Str++;
-        size_t        utf8Cur = utf8Len;
-
-        if (uic > 0x07ff)
-            utf8Len += 3;
-        else if (uic > 0x7f || uic == 0)
-            utf8Len += 2;
-        else
-            utf8Len++;
-
-        if (utf8Len < utf8Cur) /* overflow detected */
-            return SIZE_MAX-1;
-    }
-
-    /* don't return SIZE_MAX to avoid common user bug */
-    if (utf8Len == SIZE_MAX)
-        utf8Len = SIZE_MAX-1;
-
-    return utf8Len;
-}
-
-
-/**
- * Convert a Java-Style UTF-16 string + length to a JNI-Style UTF-8 string.
- *
- * This basically means: embedded \0's in the UTF-16 string are encoded
- * as "0xc0 0x80"
- *
- * Make sure you allocate "utf8Str" with the result of strlen16to8() + 1,
- * not just "len".
- *
- * Please note, a terminated \0 is always added, so your result will always
- * be "strlen16to8() + 1" bytes long.
- */
-extern char* strncpy16to8(char* utf8Str, const char16_t* utf16Str, size_t len)
-{
-    char* utf8cur = utf8Str;
-
-    /* Note on overflows: We assume the user did check the result of
-     * strnlen16to8() properly or at a minimum checked the result of
-     * its malloc(SIZE_MAX) in case of overflow.
-     */
-    while (len != 0) {
-        len--;
-        unsigned int uic = *utf16Str++;
-
-        if (uic > 0x07ff) {
-            *utf8cur++ = (uic >> 12) | 0xe0;
-            *utf8cur++ = ((uic >> 6) & 0x3f) | 0x80;
-            *utf8cur++ = (uic & 0x3f) | 0x80;
-        } else if (uic > 0x7f || uic == 0) {
-            *utf8cur++ = (uic >> 6) | 0xc0;
-            *utf8cur++ = (uic & 0x3f) | 0x80;
-        } else {
-            *utf8cur++ = uic;
-
-            if (uic == 0) {
-                break;
-            }
-        }
-    }
-
-   *utf8cur = '\0';
-
-   return utf8Str;
-}
-
-/**
- * Convert a UTF-16 string to UTF-8.
- *
- */
-char * strndup16to8 (const char16_t* s, size_t n)
-{
-    char*   ret;
-    size_t  len;
-
-    if (s == NULL) {
-        return NULL;
-    }
-
-    len = strnlen16to8(s, n);
-
-    /* We are paranoid, and we check for SIZE_MAX-1
-     * too since it is an overflow value for our
-     * strnlen16to8 implementation.
-     */
-    if (len >= SIZE_MAX-1)
-        return NULL;
-
-    ret = malloc(len + 1);
-    if (ret == NULL)
-        return NULL;
-
-    strncpy16to8 (ret, s, n);
-
-    return ret;
-}
diff --git a/libcutils/strdup16to8.cpp b/libcutils/strdup16to8.cpp
new file mode 100644
index 0000000..d89181e
--- /dev/null
+++ b/libcutils/strdup16to8.cpp
@@ -0,0 +1,168 @@
+/* libs/cutils/strdup16to8.c
+**
+** Copyright 2006, 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 <cutils/jstring.h>
+
+#include <assert.h>
+#include <limits.h>  /* for SIZE_MAX */
+#include <stdlib.h>
+
+
+/**
+ * Given a UTF-16 string, compute the length of the corresponding UTF-8
+ * string in bytes.
+ */
+extern size_t strnlen16to8(const char16_t* utf16Str, size_t len)
+{
+    size_t utf8Len = 0;
+
+    /* A small note on integer overflow. The result can
+     * potentially be as big as 3*len, which will overflow
+     * for len > SIZE_MAX/3.
+     *
+     * Moreover, the result of a strnlen16to8 is typically used
+     * to allocate a destination buffer to strncpy16to8 which
+     * requires one more byte to terminate the UTF-8 copy, and
+     * this is generally done by careless users by incrementing
+     * the result without checking for integer overflows, e.g.:
+     *
+     *   dst = malloc(strnlen16to8(utf16,len)+1)
+     *
+     * Due to this, the following code will try to detect
+     * overflows, and never return more than (SIZE_MAX-1)
+     * when it detects one. A careless user will try to malloc
+     * SIZE_MAX bytes, which will return NULL which can at least
+     * be detected appropriately.
+     *
+     * As far as I know, this function is only used by strndup16(),
+     * but better be safe than sorry.
+     */
+
+    /* Fast path for the usual case where 3*len is < SIZE_MAX-1.
+     */
+    if (len < (SIZE_MAX-1)/3) {
+        while (len != 0) {
+            len--;
+            unsigned int uic = *utf16Str++;
+
+            if (uic > 0x07ff)
+                utf8Len += 3;
+            else if (uic > 0x7f || uic == 0)
+                utf8Len += 2;
+            else
+                utf8Len++;
+        }
+        return utf8Len;
+    }
+
+    /* The slower but paranoid version */
+    while (len != 0) {
+        len--;
+        unsigned int  uic     = *utf16Str++;
+        size_t        utf8Cur = utf8Len;
+
+        if (uic > 0x07ff)
+            utf8Len += 3;
+        else if (uic > 0x7f || uic == 0)
+            utf8Len += 2;
+        else
+            utf8Len++;
+
+        if (utf8Len < utf8Cur) /* overflow detected */
+            return SIZE_MAX-1;
+    }
+
+    /* don't return SIZE_MAX to avoid common user bug */
+    if (utf8Len == SIZE_MAX)
+        utf8Len = SIZE_MAX-1;
+
+    return utf8Len;
+}
+
+
+/**
+ * Convert a Java-Style UTF-16 string + length to a JNI-Style UTF-8 string.
+ *
+ * This basically means: embedded \0's in the UTF-16 string are encoded
+ * as "0xc0 0x80"
+ *
+ * Make sure you allocate "utf8Str" with the result of strlen16to8() + 1,
+ * not just "len".
+ *
+ * Please note, a terminated \0 is always added, so your result will always
+ * be "strlen16to8() + 1" bytes long.
+ */
+extern char* strncpy16to8(char* utf8Str, const char16_t* utf16Str, size_t len)
+{
+    char* utf8cur = utf8Str;
+
+    /* Note on overflows: We assume the user did check the result of
+     * strnlen16to8() properly or at a minimum checked the result of
+     * its malloc(SIZE_MAX) in case of overflow.
+     */
+    while (len != 0) {
+        len--;
+        unsigned int uic = *utf16Str++;
+
+        if (uic > 0x07ff) {
+            *utf8cur++ = (uic >> 12) | 0xe0;
+            *utf8cur++ = ((uic >> 6) & 0x3f) | 0x80;
+            *utf8cur++ = (uic & 0x3f) | 0x80;
+        } else if (uic > 0x7f || uic == 0) {
+            *utf8cur++ = (uic >> 6) | 0xc0;
+            *utf8cur++ = (uic & 0x3f) | 0x80;
+        } else {
+            *utf8cur++ = uic;
+
+            if (uic == 0) {
+                break;
+            }
+        }
+    }
+
+   *utf8cur = '\0';
+
+   return utf8Str;
+}
+
+/**
+ * Convert a UTF-16 string to UTF-8.
+ *
+ */
+char * strndup16to8 (const char16_t* s, size_t n)
+{
+    if (s == NULL) {
+        return NULL;
+    }
+
+    size_t len = strnlen16to8(s, n);
+
+    /* We are paranoid, and we check for SIZE_MAX-1
+     * too since it is an overflow value for our
+     * strnlen16to8 implementation.
+     */
+    if (len >= SIZE_MAX-1)
+        return NULL;
+
+    char* ret = static_cast<char*>(malloc(len + 1));
+    if (ret == NULL)
+        return NULL;
+
+    strncpy16to8 (ret, s, n);
+
+    return ret;
+}
diff --git a/libcutils/strdup8to16.c b/libcutils/strdup8to16.c
deleted file mode 100644
index c23cf8b..0000000
--- a/libcutils/strdup8to16.c
+++ /dev/null
@@ -1,214 +0,0 @@
-/* libs/cutils/strdup8to16.c
-**
-** Copyright 2006, 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 <cutils/jstring.h>
-#include <assert.h>
-#include <stdlib.h>
-#include <limits.h>
-
-/* See http://www.unicode.org/reports/tr22/ for discussion
- * on invalid sequences
- */
-
-#define UTF16_REPLACEMENT_CHAR 0xfffd
-
-/* Clever trick from Dianne that returns 1-4 depending on leading bit sequence*/
-#define UTF8_SEQ_LENGTH(ch) (((0xe5000000 >> (((ch) >> 3) & 0x1e)) & 3) + 1)
-
-/* note: macro expands to multiple lines */
-#define UTF8_SHIFT_AND_MASK(unicode, byte)  \
-            (unicode)<<=6; (unicode) |= (0x3f & (byte));
-
-#define UNICODE_UPPER_LIMIT 0x10fffd    
-
-/**
- * out_len is an out parameter (which may not be null) containing the
- * length of the UTF-16 string (which may contain embedded \0's)
- */
-
-extern char16_t * strdup8to16 (const char* s, size_t *out_len)
-{
-    char16_t *ret;
-    size_t len;
-
-    if (s == NULL) return NULL;
-
-    len = strlen8to16(s);
-
-    // fail on overflow
-    if (len && SIZE_MAX/len < sizeof(char16_t))
-        return NULL;
-
-    // no plus-one here. UTF-16 strings are not null terminated
-    ret = (char16_t *) malloc (sizeof(char16_t) * len);
-
-    return strcpy8to16 (ret, s, out_len);
-}
-
-/**
- * Like "strlen", but for strings encoded with Java's modified UTF-8.
- *
- * The value returned is the number of UTF-16 characters required
- * to represent this string.
- */
-extern size_t strlen8to16 (const char* utf8Str)
-{
-    size_t len = 0;
-    int ic;
-    int expected = 0;
-
-    while ((ic = *utf8Str++) != '\0') {
-        /* bytes that start 0? or 11 are lead bytes and count as characters.*/
-        /* bytes that start 10 are extention bytes and are not counted */
-         
-        if ((ic & 0xc0) == 0x80) {
-            /* count the 0x80 extention bytes. if we have more than
-             * expected, then start counting them because strcpy8to16
-             * will insert UTF16_REPLACEMENT_CHAR's
-             */
-            expected--;
-            if (expected < 0) {
-                len++;
-            }
-        } else {
-            len++;
-            expected = UTF8_SEQ_LENGTH(ic) - 1;
-
-            /* this will result in a surrogate pair */
-            if (expected == 3) {
-                len++;
-            }
-        }
-    }
-
-    return len;
-}
-
-
-
-/*
- * Retrieve the next UTF-32 character from a UTF-8 string.
- *
- * Stops at inner \0's
- *
- * Returns UTF16_REPLACEMENT_CHAR if an invalid sequence is encountered
- *
- * Advances "*pUtf8Ptr" to the start of the next character.
- */
-static inline uint32_t getUtf32FromUtf8(const char** pUtf8Ptr)
-{
-    uint32_t ret;
-    int seq_len;
-    int i;
-
-    /* Mask for leader byte for lengths 1, 2, 3, and 4 respectively*/
-    static const char leaderMask[4] = {0xff, 0x1f, 0x0f, 0x07};
-
-    /* Bytes that start with bits "10" are not leading characters. */
-    if (((**pUtf8Ptr) & 0xc0) == 0x80) {
-        (*pUtf8Ptr)++;
-        return UTF16_REPLACEMENT_CHAR;
-    }
-
-    /* note we tolerate invalid leader 11111xxx here */    
-    seq_len = UTF8_SEQ_LENGTH(**pUtf8Ptr);
-
-    ret = (**pUtf8Ptr) & leaderMask [seq_len - 1];
-
-    if (**pUtf8Ptr == '\0') return ret;
-
-    (*pUtf8Ptr)++;
-    for (i = 1; i < seq_len ; i++, (*pUtf8Ptr)++) {
-        if ((**pUtf8Ptr) == '\0') return UTF16_REPLACEMENT_CHAR;
-        if (((**pUtf8Ptr) & 0xc0) != 0x80) return UTF16_REPLACEMENT_CHAR;
-
-        UTF8_SHIFT_AND_MASK(ret, **pUtf8Ptr);
-    }
-
-    return ret;
-}
-
-
-/**
- * out_len is an out parameter (which may not be null) containing the
- * length of the UTF-16 string (which may contain embedded \0's)
- */
-
-extern char16_t * strcpy8to16 (char16_t *utf16Str, const char*utf8Str, 
-                                       size_t *out_len)
-{   
-    char16_t *dest = utf16Str;
-
-    while (*utf8Str != '\0') {
-        uint32_t ret;
-
-        ret = getUtf32FromUtf8(&utf8Str);
-
-        if (ret <= 0xffff) {
-            *dest++ = (char16_t) ret;
-        } else if (ret <= UNICODE_UPPER_LIMIT)  {
-            /* Create surrogate pairs */
-            /* See http://en.wikipedia.org/wiki/UTF-16/UCS-2#Method_for_code_points_in_Plane_1.2C_Plane_2 */
-
-            *dest++ = 0xd800 | ((ret - 0x10000) >> 10);
-            *dest++ = 0xdc00 | ((ret - 0x10000) &  0x3ff);
-        } else {
-            *dest++ = UTF16_REPLACEMENT_CHAR;
-        }
-    }
-
-    *out_len = dest - utf16Str;
-
-    return utf16Str;
-}
-
-/**
- * length is the number of characters in the UTF-8 string.
- * out_len is an out parameter (which may not be null) containing the
- * length of the UTF-16 string (which may contain embedded \0's)
- */
-
-extern char16_t * strcpylen8to16 (char16_t *utf16Str, const char*utf8Str,
-                                       int length, size_t *out_len)
-{
-    /* TODO: Share more of this code with the method above. Only 2 lines changed. */
-    
-    char16_t *dest = utf16Str;
-
-    const char *end = utf8Str + length; /* This line */
-    while (utf8Str < end) {             /* and this line changed. */
-        uint32_t ret;
-
-        ret = getUtf32FromUtf8(&utf8Str);
-
-        if (ret <= 0xffff) {
-            *dest++ = (char16_t) ret;
-        } else if (ret <= UNICODE_UPPER_LIMIT)  {
-            /* Create surrogate pairs */
-            /* See http://en.wikipedia.org/wiki/UTF-16/UCS-2#Method_for_code_points_in_Plane_1.2C_Plane_2 */
-
-            *dest++ = 0xd800 | ((ret - 0x10000) >> 10);
-            *dest++ = 0xdc00 | ((ret - 0x10000) &  0x3ff);
-        } else {
-            *dest++ = UTF16_REPLACEMENT_CHAR;
-        }
-    }
-
-    *out_len = dest - utf16Str;
-
-    return utf16Str;
-}
diff --git a/libcutils/strdup8to16.cpp b/libcutils/strdup8to16.cpp
new file mode 100644
index 0000000..d1e51b9
--- /dev/null
+++ b/libcutils/strdup8to16.cpp
@@ -0,0 +1,215 @@
+/* libs/cutils/strdup8to16.c
+**
+** Copyright 2006, 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 <cutils/jstring.h>
+
+#include <assert.h>
+#include <limits.h>
+#include <stdlib.h>
+
+/* See http://www.unicode.org/reports/tr22/ for discussion
+ * on invalid sequences
+ */
+
+#define UTF16_REPLACEMENT_CHAR 0xfffd
+
+/* Clever trick from Dianne that returns 1-4 depending on leading bit sequence*/
+#define UTF8_SEQ_LENGTH(ch) (((0xe5000000 >> (((ch) >> 3) & 0x1e)) & 3) + 1)
+
+/* note: macro expands to multiple lines */
+#define UTF8_SHIFT_AND_MASK(unicode, byte)  \
+            (unicode)<<=6; (unicode) |= (0x3f & (byte));
+
+#define UNICODE_UPPER_LIMIT 0x10fffd    
+
+/**
+ * out_len is an out parameter (which may not be null) containing the
+ * length of the UTF-16 string (which may contain embedded \0's)
+ */
+
+extern char16_t * strdup8to16 (const char* s, size_t *out_len)
+{
+    char16_t *ret;
+    size_t len;
+
+    if (s == NULL) return NULL;
+
+    len = strlen8to16(s);
+
+    // fail on overflow
+    if (len && SIZE_MAX/len < sizeof(char16_t))
+        return NULL;
+
+    // no plus-one here. UTF-16 strings are not null terminated
+    ret = (char16_t *) malloc (sizeof(char16_t) * len);
+
+    return strcpy8to16 (ret, s, out_len);
+}
+
+/**
+ * Like "strlen", but for strings encoded with Java's modified UTF-8.
+ *
+ * The value returned is the number of UTF-16 characters required
+ * to represent this string.
+ */
+extern size_t strlen8to16 (const char* utf8Str)
+{
+    size_t len = 0;
+    int ic;
+    int expected = 0;
+
+    while ((ic = *utf8Str++) != '\0') {
+        /* bytes that start 0? or 11 are lead bytes and count as characters.*/
+        /* bytes that start 10 are extention bytes and are not counted */
+         
+        if ((ic & 0xc0) == 0x80) {
+            /* count the 0x80 extention bytes. if we have more than
+             * expected, then start counting them because strcpy8to16
+             * will insert UTF16_REPLACEMENT_CHAR's
+             */
+            expected--;
+            if (expected < 0) {
+                len++;
+            }
+        } else {
+            len++;
+            expected = UTF8_SEQ_LENGTH(ic) - 1;
+
+            /* this will result in a surrogate pair */
+            if (expected == 3) {
+                len++;
+            }
+        }
+    }
+
+    return len;
+}
+
+
+
+/*
+ * Retrieve the next UTF-32 character from a UTF-8 string.
+ *
+ * Stops at inner \0's
+ *
+ * Returns UTF16_REPLACEMENT_CHAR if an invalid sequence is encountered
+ *
+ * Advances "*pUtf8Ptr" to the start of the next character.
+ */
+static inline uint32_t getUtf32FromUtf8(const char** pUtf8Ptr)
+{
+    uint32_t ret;
+    int seq_len;
+    int i;
+
+    /* Mask for leader byte for lengths 1, 2, 3, and 4 respectively*/
+    static const unsigned char leaderMask[4] = {0xff, 0x1f, 0x0f, 0x07};
+
+    /* Bytes that start with bits "10" are not leading characters. */
+    if (((**pUtf8Ptr) & 0xc0) == 0x80) {
+        (*pUtf8Ptr)++;
+        return UTF16_REPLACEMENT_CHAR;
+    }
+
+    /* note we tolerate invalid leader 11111xxx here */    
+    seq_len = UTF8_SEQ_LENGTH(**pUtf8Ptr);
+
+    ret = (**pUtf8Ptr) & leaderMask [seq_len - 1];
+
+    if (**pUtf8Ptr == '\0') return ret;
+
+    (*pUtf8Ptr)++;
+    for (i = 1; i < seq_len ; i++, (*pUtf8Ptr)++) {
+        if ((**pUtf8Ptr) == '\0') return UTF16_REPLACEMENT_CHAR;
+        if (((**pUtf8Ptr) & 0xc0) != 0x80) return UTF16_REPLACEMENT_CHAR;
+
+        UTF8_SHIFT_AND_MASK(ret, **pUtf8Ptr);
+    }
+
+    return ret;
+}
+
+
+/**
+ * out_len is an out parameter (which may not be null) containing the
+ * length of the UTF-16 string (which may contain embedded \0's)
+ */
+
+extern char16_t * strcpy8to16 (char16_t *utf16Str, const char*utf8Str, 
+                                       size_t *out_len)
+{   
+    char16_t *dest = utf16Str;
+
+    while (*utf8Str != '\0') {
+        uint32_t ret;
+
+        ret = getUtf32FromUtf8(&utf8Str);
+
+        if (ret <= 0xffff) {
+            *dest++ = (char16_t) ret;
+        } else if (ret <= UNICODE_UPPER_LIMIT)  {
+            /* Create surrogate pairs */
+            /* See http://en.wikipedia.org/wiki/UTF-16/UCS-2#Method_for_code_points_in_Plane_1.2C_Plane_2 */
+
+            *dest++ = 0xd800 | ((ret - 0x10000) >> 10);
+            *dest++ = 0xdc00 | ((ret - 0x10000) &  0x3ff);
+        } else {
+            *dest++ = UTF16_REPLACEMENT_CHAR;
+        }
+    }
+
+    *out_len = dest - utf16Str;
+
+    return utf16Str;
+}
+
+/**
+ * length is the number of characters in the UTF-8 string.
+ * out_len is an out parameter (which may not be null) containing the
+ * length of the UTF-16 string (which may contain embedded \0's)
+ */
+
+extern char16_t * strcpylen8to16 (char16_t *utf16Str, const char*utf8Str,
+                                       int length, size_t *out_len)
+{
+    /* TODO: Share more of this code with the method above. Only 2 lines changed. */
+    
+    char16_t *dest = utf16Str;
+
+    const char *end = utf8Str + length; /* This line */
+    while (utf8Str < end) {             /* and this line changed. */
+        uint32_t ret;
+
+        ret = getUtf32FromUtf8(&utf8Str);
+
+        if (ret <= 0xffff) {
+            *dest++ = (char16_t) ret;
+        } else if (ret <= UNICODE_UPPER_LIMIT)  {
+            /* Create surrogate pairs */
+            /* See http://en.wikipedia.org/wiki/UTF-16/UCS-2#Method_for_code_points_in_Plane_1.2C_Plane_2 */
+
+            *dest++ = 0xd800 | ((ret - 0x10000) >> 10);
+            *dest++ = 0xdc00 | ((ret - 0x10000) &  0x3ff);
+        } else {
+            *dest++ = UTF16_REPLACEMENT_CHAR;
+        }
+    }
+
+    *out_len = dest - utf16Str;
+
+    return utf16Str;
+}
diff --git a/libcutils/tests/AshmemTest.cpp b/libcutils/tests/AshmemTest.cpp
index a87e23e..b37d020 100644
--- a/libcutils/tests/AshmemTest.cpp
+++ b/libcutils/tests/AshmemTest.cpp
@@ -14,11 +14,18 @@
  * limitations under the License.
  */
 
+#include <errno.h>
+#include <linux/fs.h>
+#include <stdint.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <android-base/macros.h>
 #include <android-base/unique_fd.h>
 #include <cutils/ashmem.h>
 #include <gtest/gtest.h>
-#include <linux/fs.h>
-#include <sys/mman.h>
 
 using android::base::unique_fd;
 
@@ -31,15 +38,21 @@
 }
 
 void TestMmap(const unique_fd& fd, size_t size, int prot, void** region, off_t off = 0) {
+    ASSERT_TRUE(fd >= 0);
+    ASSERT_TRUE(ashmem_valid(fd));
     *region = mmap(nullptr, size, prot, MAP_SHARED, fd, off);
     ASSERT_NE(MAP_FAILED, *region);
 }
 
 void TestProtDenied(const unique_fd &fd, size_t size, int prot) {
+    ASSERT_TRUE(fd >= 0);
+    ASSERT_TRUE(ashmem_valid(fd));
     EXPECT_EQ(MAP_FAILED, mmap(nullptr, size, prot, MAP_SHARED, fd, 0));
 }
 
 void TestProtIs(const unique_fd& fd, int prot) {
+    ASSERT_TRUE(fd >= 0);
+    ASSERT_TRUE(ashmem_valid(fd));
     EXPECT_EQ(prot, ioctl(fd, ASHMEM_GET_PROT_MASK));
 }
 
@@ -86,18 +99,23 @@
     ASSERT_EQ(0, memcmp(region1, &data, size));
     EXPECT_EQ(0, munmap(region1, size));
 
-    ASSERT_EXIT({
-        void *region2 = mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
-        if (region2 == MAP_FAILED) {
-            _exit(1);
-        }
-        if (memcmp(region2, &data, size) != 0) {
-            _exit(2);
-        }
-        memset(region2, 0, size);
-        munmap(region2, size);
-        _exit(0);
-    }, ::testing::ExitedWithCode(0),"");
+    ASSERT_EXIT(
+        {
+            if (!ashmem_valid(fd)) {
+                _exit(3);
+            }
+            void* region2 = mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+            if (region2 == MAP_FAILED) {
+                _exit(1);
+            }
+            if (memcmp(region2, &data, size) != 0) {
+                _exit(2);
+            }
+            memset(region2, 0, size);
+            munmap(region2, size);
+            _exit(0);
+        },
+        ::testing::ExitedWithCode(0), "");
 
     memset(&data, 0, size);
     void *region2;
@@ -146,6 +164,7 @@
     };
     for (const auto& cfg : seeks) {
         errno = 0;
+        ASSERT_TRUE(ashmem_valid(fd));
         auto off = lseek(fd, cfg.offset, cfg.whence);
         ASSERT_EQ(cfg.ret, off) << "lseek(" << cfg.offset << ", " << cfg.whence << ") failed"
                                 << (errno ? ": " : "") << (errno ? strerror(errno) : "");
@@ -196,15 +215,19 @@
     constexpr size_t size = PAGE_SIZE;
 
     int protFlags[] = { PROT_READ, PROT_WRITE };
-    for (int i = 0; i < 2; i++) {
+    for (size_t i = 0; i < arraysize(protFlags); i++) {
         ASSERT_NO_FATAL_FAILURE(TestCreateRegion(size, fd, PROT_READ | PROT_WRITE));
-        ASSERT_EXIT({
-            if (ashmem_set_prot_region(fd, protFlags[i]) >= 0) {
-                _exit(0);
-            } else {
-                _exit(1);
-            }
-        }, ::testing::ExitedWithCode(0), "");
+        ASSERT_EXIT(
+            {
+                if (!ashmem_valid(fd)) {
+                    _exit(3);
+                } else if (ashmem_set_prot_region(fd, protFlags[i]) >= 0) {
+                    _exit(0);
+                } else {
+                    _exit(1);
+                }
+            },
+            ::testing::ExitedWithCode(0), "");
         ASSERT_NO_FATAL_FAILURE(TestProtDenied(fd, size, protFlags[1-i]));
     }
 }
@@ -227,6 +250,9 @@
 
     ASSERT_EXIT({
         for (int i = 0; i < nRegions; i++) {
+            if (!ashmem_valid(fd[i])) {
+                _exit(3);
+            }
             void *region = mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd[i], 0);
             if (region == MAP_FAILED) {
                 _exit(1);
diff --git a/libcutils/tests/fs_config.cpp b/libcutils/tests/fs_config.cpp
index 391adb6..d5dc66a 100644
--- a/libcutils/tests/fs_config.cpp
+++ b/libcutils/tests/fs_config.cpp
@@ -81,7 +81,7 @@
         }
 
         // check if path is <partition>/
-        if (android::base::StartsWith(path, prefix.c_str())) {
+        if (android::base::StartsWith(path, prefix)) {
             // rebuild path to be system/<partition>/... to check for alias
             path = alternate + path.substr(prefix.size());
             for (second = 0; second < paths.size(); ++second) {
@@ -97,7 +97,7 @@
         }
 
         // check if path is system/<partition>/
-        if (android::base::StartsWith(path, alternate.c_str())) {
+        if (android::base::StartsWith(path, alternate)) {
             // rebuild path to be <partition>/... to check for alias
             path = prefix + path.substr(alternate.size());
             for (second = 0; second < paths.size(); ++second) {
diff --git a/libcutils/tests/multiuser_test.cpp b/libcutils/tests/multiuser_test.cpp
index 2f9d854..4b0fd13 100644
--- a/libcutils/tests/multiuser_test.cpp
+++ b/libcutils/tests/multiuser_test.cpp
@@ -57,7 +57,10 @@
     EXPECT_EQ(ERR_GID, multiuser_get_cache_gid(0, 1000));
     EXPECT_EQ(20000U, multiuser_get_cache_gid(0, 10000));
     EXPECT_EQ(ERR_GID, multiuser_get_cache_gid(0, 50000));
+    EXPECT_EQ(ERR_GID, multiuser_get_cache_gid(10, 0));
+    EXPECT_EQ(ERR_GID, multiuser_get_cache_gid(10, 1000));
     EXPECT_EQ(1020000U, multiuser_get_cache_gid(10, 10000));
+    EXPECT_EQ(ERR_GID, multiuser_get_cache_gid(10, 50000));
 }
 
 TEST(MultiuserTest, TestExt) {
@@ -77,9 +80,12 @@
 }
 
 TEST(MultiuserTest, TestShared) {
-    EXPECT_EQ(ERR_GID, multiuser_get_shared_gid(0, 0));
-    EXPECT_EQ(ERR_GID, multiuser_get_shared_gid(0, 1000));
+    EXPECT_EQ(0U, multiuser_get_shared_gid(0, 0));
+    EXPECT_EQ(1000U, multiuser_get_shared_gid(0, 1000));
     EXPECT_EQ(50000U, multiuser_get_shared_gid(0, 10000));
     EXPECT_EQ(ERR_GID, multiuser_get_shared_gid(0, 50000));
-    EXPECT_EQ(1050000U, multiuser_get_shared_gid(10, 10000));
+    EXPECT_EQ(0U, multiuser_get_shared_gid(10, 0));
+    EXPECT_EQ(1000U, multiuser_get_shared_gid(10, 1000));
+    EXPECT_EQ(50000U, multiuser_get_shared_gid(10, 10000));
+    EXPECT_EQ(ERR_GID, multiuser_get_shared_gid(10, 50000));
 }
diff --git a/libcutils/tests/sched_policy_test.cpp b/libcutils/tests/sched_policy_test.cpp
index 173174a..5942ee5 100644
--- a/libcutils/tests/sched_policy_test.cpp
+++ b/libcutils/tests/sched_policy_test.cpp
@@ -60,6 +60,12 @@
     return sleepTimes[median];
 }
 
+static void AssertPolicy(SchedPolicy expected_policy) {
+    SchedPolicy current_policy;
+    ASSERT_EQ(0, get_sched_policy(0, &current_policy));
+    EXPECT_EQ(expected_policy, current_policy);
+}
+
 TEST(SchedPolicy, set_sched_policy) {
     if (!hasCapSysNice()) {
         GTEST_LOG_(INFO) << "skipping test that requires CAP_SYS_NICE";
@@ -76,23 +82,17 @@
     const unsigned int BG_FG_SLACK_FACTOR = 100;
 
     ASSERT_EQ(0, set_sched_policy(0, SP_BACKGROUND));
+    AssertPolicy(SP_BACKGROUND);
     auto bgSleepTime = medianSleepTime();
 
     ASSERT_EQ(0, set_sched_policy(0, SP_FOREGROUND));
+    AssertPolicy(SP_FOREGROUND);
     auto fgSleepTime = medianSleepTime();
     ASSERT_GT(bgSleepTime, fgSleepTime * BG_FG_SLACK_FACTOR);
 }
 
-TEST(SchedPolicy, get_sched_policy) {
-    SchedPolicy policy;
-    ASSERT_EQ(0, get_sched_policy(0, &policy));
-
-    const char *policyName = get_sched_policy_name(policy);
-    EXPECT_NE(nullptr, policyName);
-    EXPECT_STRNE("error", policyName);
-
-    ASSERT_EQ(0, set_sched_policy(0, SP_BACKGROUND));
-    SchedPolicy newPolicy;
-    ASSERT_EQ(0, get_sched_policy(0, &newPolicy));
-    EXPECT_EQ(SP_BACKGROUND, newPolicy);
+TEST(SchedPolicy, get_sched_policy_name) {
+    EXPECT_STREQ("bg", get_sched_policy_name(SP_BACKGROUND));
+    EXPECT_STREQ("error", get_sched_policy_name(SchedPolicy(-2)));
+    EXPECT_STREQ("error", get_sched_policy_name(SP_CNT));
 }
diff --git a/libcutils/tests/trace-dev_test.cpp b/libcutils/tests/trace-dev_test.cpp
index edf981b..f8d4f00 100644
--- a/libcutils/tests/trace-dev_test.cpp
+++ b/libcutils/tests/trace-dev_test.cpp
@@ -25,7 +25,7 @@
 #include <android-base/test_utils.h>
 #include <gtest/gtest.h>
 
-#include "../trace-dev.c"
+#include "../trace-dev.cpp"
 
 class TraceDevTest : public ::testing::Test {
  protected:
diff --git a/libcutils/threads.c b/libcutils/threads.c
deleted file mode 100644
index 036f8c5..0000000
--- a/libcutils/threads.c
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
-** Copyright (C) 2007, 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 "cutils/threads.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
-
-// No definition needed for Android because we'll just pick up bionic's copy.
-#ifndef __ANDROID__
-pid_t gettid() {
-#if defined(__APPLE__)
-  return syscall(SYS_thread_selfid);
-#elif defined(__linux__)
-  return syscall(__NR_gettid);
-#elif defined(_WIN32)
-  return GetCurrentThreadId();
-#endif
-}
-#endif  // __ANDROID__
-
-#if !defined(_WIN32)
-
-void*  thread_store_get( thread_store_t*  store )
-{
-    if (!store->has_tls)
-        return NULL;
-
-    return pthread_getspecific( store->tls );
-}
-
-extern void   thread_store_set( thread_store_t*          store,
-                                void*                    value,
-                                thread_store_destruct_t  destroy)
-{
-    pthread_mutex_lock( &store->lock );
-    if (!store->has_tls) {
-        if (pthread_key_create( &store->tls, destroy) != 0) {
-            pthread_mutex_unlock(&store->lock);
-            return;
-        }
-        store->has_tls = 1;
-    }
-    pthread_mutex_unlock( &store->lock );
-
-    pthread_setspecific( store->tls, value );
-}
-
-#else /* !defined(_WIN32) */
-void*  thread_store_get( thread_store_t*  store )
-{
-    if (!store->has_tls)
-        return NULL;
-
-    return (void*) TlsGetValue( store->tls );
-}
-
-void   thread_store_set( thread_store_t*          store,
-                         void*                    value,
-                         thread_store_destruct_t  destroy )
-{
-    /* XXX: can't use destructor on thread exit */
-    if (!store->lock_init) {
-        store->lock_init = -1;
-        InitializeCriticalSection( &store->lock );
-        store->lock_init = -2;
-    } else while (store->lock_init != -2) {
-        Sleep(10); /* 10ms */
-    }
-
-    EnterCriticalSection( &store->lock );
-    if (!store->has_tls) {
-        store->tls = TlsAlloc();
-        if (store->tls == TLS_OUT_OF_INDEXES) {
-            LeaveCriticalSection( &store->lock );
-            return;
-        }
-        store->has_tls = 1;
-    }
-    LeaveCriticalSection( &store->lock );
-
-    TlsSetValue( store->tls, value );
-}
-#endif /* !defined(_WIN32) */
diff --git a/libcutils/threads.cpp b/libcutils/threads.cpp
new file mode 100644
index 0000000..a7e6b2d
--- /dev/null
+++ b/libcutils/threads.cpp
@@ -0,0 +1,111 @@
+/*
+** Copyright (C) 2007, 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 <cutils/threads.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
+
+// No definition needed for Android because we'll just pick up bionic's copy.
+#ifndef __ANDROID__
+pid_t gettid() {
+#if 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
+}
+#endif  // __ANDROID__
+
+#if !defined(_WIN32)
+
+void*  thread_store_get( thread_store_t*  store )
+{
+    if (!store->has_tls)
+        return NULL;
+
+    return pthread_getspecific( store->tls );
+}
+
+extern void   thread_store_set( thread_store_t*          store,
+                                void*                    value,
+                                thread_store_destruct_t  destroy)
+{
+    pthread_mutex_lock( &store->lock );
+    if (!store->has_tls) {
+        if (pthread_key_create( &store->tls, destroy) != 0) {
+            pthread_mutex_unlock(&store->lock);
+            return;
+        }
+        store->has_tls = 1;
+    }
+    pthread_mutex_unlock( &store->lock );
+
+    pthread_setspecific( store->tls, value );
+}
+
+#else /* !defined(_WIN32) */
+void*  thread_store_get( thread_store_t*  store )
+{
+    if (!store->has_tls)
+        return NULL;
+
+    return (void*) TlsGetValue( store->tls );
+}
+
+void   thread_store_set( thread_store_t*          store,
+                         void*                    value,
+                         thread_store_destruct_t  /*destroy*/ )
+{
+    /* XXX: can't use destructor on thread exit */
+    if (!store->lock_init) {
+        store->lock_init = -1;
+        InitializeCriticalSection( &store->lock );
+        store->lock_init = -2;
+    } else while (store->lock_init != -2) {
+        Sleep(10); /* 10ms */
+    }
+
+    EnterCriticalSection( &store->lock );
+    if (!store->has_tls) {
+        store->tls = TlsAlloc();
+        if (store->tls == TLS_OUT_OF_INDEXES) {
+            LeaveCriticalSection( &store->lock );
+            return;
+        }
+        store->has_tls = 1;
+    }
+    LeaveCriticalSection( &store->lock );
+
+    TlsSetValue( store->tls, value );
+}
+#endif /* !defined(_WIN32) */
diff --git a/libcutils/trace-container.cpp b/libcutils/trace-container.cpp
new file mode 100644
index 0000000..d981f8f
--- /dev/null
+++ b/libcutils/trace-container.cpp
@@ -0,0 +1,233 @@
+/*
+ * Copyright (C) 2017 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 <cutils/trace.h>
+
+#include "trace-dev.inc"
+
+#include <cutils/sockets.h>
+#include <sys/stat.h>
+#include <time.h>
+
+/**
+ * For tracing in container, tags are written into a socket
+ * instead of ftrace. Additional data is appended so we need extra space.
+ */
+#define CONTAINER_ATRACE_MESSAGE_LENGTH (ATRACE_MESSAGE_LENGTH + 512)
+
+static pthread_once_t atrace_once_control = PTHREAD_ONCE_INIT;
+
+// Variables used for tracing in container with socket.
+// Note that we need to manually close and reopen socket when Zygote is forking. This requires
+// writing and closing sockets on multiple threads. A rwlock is used for avoiding concurrent
+// operation on the file descriptor.
+static bool             atrace_use_container_sock    = false;
+static int              atrace_container_sock_fd     = -1;
+static pthread_mutex_t  atrace_enabling_mutex        = PTHREAD_MUTEX_INITIALIZER;
+static pthread_rwlock_t atrace_container_sock_rwlock = PTHREAD_RWLOCK_INITIALIZER;
+
+static bool atrace_init_container_sock()
+{
+    pthread_rwlock_wrlock(&atrace_container_sock_rwlock);
+    atrace_container_sock_fd =
+        socket_local_client("trace", ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_SEQPACKET);
+    if (atrace_container_sock_fd < 0) {
+        ALOGE("Error opening container trace socket: %s (%d)", strerror(errno), errno);
+    }
+    pthread_rwlock_unlock(&atrace_container_sock_rwlock);
+    return atrace_container_sock_fd != -1;
+}
+
+static void atrace_close_container_sock()
+{
+    pthread_rwlock_wrlock(&atrace_container_sock_rwlock);
+    if (atrace_container_sock_fd != -1) close(atrace_container_sock_fd);
+    atrace_container_sock_fd = -1;
+    pthread_rwlock_unlock(&atrace_container_sock_rwlock);
+}
+
+// Set whether tracing is enabled in this process.  This is used to prevent
+// the Zygote process from tracing.  We need to close the socket in the container when tracing is
+// disabled, and reopen it again after Zygote forking.
+void atrace_set_tracing_enabled(bool enabled)
+{
+    pthread_mutex_lock(&atrace_enabling_mutex);
+    if (atrace_use_container_sock) {
+        bool already_enabled = atomic_load_explicit(&atrace_is_enabled, memory_order_acquire);
+        if (enabled && !already_enabled) {
+            // Trace was disabled previously. Re-initialize container socket.
+            atrace_init_container_sock();
+        } else if (!enabled && already_enabled) {
+            // Trace was enabled previously. Close container socket.
+            atrace_close_container_sock();
+        }
+    }
+    atomic_store_explicit(&atrace_is_enabled, enabled, memory_order_release);
+    pthread_mutex_unlock(&atrace_enabling_mutex);
+    atrace_update_tags();
+}
+
+static void atrace_init_once()
+{
+    atrace_marker_fd = open("/sys/kernel/debug/tracing/trace_marker", O_WRONLY | O_CLOEXEC);
+    if (atrace_marker_fd < 0) {
+        // We're in container, ftrace may be disabled. In such case, we use the
+        // socket to write trace event.
+
+        // Protect the initialization of container socket from
+        // atrace_set_tracing_enabled.
+        pthread_mutex_lock(&atrace_enabling_mutex);
+        atrace_use_container_sock = true;
+        bool success = false;
+        if (atomic_load_explicit(&atrace_is_enabled, memory_order_acquire)) {
+            success = atrace_init_container_sock();
+        }
+        pthread_mutex_unlock(&atrace_enabling_mutex);
+
+        if (!success) {
+            atrace_enabled_tags = 0;
+            goto done;
+        }
+    }
+    atrace_enabled_tags = atrace_get_property();
+
+done:
+    atomic_store_explicit(&atrace_is_ready, true, memory_order_release);
+}
+
+void atrace_setup()
+{
+    pthread_once(&atrace_once_control, atrace_init_once);
+}
+
+static inline uint64_t gettime(clockid_t clk_id)
+{
+    struct timespec ts;
+    clock_gettime(clk_id, &ts);
+    return ts.tv_sec * 1000000 + ts.tv_nsec / 1000;
+}
+
+// Write trace events to container trace file. Note that we need to amend tid and time information
+// here comparing to normal ftrace, where those informations are added by kernel.
+#define WRITE_MSG_IN_CONTAINER_LOCKED(ph, sep_before_name, value_format, name, value) { \
+    char buf[CONTAINER_ATRACE_MESSAGE_LENGTH]; \
+    int pid = getpid(); \
+    int tid = gettid(); \
+    uint64_t ts = gettime(CLOCK_MONOTONIC); \
+    uint64_t tts = gettime(CLOCK_THREAD_CPUTIME_ID); \
+    int len = snprintf( \
+            buf, sizeof(buf), \
+            ph "|%d|%d|%" PRIu64 "|%" PRIu64 sep_before_name "%s" value_format, \
+            pid, tid, ts, tts, name, value); \
+    if (len >= (int) sizeof(buf)) { \
+        int name_len = strlen(name) - (len - sizeof(buf)) - 1; \
+        /* Truncate the name to make the message fit. */ \
+        if (name_len > 0) { \
+            ALOGW("Truncated name in %s: %s\n", __FUNCTION__, name); \
+            len = snprintf( \
+                    buf, sizeof(buf), \
+                    ph "|%d|%d|%" PRIu64 "|%" PRIu64 sep_before_name "%.*s" value_format, \
+                    pid, tid, ts, tts, name_len, name, value); \
+        } else { \
+            /* Data is still too long. Drop it. */ \
+            ALOGW("Data is too long in %s: %s\n", __FUNCTION__, name); \
+            len = 0; \
+        } \
+    } \
+    if (len > 0) { \
+        write(atrace_container_sock_fd, buf, len); \
+    } \
+}
+
+#define WRITE_MSG_IN_CONTAINER(ph, sep_before_name, value_format, name, value) { \
+    pthread_rwlock_rdlock(&atrace_container_sock_rwlock); \
+    if (atrace_container_sock_fd != -1) { \
+       WRITE_MSG_IN_CONTAINER_LOCKED(ph, sep_before_name, value_format, name, value); \
+    } \
+    pthread_rwlock_unlock(&atrace_container_sock_rwlock); \
+}
+
+void atrace_begin_body(const char* name)
+{
+    if (CC_LIKELY(atrace_use_container_sock)) {
+        WRITE_MSG_IN_CONTAINER("B", "|", "%s", name, "");
+        return;
+    }
+
+    if (atrace_marker_fd < 0) return;
+
+    WRITE_MSG("B|%d|", "%s", name, "");
+}
+
+void atrace_end_body()
+{
+    if (CC_LIKELY(atrace_use_container_sock)) {
+        WRITE_MSG_IN_CONTAINER("E", "", "%s", "", "");
+        return;
+    }
+
+    if (atrace_marker_fd < 0) return;
+
+    WRITE_MSG("E|%d", "%s", "", "");
+}
+
+void atrace_async_begin_body(const char* name, int32_t cookie)
+{
+    if (CC_LIKELY(atrace_use_container_sock)) {
+        WRITE_MSG_IN_CONTAINER("S", "|", "|%d", name, cookie);
+        return;
+    }
+
+    if (atrace_marker_fd < 0) return;
+
+    WRITE_MSG("S|%d|", "|%" PRId32, name, cookie);
+}
+
+void atrace_async_end_body(const char* name, int32_t cookie)
+{
+    if (CC_LIKELY(atrace_use_container_sock)) {
+        WRITE_MSG_IN_CONTAINER("F", "|", "|%d", name, cookie);
+        return;
+    }
+
+    if (atrace_marker_fd < 0) return;
+
+    WRITE_MSG("F|%d|", "|%" PRId32, name, cookie);
+}
+
+void atrace_int_body(const char* name, int32_t value)
+{
+    if (CC_LIKELY(atrace_use_container_sock)) {
+        WRITE_MSG_IN_CONTAINER("C", "|", "|%" PRId32, name, value);
+        return;
+    }
+
+    if (atrace_marker_fd < 0) return;
+
+    WRITE_MSG("C|%d|", "|%" PRId32, name, value);
+}
+
+void atrace_int64_body(const char* name, int64_t value)
+{
+    if (CC_LIKELY(atrace_use_container_sock)) {
+        WRITE_MSG_IN_CONTAINER("C", "|", "|%" PRId64, name, value);
+        return;
+    }
+
+    if (atrace_marker_fd < 0) return;
+
+    WRITE_MSG("C|%d|", "|%" PRId64, name, value);
+}
diff --git a/libcutils/trace-dev.c b/libcutils/trace-dev.c
deleted file mode 100644
index d45e5a9..0000000
--- a/libcutils/trace-dev.c
+++ /dev/null
@@ -1,234 +0,0 @@
-/*
- * Copyright (C) 2012 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.
- */
-
-#define LOG_TAG "cutils-trace"
-
-#include <errno.h>
-#include <fcntl.h>
-#include <limits.h>
-#include <pthread.h>
-#include <stdatomic.h>
-#include <stdbool.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/types.h>
-
-#include <cutils/compiler.h>
-#include <cutils/properties.h>
-#include <cutils/trace.h>
-#include <log/log.h>
-#include <log/log_properties.h>
-
-/**
- * Maximum size of a message that can be logged to the trace buffer.
- * Note this message includes a tag, the pid, and the string given as the name.
- * Names should be kept short to get the most use of the trace buffer.
- */
-#define ATRACE_MESSAGE_LENGTH 1024
-
-atomic_bool             atrace_is_ready      = ATOMIC_VAR_INIT(false);
-int                     atrace_marker_fd     = -1;
-uint64_t                atrace_enabled_tags  = ATRACE_TAG_NOT_READY;
-static bool             atrace_is_debuggable = false;
-static atomic_bool      atrace_is_enabled    = ATOMIC_VAR_INIT(true);
-static pthread_once_t   atrace_once_control  = PTHREAD_ONCE_INIT;
-static pthread_mutex_t  atrace_tags_mutex    = PTHREAD_MUTEX_INITIALIZER;
-
-// Set whether this process is debuggable, which determines whether
-// application-level tracing is allowed when the ro.debuggable system property
-// is not set to '1'.
-void atrace_set_debuggable(bool debuggable)
-{
-    atrace_is_debuggable = debuggable;
-    atrace_update_tags();
-}
-
-// Set whether tracing is enabled in this process.  This is used to prevent
-// the Zygote process from tracing.
-void atrace_set_tracing_enabled(bool enabled)
-{
-    atomic_store_explicit(&atrace_is_enabled, enabled, memory_order_release);
-    atrace_update_tags();
-}
-
-// Check whether the given command line matches one of the comma-separated
-// values listed in the app_cmdlines property.
-static bool atrace_is_cmdline_match(const char* cmdline)
-{
-    int count = property_get_int32("debug.atrace.app_number", 0);
-
-    char buf[PROPERTY_KEY_MAX];
-    char value[PROPERTY_VALUE_MAX];
-
-    for (int i = 0; i < count; i++) {
-        snprintf(buf, sizeof(buf), "debug.atrace.app_%d", i);
-        property_get(buf, value, "");
-        if (strcmp(value, cmdline) == 0) {
-            return true;
-        }
-    }
-
-    return false;
-}
-
-// Determine whether application-level tracing is enabled for this process.
-static bool atrace_is_app_tracing_enabled()
-{
-    bool sys_debuggable = __android_log_is_debuggable();
-    bool result = false;
-
-    if (sys_debuggable || atrace_is_debuggable) {
-        // Check whether tracing is enabled for this process.
-        FILE * file = fopen("/proc/self/cmdline", "re");
-        if (file) {
-            char cmdline[4096];
-            if (fgets(cmdline, sizeof(cmdline), file)) {
-                result = atrace_is_cmdline_match(cmdline);
-            } else {
-                ALOGE("Error reading cmdline: %s (%d)", strerror(errno), errno);
-            }
-            fclose(file);
-        } else {
-            ALOGE("Error opening /proc/self/cmdline: %s (%d)", strerror(errno),
-                    errno);
-        }
-    }
-
-    return result;
-}
-
-// Read the sysprop and return the value tags should be set to
-static uint64_t atrace_get_property()
-{
-    char value[PROPERTY_VALUE_MAX];
-    char *endptr;
-    uint64_t tags;
-
-    property_get("debug.atrace.tags.enableflags", value, "0");
-    errno = 0;
-    tags = strtoull(value, &endptr, 0);
-    if (value[0] == '\0' || *endptr != '\0') {
-        ALOGE("Error parsing trace property: Not a number: %s", value);
-        return 0;
-    } else if (errno == ERANGE || tags == ULLONG_MAX) {
-        ALOGE("Error parsing trace property: Number too large: %s", value);
-        return 0;
-    }
-
-    // Only set the "app" tag if this process was selected for app-level debug
-    // tracing.
-    if (atrace_is_app_tracing_enabled()) {
-        tags |= ATRACE_TAG_APP;
-    } else {
-        tags &= ~ATRACE_TAG_APP;
-    }
-
-    return (tags | ATRACE_TAG_ALWAYS) & ATRACE_TAG_VALID_MASK;
-}
-
-// Update tags if tracing is ready. Useful as a sysprop change callback.
-void atrace_update_tags()
-{
-    uint64_t tags;
-    if (CC_UNLIKELY(atomic_load_explicit(&atrace_is_ready, memory_order_acquire))) {
-        if (atomic_load_explicit(&atrace_is_enabled, memory_order_acquire)) {
-            tags = atrace_get_property();
-            pthread_mutex_lock(&atrace_tags_mutex);
-            atrace_enabled_tags = tags;
-            pthread_mutex_unlock(&atrace_tags_mutex);
-        } else {
-            // Tracing is disabled for this process, so we simply don't
-            // initialize the tags.
-            pthread_mutex_lock(&atrace_tags_mutex);
-            atrace_enabled_tags = ATRACE_TAG_NOT_READY;
-            pthread_mutex_unlock(&atrace_tags_mutex);
-        }
-    }
-}
-
-static void atrace_init_once()
-{
-    atrace_marker_fd = open("/sys/kernel/debug/tracing/trace_marker", O_WRONLY | O_CLOEXEC);
-    if (atrace_marker_fd == -1) {
-        ALOGE("Error opening trace file: %s (%d)", strerror(errno), errno);
-        atrace_enabled_tags = 0;
-        goto done;
-    }
-
-    atrace_enabled_tags = atrace_get_property();
-
-done:
-    atomic_store_explicit(&atrace_is_ready, true, memory_order_release);
-}
-
-void atrace_setup()
-{
-    pthread_once(&atrace_once_control, atrace_init_once);
-}
-
-void atrace_begin_body(const char* name)
-{
-    char buf[ATRACE_MESSAGE_LENGTH];
-
-    int len = snprintf(buf, sizeof(buf), "B|%d|%s", getpid(), name);
-    if (len >= (int) sizeof(buf)) {
-        ALOGW("Truncated name in %s: %s\n", __FUNCTION__, name);
-        len = sizeof(buf) - 1;
-    }
-    write(atrace_marker_fd, buf, len);
-}
-
-void atrace_end_body()
-{
-    char c = 'E';
-    write(atrace_marker_fd, &c, 1);
-}
-
-#define WRITE_MSG(format_begin, format_end, pid, name, value) { \
-    char buf[ATRACE_MESSAGE_LENGTH]; \
-    int len = snprintf(buf, sizeof(buf), format_begin "%s" format_end, pid, \
-        name, value); \
-    if (len >= (int) sizeof(buf)) { \
-        /* Given the sizeof(buf), and all of the current format buffers, \
-         * it is impossible for name_len to be < 0 if len >= sizeof(buf). */ \
-        int name_len = strlen(name) - (len - sizeof(buf)) - 1; \
-        /* Truncate the name to make the message fit. */ \
-        ALOGW("Truncated name in %s: %s\n", __FUNCTION__, name); \
-        len = snprintf(buf, sizeof(buf), format_begin "%.*s" format_end, pid, \
-            name_len, name, value); \
-    } \
-    write(atrace_marker_fd, buf, len); \
-}
-
-void atrace_async_begin_body(const char* name, int32_t cookie)
-{
-    WRITE_MSG("S|%d|", "|%" PRId32, getpid(), name, cookie);
-}
-
-void atrace_async_end_body(const char* name, int32_t cookie)
-{
-    WRITE_MSG("F|%d|", "|%" PRId32, getpid(), name, cookie);
-}
-
-void atrace_int_body(const char* name, int32_t value)
-{
-    WRITE_MSG("C|%d|", "|%" PRId32, getpid(), name, value);
-}
-
-void atrace_int64_body(const char* name, int64_t value)
-{
-    WRITE_MSG("C|%d|", "|%" PRId64, getpid(), name, value);
-}
diff --git a/libcutils/trace-dev.cpp b/libcutils/trace-dev.cpp
new file mode 100644
index 0000000..4da8215
--- /dev/null
+++ b/libcutils/trace-dev.cpp
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2012 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 <cutils/trace.h>
+
+#include "trace-dev.inc"
+
+static pthread_once_t atrace_once_control = PTHREAD_ONCE_INIT;
+
+// Set whether tracing is enabled in this process.  This is used to prevent
+// the Zygote process from tracing.
+void atrace_set_tracing_enabled(bool enabled)
+{
+    atomic_store_explicit(&atrace_is_enabled, enabled, memory_order_release);
+    atrace_update_tags();
+}
+
+static void atrace_init_once()
+{
+    atrace_marker_fd = open("/sys/kernel/debug/tracing/trace_marker", O_WRONLY | O_CLOEXEC);
+    if (atrace_marker_fd == -1) {
+        ALOGE("Error opening trace file: %s (%d)", strerror(errno), errno);
+        atrace_enabled_tags = 0;
+        goto done;
+    }
+
+    atrace_enabled_tags = atrace_get_property();
+
+done:
+    atomic_store_explicit(&atrace_is_ready, true, memory_order_release);
+}
+
+void atrace_setup()
+{
+    pthread_once(&atrace_once_control, atrace_init_once);
+}
+
+void atrace_begin_body(const char* name)
+{
+    WRITE_MSG("B|%d|", "%s", name, "");
+}
+
+void atrace_end_body()
+{
+    WRITE_MSG("E|%d", "%s", "", "");
+}
+
+void atrace_async_begin_body(const char* name, int32_t cookie)
+{
+    WRITE_MSG("S|%d|", "|%" PRId32, name, cookie);
+}
+
+void atrace_async_end_body(const char* name, int32_t cookie)
+{
+    WRITE_MSG("F|%d|", "|%" PRId32, name, cookie);
+}
+
+void atrace_int_body(const char* name, int32_t value)
+{
+    WRITE_MSG("C|%d|", "|%" PRId32, name, value);
+}
+
+void atrace_int64_body(const char* name, int64_t value)
+{
+    WRITE_MSG("C|%d|", "|%" PRId64, name, value);
+}
diff --git a/libcutils/trace-dev.inc b/libcutils/trace-dev.inc
new file mode 100644
index 0000000..c9580af
--- /dev/null
+++ b/libcutils/trace-dev.inc
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2017 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 __TRACE_DEV_INC
+#define __TRACE_DEV_INC
+
+#define LOG_TAG "cutils-trace"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <pthread.h>
+#include <stdatomic.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+
+#include <cutils/compiler.h>
+#include <cutils/properties.h>
+#include <cutils/trace.h>
+#include <log/log.h>
+#include <log/log_properties.h>
+
+/**
+ * Maximum size of a message that can be logged to the trace buffer.
+ * Note this message includes a tag, the pid, and the string given as the name.
+ * Names should be kept short to get the most use of the trace buffer.
+ */
+#define ATRACE_MESSAGE_LENGTH 1024
+
+atomic_bool             atrace_is_ready      = ATOMIC_VAR_INIT(false);
+int                     atrace_marker_fd     = -1;
+uint64_t                atrace_enabled_tags  = ATRACE_TAG_NOT_READY;
+static bool             atrace_is_debuggable = false;
+static atomic_bool      atrace_is_enabled    = ATOMIC_VAR_INIT(true);
+static pthread_mutex_t  atrace_tags_mutex    = PTHREAD_MUTEX_INITIALIZER;
+
+// Set whether this process is debuggable, which determines whether
+// application-level tracing is allowed when the ro.debuggable system property
+// is not set to '1'.
+void atrace_set_debuggable(bool debuggable)
+{
+    atrace_is_debuggable = debuggable;
+    atrace_update_tags();
+}
+
+// Check whether the given command line matches one of the comma-separated
+// values listed in the app_cmdlines property.
+static bool atrace_is_cmdline_match(const char* cmdline)
+{
+    int count = property_get_int32("debug.atrace.app_number", 0);
+
+    char buf[PROPERTY_KEY_MAX];
+    char value[PROPERTY_VALUE_MAX];
+
+    for (int i = 0; i < count; i++) {
+        snprintf(buf, sizeof(buf), "debug.atrace.app_%d", i);
+        property_get(buf, value, "");
+        if (strcmp(value, "*") == 0 || strcmp(value, cmdline) == 0) {
+            return true;
+        }
+    }
+
+    return false;
+}
+
+// Determine whether application-level tracing is enabled for this process.
+static bool atrace_is_app_tracing_enabled()
+{
+    bool sys_debuggable = __android_log_is_debuggable();
+    bool result = false;
+
+    if (sys_debuggable || atrace_is_debuggable) {
+        // Check whether tracing is enabled for this process.
+        FILE * file = fopen("/proc/self/cmdline", "re");
+        if (file) {
+            char cmdline[4096];
+            if (fgets(cmdline, sizeof(cmdline), file)) {
+                result = atrace_is_cmdline_match(cmdline);
+            } else {
+                ALOGE("Error reading cmdline: %s (%d)", strerror(errno), errno);
+            }
+            fclose(file);
+        } else {
+            ALOGE("Error opening /proc/self/cmdline: %s (%d)", strerror(errno),
+                    errno);
+        }
+    }
+
+    return result;
+}
+
+// Read the sysprop and return the value tags should be set to
+static uint64_t atrace_get_property()
+{
+    char value[PROPERTY_VALUE_MAX];
+    char *endptr;
+    uint64_t tags;
+
+    property_get("debug.atrace.tags.enableflags", value, "0");
+    errno = 0;
+    tags = strtoull(value, &endptr, 0);
+    if (value[0] == '\0' || *endptr != '\0') {
+        ALOGE("Error parsing trace property: Not a number: %s", value);
+        return 0;
+    } else if (errno == ERANGE || tags == ULLONG_MAX) {
+        ALOGE("Error parsing trace property: Number too large: %s", value);
+        return 0;
+    }
+
+    // Only set the "app" tag if this process was selected for app-level debug
+    // tracing.
+    if (atrace_is_app_tracing_enabled()) {
+        tags |= ATRACE_TAG_APP;
+    } else {
+        tags &= ~ATRACE_TAG_APP;
+    }
+
+    return (tags | ATRACE_TAG_ALWAYS) & ATRACE_TAG_VALID_MASK;
+}
+
+// Update tags if tracing is ready. Useful as a sysprop change callback.
+void atrace_update_tags()
+{
+    uint64_t tags;
+    if (CC_UNLIKELY(atomic_load_explicit(&atrace_is_ready, memory_order_acquire))) {
+        if (atomic_load_explicit(&atrace_is_enabled, memory_order_acquire)) {
+            tags = atrace_get_property();
+            pthread_mutex_lock(&atrace_tags_mutex);
+            atrace_enabled_tags = tags;
+            pthread_mutex_unlock(&atrace_tags_mutex);
+        } else {
+            // Tracing is disabled for this process, so we simply don't
+            // initialize the tags.
+            pthread_mutex_lock(&atrace_tags_mutex);
+            atrace_enabled_tags = ATRACE_TAG_NOT_READY;
+            pthread_mutex_unlock(&atrace_tags_mutex);
+        }
+    }
+}
+
+#define WRITE_MSG(format_begin, format_end, name, value) { \
+    char buf[ATRACE_MESSAGE_LENGTH]; \
+    int pid = getpid(); \
+    int len = snprintf(buf, sizeof(buf), format_begin "%s" format_end, pid, \
+        name, value); \
+    if (len >= (int) sizeof(buf)) { \
+        /* Given the sizeof(buf), and all of the current format buffers, \
+         * it is impossible for name_len to be < 0 if len >= sizeof(buf). */ \
+        int name_len = strlen(name) - (len - sizeof(buf)) - 1; \
+        /* Truncate the name to make the message fit. */ \
+        ALOGW("Truncated name in %s: %s\n", __FUNCTION__, name); \
+        len = snprintf(buf, sizeof(buf), format_begin "%.*s" format_end, pid, \
+            name_len, name, value); \
+    } \
+    write(atrace_marker_fd, buf, len); \
+}
+
+#endif  // __TRACE_DEV_INC
diff --git a/libcutils/trace-host.c b/libcutils/trace-host.c
deleted file mode 100644
index 05842cd..0000000
--- a/libcutils/trace-host.c
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright (C) 2012 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 <cutils/trace.h>
-
-#ifndef __unused
-#define __unused __attribute__((__unused__))
-#endif
-
-atomic_bool             atrace_is_ready      = ATOMIC_VAR_INIT(true);
-int                     atrace_marker_fd     = -1;
-uint64_t                atrace_enabled_tags  = 0;
-
-void atrace_set_debuggable(bool debuggable __unused) { }
-void atrace_set_tracing_enabled(bool enabled __unused) { }
-void atrace_update_tags() { }
-void atrace_setup() { }
-void atrace_begin_body(const char* name __unused) { }
-void atrace_end_body() { }
-void atrace_async_begin_body(const char* name __unused, int32_t cookie __unused) { }
-void atrace_async_end_body(const char* name __unused, int32_t cookie __unused) { }
-void atrace_int_body(const char* name __unused, int32_t value __unused) { }
-void atrace_int64_body(const char* name __unused, int64_t value __unused) { }
diff --git a/libcutils/trace-host.cpp b/libcutils/trace-host.cpp
new file mode 100644
index 0000000..d47cc18
--- /dev/null
+++ b/libcutils/trace-host.cpp
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2012 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 <cutils/trace.h>
+
+atomic_bool             atrace_is_ready      = ATOMIC_VAR_INIT(true);
+int                     atrace_marker_fd     = -1;
+uint64_t                atrace_enabled_tags  = 0;
+
+void atrace_set_debuggable(bool /*debuggable*/) {}
+void atrace_set_tracing_enabled(bool /*enabled*/) {}
+void atrace_update_tags() { }
+void atrace_setup() { }
+void atrace_begin_body(const char* /*name*/) {}
+void atrace_end_body() { }
+void atrace_async_begin_body(const char* /*name*/, int32_t /*cookie*/) {}
+void atrace_async_end_body(const char* /*name*/, int32_t /*cookie*/) {}
+void atrace_int_body(const char* /*name*/, int32_t /*value*/) {}
+void atrace_int64_body(const char* /*name*/, int64_t /*value*/) {}
diff --git a/libcutils/uevent.c b/libcutils/uevent.c
deleted file mode 100644
index f548dca..0000000
--- a/libcutils/uevent.c
+++ /dev/null
@@ -1,133 +0,0 @@
-/*
- * Copyright (C) 2011 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 <cutils/uevent.h>
-
-#include <errno.h>
-#include <stdbool.h>
-#include <string.h>
-#include <strings.h>
-#include <sys/socket.h>
-#include <sys/un.h>
-#include <unistd.h>
-
-#include <linux/netlink.h>
-
-/**
- * Like recv(), but checks that messages actually originate from the kernel.
- */
-ssize_t uevent_kernel_multicast_recv(int socket, void *buffer, size_t length)
-{
-    uid_t uid = -1;
-    return uevent_kernel_multicast_uid_recv(socket, buffer, length, &uid);
-}
-
-/**
- * Like the above, but passes a uid_t in by pointer. In the event that this
- * fails due to a bad uid check, the uid_t will be set to the uid of the
- * socket's peer.
- *
- * If this method rejects a netlink message from outside the kernel, it
- * returns -1, sets errno to EIO, and sets "user" to the UID associated with the
- * message. If the peer UID cannot be determined, "user" is set to -1."
- */
-ssize_t uevent_kernel_multicast_uid_recv(int socket, void *buffer, size_t length, uid_t *uid)
-{
-    return uevent_kernel_recv(socket, buffer, length, true, uid);
-}
-
-ssize_t uevent_kernel_recv(int socket, void *buffer, size_t length, bool require_group, uid_t *uid)
-{
-    struct iovec iov = { buffer, length };
-    struct sockaddr_nl addr;
-    char control[CMSG_SPACE(sizeof(struct ucred))];
-    struct msghdr hdr = {
-        &addr,
-        sizeof(addr),
-        &iov,
-        1,
-        control,
-        sizeof(control),
-        0,
-    };
-
-    *uid = -1;
-    ssize_t n = recvmsg(socket, &hdr, 0);
-    if (n <= 0) {
-        return n;
-    }
-
-    struct cmsghdr *cmsg = CMSG_FIRSTHDR(&hdr);
-    if (cmsg == NULL || cmsg->cmsg_type != SCM_CREDENTIALS) {
-        /* ignoring netlink message with no sender credentials */
-        goto out;
-    }
-
-    struct ucred *cred = (struct ucred *)CMSG_DATA(cmsg);
-    *uid = cred->uid;
-    if (cred->uid != 0) {
-        /* ignoring netlink message from non-root user */
-        goto out;
-    }
-
-    if (addr.nl_pid != 0) {
-        /* ignore non-kernel */
-        goto out;
-    }
-    if (require_group && addr.nl_groups == 0) {
-        /* ignore unicast messages when requested */
-        goto out;
-    }
-
-    return n;
-
-out:
-    /* clear residual potentially malicious data */
-    bzero(buffer, length);
-    errno = EIO;
-    return -1;
-}
-
-int uevent_open_socket(int buf_sz, bool passcred)
-{
-    struct sockaddr_nl addr;
-    int on = passcred;
-    int s;
-
-    memset(&addr, 0, sizeof(addr));
-    addr.nl_family = AF_NETLINK;
-    addr.nl_pid = getpid();
-    addr.nl_groups = 0xffffffff;
-
-    s = socket(PF_NETLINK, SOCK_DGRAM | SOCK_CLOEXEC, NETLINK_KOBJECT_UEVENT);
-    if(s < 0)
-        return -1;
-
-    /* buf_sz should be less than net.core.rmem_max for this to succeed */
-    if (setsockopt(s, SOL_SOCKET, SO_RCVBUF, &buf_sz, sizeof(buf_sz)) < 0) {
-        close(s);
-        return -1;
-    }
-
-    setsockopt(s, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on));
-
-    if(bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
-        close(s);
-        return -1;
-    }
-
-    return s;
-}
diff --git a/libcutils/uevent.cpp b/libcutils/uevent.cpp
new file mode 100644
index 0000000..2dfceed
--- /dev/null
+++ b/libcutils/uevent.cpp
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2011 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 <cutils/uevent.h>
+
+#include <errno.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <strings.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <unistd.h>
+
+#include <linux/netlink.h>
+
+extern "C" {
+
+/**
+ * Like recv(), but checks that messages actually originate from the kernel.
+ */
+ssize_t uevent_kernel_multicast_recv(int socket, void* buffer, size_t length) {
+    uid_t uid = -1;
+    return uevent_kernel_multicast_uid_recv(socket, buffer, length, &uid);
+}
+
+/**
+ * Like the above, but passes a uid_t in by pointer. In the event that this
+ * fails due to a bad uid check, the uid_t will be set to the uid of the
+ * socket's peer.
+ *
+ * If this method rejects a netlink message from outside the kernel, it
+ * returns -1, sets errno to EIO, and sets "user" to the UID associated with the
+ * message. If the peer UID cannot be determined, "user" is set to -1."
+ */
+ssize_t uevent_kernel_multicast_uid_recv(int socket, void* buffer, size_t length, uid_t* uid) {
+    return uevent_kernel_recv(socket, buffer, length, true, uid);
+}
+
+ssize_t uevent_kernel_recv(int socket, void* buffer, size_t length, bool require_group, uid_t* uid) {
+    struct iovec iov = {buffer, length};
+    struct sockaddr_nl addr;
+    char control[CMSG_SPACE(sizeof(struct ucred))];
+    struct msghdr hdr = {
+        &addr, sizeof(addr), &iov, 1, control, sizeof(control), 0,
+    };
+    struct ucred* cred;
+
+    *uid = -1;
+    ssize_t n = recvmsg(socket, &hdr, 0);
+    if (n <= 0) {
+        return n;
+    }
+
+    struct cmsghdr* cmsg = CMSG_FIRSTHDR(&hdr);
+    if (cmsg == NULL || cmsg->cmsg_type != SCM_CREDENTIALS) {
+        /* ignoring netlink message with no sender credentials */
+        goto out;
+    }
+
+    cred = (struct ucred*)CMSG_DATA(cmsg);
+    *uid = cred->uid;
+
+    if (addr.nl_pid != 0) {
+        /* ignore non-kernel */
+        goto out;
+    }
+    if (require_group && addr.nl_groups == 0) {
+        /* ignore unicast messages when requested */
+        goto out;
+    }
+
+    return n;
+
+out:
+    /* clear residual potentially malicious data */
+    bzero(buffer, length);
+    errno = EIO;
+    return -1;
+}
+
+int uevent_open_socket(int buf_sz, bool passcred) {
+    struct sockaddr_nl addr;
+    int on = passcred;
+    int s;
+
+    memset(&addr, 0, sizeof(addr));
+    addr.nl_family = AF_NETLINK;
+    addr.nl_pid = getpid();
+    addr.nl_groups = 0xffffffff;
+
+    s = socket(PF_NETLINK, SOCK_DGRAM | SOCK_CLOEXEC, NETLINK_KOBJECT_UEVENT);
+    if (s < 0) return -1;
+
+    /* buf_sz should be less than net.core.rmem_max for this to succeed */
+    if (setsockopt(s, SOL_SOCKET, SO_RCVBUF, &buf_sz, sizeof(buf_sz)) < 0) {
+        close(s);
+        return -1;
+    }
+
+    setsockopt(s, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on));
+
+    if (bind(s, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
+        close(s);
+        return -1;
+    }
+
+    return s;
+}
+
+}  // extern "C"
diff --git a/libdiskconfig/Android.bp b/libdiskconfig/Android.bp
index 088981a..b92f086 100644
--- a/libdiskconfig/Android.bp
+++ b/libdiskconfig/Android.bp
@@ -1,6 +1,9 @@
 cc_library {
     name: "libdiskconfig",
     vendor_available: true,
+    vndk: {
+        enabled: true,
+    },
     srcs: [
         "diskconfig.c",
         "diskutils.c",
@@ -20,7 +23,7 @@
         darwin: {
             enabled: false,
         },
-        linux: {
+        linux_glibc: {
             cflags: [
                 "-O2",
                 "-g",
diff --git a/libgrallocusage/Android.bp b/libgrallocusage/Android.bp
new file mode 100644
index 0000000..bcc0616
--- /dev/null
+++ b/libgrallocusage/Android.bp
@@ -0,0 +1,34 @@
+// Copyright 2017 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_library_static {
+    name: "libgrallocusage",
+    vendor_available: true,
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
+    cppflags: [
+        "-Weverything",
+        "-Wno-c++98-compat-pedantic",
+        // Hide errors in headers we include
+        "-Wno-global-constructors",
+        "-Wno-exit-time-destructors",
+        "-Wno-padded",
+    ],
+    srcs: ["GrallocUsageConversion.cpp"],
+    export_include_dirs: ["include"],
+    shared_libs: ["android.hardware.graphics.allocator@2.0"],
+    header_libs: ["libhardware_headers"],
+}
diff --git a/libgrallocusage/GrallocUsageConversion.cpp b/libgrallocusage/GrallocUsageConversion.cpp
new file mode 100644
index 0000000..05c8ec4
--- /dev/null
+++ b/libgrallocusage/GrallocUsageConversion.cpp
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2017 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 <grallocusage/GrallocUsageConversion.h>
+
+#include <hardware/gralloc.h>
+#include <hardware/gralloc1.h>
+
+void android_convertGralloc0To1Usage(int32_t usage, uint64_t* producerUsage,
+                                     uint64_t* consumerUsage) {
+    constexpr uint64_t PRODUCER_MASK =
+        GRALLOC1_PRODUCER_USAGE_CPU_READ |
+        /* GRALLOC1_PRODUCER_USAGE_CPU_READ_OFTEN | */
+        GRALLOC1_PRODUCER_USAGE_CPU_WRITE |
+        /* GRALLOC1_PRODUCER_USAGE_CPU_WRITE_OFTEN | */
+        GRALLOC1_PRODUCER_USAGE_GPU_RENDER_TARGET | GRALLOC1_PRODUCER_USAGE_PROTECTED |
+        GRALLOC1_PRODUCER_USAGE_CAMERA | GRALLOC1_PRODUCER_USAGE_VIDEO_DECODER |
+        GRALLOC1_PRODUCER_USAGE_SENSOR_DIRECT_DATA;
+    constexpr uint64_t CONSUMER_MASK =
+        GRALLOC1_CONSUMER_USAGE_CPU_READ |
+        /* GRALLOC1_CONSUMER_USAGE_CPU_READ_OFTEN | */
+        GRALLOC1_CONSUMER_USAGE_GPU_TEXTURE | GRALLOC1_CONSUMER_USAGE_HWCOMPOSER |
+        GRALLOC1_CONSUMER_USAGE_CLIENT_TARGET | GRALLOC1_CONSUMER_USAGE_CURSOR |
+        GRALLOC1_CONSUMER_USAGE_VIDEO_ENCODER | GRALLOC1_CONSUMER_USAGE_CAMERA |
+        GRALLOC1_CONSUMER_USAGE_RENDERSCRIPT | GRALLOC1_CONSUMER_USAGE_GPU_DATA_BUFFER;
+    *producerUsage = static_cast<uint64_t>(usage) & PRODUCER_MASK;
+    *consumerUsage = static_cast<uint64_t>(usage) & CONSUMER_MASK;
+    if ((static_cast<uint32_t>(usage) & GRALLOC_USAGE_SW_READ_OFTEN) == GRALLOC_USAGE_SW_READ_OFTEN) {
+        *producerUsage |= GRALLOC1_PRODUCER_USAGE_CPU_READ_OFTEN;
+        *consumerUsage |= GRALLOC1_CONSUMER_USAGE_CPU_READ_OFTEN;
+    }
+    if ((static_cast<uint32_t>(usage) & GRALLOC_USAGE_SW_WRITE_OFTEN) ==
+        GRALLOC_USAGE_SW_WRITE_OFTEN) {
+        *producerUsage |= GRALLOC1_PRODUCER_USAGE_CPU_WRITE_OFTEN;
+    }
+}
+
+int32_t android_convertGralloc1To0Usage(uint64_t producerUsage, uint64_t consumerUsage) {
+    static_assert(uint64_t(GRALLOC1_CONSUMER_USAGE_CPU_READ_OFTEN) ==
+                      uint64_t(GRALLOC1_PRODUCER_USAGE_CPU_READ_OFTEN),
+                  "expected ConsumerUsage and ProducerUsage CPU_READ_OFTEN bits to match");
+    uint64_t merged = producerUsage | consumerUsage;
+    if ((merged & (GRALLOC1_CONSUMER_USAGE_CPU_READ_OFTEN)) ==
+        GRALLOC1_CONSUMER_USAGE_CPU_READ_OFTEN) {
+        merged &= ~uint64_t(GRALLOC1_CONSUMER_USAGE_CPU_READ_OFTEN);
+        merged |= GRALLOC_USAGE_SW_READ_OFTEN;
+    }
+    if ((merged & (GRALLOC1_PRODUCER_USAGE_CPU_WRITE_OFTEN)) ==
+        GRALLOC1_PRODUCER_USAGE_CPU_WRITE_OFTEN) {
+        merged &= ~uint64_t(GRALLOC1_PRODUCER_USAGE_CPU_WRITE_OFTEN);
+        merged |= GRALLOC_USAGE_SW_WRITE_OFTEN;
+    }
+    return static_cast<int32_t>(merged);
+}
diff --git a/toolbox/MODULE_LICENSE_BSD b/libgrallocusage/MODULE_LICENSE_APACHE2
similarity index 100%
copy from toolbox/MODULE_LICENSE_BSD
copy to libgrallocusage/MODULE_LICENSE_APACHE2
diff --git a/debuggerd/NOTICE b/libgrallocusage/NOTICE
similarity index 100%
rename from debuggerd/NOTICE
rename to libgrallocusage/NOTICE
diff --git a/libgrallocusage/include/grallocusage/GrallocUsageConversion.h b/libgrallocusage/include/grallocusage/GrallocUsageConversion.h
new file mode 100644
index 0000000..5c94343
--- /dev/null
+++ b/libgrallocusage/include/grallocusage/GrallocUsageConversion.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2017 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 ANDROID_GRALLOCUSAGE_GRALLOC_USAGE_CONVERSION_H
+#define ANDROID_GRALLOCUSAGE_GRALLOC_USAGE_CONVERSION_H 1
+
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// Conversion functions are out-of-line so that users don't have to be exposed to
+// android/hardware/graphics/allocator/2.0/types.h and link against
+// android.hardware.graphics.allocator@2.0 to get that in their search path.
+
+// Convert a 32-bit gralloc0 usage mask to a producer/consumer pair of 64-bit usage masks as used
+// by android.hardware.graphics.allocator@2.0 (and gralloc1). This conversion properly handles the
+// mismatch between a.h.g.allocator@2.0's CPU_{READ,WRITE}_OFTEN and gralloc0's
+// SW_{READ,WRITE}_OFTEN.
+void android_convertGralloc0To1Usage(int32_t usage, uint64_t* producerUsage,
+                                     uint64_t* consumerUsage);
+
+// Convert a producer/consumer pair of 64-bit usage masks as used by
+// android.hardware.graphics.allocator@2.0 (and gralloc1) to a 32-bit gralloc0 usage mask. This
+// conversion properly handles the mismatch between a.h.g.allocator@2.0's CPU_{READ,WRITE}_OFTEN
+// and gralloc0's SW_{READ,WRITE}_OFTEN.
+int32_t android_convertGralloc1To0Usage(uint64_t producerUsage, uint64_t consumerUsage);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // ANDROID_GRALLOCUSAGE_GRALLOC_USAGE_CONVERSION_H
diff --git a/libion/Android.bp b/libion/Android.bp
index 6f267e4..2f73d92 100644
--- a/libion/Android.bp
+++ b/libion/Android.bp
@@ -1,7 +1,10 @@
-
 cc_library {
     name: "libion",
-		vendor_available: true,
+    vendor_available: true,
+    vndk: {
+        enabled: true,
+        support_system_process: true,
+    },
     srcs: ["ion.c"],
     shared_libs: ["liblog"],
     local_include_dirs: [
diff --git a/libion/include/ion/ion.h b/libion/include/ion/ion.h
index f47793d..a60d24e 100644
--- a/libion/include/ion/ion.h
+++ b/libion/include/ion/ion.h
@@ -41,6 +41,15 @@
 int ion_share(int fd, ion_user_handle_t handle, int *share_fd);
 int ion_import(int fd, int share_fd, ion_user_handle_t *handle);
 
+/**
+  * Add 4.12+ kernel ION interfaces here for forward compatibility
+  * This should be needed till the pre-4.12+ ION interfaces are backported.
+  */
+int ion_query_heap_cnt(int fd, int* cnt);
+int ion_query_get_heaps(int fd, int cnt, void* buffers);
+
+int ion_is_legacy(int fd);
+
 __END_DECLS
 
 #endif /* __SYS_CORE_ION_H */
diff --git a/libion/ion.c b/libion/ion.c
index 9aaa6f2..b8de5a4 100644
--- a/libion/ion.c
+++ b/libion/ion.c
@@ -22,6 +22,7 @@
 #include <errno.h>
 #include <fcntl.h>
 #include <linux/ion.h>
+#include <stdatomic.h>
 #include <stdio.h>
 #include <string.h>
 #include <sys/ioctl.h>
@@ -30,88 +31,96 @@
 #include <unistd.h>
 
 #include <ion/ion.h>
+#include "ion_4.12.h"
+
 #include <log/log.h>
 
-int ion_open()
-{
+enum ion_version { ION_VERSION_UNKNOWN, ION_VERSION_MODERN, ION_VERSION_LEGACY };
+
+static atomic_int g_ion_version = ATOMIC_VAR_INIT(ION_VERSION_UNKNOWN);
+
+int ion_is_legacy(int fd) {
+    int version = atomic_load_explicit(&g_ion_version, memory_order_acquire);
+    if (version == ION_VERSION_UNKNOWN) {
+        /**
+          * Check for FREE IOCTL here; it is available only in the old
+          * kernels, not the new ones.
+          */
+        int err = ion_free(fd, (ion_user_handle_t)NULL);
+        version = (err == -ENOTTY) ? ION_VERSION_MODERN : ION_VERSION_LEGACY;
+        atomic_store_explicit(&g_ion_version, version, memory_order_release);
+    }
+    return version == ION_VERSION_LEGACY;
+}
+
+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;
 }
 
-int ion_close(int fd)
-{
+int ion_close(int fd) {
     int ret = close(fd);
-    if (ret < 0)
-        return -errno;
+    if (ret < 0) return -errno;
     return ret;
 }
 
-static int ion_ioctl(int fd, int req, void *arg)
-{
+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;
 }
 
-int ion_alloc(int fd, size_t len, size_t align, unsigned int heap_mask,
-              unsigned int flags, ion_user_handle_t *handle)
-{
-    int ret;
+int ion_alloc(int fd, size_t len, size_t align, unsigned int heap_mask, unsigned int flags,
+              ion_user_handle_t* handle) {
+    int ret = 0;
+
+    if ((handle == NULL) || (!ion_is_legacy(fd))) return -EINVAL;
+
     struct ion_allocation_data data = {
-        .len = len,
-        .align = align,
-        .heap_id_mask = heap_mask,
-        .flags = flags,
+        .len = len, .align = align, .heap_id_mask = heap_mask, .flags = flags,
     };
 
-    if (handle == NULL)
-        return -EINVAL;
-
     ret = ion_ioctl(fd, ION_IOC_ALLOC, &data);
-    if (ret < 0)
-        return ret;
+    if (ret < 0) return ret;
+
     *handle = data.handle;
+
     return ret;
 }
 
-int ion_free(int fd, ion_user_handle_t handle)
-{
+int ion_free(int fd, ion_user_handle_t handle) {
     struct ion_handle_data data = {
         .handle = handle,
     };
     return ion_ioctl(fd, ION_IOC_FREE, &data);
 }
 
-int ion_map(int fd, ion_user_handle_t handle, size_t length, int prot,
-            int flags, off_t offset, unsigned char **ptr, int *map_fd)
-{
+int ion_map(int fd, ion_user_handle_t handle, size_t length, int prot, int flags, off_t offset,
+            unsigned char** ptr, int* map_fd) {
+    if (!ion_is_legacy(fd)) return -EINVAL;
     int ret;
-    unsigned char *tmp_ptr;
+    unsigned char* tmp_ptr;
     struct ion_fd_data data = {
         .handle = handle,
     };
 
-    if (map_fd == NULL)
-        return -EINVAL;
-    if (ptr == NULL)
-        return -EINVAL;
+    if (map_fd == NULL) return -EINVAL;
+    if (ptr == NULL) return -EINVAL;
 
     ret = ion_ioctl(fd, ION_IOC_MAP, &data);
-    if (ret < 0)
-        return ret;
+    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;
@@ -119,61 +128,94 @@
     return ret;
 }
 
-int ion_share(int fd, ion_user_handle_t handle, int *share_fd)
-{
+int ion_share(int fd, ion_user_handle_t handle, int* share_fd) {
     int ret;
     struct ion_fd_data data = {
         .handle = handle,
     };
 
-    if (share_fd == NULL)
-        return -EINVAL;
+    if (!ion_is_legacy(fd)) return -EINVAL;
+    if (share_fd == NULL) return -EINVAL;
 
     ret = ion_ioctl(fd, ION_IOC_SHARE, &data);
-    if (ret < 0)
-        return ret;
+    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;
     return ret;
 }
 
-int ion_alloc_fd(int fd, size_t len, size_t align, unsigned int heap_mask,
-                 unsigned int flags, int *handle_fd) {
+int ion_alloc_fd(int fd, size_t len, size_t align, unsigned int heap_mask, unsigned int flags,
+                 int* handle_fd) {
     ion_user_handle_t handle;
     int ret;
 
-    ret = ion_alloc(fd, len, align, heap_mask, flags, &handle);
-    if (ret < 0)
-        return ret;
-    ret = ion_share(fd, handle, handle_fd);
-    ion_free(fd, handle);
+    if (!ion_is_legacy(fd)) {
+        struct ion_new_allocation_data data = {
+            .len = len,
+            .heap_id_mask = heap_mask,
+            .flags = flags,
+        };
+
+        ret = ion_ioctl(fd, ION_IOC_NEW_ALLOC, &data);
+        if (ret < 0) return ret;
+        *handle_fd = data.fd;
+    } else {
+        ret = ion_alloc(fd, len, align, heap_mask, flags, &handle);
+        if (ret < 0) return ret;
+        ret = ion_share(fd, handle, handle_fd);
+        ion_free(fd, handle);
+    }
     return ret;
 }
 
-int ion_import(int fd, int share_fd, ion_user_handle_t *handle)
-{
+int ion_import(int fd, int share_fd, ion_user_handle_t* handle) {
     int ret;
     struct ion_fd_data data = {
         .fd = share_fd,
     };
 
-    if (handle == NULL)
-        return -EINVAL;
+    if (!ion_is_legacy(fd)) return -EINVAL;
+
+    if (handle == NULL) return -EINVAL;
 
     ret = ion_ioctl(fd, ION_IOC_IMPORT, &data);
-    if (ret < 0)
-        return ret;
+    if (ret < 0) return ret;
     *handle = data.handle;
     return ret;
 }
 
-int ion_sync_fd(int fd, int handle_fd)
-{
+int ion_sync_fd(int fd, int handle_fd) {
     struct ion_fd_data data = {
         .fd = handle_fd,
     };
+
+    if (!ion_is_legacy(fd)) return -EINVAL;
+
     return ion_ioctl(fd, ION_IOC_SYNC, &data);
 }
+
+int ion_query_heap_cnt(int fd, int* cnt) {
+    int ret;
+    struct ion_heap_query query;
+
+    memset(&query, 0, sizeof(query));
+
+    ret = ion_ioctl(fd, ION_IOC_HEAP_QUERY, &query);
+    if (ret < 0) return ret;
+
+    *cnt = query.cnt;
+    return ret;
+}
+
+int ion_query_get_heaps(int fd, int cnt, void* buffers) {
+    int ret;
+    struct ion_heap_query query = {
+        .cnt = cnt, .heaps = (uintptr_t)buffers,
+    };
+
+    ret = ion_ioctl(fd, ION_IOC_HEAP_QUERY, &query);
+    return ret;
+}
diff --git a/libion/ion_4.12.h b/libion/ion_4.12.h
new file mode 100644
index 0000000..6ae79d4
--- /dev/null
+++ b/libion/ion_4.12.h
@@ -0,0 +1,125 @@
+/*
+ * Adapted from drivers/staging/android/uapi/ion.h
+ *
+ * Copyright (C) 2011 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _UAPI_LINUX_ION_NEW_H
+#define _UAPI_LINUX_ION_NEW_H
+
+#include <linux/ioctl.h>
+#include <linux/types.h>
+
+#define ION_NUM_HEAP_IDS (sizeof(unsigned int) * 8)
+
+/**
+ * DOC: Ion Userspace API
+ *
+ * create a client by opening /dev/ion
+ * most operations handled via following ioctls
+ *
+ */
+
+/**
+ * struct ion_new_allocation_data - metadata passed from userspace for allocations
+ * @len:		size of the allocation
+ * @heap_id_mask:	mask of heap ids to allocate from
+ * @flags:		flags passed to heap
+ * @handle:		pointer that will be populated with a cookie to use to
+ *			refer to this allocation
+ *
+ * Provided by userspace as an argument to the ioctl - added _new to denote
+ * this belongs to the new ION interface.
+ */
+struct ion_new_allocation_data {
+    __u64 len;
+    __u32 heap_id_mask;
+    __u32 flags;
+    __u32 fd;
+    __u32 unused;
+};
+
+#define MAX_HEAP_NAME 32
+
+/**
+ * struct ion_heap_data - data about a heap
+ * @name - first 32 characters of the heap name
+ * @type - heap type
+ * @heap_id - heap id for the heap
+ */
+struct ion_heap_data {
+    char name[MAX_HEAP_NAME];
+    __u32 type;
+    __u32 heap_id;
+    __u32 reserved0;
+    __u32 reserved1;
+    __u32 reserved2;
+};
+
+/**
+ * struct ion_heap_query - collection of data about all heaps
+ * @cnt - total number of heaps to be copied
+ * @heaps - buffer to copy heap data
+ */
+struct ion_heap_query {
+    __u32 cnt;       /* Total number of heaps to be copied */
+    __u32 reserved0; /* align to 64bits */
+    __u64 heaps;     /* buffer to be populated */
+    __u32 reserved1;
+    __u32 reserved2;
+};
+
+#define ION_IOC_MAGIC 'I'
+
+/**
+ * DOC: ION_IOC_NEW_ALLOC - allocate memory
+ *
+ * Takes an ion_allocation_data struct and returns it with the handle field
+ * populated with the opaque handle for the allocation.
+ * TODO: This IOCTL will clash by design; however, only one of
+ *  ION_IOC_ALLOC or ION_IOC_NEW_ALLOC paths will be exercised,
+ *  so this should not conflict.
+ */
+#define ION_IOC_NEW_ALLOC _IOWR(ION_IOC_MAGIC, 0, struct ion_new_allocation_data)
+
+/**
+ * DOC: ION_IOC_FREE - free memory
+ *
+ * Takes an ion_handle_data struct and frees the handle.
+ *
+ * #define ION_IOC_FREE		_IOWR(ION_IOC_MAGIC, 1, struct ion_handle_data)
+ * This will come from the older kernels, so don't redefine here
+ */
+
+/**
+ * DOC: ION_IOC_SHARE - creates a file descriptor to use to share an allocation
+ *
+ * Takes an ion_fd_data struct with the handle field populated with a valid
+ * opaque handle.  Returns the struct with the fd field set to a file
+ * descriptor open in the current address space.  This file descriptor
+ * can then be passed to another process.  The corresponding opaque handle can
+ * be retrieved via ION_IOC_IMPORT.
+ *
+ * #define ION_IOC_SHARE		_IOWR(ION_IOC_MAGIC, 4, struct ion_fd_data)
+ * This will come from the older kernels, so don't redefine here
+ */
+
+/**
+ * DOC: ION_IOC_HEAP_QUERY - information about available heaps
+ *
+ * Takes an ion_heap_query structure and populates information about
+ * available Ion heaps.
+ */
+#define ION_IOC_HEAP_QUERY _IOWR(ION_IOC_MAGIC, 8, struct ion_heap_query)
+
+#endif /* _UAPI_LINUX_ION_NEW_H */
diff --git a/libion/ion_test.c b/libion/ion_test.c
index b7d5583..f3874ae 100644
--- a/libion/ion_test.c
+++ b/libion/ion_test.c
@@ -250,7 +250,7 @@
         case 'p':
             prot = 0;
             prot |= strstr(optarg, "MAP_PRIVATE") ? MAP_PRIVATE : 0;
-            prot |= strstr(optarg, "MAP_SHARED") ? MAP_PRIVATE : 0;
+            prot |= strstr(optarg, "MAP_SHARED") ? MAP_SHARED : 0;
             break;
         case 'f':
             alloc_flags = atol(optarg);
diff --git a/libion/tests/Android.bp b/libion/tests/Android.bp
index 4428848..b3fcb3b 100644
--- a/libion/tests/Android.bp
+++ b/libion/tests/Android.bp
@@ -16,7 +16,6 @@
 
 cc_test {
     name: "ion-unit-tests",
-    clang: true,
     cflags: [
         "-g",
         "-Wall",
diff --git a/libkeyutils/Android.bp b/libkeyutils/Android.bp
index 0285259..b388e95 100644
--- a/libkeyutils/Android.bp
+++ b/libkeyutils/Android.bp
@@ -2,6 +2,7 @@
     name: "libkeyutils",
     cflags: ["-Werror"],
     defaults: ["linux_bionic_supported"],
+    recovery_available: true,
     export_include_dirs: ["include/"],
     local_include_dirs: ["include/"],
     srcs: ["keyutils.cpp"],
@@ -13,4 +14,5 @@
     cflags: ["-Werror"],
     shared_libs: ["libkeyutils"],
     srcs: ["keyutils_test.cpp"],
+    test_suites: ["device-tests"],
 }
diff --git a/liblog/Android.bp b/liblog/Android.bp
index e74aa82..4a165a0 100644
--- a/liblog/Android.bp
+++ b/liblog/Android.bp
@@ -42,12 +42,31 @@
     "logd_writer.c",
 ]
 
+cc_library_headers {
+    name: "liblog_headers",
+    host_supported: true,
+    vendor_available: true,
+    recovery_available: true,
+    export_include_dirs: ["include"],
+    target: {
+        windows: {
+            enabled: true,
+        },
+        linux_bionic: {
+            enabled: true,
+        },
+        vendor: {
+            override_export_include_dirs: ["include_vndk"],
+        },
+    },
+}
+
 // Shared and static library for host and device
 // ========================================================
 cc_library {
     name: "liblog",
     host_supported: true,
-
+    recovery_available: true,
     srcs: liblog_sources,
 
     target: {
@@ -64,6 +83,7 @@
         },
         android_arm: {
             // TODO: This is to work around b/24465209. Remove after root cause is fixed
+            pack_relocations: false,
             ldflags: ["-Wl,--hash-style=both"],
         },
         windows: {
@@ -73,15 +93,13 @@
         not_windows: {
             srcs: ["event_tag_map.cpp"],
         },
-        linux: {
-            host_ldlibs: ["-lrt"],
-        },
         linux_bionic: {
             enabled: true,
         },
     },
 
-    export_include_dirs: ["include"],
+    header_libs: ["liblog_headers"],
+    export_header_lib_headers: ["liblog_headers"],
 
     cflags: [
         "-Werror",
@@ -100,7 +118,7 @@
 }
 
 ndk_headers {
-    name: "liblog_headers",
+    name: "liblog_ndk_headers",
     from: "include/android",
     to: "android",
     srcs: ["include/android/log.h"],
diff --git a/liblog/OWNERS b/liblog/OWNERS
new file mode 100644
index 0000000..babbe4d
--- /dev/null
+++ b/liblog/OWNERS
@@ -0,0 +1 @@
+tomcherry@google.com
diff --git a/liblog/event_tag_map.cpp b/liblog/event_tag_map.cpp
index 83064fd..2e2bf87 100644
--- a/liblog/event_tag_map.cpp
+++ b/liblog/event_tag_map.cpp
@@ -25,9 +25,9 @@
 #include <string.h>
 #include <sys/mman.h>
 
-#include <experimental/string_view>
 #include <functional>
 #include <string>
+#include <string_view>
 #include <unordered_map>
 
 #include <log/event_tag_map.h>
@@ -44,10 +44,10 @@
 class MapString {
  private:
   const std::string* alloc;                  // HAS-AN
-  const std::experimental::string_view str;  // HAS-A
+  const std::string_view str;                // HAS-A
 
  public:
-  operator const std::experimental::string_view() const {
+  operator const std::string_view() const {
     return str;
   }
 
@@ -92,8 +92,7 @@
     : public std::unary_function<const MapString&, size_t> {
   size_t operator()(const MapString& __t) const noexcept {
     if (!__t.length()) return 0;
-    return std::hash<std::experimental::string_view>()(
-        std::experimental::string_view(__t));
+    return std::hash<std::string_view>()(std::string_view(__t));
   }
 };
 
diff --git a/liblog/include/android/log.h b/liblog/include/android/log.h
index 15fba8b..ee9220d 100644
--- a/liblog/include/android/log.h
+++ b/liblog/include/android/log.h
@@ -34,9 +34,16 @@
  *   - DO NOT CHANGE THE LAYOUT OR SIZE OF STRUCTURES
  */
 
-/*
- * Support routines to send messages to the Android in-kernel log buffer,
- * which can later be accessed through the 'logcat' utility.
+/**
+ * @addtogroup Logging
+ * @{
+ */
+
+/**
+ * \file
+ *
+ * Support routines to send messages to the Android log buffer,
+ * which can later be accessed through the `logcat` utility.
  *
  * Each log message must have
  *   - a priority
@@ -47,24 +54,22 @@
  * and should be reasonably small.
  *
  * Log message text may be truncated to less than an implementation-specific
- * limit (e.g. 1023 characters max).
+ * limit (1023 bytes).
  *
  * Note that a newline character ("\n") will be appended automatically to your
  * log message, if not already there. It is not possible to send several
  * messages and have them appear on a single line in logcat.
  *
- * PLEASE USE LOGS WITH MODERATION:
+ * Please use logging in moderation:
  *
  *  - Sending log messages eats CPU and slow down your application and the
  *    system.
  *
- *  - The circular log buffer is pretty small (<64KB), sending many messages
- *    might push off other important log messages from the rest of the system.
+ *  - The circular log buffer is pretty small, so sending many messages
+ *    will hide other important log messages.
  *
  *  - In release builds, only send log messages to account for exceptional
  *    conditions.
- *
- * NOTE: These functions MUST be implemented by /system/lib/liblog.so
  */
 
 #include <stdarg.h>
@@ -73,78 +78,107 @@
 extern "C" {
 #endif
 
-/*
- * Android log priority values, in ascending priority order.
+/**
+ * Android log priority values, in increasing order of priority.
  */
 typedef enum android_LogPriority {
+  /** For internal use only.  */
   ANDROID_LOG_UNKNOWN = 0,
+  /** The default priority, for internal use only.  */
   ANDROID_LOG_DEFAULT, /* only for SetMinPriority() */
+  /** Verbose logging. Should typically be disabled for a release apk. */
   ANDROID_LOG_VERBOSE,
+  /** Debug logging. Should typically be disabled for a release apk. */
   ANDROID_LOG_DEBUG,
+  /** Informational logging. Should typically be disabled for a release apk. */
   ANDROID_LOG_INFO,
+  /** Warning logging. For use with recoverable failures. */
   ANDROID_LOG_WARN,
+  /** Error logging. For use with unrecoverable failures. */
   ANDROID_LOG_ERROR,
+  /** Fatal logging. For use when aborting. */
   ANDROID_LOG_FATAL,
+  /** For internal use only.  */
   ANDROID_LOG_SILENT, /* only for SetMinPriority(); must be last */
 } android_LogPriority;
 
-/*
- * Send a simple string to the log.
+/**
+ * Writes the constant string `text` to the log, with priority `prio` and tag
+ * `tag`.
  */
 int __android_log_write(int prio, const char* tag, const char* text);
 
-/*
- * Send a formatted string to the log, used like printf(fmt,...)
+/**
+ * Writes a formatted string to the log, with priority `prio` and tag `tag`.
+ * The details of formatting are the same as for
+ * [printf(3)](http://man7.org/linux/man-pages/man3/printf.3.html).
  */
 int __android_log_print(int prio, const char* tag, const char* fmt, ...)
 #if defined(__GNUC__)
-#ifdef __USE_MINGW_ANSI_STDIO
-#if __USE_MINGW_ANSI_STDIO
-    __attribute__((__format__(gnu_printf, 3, 4)))
-#else
     __attribute__((__format__(printf, 3, 4)))
 #endif
-#else
-    __attribute__((__format__(printf, 3, 4)))
-#endif
-#endif
     ;
 
-/*
- * A variant of __android_log_print() that takes a va_list to list
- * additional parameters.
+/**
+ * Equivalent to `__android_log_print`, but taking a `va_list`.
+ * (If `__android_log_print` is like `printf`, this is like `vprintf`.)
  */
 int __android_log_vprint(int prio, const char* tag, const char* fmt, va_list ap)
 #if defined(__GNUC__)
-#ifdef __USE_MINGW_ANSI_STDIO
-#if __USE_MINGW_ANSI_STDIO
-    __attribute__((__format__(gnu_printf, 3, 0)))
-#else
     __attribute__((__format__(printf, 3, 0)))
 #endif
-#else
-    __attribute__((__format__(printf, 3, 0)))
-#endif
-#endif
     ;
 
-/*
- * Log an assertion failure and abort the process to have a chance
- * to inspect it if a debugger is attached. This uses the FATAL priority.
+/**
+ * Writes an assertion failure to the log (as `ANDROID_LOG_FATAL`) and to
+ * stderr, before calling
+ * [abort(3)](http://man7.org/linux/man-pages/man3/abort.3.html).
+ *
+ * If `fmt` is non-null, `cond` is unused. If `fmt` is null, the string
+ * `Assertion failed: %s` is used with `cond` as the string argument.
+ * If both `fmt` and `cond` are null, a default string is provided.
+ *
+ * Most callers should use
+ * [assert(3)](http://man7.org/linux/man-pages/man3/assert.3.html) from
+ * `<assert.h>` instead, or the `__assert` and `__assert2` functions provided by
+ * bionic if more control is needed. They support automatically including the
+ * source filename and line number more conveniently than this function.
  */
 void __android_log_assert(const char* cond, const char* tag, const char* fmt,
                           ...)
 #if defined(__GNUC__)
     __attribute__((__noreturn__))
-#ifdef __USE_MINGW_ANSI_STDIO
-#if __USE_MINGW_ANSI_STDIO
-    __attribute__((__format__(gnu_printf, 3, 4)))
-#else
     __attribute__((__format__(printf, 3, 4)))
 #endif
-#else
-    __attribute__((__format__(printf, 3, 4)))
+    ;
+
+#ifndef log_id_t_defined
+#define log_id_t_defined
+typedef enum log_id {
+  LOG_ID_MIN = 0,
+
+  LOG_ID_MAIN = 0,
+  LOG_ID_RADIO = 1,
+  LOG_ID_EVENTS = 2,
+  LOG_ID_SYSTEM = 3,
+  LOG_ID_CRASH = 4,
+  LOG_ID_STATS = 5,
+  LOG_ID_SECURITY = 6,
+  LOG_ID_KERNEL = 7, /* place last, third-parties can not use it */
+
+  LOG_ID_MAX
+} log_id_t;
 #endif
+
+/*
+ * Send a simple string to the log.
+ */
+int __android_log_buf_write(int bufID, int prio, const char* tag,
+                            const char* text);
+int __android_log_buf_print(int bufID, int prio, const char* tag,
+                            const char* fmt, ...)
+#if defined(__GNUC__)
+    __attribute__((__format__(printf, 4, 5)))
 #endif
     ;
 
@@ -152,4 +186,6 @@
 }
 #endif
 
+/** @} */
+
 #endif /* _ANDROID_LOG_H */
diff --git a/liblog/include/log/log.h b/liblog/include/log/log.h
index d01708d..3813e6e 100644
--- a/liblog/include/log/log.h
+++ b/liblog/include/log/log.h
@@ -95,6 +95,8 @@
                           size_t len);
 int __android_log_bswrite(int32_t tag, const char* payload);
 
+int __android_log_stats_bwrite(int32_t tag, const void* payload, size_t len);
+
 #define android_bWriteLog(tag, payload, len) \
   __android_log_bwrite(tag, payload, len)
 #define android_btWriteLog(tag, type, payload, len) \
diff --git a/liblog/include/log/log_id.h b/liblog/include/log/log_id.h
index 7bfa277..c44f5a2 100644
--- a/liblog/include/log/log_id.h
+++ b/liblog/include/log/log_id.h
@@ -31,8 +31,9 @@
   LOG_ID_EVENTS = 2,
   LOG_ID_SYSTEM = 3,
   LOG_ID_CRASH = 4,
-  LOG_ID_SECURITY = 5,
-  LOG_ID_KERNEL = 6, /* place last, third-parties can not use it */
+  LOG_ID_STATS = 5,
+  LOG_ID_SECURITY = 6,
+  LOG_ID_KERNEL = 7, /* place last, third-parties can not use it */
 
   LOG_ID_MAX
 } log_id_t;
diff --git a/liblog/include/log/log_main.h b/liblog/include/log/log_main.h
index da16158..e7b1728 100644
--- a/liblog/include/log/log_main.h
+++ b/liblog/include/log/log_main.h
@@ -17,11 +17,12 @@
 #ifndef _LIBS_LOG_LOG_MAIN_H
 #define _LIBS_LOG_LOG_MAIN_H
 
-#include <android/log.h>
+#include <stdbool.h>
 
-#ifdef __cplusplus
-extern "C" {
-#endif
+#include <android/log.h>
+#include <sys/cdefs.h>
+
+__BEGIN_DECLS
 
 /*
  * Normally we strip the effects of ALOGV (VERBOSE messages),
@@ -52,6 +53,19 @@
 #pragma clang diagnostic ignored "-Wgnu-zero-variadic-macro-arguments"
 #endif
 
+/*
+ * Use __VA_ARGS__ if running a static analyzer,
+ * to avoid warnings of unused variables in __VA_ARGS__.
+ * __FAKE_USE_VA_ARGS is undefined at link time,
+ * so don't link with __clang_analyzer__ defined.
+ */
+#ifdef __clang_analyzer__
+extern void __fake_use_va_args(int, ...);
+#define __FAKE_USE_VA_ARGS(...) __fake_use_va_args(0, ##__VA_ARGS__)
+#else
+#define __FAKE_USE_VA_ARGS(...) ((void)(0))
+#endif
+
 #ifndef __predict_false
 #define __predict_false(exp) __builtin_expect((exp) != 0, 0)
 #endif
@@ -111,7 +125,7 @@
 #define LOG_ALWAYS_FATAL_IF(cond, ...)                              \
   ((__predict_false(cond))                                          \
        ? ((void)android_printAssert(#cond, LOG_TAG, ##__VA_ARGS__)) \
-       : (void)0)
+       : __FAKE_USE_VA_ARGS(__VA_ARGS__))
 #endif
 
 #ifndef LOG_ALWAYS_FATAL
@@ -127,10 +141,10 @@
 #if LOG_NDEBUG
 
 #ifndef LOG_FATAL_IF
-#define LOG_FATAL_IF(cond, ...) ((void)0)
+#define LOG_FATAL_IF(cond, ...) __FAKE_USE_VA_ARGS(__VA_ARGS__)
 #endif
 #ifndef LOG_FATAL
-#define LOG_FATAL(...) ((void)0)
+#define LOG_FATAL(...) __FAKE_USE_VA_ARGS(__VA_ARGS__)
 #endif
 
 #else
@@ -174,12 +188,13 @@
 #ifndef ALOGV
 #define __ALOGV(...) ((void)ALOG(LOG_VERBOSE, LOG_TAG, __VA_ARGS__))
 #if LOG_NDEBUG
-#define ALOGV(...)          \
-  do {                      \
-    if (0) {                \
-      __ALOGV(__VA_ARGS__); \
-    }                       \
-  } while (0)
+#define ALOGV(...)                   \
+  do {                               \
+    __FAKE_USE_VA_ARGS(__VA_ARGS__); \
+    if (false) {                     \
+      __ALOGV(__VA_ARGS__);          \
+    }                                \
+  } while (false)
 #else
 #define ALOGV(...) __ALOGV(__VA_ARGS__)
 #endif
@@ -187,11 +202,11 @@
 
 #ifndef ALOGV_IF
 #if LOG_NDEBUG
-#define ALOGV_IF(cond, ...) ((void)0)
+#define ALOGV_IF(cond, ...) __FAKE_USE_VA_ARGS(__VA_ARGS__)
 #else
 #define ALOGV_IF(cond, ...)                                                  \
   ((__predict_false(cond)) ? ((void)ALOG(LOG_VERBOSE, LOG_TAG, __VA_ARGS__)) \
-                           : (void)0)
+                           : __FAKE_USE_VA_ARGS(__VA_ARGS__))
 #endif
 #endif
 
@@ -205,7 +220,7 @@
 #ifndef ALOGD_IF
 #define ALOGD_IF(cond, ...)                                                \
   ((__predict_false(cond)) ? ((void)ALOG(LOG_DEBUG, LOG_TAG, __VA_ARGS__)) \
-                           : (void)0)
+                           : __FAKE_USE_VA_ARGS(__VA_ARGS__))
 #endif
 
 /*
@@ -218,7 +233,7 @@
 #ifndef ALOGI_IF
 #define ALOGI_IF(cond, ...)                                               \
   ((__predict_false(cond)) ? ((void)ALOG(LOG_INFO, LOG_TAG, __VA_ARGS__)) \
-                           : (void)0)
+                           : __FAKE_USE_VA_ARGS(__VA_ARGS__))
 #endif
 
 /*
@@ -231,7 +246,7 @@
 #ifndef ALOGW_IF
 #define ALOGW_IF(cond, ...)                                               \
   ((__predict_false(cond)) ? ((void)ALOG(LOG_WARN, LOG_TAG, __VA_ARGS__)) \
-                           : (void)0)
+                           : __FAKE_USE_VA_ARGS(__VA_ARGS__))
 #endif
 
 /*
@@ -244,7 +259,7 @@
 #ifndef ALOGE_IF
 #define ALOGE_IF(cond, ...)                                                \
   ((__predict_false(cond)) ? ((void)ALOG(LOG_ERROR, LOG_TAG, __VA_ARGS__)) \
-                           : (void)0)
+                           : __FAKE_USE_VA_ARGS(__VA_ARGS__))
 #endif
 
 /* --------------------------------------------------------------------- */
@@ -355,11 +370,11 @@
 
 #if LOG_NDEBUG /* Production */
 #define android_testLog(prio, tag)                                           \
-  (__android_log_is_loggable_len(prio, tag, (tag && *tag) ? strlen(tag) : 0, \
+  (__android_log_is_loggable_len(prio, tag, ((tag) && *(tag)) ? strlen(tag) : 0, \
                                  ANDROID_LOG_DEBUG) != 0)
 #else
 #define android_testLog(prio, tag)                                           \
-  (__android_log_is_loggable_len(prio, tag, (tag && *tag) ? strlen(tag) : 0, \
+  (__android_log_is_loggable_len(prio, tag, ((tag) && *(tag)) ? strlen(tag) : 0, \
                                  ANDROID_LOG_VERBOSE) != 0)
 #endif
 
@@ -385,8 +400,6 @@
 #pragma clang diagnostic pop
 #endif
 
-#ifdef __cplusplus
-}
-#endif
+__END_DECLS
 
 #endif /* _LIBS_LOG_LOG_MAIN_H */
diff --git a/liblog/include/log/log_read.h b/liblog/include/log/log_read.h
index d118563..93b9d4e 100644
--- a/liblog/include/log/log_read.h
+++ b/liblog/include/log/log_read.h
@@ -184,7 +184,7 @@
       hdr_size = sizeof(entry_v1);
     }
     if ((hdr_size < sizeof(entry_v1)) || (hdr_size > sizeof(entry))) {
-      return NULL;
+      return nullptr;
     }
     return reinterpret_cast<char*>(buf) + hdr_size;
   }
diff --git a/liblog/include/log/log_safetynet.h b/liblog/include/log/log_safetynet.h
index b8ca475..07e8c8a 100644
--- a/liblog/include/log/log_safetynet.h
+++ b/liblog/include/log/log_safetynet.h
@@ -10,6 +10,8 @@
 #ifndef _LIBS_LOG_SAFETYNET_H
 #define _LIBS_LOG_SAFETYNET_H
 
+#include <stdint.h>
+
 #ifdef __cplusplus
 extern "C" {
 #endif
diff --git a/liblog/include/log/log_time.h b/liblog/include/log/log_time.h
index 3764faf..309f5d1 100644
--- a/liblog/include/log/log_time.h
+++ b/liblog/include/log/log_time.h
@@ -28,6 +28,10 @@
 #ifndef __struct_log_time_defined
 #define __struct_log_time_defined
 
+#define LOG_TIME_SEC(t) ((t)->tv_sec)
+/* next power of two after NS_PER_SEC */
+#define LOG_TIME_NSEC(t) ((t)->tv_nsec & (UINT32_MAX >> 2))
+
 #ifdef __cplusplus
 
 /*
@@ -167,15 +171,15 @@
 #endif
 } __attribute__((__packed__));
 
-#else
+#else /* __cplusplus */
 
 typedef struct log_time {
   uint32_t tv_sec;
   uint32_t tv_nsec;
 } __attribute__((__packed__)) log_time;
 
-#endif
+#endif /* __cplusplus */
 
-#endif
+#endif /* __struct_log_time_defined */
 
 #endif /* _LIBS_LOG_LOG_TIME_H */
diff --git a/liblog/include/private/android_logger.h b/liblog/include/private/android_logger.h
index 965de37..b927b46 100644
--- a/liblog/include/private/android_logger.h
+++ b/liblog/include/private/android_logger.h
@@ -173,7 +173,7 @@
 #if defined(_USING_LIBCXX)
   operator std::string() {
     if (ret) return std::string("");
-    const char* cp = NULL;
+    const char* cp = nullptr;
     ssize_t len = android_log_write_list_buffer(ctx, &cp);
     if (len < 0) ret = len;
     if (!cp || (len <= 0)) return std::string("");
diff --git a/liblog/include_vndk/log/log_event_list.h b/liblog/include_vndk/log/log_event_list.h
new file mode 100644
index 0000000..cbd3091
--- /dev/null
+++ b/liblog/include_vndk/log/log_event_list.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2005-2016 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.
+ */
+
+/* Special log_event_list.h file for VNDK linking modules */
+
+#ifndef _LIBS_LOG_EVENT_LIST_H
+#define _LIBS_LOG_EVENT_LIST_H
+
+#include <stdint.h>
+
+#include <log/log_id.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define __ANDROID_USE_LIBLOG_EVENT_INTERFACE 1
+
+/*
+ * The opaque context used to manipulate lists of events.
+ */
+#ifndef __android_log_context_defined
+#define __android_log_context_defined
+typedef struct android_log_context_internal* android_log_context;
+#endif
+
+/*
+ * Creates a context associated with an event tag to write elements to
+ * the list of events.
+ */
+android_log_context create_android_logger(uint32_t tag);
+
+/* All lists must be braced by a begin and end call */
+/*
+ * NB: If the first level braces are missing when specifying multiple
+ *     elements, we will manufacturer a list to embrace it for your API
+ *     convenience. For a single element, it will remain solitary.
+ */
+int android_log_write_list_begin(android_log_context ctx);
+int android_log_write_list_end(android_log_context ctx);
+
+int android_log_write_int32(android_log_context ctx, int32_t value);
+int android_log_write_int64(android_log_context ctx, int64_t value);
+int android_log_write_string8(android_log_context ctx, const char* value);
+int android_log_write_string8_len(android_log_context ctx, const char* value,
+                                  size_t maxlen);
+int android_log_write_float32(android_log_context ctx, float value);
+
+/* Submit the composed list context to the specified logger id */
+/* NB: LOG_ID_EVENTS and LOG_ID_SECURITY only valid binary buffers */
+int android_log_write_list(android_log_context ctx, log_id_t id);
+
+/* Finished with reader or writer context */
+int android_log_destroy(android_log_context* ctx);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LIBS_LOG_EVENT_LIST_H */
diff --git a/liblog/include_vndk/log/log_time.h b/liblog/include_vndk/log/log_time.h
deleted file mode 120000
index abfe439..0000000
--- a/liblog/include_vndk/log/log_time.h
+++ /dev/null
@@ -1 +0,0 @@
-../../include/log/log_time.h
\ No newline at end of file
diff --git a/liblog/include_vndk/log/log_time.h b/liblog/include_vndk/log/log_time.h
new file mode 100644
index 0000000..5a09959
--- /dev/null
+++ b/liblog/include_vndk/log/log_time.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2005-2017 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 _LIBS_LOG_LOG_TIME_H
+#define _LIBS_LOG_LOG_TIME_H
+
+#include <stdint.h>
+
+/* struct log_time is a wire-format variant of struct timespec */
+#ifndef NS_PER_SEC
+#define NS_PER_SEC 1000000000ULL
+#endif
+#ifndef US_PER_SEC
+#define US_PER_SEC 1000000ULL
+#endif
+#ifndef MS_PER_SEC
+#define MS_PER_SEC 1000ULL
+#endif
+
+#ifndef __struct_log_time_defined
+#define __struct_log_time_defined
+
+#define LOG_TIME_SEC(t) ((t)->tv_sec)
+/* next power of two after NS_PER_SEC */
+#define LOG_TIME_NSEC(t) ((t)->tv_nsec & (UINT32_MAX >> 2))
+
+typedef struct log_time {
+  uint32_t tv_sec;
+  uint32_t tv_nsec;
+} __attribute__((__packed__)) log_time;
+
+#endif
+
+#endif /* _LIBS_LOG_LOG_TIME_H */
diff --git a/liblog/liblog.map.txt b/liblog/liblog.map.txt
index 3c4c1f1..66670fe 100644
--- a/liblog/liblog.map.txt
+++ b/liblog/liblog.map.txt
@@ -3,8 +3,8 @@
     android_name_to_log_id; # vndk
     android_log_id_to_name; # vndk
     __android_log_assert;
-    __android_log_buf_print; # vndk
-    __android_log_buf_write; # vndk
+    __android_log_buf_print;
+    __android_log_buf_write;
     __android_log_print;
     __android_log_vprint;
     __android_log_write;
@@ -35,6 +35,17 @@
     android_logger_get_statistics; # vndk
     __android_log_error_write; # vndk
     __android_log_is_loggable;
+    create_android_logger; #vndk
+    android_log_destroy; #vndk
+    android_log_write_list_begin; #vndk
+    android_log_write_list_end; #vndk
+    android_log_write_int32; #vndk
+    android_log_write_int64; #vndk
+    android_log_write_string8; #vndk
+    android_log_write_string8_len; #vndk
+    android_log_write_float32; #vndk
+    android_log_write_list; #vndk
+
 };
 
 LIBLOG_O {
diff --git a/liblog/local_logger.c b/liblog/local_logger.c
index 522867d..563cb3f 100644
--- a/liblog/local_logger.c
+++ b/liblog/local_logger.c
@@ -222,6 +222,7 @@
       log->last[logId] = node->prev;
     }
     list_remove(node);
+    LOG_ALWAYS_FATAL_IF(node == log->last[logId], "corrupted list");
     free(e);
   }
   /* add entry to list */
diff --git a/liblog/log_event_list.c b/liblog/log_event_list.c
index 59ea5ef..a59cb87 100644
--- a/liblog/log_event_list.c
+++ b/liblog/log_event_list.c
@@ -301,7 +301,7 @@
   const char* msg;
   ssize_t len;
 
-  if ((id != LOG_ID_EVENTS) && (id != LOG_ID_SECURITY)) {
+  if ((id != LOG_ID_EVENTS) && (id != LOG_ID_SECURITY) && (id != LOG_ID_STATS)) {
     return -EINVAL;
   }
 
@@ -326,7 +326,9 @@
   }
   return (id == LOG_ID_EVENTS)
              ? __android_log_bwrite(context->tag, msg, len)
-             : __android_log_security_bwrite(context->tag, msg, len);
+             : ((id == LOG_ID_STATS)
+                    ? __android_log_stats_bwrite(context->tag, msg, len)
+                    : __android_log_security_bwrite(context->tag, msg, len));
 }
 
 LIBLOG_ABI_PRIVATE int android_log_write_list_buffer(android_log_context ctx,
diff --git a/liblog/logd_reader.c b/liblog/logd_reader.c
index 600f4bb..603ba24 100644
--- a/liblog/logd_reader.c
+++ b/liblog/logd_reader.c
@@ -590,20 +590,30 @@
 
   memset(log_msg, 0, sizeof(*log_msg));
 
+  unsigned int new_alarm = 0;
   if (logger_list->mode & ANDROID_LOG_NONBLOCK) {
+    if ((logger_list->mode & ANDROID_LOG_WRAP) &&
+        (logger_list->start.tv_sec || logger_list->start.tv_nsec)) {
+      /* b/64143705 */
+      new_alarm = (ANDROID_LOG_WRAP_DEFAULT_TIMEOUT * 11) / 10 + 10;
+      logger_list->mode &= ~ANDROID_LOG_WRAP;
+    } else {
+      new_alarm = 30;
+    }
+
     memset(&ignore, 0, sizeof(ignore));
     ignore.sa_handler = caught_signal;
     sigemptyset(&ignore.sa_mask);
     /* particularily useful if tombstone is reporting for logd */
     sigaction(SIGALRM, &ignore, &old_sigaction);
-    old_alarm = alarm(30);
+    old_alarm = alarm(new_alarm);
   }
 
   /* NOTE: SOCK_SEQPACKET guarantees we read exactly one full entry */
   ret = recv(ret, log_msg, LOGGER_ENTRY_MAX_LEN, 0);
   e = errno;
 
-  if (logger_list->mode & ANDROID_LOG_NONBLOCK) {
+  if (new_alarm) {
     if ((ret == 0) || (e == EINTR)) {
       e = EAGAIN;
       ret = -1;
diff --git a/liblog/logger_name.c b/liblog/logger_name.c
index a5a83e0..479bbfe 100644
--- a/liblog/logger_name.c
+++ b/liblog/logger_name.c
@@ -22,12 +22,13 @@
 
 /* In the future, we would like to make this list extensible */
 static const char* LOG_NAME[LOG_ID_MAX] = {
-  /* clang-format off */
+      /* clang-format off */
   [LOG_ID_MAIN] = "main",
   [LOG_ID_RADIO] = "radio",
   [LOG_ID_EVENTS] = "events",
   [LOG_ID_SYSTEM] = "system",
   [LOG_ID_CRASH] = "crash",
+  [LOG_ID_STATS] = "stats",
   [LOG_ID_SECURITY] = "security",
   [LOG_ID_KERNEL] = "kernel",
   /* clang-format on */
diff --git a/liblog/logger_write.c b/liblog/logger_write.c
index 84feb20..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,28 +254,33 @@
     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) {
+  } else if (log_id == LOG_ID_EVENTS || log_id == LOG_ID_STATS) {
     const char* tag;
     size_t len;
     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,
@@ -546,6 +557,19 @@
   return write_to_log(LOG_ID_EVENTS, vec, 2);
 }
 
+LIBLOG_ABI_PUBLIC int __android_log_stats_bwrite(int32_t tag,
+                                                 const void* payload,
+                                                 size_t len) {
+  struct iovec vec[2];
+
+  vec[0].iov_base = &tag;
+  vec[0].iov_len = sizeof(tag);
+  vec[1].iov_base = (void*)payload;
+  vec[1].iov_len = len;
+
+  return write_to_log(LOG_ID_STATS, vec, 2);
+}
+
 LIBLOG_ABI_PUBLIC int __android_log_security_bwrite(int32_t tag,
                                                     const void* payload,
                                                     size_t len) {
diff --git a/liblog/logprint.c b/liblog/logprint.c
index b62f8b4..7937cb1 100644
--- a/liblog/logprint.c
+++ b/liblog/logprint.c
@@ -250,6 +250,7 @@
   while (!list_empty(&convertHead)) {
     struct listnode* node = list_head(&convertHead);
     list_remove(node);
+    LOG_ALWAYS_FATAL_IF(node == list_head(&convertHead), "corrupted list");
     free(node);
   }
 }
@@ -1631,8 +1632,10 @@
     prefixLen = snprintf(prefixBuf, sizeof(prefixBuf), "\x1B[38;5;%dm",
                          colorFromPri(entry->priority));
     prefixLen = MIN(prefixLen, sizeof(prefixBuf));
-    suffixLen = snprintf(suffixBuf, sizeof(suffixBuf), "\x1B[0m");
-    suffixLen = MIN(suffixLen, sizeof(suffixBuf));
+
+    const char suffixContents[] = "\x1B[0m";
+    strcpy(suffixBuf, suffixContents);
+    suffixLen = strlen(suffixContents);
   }
 
   char uid[16];
diff --git a/liblog/tests/Android.mk b/liblog/tests/Android.mk
index ab96429..cfa849b 100644
--- a/liblog/tests/Android.mk
+++ b/liblog/tests/Android.mk
@@ -24,13 +24,12 @@
 test_tags := tests
 
 benchmark_c_flags := \
-    -Ibionic/tests \
-    -Wall -Wextra \
+    -Wall \
+    -Wextra \
     -Werror \
     -fno-builtin \
 
 benchmark_src_files := \
-    benchmark_main.cpp \
     liblog_benchmark.cpp
 
 # Build benchmarks for the device. Run with:
@@ -41,7 +40,7 @@
 LOCAL_CFLAGS += $(benchmark_c_flags)
 LOCAL_SHARED_LIBRARIES += liblog libm libbase
 LOCAL_SRC_FILES := $(benchmark_src_files)
-include $(BUILD_NATIVE_TEST)
+include $(BUILD_NATIVE_BENCHMARK)
 
 # -----------------------------------------------------------------------------
 # Unit tests.
@@ -54,7 +53,7 @@
     -Werror \
     -fno-builtin \
 
-test_src_files := \
+cts_src_files := \
     libc_test.cpp \
     liblog_test_default.cpp \
     liblog_test_local.cpp \
@@ -64,7 +63,11 @@
     log_radio_test.cpp \
     log_read_test.cpp \
     log_system_test.cpp \
-    log_time_test.cpp
+    log_time_test.cpp \
+    log_wrap_test.cpp
+
+test_src_files := \
+    $(cts_src_files) \
 
 # Build tests for the device (with .so). Run with:
 #   adb shell /data/nativetest/liblog-unit-tests/liblog-unit-tests
@@ -81,15 +84,15 @@
 include $(CLEAR_VARS)
 LOCAL_MODULE := $(cts_executable)
 LOCAL_MODULE_TAGS := tests
-LOCAL_CFLAGS += $(test_c_flags)
-LOCAL_SRC_FILES := $(test_src_files)
+LOCAL_CFLAGS += $(test_c_flags) -DNO_PSTORE
+LOCAL_SRC_FILES := $(cts_src_files)
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA)/nativetest
 LOCAL_MULTILIB := both
 LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
 LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
 LOCAL_SHARED_LIBRARIES := liblog libcutils libbase
 LOCAL_STATIC_LIBRARIES := libgtest libgtest_main
-LOCAL_COMPATIBILITY_SUITE := cts
+LOCAL_COMPATIBILITY_SUITE := cts vts
 LOCAL_CTS_TEST_PACKAGE := android.core.liblog
 include $(BUILD_CTS_EXECUTABLE)
 
diff --git a/liblog/tests/AndroidTest.xml b/liblog/tests/AndroidTest.xml
index b8d87e6..7b64433 100644
--- a/liblog/tests/AndroidTest.xml
+++ b/liblog/tests/AndroidTest.xml
@@ -14,6 +14,8 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS Logging Library test cases">
+    <option name="test-suite-tag" value="cts" />
+    <option name="config-descriptor:metadata" key="component" value="systems" />
     <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
         <option name="cleanup" value="true" />
         <option name="push" value="CtsLiblogTestCases->/data/local/tmp/CtsLiblogTestCases" />
diff --git a/liblog/tests/benchmark.h b/liblog/tests/benchmark.h
deleted file mode 100644
index e9280f6..0000000
--- a/liblog/tests/benchmark.h
+++ /dev/null
@@ -1,147 +0,0 @@
-/*
- * Copyright (C) 2012-2014 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 <stdint.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include <vector>
-
-#ifndef BIONIC_BENCHMARK_H_
-#define BIONIC_BENCHMARK_H_
-
-namespace testing {
-
-class Benchmark;
-template <typename T> class BenchmarkWantsArg;
-template <typename T> class BenchmarkWithArg;
-
-void BenchmarkRegister(Benchmark* bm);
-int PrettyPrintInt(char* str, int len, unsigned int arg);
-
-class Benchmark {
- public:
-  Benchmark(const char* name, void (*fn)(int)) : name_(strdup(name)), fn_(fn) {
-    BenchmarkRegister(this);
-  }
-  explicit Benchmark(const char* name) : name_(strdup(name)), fn_(NULL) {}
-
-  virtual ~Benchmark() {
-    free(name_);
-  }
-
-  const char* Name() { return name_; }
-  virtual const char* ArgName() { return NULL; }
-  virtual void RunFn(int iterations) { fn_(iterations); }
-
- protected:
-  char* name_;
-
- private:
-  void (*fn_)(int);
-};
-
-template <typename T>
-class BenchmarkWantsArgBase : public Benchmark {
- public:
-  BenchmarkWantsArgBase(const char* name, void (*fn)(int, T)) : Benchmark(name) {
-    fn_arg_ = fn;
-  }
-
-  BenchmarkWantsArgBase<T>* Arg(const char* arg_name, T arg) {
-    BenchmarkRegister(new BenchmarkWithArg<T>(name_, fn_arg_, arg_name, arg));
-    return this;
-  }
-
- protected:
-  virtual void RunFn(int) { printf("can't run arg benchmark %s without arg\n", Name()); }
-  void (*fn_arg_)(int, T);
-};
-
-template <typename T>
-class BenchmarkWithArg : public BenchmarkWantsArg<T> {
- public:
-  BenchmarkWithArg(const char* name, void (*fn)(int, T), const char* arg_name, T arg) :
-      BenchmarkWantsArg<T>(name, fn), arg_(arg) {
-    arg_name_ = strdup(arg_name);
-  }
-
-  virtual ~BenchmarkWithArg() {
-    free(arg_name_);
-  }
-
-  virtual const char* ArgName() { return arg_name_; }
-
- protected:
-  virtual void RunFn(int iterations) { BenchmarkWantsArg<T>::fn_arg_(iterations, arg_); }
-
- private:
-  T arg_;
-  char* arg_name_;
-};
-
-template <typename T>
-class BenchmarkWantsArg : public BenchmarkWantsArgBase<T> {
- public:
-  BenchmarkWantsArg<T>(const char* name, void (*fn)(int, T)) :
-    BenchmarkWantsArgBase<T>(name, fn) { }
-};
-
-template <>
-class BenchmarkWantsArg<int> : public BenchmarkWantsArgBase<int> {
- public:
-  BenchmarkWantsArg<int>(const char* name, void (*fn)(int, int)) :
-    BenchmarkWantsArgBase<int>(name, fn) { }
-
-  BenchmarkWantsArg<int>* Arg(int arg) {
-    char arg_name[100];
-    PrettyPrintInt(arg_name, sizeof(arg_name), arg);
-    BenchmarkRegister(new BenchmarkWithArg<int>(name_, fn_arg_, arg_name, arg));
-    return this;
-  }
-};
-
-static inline Benchmark* BenchmarkFactory(const char* name, void (*fn)(int)) {
-  return new Benchmark(name, fn);
-}
-
-template <typename T>
-static inline BenchmarkWantsArg<T>* BenchmarkFactory(const char* name, void (*fn)(int, T)) {
-  return new BenchmarkWantsArg<T>(name, fn);
-}
-
-}  // namespace testing
-
-template <typename T>
-static inline void BenchmarkAddArg(::testing::Benchmark* b, const char* name, T arg) {
-  ::testing::BenchmarkWantsArg<T>* ba;
-  ba = static_cast< ::testing::BenchmarkWantsArg<T>* >(b);
-  ba->Arg(name, arg);
-}
-
-void SetBenchmarkBytesProcessed(uint64_t);
-void ResetBenchmarkTiming(void);
-void StopBenchmarkTiming(void);
-void StartBenchmarkTiming(void);
-void StartBenchmarkTiming(uint64_t);
-void StopBenchmarkTiming(uint64_t);
-
-#define BENCHMARK(f) \
-    static ::testing::Benchmark* _benchmark_##f __attribute__((unused)) = /* NOLINT */ \
-        (::testing::Benchmark*)::testing::BenchmarkFactory(#f, f) /* NOLINT */
-
-#endif // BIONIC_BENCHMARK_H_
diff --git a/liblog/tests/benchmark_main.cpp b/liblog/tests/benchmark_main.cpp
deleted file mode 100644
index 7367f1b..0000000
--- a/liblog/tests/benchmark_main.cpp
+++ /dev/null
@@ -1,247 +0,0 @@
-/*
- * Copyright (C) 2012-2014 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 <benchmark.h>
-
-#include <inttypes.h>
-#include <math.h>
-#include <regex.h>
-#include <stdio.h>
-#include <stdlib.h>
-
-#include <map>
-#include <string>
-#include <vector>
-
-static uint64_t gBytesProcessed;
-static uint64_t gBenchmarkTotalTimeNs;
-static uint64_t gBenchmarkTotalTimeNsSquared;
-static uint64_t gBenchmarkNum;
-static uint64_t gBenchmarkStartTimeNs;
-
-typedef std::vector< ::testing::Benchmark*> BenchmarkList;
-static BenchmarkList* gBenchmarks;
-
-static int Round(int n) {
-  int base = 1;
-  while (base * 10 < n) {
-    base *= 10;
-  }
-  if (n < 2 * base) {
-    return 2 * base;
-  }
-  if (n < 5 * base) {
-    return 5 * base;
-  }
-  return 10 * base;
-}
-
-static uint64_t NanoTime() {
-  struct timespec t;
-  t.tv_sec = t.tv_nsec = 0;
-  clock_gettime(CLOCK_MONOTONIC, &t);
-  return static_cast<uint64_t>(t.tv_sec) * 1000000000ULL + t.tv_nsec;
-}
-
-namespace testing {
-
-int PrettyPrintInt(char* str, int len, unsigned int arg) {
-  if (arg >= (1 << 30) && arg % (1 << 30) == 0) {
-    return snprintf(str, len, "%uGi", arg / (1 << 30));
-  } else if (arg >= (1 << 20) && arg % (1 << 20) == 0) {
-    return snprintf(str, len, "%uMi", arg / (1 << 20));
-  } else if (arg >= (1 << 10) && arg % (1 << 10) == 0) {
-    return snprintf(str, len, "%uKi", arg / (1 << 10));
-  } else if (arg >= 1000000000 && arg % 1000000000 == 0) {
-    return snprintf(str, len, "%uG", arg / 1000000000);
-  } else if (arg >= 1000000 && arg % 1000000 == 0) {
-    return snprintf(str, len, "%uM", arg / 1000000);
-  } else if (arg >= 1000 && arg % 1000 == 0) {
-    return snprintf(str, len, "%uK", arg / 1000);
-  } else {
-    return snprintf(str, len, "%u", arg);
-  }
-}
-
-bool ShouldRun(Benchmark* b, int argc, char* argv[]) {
-  if (argc == 1) {
-    return true;  // With no arguments, we run all benchmarks.
-  }
-  // Otherwise, we interpret each argument as a regular expression and
-  // see if any of our benchmarks match.
-  for (int i = 1; i < argc; i++) {
-    regex_t re;
-    if (regcomp(&re, argv[i], 0) != 0) {
-      fprintf(stderr, "couldn't compile \"%s\" as a regular expression!\n",
-              argv[i]);
-      exit(EXIT_FAILURE);
-    }
-    int match = regexec(&re, b->Name(), 0, NULL, 0);
-    regfree(&re);
-    if (match != REG_NOMATCH) {
-      return true;
-    }
-  }
-  return false;
-}
-
-void BenchmarkRegister(Benchmark* b) {
-  if (gBenchmarks == NULL) {
-    gBenchmarks = new BenchmarkList;
-  }
-  gBenchmarks->push_back(b);
-}
-
-void RunRepeatedly(Benchmark* b, int iterations) {
-  gBytesProcessed = 0;
-  ResetBenchmarkTiming();
-  uint64_t StartTimeNs = NanoTime();
-  b->RunFn(iterations);
-  // Catch us if we fail to log anything.
-  if ((gBenchmarkTotalTimeNs == 0) && (StartTimeNs != 0) &&
-      (gBenchmarkStartTimeNs == 0)) {
-    gBenchmarkTotalTimeNs = NanoTime() - StartTimeNs;
-  }
-}
-
-void Run(Benchmark* b) {
-  // run once in case it's expensive
-  unsigned iterations = 1;
-  uint64_t s = NanoTime();
-  RunRepeatedly(b, iterations);
-  s = NanoTime() - s;
-  while (s < 2e9 && gBenchmarkTotalTimeNs < 1e9 && iterations < 1e9) {
-    unsigned last = iterations;
-    if (gBenchmarkTotalTimeNs / iterations == 0) {
-      iterations = 1e9;
-    } else {
-      iterations = 1e9 / (gBenchmarkTotalTimeNs / iterations);
-    }
-    iterations =
-        std::max(last + 1, std::min(iterations + iterations / 2, 100 * last));
-    iterations = Round(iterations);
-    s = NanoTime();
-    RunRepeatedly(b, iterations);
-    s = NanoTime() - s;
-  }
-
-  char throughput[100];
-  throughput[0] = '\0';
-  if (gBenchmarkTotalTimeNs > 0 && gBytesProcessed > 0) {
-    double mib_processed = static_cast<double>(gBytesProcessed) / 1e6;
-    double seconds = static_cast<double>(gBenchmarkTotalTimeNs) / 1e9;
-    snprintf(throughput, sizeof(throughput), " %8.2f MiB/s",
-             mib_processed / seconds);
-  }
-
-  char full_name[100];
-  snprintf(full_name, sizeof(full_name), "%s%s%s", b->Name(),
-           b->ArgName() ? "/" : "", b->ArgName() ? b->ArgName() : "");
-
-  uint64_t mean = gBenchmarkTotalTimeNs / iterations;
-  uint64_t sdev = 0;
-  if (gBenchmarkNum == iterations) {
-    mean = gBenchmarkTotalTimeNs / gBenchmarkNum;
-    uint64_t nXvariance = gBenchmarkTotalTimeNsSquared * gBenchmarkNum -
-                          (gBenchmarkTotalTimeNs * gBenchmarkTotalTimeNs);
-    sdev = (sqrt((double)nXvariance) / gBenchmarkNum / gBenchmarkNum) + 0.5;
-  }
-  if (mean > (10000 * sdev)) {
-    printf("%-25s %10" PRIu64 " %10" PRIu64 "%s\n", full_name,
-           static_cast<uint64_t>(iterations), mean, throughput);
-  } else {
-    printf("%-25s %10" PRIu64 " %10" PRIu64 "(\317\203%" PRIu64 ")%s\n",
-           full_name, static_cast<uint64_t>(iterations), mean, sdev, throughput);
-  }
-  fflush(stdout);
-}
-
-}  // namespace testing
-
-void SetBenchmarkBytesProcessed(uint64_t x) {
-  gBytesProcessed = x;
-}
-
-void ResetBenchmarkTiming() {
-  gBenchmarkStartTimeNs = 0;
-  gBenchmarkTotalTimeNs = 0;
-  gBenchmarkTotalTimeNsSquared = 0;
-  gBenchmarkNum = 0;
-}
-
-void StopBenchmarkTiming(void) {
-  if (gBenchmarkStartTimeNs != 0) {
-    int64_t diff = NanoTime() - gBenchmarkStartTimeNs;
-    gBenchmarkTotalTimeNs += diff;
-    gBenchmarkTotalTimeNsSquared += diff * diff;
-    ++gBenchmarkNum;
-  }
-  gBenchmarkStartTimeNs = 0;
-}
-
-void StartBenchmarkTiming(void) {
-  if (gBenchmarkStartTimeNs == 0) {
-    gBenchmarkStartTimeNs = NanoTime();
-  }
-}
-
-void StopBenchmarkTiming(uint64_t NanoTime) {
-  if (gBenchmarkStartTimeNs != 0) {
-    int64_t diff = NanoTime - gBenchmarkStartTimeNs;
-    gBenchmarkTotalTimeNs += diff;
-    gBenchmarkTotalTimeNsSquared += diff * diff;
-    if (NanoTime != 0) {
-      ++gBenchmarkNum;
-    }
-  }
-  gBenchmarkStartTimeNs = 0;
-}
-
-void StartBenchmarkTiming(uint64_t NanoTime) {
-  if (gBenchmarkStartTimeNs == 0) {
-    gBenchmarkStartTimeNs = NanoTime;
-  }
-}
-
-int main(int argc, char* argv[]) {
-  if (gBenchmarks->empty()) {
-    fprintf(stderr, "No benchmarks registered!\n");
-    exit(EXIT_FAILURE);
-  }
-
-  bool need_header = true;
-  for (auto b : *gBenchmarks) {
-    if (ShouldRun(b, argc, argv)) {
-      if (need_header) {
-        printf("%-25s %10s %10s\n", "", "iterations", "ns/op");
-        fflush(stdout);
-        need_header = false;
-      }
-      Run(b);
-    }
-  }
-
-  if (need_header) {
-    fprintf(stderr, "No matching benchmarks!\n");
-    fprintf(stderr, "Available benchmarks:\n");
-    for (auto b : *gBenchmarks) {
-      fprintf(stderr, "  %s\n", b->Name());
-    }
-    exit(EXIT_FAILURE);
-  }
-
-  return 0;
-}
diff --git a/liblog/tests/libc_test.cpp b/liblog/tests/libc_test.cpp
index 329ba85..6d78ed6 100644
--- a/liblog/tests/libc_test.cpp
+++ b/liblog/tests/libc_test.cpp
@@ -21,6 +21,7 @@
 
 TEST(libc, __pstore_append) {
 #ifdef __ANDROID__
+#ifndef NO_PSTORE
   FILE* fp;
   ASSERT_TRUE(NULL != (fp = fopen("/dev/pmsg0", "a")));
   static const char message[] = "libc.__pstore_append\n";
@@ -43,6 +44,9 @@
             "Reboot, ensure string libc.__pstore_append is in "
             "/sys/fs/pstore/pmsg-ramoops-0\n");
   }
+#else  /* NO_PSTORE */
+  GTEST_LOG_(INFO) << "This test does nothing because of NO_PSTORE.\n";
+#endif /* NO_PSTORE */
 #else
   GTEST_LOG_(INFO) << "This test does nothing.\n";
 #endif
diff --git a/liblog/tests/liblog_benchmark.cpp b/liblog/tests/liblog_benchmark.cpp
index c4bf959..c2f3f83 100644
--- a/liblog/tests/liblog_benchmark.cpp
+++ b/liblog/tests/liblog_benchmark.cpp
@@ -19,18 +19,20 @@
 #include <poll.h>
 #include <sys/endian.h>
 #include <sys/socket.h>
+#include <sys/syscall.h>
 #include <sys/types.h>
 #include <unistd.h>
 
 #include <unordered_set>
 
 #include <android-base/file.h>
+#include <benchmark/benchmark.h>
 #include <cutils/sockets.h>
 #include <log/event_tag_map.h>
 #include <log/log_transport.h>
 #include <private/android_logger.h>
 
-#include "benchmark.h"
+BENCHMARK_MAIN();
 
 // enhanced version of LOG_FAILURE_RETRY to add support for EAGAIN and
 // non-syscall libs. Since we are benchmarking, or using this in the emergency
@@ -51,15 +53,11 @@
  * the log at high pressure. Expect this to be less than double the process
  * wakeup time (2ms?)
  */
-static void BM_log_maximum_retry(int iters) {
-  StartBenchmarkTiming();
-
-  for (int i = 0; i < iters; ++i) {
-    LOG_FAILURE_RETRY(
-        __android_log_print(ANDROID_LOG_INFO, "BM_log_maximum_retry", "%d", i));
+static void BM_log_maximum_retry(benchmark::State& state) {
+  while (state.KeepRunning()) {
+    LOG_FAILURE_RETRY(__android_log_print(
+        ANDROID_LOG_INFO, "BM_log_maximum_retry", "%zu", state.iterations()));
   }
-
-  StopBenchmarkTiming();
 }
 BENCHMARK(BM_log_maximum_retry);
 
@@ -68,14 +66,11 @@
  * at high pressure. Expect this to be less than double the process wakeup
  * time (2ms?)
  */
-static void BM_log_maximum(int iters) {
-  StartBenchmarkTiming();
-
-  for (int i = 0; i < iters; ++i) {
-    __android_log_print(ANDROID_LOG_INFO, "BM_log_maximum", "%d", i);
+static void BM_log_maximum(benchmark::State& state) {
+  while (state.KeepRunning()) {
+    __android_log_print(ANDROID_LOG_INFO, "BM_log_maximum", "%zu",
+                        state.iterations());
   }
-
-  StopBenchmarkTiming();
 }
 BENCHMARK(BM_log_maximum);
 
@@ -87,36 +82,98 @@
   android_set_log_transport(LOGGER_DEFAULT);
 }
 
-static void BM_log_maximum_null(int iters) {
+static void BM_log_maximum_null(benchmark::State& state) {
   set_log_null();
-  BM_log_maximum(iters);
+  BM_log_maximum(state);
   set_log_default();
 }
 BENCHMARK(BM_log_maximum_null);
 
 /*
  *	Measure the time it takes to collect the time using
- * discrete acquisition (StartBenchmarkTiming() -> StopBenchmarkTiming())
+ * discrete acquisition (state.PauseTiming() to state.ResumeTiming())
  * under light load. Expect this to be a syscall period (2us) or
  * data read time if zero-syscall.
  *
  * vdso support in the kernel and the library can allow
- * clock_gettime to be zero-syscall.
+ * clock_gettime to be zero-syscall, but there there does remain some
+ * benchmarking overhead to pause and resume; assumptions are both are
+ * covered.
  */
-static void BM_clock_overhead(int iters) {
-  for (int i = 0; i < iters; ++i) {
-    StartBenchmarkTiming();
-    StopBenchmarkTiming();
+static void BM_clock_overhead(benchmark::State& state) {
+  while (state.KeepRunning()) {
+    state.PauseTiming();
+    state.ResumeTiming();
   }
 }
 BENCHMARK(BM_clock_overhead);
 
+static void do_clock_overhead(benchmark::State& state, clockid_t clk_id) {
+  timespec t;
+  while (state.KeepRunning()) {
+    clock_gettime(clk_id, &t);
+  }
+}
+
+static void BM_time_clock_gettime_REALTIME(benchmark::State& state) {
+  do_clock_overhead(state, CLOCK_REALTIME);
+}
+BENCHMARK(BM_time_clock_gettime_REALTIME);
+
+static void BM_time_clock_gettime_MONOTONIC(benchmark::State& state) {
+  do_clock_overhead(state, CLOCK_MONOTONIC);
+}
+BENCHMARK(BM_time_clock_gettime_MONOTONIC);
+
+static void BM_time_clock_gettime_MONOTONIC_syscall(benchmark::State& state) {
+  timespec t;
+  while (state.KeepRunning()) {
+    syscall(__NR_clock_gettime, CLOCK_MONOTONIC, &t);
+  }
+}
+BENCHMARK(BM_time_clock_gettime_MONOTONIC_syscall);
+
+static void BM_time_clock_gettime_MONOTONIC_RAW(benchmark::State& state) {
+  do_clock_overhead(state, CLOCK_MONOTONIC_RAW);
+}
+BENCHMARK(BM_time_clock_gettime_MONOTONIC_RAW);
+
+static void BM_time_clock_gettime_BOOTTIME(benchmark::State& state) {
+  do_clock_overhead(state, CLOCK_BOOTTIME);
+}
+BENCHMARK(BM_time_clock_gettime_BOOTTIME);
+
+static void BM_time_clock_getres_MONOTONIC(benchmark::State& state) {
+  timespec t;
+  while (state.KeepRunning()) {
+    clock_getres(CLOCK_MONOTONIC, &t);
+  }
+}
+BENCHMARK(BM_time_clock_getres_MONOTONIC);
+
+static void BM_time_clock_getres_MONOTONIC_syscall(benchmark::State& state) {
+  timespec t;
+  while (state.KeepRunning()) {
+    syscall(__NR_clock_getres, CLOCK_MONOTONIC, &t);
+  }
+}
+BENCHMARK(BM_time_clock_getres_MONOTONIC_syscall);
+
+static void BM_time_time(benchmark::State& state) {
+  while (state.KeepRunning()) {
+    time_t now;
+    now = time(&now);
+  }
+}
+BENCHMARK(BM_time_time);
+
 /*
  * Measure the time it takes to submit the android logging data to pstore
  */
-static void BM_pmsg_short(int iters) {
+static void BM_pmsg_short(benchmark::State& state) {
   int pstore_fd = TEMP_FAILURE_RETRY(open("/dev/pmsg0", O_WRONLY));
   if (pstore_fd < 0) {
+    state.SkipWithError("/dev/pmsg0");
     return;
   }
 
@@ -175,13 +232,12 @@
   newVec[2].iov_base = &buffer;
   newVec[2].iov_len = sizeof(buffer);
 
-  StartBenchmarkTiming();
-  for (int i = 0; i < iters; ++i) {
+  while (state.KeepRunning()) {
     ++snapshot;
     buffer.payload.data = htole32(snapshot);
     writev(pstore_fd, newVec, nr);
   }
-  StopBenchmarkTiming();
+  state.PauseTiming();
   close(pstore_fd);
 }
 BENCHMARK(BM_pmsg_short);
@@ -190,9 +246,10 @@
  * Measure the time it takes to submit the android logging data to pstore
  * best case aligned single block.
  */
-static void BM_pmsg_short_aligned(int iters) {
+static void BM_pmsg_short_aligned(benchmark::State& state) {
   int pstore_fd = TEMP_FAILURE_RETRY(open("/dev/pmsg0", O_WRONLY));
   if (pstore_fd < 0) {
+    state.SkipWithError("/dev/pmsg0");
     return;
   }
 
@@ -228,7 +285,8 @@
   memset(buf, 0, sizeof(buf));
   struct packet* buffer = (struct packet*)(((uintptr_t)buf + 7) & ~7);
   if (((uintptr_t)&buffer->pmsg_header) & 7) {
-    fprintf(stderr, "&buffer=0x%p iters=%d\n", &buffer->pmsg_header, iters);
+    fprintf(stderr, "&buffer=0x%p iterations=%zu\n", &buffer->pmsg_header,
+            state.iterations());
   }
 
   buffer->pmsg_header.magic = LOGGER_MAGIC;
@@ -247,15 +305,14 @@
   uint32_t snapshot = 0;
   buffer->payload.payload.data = htole32(snapshot);
 
-  StartBenchmarkTiming();
-  for (int i = 0; i < iters; ++i) {
+  while (state.KeepRunning()) {
     ++snapshot;
     buffer->payload.payload.data = htole32(snapshot);
     write(pstore_fd, &buffer->pmsg_header,
           sizeof(android_pmsg_log_header_t) + sizeof(android_log_header_t) +
               sizeof(android_log_event_int_t));
   }
-  StopBenchmarkTiming();
+  state.PauseTiming();
   close(pstore_fd);
 }
 BENCHMARK(BM_pmsg_short_aligned);
@@ -264,9 +321,10 @@
  * Measure the time it takes to submit the android logging data to pstore
  * best case aligned single block.
  */
-static void BM_pmsg_short_unaligned1(int iters) {
+static void BM_pmsg_short_unaligned1(benchmark::State& state) {
   int pstore_fd = TEMP_FAILURE_RETRY(open("/dev/pmsg0", O_WRONLY));
   if (pstore_fd < 0) {
+    state.SkipWithError("/dev/pmsg0");
     return;
   }
 
@@ -302,7 +360,8 @@
   memset(buf, 0, sizeof(buf));
   struct packet* buffer = (struct packet*)((((uintptr_t)buf + 7) & ~7) + 1);
   if ((((uintptr_t)&buffer->pmsg_header) & 7) != 1) {
-    fprintf(stderr, "&buffer=0x%p iters=%d\n", &buffer->pmsg_header, iters);
+    fprintf(stderr, "&buffer=0x%p iterations=%zu\n", &buffer->pmsg_header,
+            state.iterations());
   }
 
   buffer->pmsg_header.magic = LOGGER_MAGIC;
@@ -321,15 +380,14 @@
   uint32_t snapshot = 0;
   buffer->payload.payload.data = htole32(snapshot);
 
-  StartBenchmarkTiming();
-  for (int i = 0; i < iters; ++i) {
+  while (state.KeepRunning()) {
     ++snapshot;
     buffer->payload.payload.data = htole32(snapshot);
     write(pstore_fd, &buffer->pmsg_header,
           sizeof(android_pmsg_log_header_t) + sizeof(android_log_header_t) +
               sizeof(android_log_event_int_t));
   }
-  StopBenchmarkTiming();
+  state.PauseTiming();
   close(pstore_fd);
 }
 BENCHMARK(BM_pmsg_short_unaligned1);
@@ -338,9 +396,10 @@
  * Measure the time it takes to submit the android logging data to pstore
  * best case aligned single block.
  */
-static void BM_pmsg_long_aligned(int iters) {
+static void BM_pmsg_long_aligned(benchmark::State& state) {
   int pstore_fd = TEMP_FAILURE_RETRY(open("/dev/pmsg0", O_WRONLY));
   if (pstore_fd < 0) {
+    state.SkipWithError("/dev/pmsg0");
     return;
   }
 
@@ -376,7 +435,8 @@
   memset(buf, 0, sizeof(buf));
   struct packet* buffer = (struct packet*)(((uintptr_t)buf + 7) & ~7);
   if (((uintptr_t)&buffer->pmsg_header) & 7) {
-    fprintf(stderr, "&buffer=0x%p iters=%d\n", &buffer->pmsg_header, iters);
+    fprintf(stderr, "&buffer=0x%p iterations=%zu\n", &buffer->pmsg_header,
+            state.iterations());
   }
 
   buffer->pmsg_header.magic = LOGGER_MAGIC;
@@ -395,13 +455,12 @@
   uint32_t snapshot = 0;
   buffer->payload.payload.data = htole32(snapshot);
 
-  StartBenchmarkTiming();
-  for (int i = 0; i < iters; ++i) {
+  while (state.KeepRunning()) {
     ++snapshot;
     buffer->payload.payload.data = htole32(snapshot);
     write(pstore_fd, &buffer->pmsg_header, LOGGER_ENTRY_MAX_PAYLOAD);
   }
-  StopBenchmarkTiming();
+  state.PauseTiming();
   close(pstore_fd);
 }
 BENCHMARK(BM_pmsg_long_aligned);
@@ -410,9 +469,10 @@
  * Measure the time it takes to submit the android logging data to pstore
  * best case aligned single block.
  */
-static void BM_pmsg_long_unaligned1(int iters) {
+static void BM_pmsg_long_unaligned1(benchmark::State& state) {
   int pstore_fd = TEMP_FAILURE_RETRY(open("/dev/pmsg0", O_WRONLY));
   if (pstore_fd < 0) {
+    state.SkipWithError("/dev/pmsg0");
     return;
   }
 
@@ -448,7 +508,8 @@
   memset(buf, 0, sizeof(buf));
   struct packet* buffer = (struct packet*)((((uintptr_t)buf + 7) & ~7) + 1);
   if ((((uintptr_t)&buffer->pmsg_header) & 7) != 1) {
-    fprintf(stderr, "&buffer=0x%p iters=%d\n", &buffer->pmsg_header, iters);
+    fprintf(stderr, "&buffer=0x%p iterations=%zu\n", &buffer->pmsg_header,
+            state.iterations());
   }
 
   buffer->pmsg_header.magic = LOGGER_MAGIC;
@@ -467,22 +528,20 @@
   uint32_t snapshot = 0;
   buffer->payload.payload.data = htole32(snapshot);
 
-  StartBenchmarkTiming();
-  for (int i = 0; i < iters; ++i) {
+  while (state.KeepRunning()) {
     ++snapshot;
     buffer->payload.payload.data = htole32(snapshot);
     write(pstore_fd, &buffer->pmsg_header, LOGGER_ENTRY_MAX_PAYLOAD);
   }
-  StopBenchmarkTiming();
+  state.PauseTiming();
   close(pstore_fd);
 }
 BENCHMARK(BM_pmsg_long_unaligned1);
 
 /*
  *	Measure the time it takes to form sprintf plus time using
- * discrete acquisition (StartBenchmarkTiming() -> StopBenchmarkTiming())
- * under light load. Expect this to be a syscall period (2us) or sprintf
- * time if zero-syscall time.
+ * discrete acquisition under light load. Expect this to be a syscall period
+ * (2us) or sprintf time if zero-syscall time.
  */
 /* helper function */
 static void test_print(const char* fmt, ...) {
@@ -498,58 +557,55 @@
 #define logd_sleep() usleep(50)     // really allow logd to catch up
 
 /* performance test */
-static void BM_sprintf_overhead(int iters) {
-  for (int i = 0; i < iters; ++i) {
-    StartBenchmarkTiming();
-    test_print("BM_sprintf_overhead:%d", i);
-    StopBenchmarkTiming();
+static void BM_sprintf_overhead(benchmark::State& state) {
+  while (state.KeepRunning()) {
+    test_print("BM_sprintf_overhead:%zu", state.iterations());
+    state.PauseTiming();
     logd_yield();
+    state.ResumeTiming();
   }
 }
 BENCHMARK(BM_sprintf_overhead);
 
 /*
  *	Measure the time it takes to submit the android printing logging call
- * using discrete acquisition discrete acquisition (StartBenchmarkTiming() ->
- * StopBenchmarkTiming()) under light load. Expect this to be a dozen or so
- * syscall periods (40us) plus time to run *printf
+ * using discrete acquisition discrete acquisition under light load. Expect
+ * this to be a dozen or so syscall periods (40us) plus time to run *printf
  */
-static void BM_log_print_overhead(int iters) {
-  for (int i = 0; i < iters; ++i) {
-    StartBenchmarkTiming();
-    __android_log_print(ANDROID_LOG_INFO, "BM_log_overhead", "%d", i);
-    StopBenchmarkTiming();
+static void BM_log_print_overhead(benchmark::State& state) {
+  while (state.KeepRunning()) {
+    __android_log_print(ANDROID_LOG_INFO, "BM_log_overhead", "%zu",
+                        state.iterations());
+    state.PauseTiming();
     logd_yield();
+    state.ResumeTiming();
   }
 }
 BENCHMARK(BM_log_print_overhead);
 
 /*
  *	Measure the time it takes to submit the android event logging call
- * using discrete acquisition (StartBenchmarkTiming() -> StopBenchmarkTiming())
- * under light load. Expect this to be a long path to logger to convert the
- * unknown tag (0) into a tagname (less than 200us).
+ * using discrete acquisition under light load. Expect this to be a long path
+ * to logger to convert the unknown tag (0) into a tagname (less than 200us).
  */
-static void BM_log_event_overhead(int iters) {
-  for (unsigned long long i = 0; i < (unsigned)iters; ++i) {
-    StartBenchmarkTiming();
+static void BM_log_event_overhead(benchmark::State& state) {
+  for (int64_t i = 0; state.KeepRunning(); ++i) {
     // log tag number 0 is not known, nor shall it ever be known
     __android_log_btwrite(0, EVENT_TYPE_LONG, &i, sizeof(i));
-    StopBenchmarkTiming();
+    state.PauseTiming();
     logd_yield();
+    state.ResumeTiming();
   }
 }
 BENCHMARK(BM_log_event_overhead);
 
 /*
  *	Measure the time it takes to submit the android event logging call
- * using discrete acquisition (StartBenchmarkTiming() -> StopBenchmarkTiming())
- * under light load with a known logtag.  Expect this to be a dozen or so
- * syscall periods (less than 40us)
+ * using discrete acquisition under light load with a known logtag.  Expect
+ * this to be a dozen or so syscall periods (less than 40us)
  */
-static void BM_log_event_overhead_42(int iters) {
-  for (unsigned long long i = 0; i < (unsigned)iters; ++i) {
-    StartBenchmarkTiming();
+static void BM_log_event_overhead_42(benchmark::State& state) {
+  for (int64_t i = 0; state.KeepRunning(); ++i) {
     // In system/core/logcat/event.logtags:
     // # These are used for testing, do not modify without updating
     // # tests/framework-tests/src/android/util/EventLogFunctionalTest.java.
@@ -557,40 +613,42 @@
     // # system/core/liblog/tests/liblog_test.cpp
     // 42    answer (to life the universe etc|3)
     __android_log_btwrite(42, EVENT_TYPE_LONG, &i, sizeof(i));
-    StopBenchmarkTiming();
+    state.PauseTiming();
     logd_yield();
+    state.ResumeTiming();
   }
 }
 BENCHMARK(BM_log_event_overhead_42);
 
-static void BM_log_event_overhead_null(int iters) {
+static void BM_log_event_overhead_null(benchmark::State& state) {
   set_log_null();
-  BM_log_event_overhead(iters);
+  BM_log_event_overhead(state);
   set_log_default();
 }
 BENCHMARK(BM_log_event_overhead_null);
 
 /*
  *	Measure the time it takes to submit the android event logging call
- * using discrete acquisition (StartBenchmarkTiming() -> StopBenchmarkTiming())
- * under very-light load (<1% CPU utilization).
+ * using discrete acquisition under very-light load (<1% CPU utilization).
  */
-static void BM_log_light_overhead(int iters) {
-  for (unsigned long long i = 0; i < (unsigned)iters; ++i) {
-    StartBenchmarkTiming();
+static void BM_log_light_overhead(benchmark::State& state) {
+  for (int64_t i = 0; state.KeepRunning(); ++i) {
     __android_log_btwrite(0, EVENT_TYPE_LONG, &i, sizeof(i));
-    StopBenchmarkTiming();
+    state.PauseTiming();
     usleep(10000);
+    state.ResumeTiming();
   }
 }
 BENCHMARK(BM_log_light_overhead);
 
-static void BM_log_light_overhead_null(int iters) {
+static void BM_log_light_overhead_null(benchmark::State& state) {
   set_log_null();
-  BM_log_light_overhead(iters);
+  BM_log_light_overhead(state);
   set_log_default();
 }
-BENCHMARK(BM_log_light_overhead_null);
+// Default gets out of hand for this test, so we set a reasonable number of
+// iterations for a timely result.
+BENCHMARK(BM_log_light_overhead_null)->Iterations(500);
 
 static void caught_latency(int /*signum*/) {
   unsigned long long v = 0xDEADBEEFA55A5AA5ULL;
@@ -614,10 +672,12 @@
 
 /*
  *	Measure the time it takes for the logd posting call to acquire the
- * timestamp to place into the internal record. Expect this to be less than
- * 4 syscalls (3us).
+ * timestamp to place into the internal record.  Expect this to be less than
+ * 4 syscalls (3us).  This test uses manual injection of timing because it is
+ * comparing the timestamp at send, and then picking up the corresponding log
+ * end-to-end long path from logd to see what actual timestamp was submitted.
  */
-static void BM_log_latency(int iters) {
+static void BM_log_latency(benchmark::State& state) {
   pid_t pid = getpid();
 
   struct logger_list* logger_list =
@@ -631,7 +691,8 @@
   signal(SIGALRM, caught_latency);
   alarm(alarm_time);
 
-  for (int j = 0, i = 0; i < iters && j < 10 * iters; ++i, ++j) {
+  for (size_t j = 0; state.KeepRunning() && j < 10 * state.iterations(); ++j) {
+  retry:  // We allow transitory errors (logd overloaded) to be retried.
     log_time ts;
     LOG_FAILURE_RETRY((ts = log_time(CLOCK_REALTIME),
                        android_btWriteLog(0, EVENT_TYPE_LONG, &ts, sizeof(ts))));
@@ -642,7 +703,7 @@
       alarm(alarm_time);
 
       if (ret <= 0) {
-        iters = i;
+        state.SkipWithError("android_logger_list_read");
         break;
       }
       if ((log_msg.entry.len != (4 + 1 + 8)) ||
@@ -658,7 +719,7 @@
       log_time tx(eventData + 4 + 1);
       if (ts != tx) {
         if (0xDEADBEEFA55A5AA5ULL == caught_convert(eventData + 4 + 1)) {
-          iters = i;
+          state.SkipWithError("signal");
           break;
         }
         continue;
@@ -666,12 +727,8 @@
 
       uint64_t start = ts.nsec();
       uint64_t end = log_msg.nsec();
-      if (end >= start) {
-        StartBenchmarkTiming(start);
-        StopBenchmarkTiming(end);
-      } else {
-        --i;
-      }
+      if (end < start) goto retry;
+      state.SetIterationTime((end - start) / (double)NS_PER_SEC);
       break;
     }
   }
@@ -681,7 +738,9 @@
 
   android_logger_list_free(logger_list);
 }
-BENCHMARK(BM_log_latency);
+// Default gets out of hand for this test, so we set a reasonable number of
+// iterations for a timely result.
+BENCHMARK(BM_log_latency)->UseManualTime()->Iterations(200);
 
 static void caught_delay(int /*signum*/) {
   unsigned long long v = 0xDEADBEEFA55A5AA6ULL;
@@ -693,7 +752,7 @@
  *	Measure the time it takes for the logd posting call to make it into
  * the logs. Expect this to be less than double the process wakeup time (2ms).
  */
-static void BM_log_delay(int iters) {
+static void BM_log_delay(benchmark::State& state) {
   pid_t pid = getpid();
 
   struct logger_list* logger_list =
@@ -707,9 +766,7 @@
   signal(SIGALRM, caught_delay);
   alarm(alarm_time);
 
-  StartBenchmarkTiming();
-
-  for (int i = 0; i < iters; ++i) {
+  while (state.KeepRunning()) {
     log_time ts(CLOCK_REALTIME);
 
     LOG_FAILURE_RETRY(android_btWriteLog(0, EVENT_TYPE_LONG, &ts, sizeof(ts)));
@@ -720,7 +777,7 @@
       alarm(alarm_time);
 
       if (ret <= 0) {
-        iters = i;
+        state.SkipWithError("android_logger_list_read");
         break;
       }
       if ((log_msg.entry.len != (4 + 1 + 8)) ||
@@ -736,7 +793,7 @@
       log_time tx(eventData + 4 + 1);
       if (ts != tx) {
         if (0xDEADBEEFA55A5AA6ULL == caught_convert(eventData + 4 + 1)) {
-          iters = i;
+          state.SkipWithError("signal");
           break;
         }
         continue;
@@ -745,12 +802,11 @@
       break;
     }
   }
+  state.PauseTiming();
 
   signal(SIGALRM, SIG_DFL);
   alarm(0);
 
-  StopBenchmarkTiming();
-
   android_logger_list_free(logger_list);
 }
 BENCHMARK(BM_log_delay);
@@ -758,45 +814,33 @@
 /*
  *	Measure the time it takes for __android_log_is_loggable.
  */
-static void BM_is_loggable(int iters) {
+static void BM_is_loggable(benchmark::State& state) {
   static const char logd[] = "logd";
 
-  StartBenchmarkTiming();
-
-  for (int i = 0; i < iters; ++i) {
+  while (state.KeepRunning()) {
     __android_log_is_loggable_len(ANDROID_LOG_WARN, logd, strlen(logd),
                                   ANDROID_LOG_VERBOSE);
   }
-
-  StopBenchmarkTiming();
 }
 BENCHMARK(BM_is_loggable);
 
 /*
  *	Measure the time it takes for android_log_clockid.
  */
-static void BM_clockid(int iters) {
-  StartBenchmarkTiming();
-
-  for (int i = 0; i < iters; ++i) {
+static void BM_clockid(benchmark::State& state) {
+  while (state.KeepRunning()) {
     android_log_clockid();
   }
-
-  StopBenchmarkTiming();
 }
 BENCHMARK(BM_clockid);
 
 /*
  *	Measure the time it takes for __android_log_security.
  */
-static void BM_security(int iters) {
-  StartBenchmarkTiming();
-
-  for (int i = 0; i < iters; ++i) {
+static void BM_security(benchmark::State& state) {
+  while (state.KeepRunning()) {
     __android_log_security();
   }
-
-  StopBenchmarkTiming();
 }
 BENCHMARK(BM_security);
 
@@ -824,21 +868,17 @@
 /*
  *	Measure the time it takes for android_lookupEventTag_len
  */
-static void BM_lookupEventTag(int iters) {
+static void BM_lookupEventTag(benchmark::State& state) {
   prechargeEventMap();
 
   std::unordered_set<uint32_t>::const_iterator it = set.begin();
 
-  StartBenchmarkTiming();
-
-  for (int i = 0; i < iters; ++i) {
+  while (state.KeepRunning()) {
     size_t len;
     android_lookupEventTag_len(map, &len, (*it));
     ++it;
     if (it == set.end()) it = set.begin();
   }
-
-  StopBenchmarkTiming();
 }
 BENCHMARK(BM_lookupEventTag);
 
@@ -847,7 +887,7 @@
  */
 static uint32_t notTag = 1;
 
-static void BM_lookupEventTag_NOT(int iters) {
+static void BM_lookupEventTag_NOT(benchmark::State& state) {
   prechargeEventMap();
 
   while (set.find(notTag) != set.end()) {
@@ -855,15 +895,11 @@
     if (notTag >= USHRT_MAX) notTag = 1;
   }
 
-  StartBenchmarkTiming();
-
-  for (int i = 0; i < iters; ++i) {
+  while (state.KeepRunning()) {
     size_t len;
     android_lookupEventTag_len(map, &len, notTag);
   }
 
-  StopBenchmarkTiming();
-
   ++notTag;
   if (notTag >= USHRT_MAX) notTag = 1;
 }
@@ -872,42 +908,38 @@
 /*
  *	Measure the time it takes for android_lookupEventFormat_len
  */
-static void BM_lookupEventFormat(int iters) {
+static void BM_lookupEventFormat(benchmark::State& state) {
   prechargeEventMap();
 
   std::unordered_set<uint32_t>::const_iterator it = set.begin();
 
-  StartBenchmarkTiming();
-
-  for (int i = 0; i < iters; ++i) {
+  while (state.KeepRunning()) {
     size_t len;
     android_lookupEventFormat_len(map, &len, (*it));
     ++it;
     if (it == set.end()) it = set.begin();
   }
-
-  StopBenchmarkTiming();
 }
 BENCHMARK(BM_lookupEventFormat);
 
 /*
  *	Measure the time it takes for android_lookupEventTagNum plus above
  */
-static void BM_lookupEventTagNum(int iters) {
+static void BM_lookupEventTagNum(benchmark::State& state) {
   prechargeEventMap();
 
   std::unordered_set<uint32_t>::const_iterator it = set.begin();
 
-  for (int i = 0; i < iters; ++i) {
+  while (state.KeepRunning()) {
     size_t len;
     const char* name = android_lookupEventTag_len(map, &len, (*it));
     std::string Name(name, len);
     const char* format = android_lookupEventFormat_len(map, &len, (*it));
     std::string Format(format, len);
-    StartBenchmarkTiming();
+    state.ResumeTiming();
     android_lookupEventTagNum(map, Name.c_str(), Format.c_str(),
                               ANDROID_LOG_UNKNOWN);
-    StopBenchmarkTiming();
+    state.PauseTiming();
     ++it;
     if (it == set.end()) it = set.begin();
   }
@@ -943,7 +975,7 @@
   close(sock);
 }
 
-static void BM_lookupEventTagNum_logd_new(int iters) {
+static void BM_lookupEventTagNum_logd_new(benchmark::State& state) {
   fprintf(stderr,
           "WARNING: "
           "This test can cause logd to grow in size and hit DOS limiter\n");
@@ -965,7 +997,7 @@
     data_event_log_tags = empty_event_log_tags;
   }
 
-  for (int i = 0; i < iters; ++i) {
+  while (state.KeepRunning()) {
     char buffer[256];
     memset(buffer, 0, sizeof(buffer));
     log_time now(CLOCK_MONOTONIC);
@@ -973,9 +1005,9 @@
     snprintf(name, sizeof(name), "a%" PRIu64, now.nsec());
     snprintf(buffer, sizeof(buffer), "getEventTag name=%s format=\"(new|1)\"",
              name);
-    StartBenchmarkTiming();
+    state.ResumeTiming();
     send_to_control(buffer, sizeof(buffer));
-    StopBenchmarkTiming();
+    state.PauseTiming();
   }
 
   // Restore copies (logd still know about them, until crash or reboot)
@@ -1002,12 +1034,12 @@
 }
 BENCHMARK(BM_lookupEventTagNum_logd_new);
 
-static void BM_lookupEventTagNum_logd_existing(int iters) {
+static void BM_lookupEventTagNum_logd_existing(benchmark::State& state) {
   prechargeEventMap();
 
   std::unordered_set<uint32_t>::const_iterator it = set.begin();
 
-  for (int i = 0; i < iters; ++i) {
+  while (state.KeepRunning()) {
     size_t len;
     const char* name = android_lookupEventTag_len(map, &len, (*it));
     std::string Name(name, len);
@@ -1018,9 +1050,9 @@
     snprintf(buffer, sizeof(buffer), "getEventTag name=%s format=\"%s\"",
              Name.c_str(), Format.c_str());
 
-    StartBenchmarkTiming();
+    state.ResumeTiming();
     send_to_control(buffer, sizeof(buffer));
-    StopBenchmarkTiming();
+    state.PauseTiming();
     ++it;
     if (it == set.end()) it = set.begin();
   }
diff --git a/liblog/tests/liblog_test.cpp b/liblog/tests/liblog_test.cpp
index 46ec5ef..a8a9a12 100644
--- a/liblog/tests/liblog_test.cpp
+++ b/liblog/tests/liblog_test.cpp
@@ -105,7 +105,7 @@
 }
 
 #if (defined(__ANDROID__) && defined(USING_LOGGER_DEFAULT))
-static std::string popenToString(std::string command) {
+static std::string popenToString(const std::string& command) {
   std::string ret;
 
   FILE* fp = popen(command.c_str(), "r");
@@ -116,6 +116,7 @@
   return ret;
 }
 
+#ifndef NO_PSTORE
 static bool isPmsgActive() {
   pid_t pid = getpid();
 
@@ -125,21 +126,22 @@
 
   return std::string::npos != myPidFds.find(" -> /dev/pmsg0");
 }
+#endif /* NO_PSTORE */
 
 static bool isLogdwActive() {
   std::string logdwSignature =
       popenToString("grep /dev/socket/logdw /proc/net/unix");
-  size_t beginning = logdwSignature.find(" ");
+  size_t beginning = logdwSignature.find(' ');
   if (beginning == std::string::npos) return true;
-  beginning = logdwSignature.find(" ", beginning + 1);
+  beginning = logdwSignature.find(' ', beginning + 1);
   if (beginning == std::string::npos) return true;
-  size_t end = logdwSignature.find(" ", beginning + 1);
+  size_t end = logdwSignature.find(' ', beginning + 1);
   if (end == std::string::npos) return true;
-  end = logdwSignature.find(" ", end + 1);
+  end = logdwSignature.find(' ', end + 1);
   if (end == std::string::npos) return true;
-  end = logdwSignature.find(" ", end + 1);
+  end = logdwSignature.find(' ', end + 1);
   if (end == std::string::npos) return true;
-  end = logdwSignature.find(" ", end + 1);
+  end = logdwSignature.find(' ', end + 1);
   if (end == std::string::npos) return true;
   std::string allLogdwEndpoints = popenToString(
       "grep ' 00000002" + logdwSignature.substr(beginning, end - beginning) +
@@ -159,7 +161,7 @@
 
   // NB: fgrep with multiple strings is broken in Android
   for (beginning = 0;
-       (end = allLogdwEndpoints.find("\n", beginning)) != std::string::npos;
+       (end = allLogdwEndpoints.find('\n', beginning)) != std::string::npos;
        beginning = end + 1) {
     if (myPidFds.find(allLogdwEndpoints.substr(beginning, end - beginning)) !=
         std::string::npos)
@@ -189,22 +191,25 @@
   EXPECT_LT(0, __android_log_btwrite(0, EVENT_TYPE_LONG, &ts, sizeof(ts)));
 #ifdef USING_LOGGER_DEFAULT
   // Check that we can close and reopen the logger
-  bool pmsgActiveAfter__android_log_btwrite;
   bool logdwActiveAfter__android_log_btwrite;
   if (getuid() == AID_ROOT) {
     tested__android_log_close = true;
-    pmsgActiveAfter__android_log_btwrite = isPmsgActive();
-    logdwActiveAfter__android_log_btwrite = isLogdwActive();
+#ifndef NO_PSTORE
+    bool pmsgActiveAfter__android_log_btwrite = isPmsgActive();
     EXPECT_TRUE(pmsgActiveAfter__android_log_btwrite);
+#endif /* NO_PSTORE */
+    logdwActiveAfter__android_log_btwrite = isLogdwActive();
     EXPECT_TRUE(logdwActiveAfter__android_log_btwrite);
   } else if (!tested__android_log_close) {
     fprintf(stderr, "WARNING: can not test __android_log_close()\n");
   }
   __android_log_close();
   if (getuid() == AID_ROOT) {
+#ifndef NO_PSTORE
     bool pmsgActiveAfter__android_log_close = isPmsgActive();
-    bool logdwActiveAfter__android_log_close = isLogdwActive();
     EXPECT_FALSE(pmsgActiveAfter__android_log_close);
+#endif /* NO_PSTORE */
+    bool logdwActiveAfter__android_log_close = isLogdwActive();
     EXPECT_FALSE(logdwActiveAfter__android_log_close);
   }
 #endif
@@ -213,9 +218,11 @@
   EXPECT_LT(0, __android_log_btwrite(0, EVENT_TYPE_LONG, &ts1, sizeof(ts1)));
 #ifdef USING_LOGGER_DEFAULT
   if (getuid() == AID_ROOT) {
-    pmsgActiveAfter__android_log_btwrite = isPmsgActive();
-    logdwActiveAfter__android_log_btwrite = isLogdwActive();
+#ifndef NO_PSTORE
+    bool pmsgActiveAfter__android_log_btwrite = isPmsgActive();
     EXPECT_TRUE(pmsgActiveAfter__android_log_btwrite);
+#endif /* NO_PSTORE */
+    logdwActiveAfter__android_log_btwrite = isLogdwActive();
     EXPECT_TRUE(logdwActiveAfter__android_log_btwrite);
   }
 #endif
@@ -3036,12 +3043,15 @@
 
 #ifdef USING_LOGGER_DEFAULT  // Do not retest pmsg functionality
 #ifdef __ANDROID__
+#ifndef NO_PSTORE
 static const char __pmsg_file[] =
     "/data/william-shakespeare/MuchAdoAboutNothing.txt";
+#endif /* NO_PSTORE */
 #endif
 
 TEST(liblog, __android_log_pmsg_file_write) {
 #ifdef __ANDROID__
+#ifndef NO_PSTORE
   __android_log_close();
   if (getuid() == AID_ROOT) {
     tested__android_log_close = true;
@@ -3092,12 +3102,16 @@
     EXPECT_TRUE(pmsgActiveAfter__android_pmsg_file_write);
     EXPECT_TRUE(logdwActiveAfter__android_pmsg_file_write);
   }
+#else  /* NO_PSTORE */
+  GTEST_LOG_(INFO) << "This test does nothing because of NO_PSTORE.\n";
+#endif /* NO_PSTORE */
 #else
   GTEST_LOG_(INFO) << "This test does nothing.\n";
 #endif
 }
 
 #ifdef __ANDROID__
+#ifndef NO_PSTORE
 static ssize_t __pmsg_fn(log_id_t logId, char prio, const char* filename,
                          const char* buf, size_t len, void* arg) {
   EXPECT_TRUE(NULL == arg);
@@ -3118,10 +3132,12 @@
              ? -ENOEXEC
              : 1;
 }
+#endif /* NO_PSTORE */
 #endif
 
 TEST(liblog, __android_log_pmsg_file_read) {
 #ifdef __ANDROID__
+#ifndef NO_PSTORE
   signaled = 0;
 
   __android_log_close();
@@ -3155,113 +3171,9 @@
 
   EXPECT_LT(0, ret);
   EXPECT_EQ(1U, signaled);
-#else
-  GTEST_LOG_(INFO) << "This test does nothing.\n";
-#endif
-}
-#endif  // USING_LOGGER_DEFAULT
-
-#ifdef USING_LOGGER_DEFAULT  // Do not retest event mapping functionality
-#ifdef __ANDROID__
-// must be: '<needle:> 0 kB'
-static bool isZero(const std::string& content, std::string::size_type pos,
-                   const char* needle) {
-  std::string::size_type offset = content.find(needle, pos);
-  return (offset != std::string::npos) &&
-         ((offset = content.find_first_not_of(" \t", offset + strlen(needle))) !=
-          std::string::npos) &&
-         (content.find_first_not_of("0", offset) != offset);
-}
-
-// must not be: '<needle:> 0 kB'
-static bool isNotZero(const std::string& content, std::string::size_type pos,
-                      const char* needle) {
-  std::string::size_type offset = content.find(needle, pos);
-  return (offset != std::string::npos) &&
-         ((offset = content.find_first_not_of(" \t", offset + strlen(needle))) !=
-          std::string::npos) &&
-         (content.find_first_not_of("123456789", offset) != offset);
-}
-
-static void event_log_tags_test_smap(pid_t pid) {
-  std::string filename = android::base::StringPrintf("/proc/%d/smaps", pid);
-
-  std::string content;
-  if (!android::base::ReadFileToString(filename, &content)) return;
-
-  bool shared_ok = false;
-  bool private_ok = false;
-  bool anonymous_ok = false;
-  bool pass_ok = false;
-
-  static const char event_log_tags[] = "event-log-tags";
-  std::string::size_type pos = 0;
-  while ((pos = content.find(event_log_tags, pos)) != std::string::npos) {
-    pos += strlen(event_log_tags);
-
-    // must not be: 'Shared_Clean: 0 kB'
-    bool ok =
-        isNotZero(content, pos, "Shared_Clean:") ||
-        // If not /etc/event-log-tags, thus r/w, then half points
-        // back for not 'Shared_Dirty: 0 kB'
-        ((content.substr(pos - 5 - strlen(event_log_tags), 5) != "/etc/") &&
-         isNotZero(content, pos, "Shared_Dirty:"));
-    if (ok && !pass_ok) {
-      shared_ok = true;
-    } else if (!ok) {
-      shared_ok = false;
-    }
-
-    // must be: 'Private_Dirty: 0 kB' and 'Private_Clean: 0 kB'
-    ok = isZero(content, pos, "Private_Dirty:") ||
-         isZero(content, pos, "Private_Clean:");
-    if (ok && !pass_ok) {
-      private_ok = true;
-    } else if (!ok) {
-      private_ok = false;
-    }
-
-    // must be: 'Anonymous: 0 kB'
-    ok = isZero(content, pos, "Anonymous:");
-    if (ok && !pass_ok) {
-      anonymous_ok = true;
-    } else if (!ok) {
-      anonymous_ok = false;
-    }
-
-    pass_ok = true;
-  }
-  content = "";
-
-  if (!pass_ok) return;
-  if (shared_ok && anonymous_ok && private_ok) return;
-
-  filename = android::base::StringPrintf("/proc/%d/comm", pid);
-  android::base::ReadFileToString(filename, &content);
-  content = android::base::StringPrintf(
-      "%d:%s", pid, content.substr(0, content.find("\n")).c_str());
-
-  EXPECT_TRUE(IsOk(shared_ok, content));
-  EXPECT_TRUE(IsOk(private_ok, content));
-  EXPECT_TRUE(IsOk(anonymous_ok, content));
-}
-#endif  // __ANDROID__
-
-TEST(liblog, event_log_tags) {
-#ifdef __ANDROID__
-  std::unique_ptr<DIR, int (*)(DIR*)> proc_dir(opendir("/proc"), closedir);
-  ASSERT_FALSE(!proc_dir);
-
-  dirent* e;
-  while ((e = readdir(proc_dir.get()))) {
-    if (e->d_type != DT_DIR) continue;
-    if (!isdigit(e->d_name[0])) continue;
-    long long id = atoll(e->d_name);
-    if (id <= 0) continue;
-    pid_t pid = id;
-    if (id != pid) continue;
-    event_log_tags_test_smap(pid);
-  }
+#else  /* NO_PSTORE */
+  GTEST_LOG_(INFO) << "This test does nothing because of NO_PSTORE.\n";
+#endif /* NO_PSTORE */
 #else
   GTEST_LOG_(INFO) << "This test does nothing.\n";
 #endif
diff --git a/liblog/tests/log_id_test.cpp b/liblog/tests/log_id_test.cpp
index c56fa8b..9fb5a2c 100644
--- a/liblog/tests/log_id_test.cpp
+++ b/liblog/tests/log_id_test.cpp
@@ -89,12 +89,12 @@
     ASSERT_EQ(0, pthread_create(&t[i], NULL, ConcurrentPrintFn,
                                 reinterpret_cast<void*>(i)));
   }
-  int ret = 0;
+  int ret = 1;
   for (i = 0; i < NUM_CONCURRENT; i++) {
     void* result;
     ASSERT_EQ(0, pthread_join(t[i], &result));
     int this_result = reinterpret_cast<uintptr_t>(result);
-    if ((0 == ret) && (0 != this_result)) {
+    if ((0 < ret) && (ret != this_result)) {
       ret = this_result;
     }
   }
diff --git a/liblog/tests/log_read_test.cpp b/liblog/tests/log_read_test.cpp
index 6ed568a..443c3ea 100644
--- a/liblog/tests/log_read_test.cpp
+++ b/liblog/tests/log_read_test.cpp
@@ -27,6 +27,7 @@
 // Test the APIs in this standalone include file
 #include <log/log_read.h>
 // Do not use anything in log/log_time.h despite side effects of the above.
+#include <private/android_logger.h>
 
 TEST(liblog, __android_log_write__android_logger_list_read) {
 #ifdef __ANDROID__
@@ -100,9 +101,16 @@
       EXPECT_LT(0, get_log_size);
       // crash buffer is allowed to be empty, that is actually healthy!
       // kernel buffer is allowed to be empty on "user" builds
+      // stats buffer is allowed to be empty TEMPORARILY.
+      // TODO: remove stats buffer from here once we start to use it in
+      // framework (b/68266385).
       EXPECT_LE(  // boolean 1 or 0 depending on expected content or empty
           !!((strcmp("crash", name) != 0) &&
-             ((strcmp("kernel", name) != 0) || __android_log_is_debuggable())),
+             ((strcmp("kernel", name) != 0) ||
+              __android_logger_property_get_bool(
+                  "ro.logd.kernel", BOOL_DEFAULT_TRUE | BOOL_DEFAULT_FLAG_ENG |
+                                        BOOL_DEFAULT_FLAG_SVELTE)) &&
+             (strcmp("stats", name) != 0)),
           android_logger_get_log_readable_size(logger));
     } else {
       EXPECT_NE(0, get_log_size);
diff --git a/liblog/tests/log_wrap_test.cpp b/liblog/tests/log_wrap_test.cpp
new file mode 100644
index 0000000..ebf0b15
--- /dev/null
+++ b/liblog/tests/log_wrap_test.cpp
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2013-2017 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 <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <string>
+
+#include <android-base/chrono_utils.h>
+#include <android-base/stringprintf.h>
+#include <android/log.h>  // minimal logging API
+#include <gtest/gtest.h>
+#include <log/log_properties.h>
+#include <log/log_read.h>
+#include <log/log_time.h>
+#include <log/log_transport.h>
+
+#ifdef __ANDROID__
+static void read_with_wrap() {
+  android_set_log_transport(LOGGER_LOGD);
+
+  // Read the last line in the log to get a starting timestamp. We're assuming
+  // the log is not empty.
+  const int mode = ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK;
+  struct logger_list* logger_list =
+      android_logger_list_open(LOG_ID_MAIN, mode, 1000, 0);
+
+  ASSERT_NE(logger_list, nullptr);
+
+  log_msg log_msg;
+  int ret = android_logger_list_read(logger_list, &log_msg);
+  android_logger_list_close(logger_list);
+  ASSERT_GT(ret, 0);
+
+  log_time start(log_msg.entry.sec, log_msg.entry.nsec);
+  ASSERT_NE(start, log_time());
+
+  logger_list =
+      android_logger_list_alloc_time(mode | ANDROID_LOG_WRAP, start, 0);
+  ASSERT_NE(logger_list, nullptr);
+
+  struct logger* logger = android_logger_open(logger_list, LOG_ID_MAIN);
+  EXPECT_NE(logger, nullptr);
+  if (logger) {
+    android_logger_list_read(logger_list, &log_msg);
+  }
+
+  android_logger_list_close(logger_list);
+}
+
+static void caught_signal(int /* signum */) {
+}
+#endif
+
+// b/64143705 confirm fixed
+TEST(liblog, wrap_mode_blocks) {
+#ifdef __ANDROID__
+
+  android::base::Timer timer;
+
+  // The read call is expected to take up to 2 hours in the happy case.
+  // We only want to make sure it waits for longer than 30s, but we can't
+  // use an alarm as the implementation uses it. So we run the test in
+  // a separate process.
+  pid_t pid = fork();
+
+  if (pid == 0) {
+    // child
+    read_with_wrap();
+    _exit(0);
+  }
+
+  struct sigaction ignore, old_sigaction;
+  memset(&ignore, 0, sizeof(ignore));
+  ignore.sa_handler = caught_signal;
+  sigemptyset(&ignore.sa_mask);
+  sigaction(SIGALRM, &ignore, &old_sigaction);
+  alarm(45);
+
+  bool killed = false;
+  for (;;) {
+    siginfo_t info = {};
+    // This wait will succeed if the child exits, or fail with EINTR if the
+    // alarm goes off first - a loose approximation to a timed wait.
+    int ret = waitid(P_PID, pid, &info, WEXITED);
+    if (ret >= 0 || errno != EINTR) {
+      EXPECT_EQ(ret, 0);
+      if (!killed) {
+        EXPECT_EQ(info.si_status, 0);
+      }
+      break;
+    }
+    unsigned int alarm_left = alarm(0);
+    if (alarm_left > 0) {
+      alarm(alarm_left);
+    } else {
+      kill(pid, SIGTERM);
+      killed = true;
+    }
+  }
+
+  alarm(0);
+  EXPECT_GT(timer.duration(), std::chrono::seconds(40));
+#else
+  GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+}
diff --git a/libmemtrack/Android.bp b/libmemtrack/Android.bp
index 98413dd..0955633 100644
--- a/libmemtrack/Android.bp
+++ b/libmemtrack/Android.bp
@@ -2,13 +2,23 @@
 
 cc_library_shared {
     name: "libmemtrack",
-    srcs: ["memtrack.c"],
+    vendor_available: true,
+    vndk: {
+        enabled: true,
+    },
+    srcs: ["memtrack.cpp"],
     export_include_dirs: ["include"],
     local_include_dirs: ["include"],
     include_dirs: ["hardware/libhardware/include"],
     shared_libs: [
         "libhardware",
         "liblog",
+        "libbase",
+        "libhidlbase",
+        "libhidltransport",
+        "libhwbinder",
+        "libutils",
+        "android.hardware.memtrack@1.0",
     ],
     cflags: [
         "-Wall",
diff --git a/libmemtrack/OWNERS b/libmemtrack/OWNERS
new file mode 100644
index 0000000..70b375f
--- /dev/null
+++ b/libmemtrack/OWNERS
@@ -0,0 +1 @@
+ccross@google.com
diff --git a/libmemtrack/include/memtrack/memtrack.h b/libmemtrack/include/memtrack/memtrack.h
index 8c0ab89..2134a6f 100644
--- a/libmemtrack/include/memtrack/memtrack.h
+++ b/libmemtrack/include/memtrack/memtrack.h
@@ -35,16 +35,6 @@
 struct memtrack_proc;
 
 /**
- * memtrack_init
- *
- * Must be called once before calling any other functions.  After this function
- * is called, everything else is thread-safe.
- *
- * Returns 0 on success, -errno on error.
- */
-int memtrack_init(void);
-
-/**
  * memtrack_proc_new
  *
  * Return a new handle to hold process memory stats.
diff --git a/libmemtrack/memtrack.c b/libmemtrack/memtrack.c
deleted file mode 100644
index 9ed9451..0000000
--- a/libmemtrack/memtrack.c
+++ /dev/null
@@ -1,203 +0,0 @@
-/*
- * Copyright (C) 2013 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.
- */
-
-#define LOG_TAG "memtrack"
-
-#include <memtrack/memtrack.h>
-
-#include <errno.h>
-#include <malloc.h>
-#include <string.h>
-
-#include <hardware/memtrack.h>
-#include <log/log.h>
-
-#define ARRAY_SIZE(x) (sizeof(x)/sizeof((x)[0]))
-
-static const memtrack_module_t *module;
-
-struct memtrack_proc {
-    pid_t pid;
-    struct memtrack_proc_type {
-        enum memtrack_type type;
-        size_t num_records;
-        size_t allocated_records;
-        struct memtrack_record *records;
-    } types[MEMTRACK_NUM_TYPES];
-};
-
-int memtrack_init(void)
-{
-    int err;
-
-    if (module) {
-        return 0;
-    }
-
-    err = hw_get_module(MEMTRACK_HARDWARE_MODULE_ID,
-            (hw_module_t const**)&module);
-    if (err) {
-        ALOGE("Couldn't load %s module (%s)", MEMTRACK_HARDWARE_MODULE_ID,
-                strerror(-err));
-        return err;
-    }
-
-    return module->init(module);
-}
-
-struct memtrack_proc *memtrack_proc_new(void)
-{
-    if (!module) {
-        return NULL;
-    }
-
-    return calloc(sizeof(struct memtrack_proc), 1);
-}
-
-void memtrack_proc_destroy(struct memtrack_proc *p)
-{
-    enum memtrack_type i;
-
-    if (p) {
-        for (i = 0; i < MEMTRACK_NUM_TYPES; i++) {
-            free(p->types[i].records);
-        }
-    }
-    free(p);
-}
-
-static int memtrack_proc_get_type(struct memtrack_proc_type *t,
-            pid_t pid, enum memtrack_type type)
-{
-    size_t num_records = t->num_records;
-    int ret;
-
-retry:
-    ret = module->getMemory(module, pid, type, t->records, &num_records);
-    if (ret) {
-        t->num_records = 0;
-        return ret;
-    }
-    if (num_records > t->allocated_records) {
-        /* Need more records than allocated */
-        free(t->records);
-        t->records = calloc(sizeof(*t->records), num_records);
-        if (!t->records) {
-            return -ENOMEM;
-        }
-        t->allocated_records = num_records;
-        goto retry;
-    }
-    t->num_records = num_records;
-
-    return 0;
-}
-
-/* TODO: sanity checks on return values from HALs:
- *   make sure no records have invalid flags set
- *    - unknown flags
- *    - too many flags of a single category
- *    - missing ACCOUNTED/UNACCOUNTED
- *   make sure there are not overlapping SHARED and SHARED_PSS records
- */
-static int memtrack_proc_sanity_check(struct memtrack_proc *p)
-{
-    (void)p;
-    return 0;
-}
-
-int memtrack_proc_get(struct memtrack_proc *p, pid_t pid)
-{
-    enum memtrack_type i;
-
-    if (!module) {
-        return -EINVAL;
-    }
-
-    if (!p) {
-        return -EINVAL;
-    }
-
-    p->pid = pid;
-    for (i = 0; i < MEMTRACK_NUM_TYPES; i++) {
-        memtrack_proc_get_type(&p->types[i], pid, i);
-    }
-
-    return memtrack_proc_sanity_check(p);
-}
-
-static ssize_t memtrack_proc_sum(struct memtrack_proc *p,
-            enum memtrack_type types[], size_t num_types,
-            unsigned int flags)
-{
-    ssize_t sum = 0;
-    size_t i;
-    size_t j;
-
-    for (i = 0; i < num_types; i++) {
-        enum memtrack_type type = types[i];
-        for (j = 0; j < p->types[type].num_records; j++) {
-            if ((p->types[type].records[j].flags & flags) == flags) {
-                sum += p->types[type].records[j].size_in_bytes;
-            }
-        }
-    }
-
-    return sum;
-}
-
-ssize_t memtrack_proc_graphics_total(struct memtrack_proc *p)
-{
-    enum memtrack_type types[] = { MEMTRACK_TYPE_GRAPHICS };
-    return memtrack_proc_sum(p, types, ARRAY_SIZE(types), 0);
-}
-
-ssize_t memtrack_proc_graphics_pss(struct memtrack_proc *p)
-{
-    enum memtrack_type types[] = { MEMTRACK_TYPE_GRAPHICS };
-    return memtrack_proc_sum(p, types, ARRAY_SIZE(types),
-                MEMTRACK_FLAG_SMAPS_UNACCOUNTED);
-}
-
-ssize_t memtrack_proc_gl_total(struct memtrack_proc *p)
-{
-    enum memtrack_type types[] = { MEMTRACK_TYPE_GL };
-    return memtrack_proc_sum(p, types, ARRAY_SIZE(types), 0);
-}
-
-ssize_t memtrack_proc_gl_pss(struct memtrack_proc *p)
-{
-    enum memtrack_type types[] = { MEMTRACK_TYPE_GL };
-    return memtrack_proc_sum(p, types, ARRAY_SIZE(types),
-                MEMTRACK_FLAG_SMAPS_UNACCOUNTED);
-}
-
-ssize_t memtrack_proc_other_total(struct memtrack_proc *p)
-{
-    enum memtrack_type types[] = { MEMTRACK_TYPE_MULTIMEDIA,
-                                        MEMTRACK_TYPE_CAMERA,
-                                        MEMTRACK_TYPE_OTHER };
-    return memtrack_proc_sum(p, types, ARRAY_SIZE(types), 0);
-}
-
-ssize_t memtrack_proc_other_pss(struct memtrack_proc *p)
-{
-    enum memtrack_type types[] = { MEMTRACK_TYPE_MULTIMEDIA,
-                                        MEMTRACK_TYPE_CAMERA,
-                                        MEMTRACK_TYPE_OTHER };
-    return memtrack_proc_sum(p, types, ARRAY_SIZE(types),
-                MEMTRACK_FLAG_SMAPS_UNACCOUNTED);
-}
diff --git a/libmemtrack/memtrack.cpp b/libmemtrack/memtrack.cpp
new file mode 100644
index 0000000..c5e74c1
--- /dev/null
+++ b/libmemtrack/memtrack.cpp
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2013 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.
+ */
+#define LOG_TAG "memtrack"
+#include <android/hardware/memtrack/1.0/IMemtrack.h>
+#include <memtrack/memtrack.h>
+
+#include <errno.h>
+#include <malloc.h>
+#include <vector>
+#include <string.h>
+#include <mutex>
+
+#include <log/log.h>
+
+using android::hardware::memtrack::V1_0::IMemtrack;
+using android::hardware::memtrack::V1_0::MemtrackType;
+using android::hardware::memtrack::V1_0::MemtrackRecord;
+using android::hardware::memtrack::V1_0::MemtrackFlag;
+using android::hardware::memtrack::V1_0::MemtrackStatus;
+using android::hardware::hidl_vec;
+using android::hardware::Return;
+
+struct memtrack_proc_type {
+    MemtrackType type;
+    std::vector<MemtrackRecord> records;
+};
+
+struct memtrack_proc {
+    pid_t pid;
+    memtrack_proc_type types[static_cast<int>(MemtrackType::NUM_TYPES)];
+};
+
+//TODO(b/31632518)
+static android::sp<IMemtrack> get_instance() {
+    static android::sp<IMemtrack> module = IMemtrack::getService();
+    if (module == nullptr) {
+        ALOGE("Couldn't load memtrack module");
+    }
+    return module;
+}
+
+memtrack_proc *memtrack_proc_new(void)
+{
+    return new memtrack_proc();
+}
+
+void memtrack_proc_destroy(memtrack_proc *p)
+{
+    delete(p);
+}
+
+static int memtrack_proc_get_type(memtrack_proc_type *t,
+        pid_t pid, MemtrackType type)
+{
+    int err = 0;
+    android::sp<IMemtrack> memtrack = get_instance();
+    if (memtrack == nullptr)
+        return -1;
+
+    Return<void> ret = memtrack->getMemory(pid, type,
+        [&t, &err](MemtrackStatus status, hidl_vec<MemtrackRecord> records) {
+            if (status != MemtrackStatus::SUCCESS) {
+                err = -1;
+                t->records.resize(0);
+            }
+            t->records.resize(records.size());
+            for (size_t i = 0; i < records.size(); i++) {
+                t->records[i].sizeInBytes = records[i].sizeInBytes;
+                t->records[i].flags = records[i].flags;
+            }
+    });
+    return ret.isOk() ? err : -1;
+}
+
+/* TODO: sanity checks on return values from HALs:
+ *   make sure no records have invalid flags set
+ *    - unknown flags
+ *    - too many flags of a single category
+ *    - missing ACCOUNTED/UNACCOUNTED
+ *   make sure there are not overlapping SHARED and SHARED_PSS records
+ */
+static int memtrack_proc_sanity_check(memtrack_proc* /*p*/)
+{
+    return 0;
+}
+
+int memtrack_proc_get(memtrack_proc *p, pid_t pid)
+{
+    if (!p) {
+        return -EINVAL;
+    }
+
+    p->pid = pid;
+    for (uint32_t i = 0; i < (uint32_t)MemtrackType::NUM_TYPES; i++) {
+        int ret = memtrack_proc_get_type(&p->types[i], pid, (MemtrackType)i);
+        if (ret != 0)
+           return ret;
+    }
+
+    return memtrack_proc_sanity_check(p);
+}
+
+static ssize_t memtrack_proc_sum(memtrack_proc *p,
+        const std::vector<MemtrackType>& types, uint32_t flags)
+{
+    ssize_t sum = 0;
+
+    for (size_t i = 0; i < types.size(); i++) {
+        memtrack_proc_type type = p->types[static_cast<int>(types[i])];
+        std::vector<MemtrackRecord> records = type.records;
+        for (size_t j = 0; j < records.size(); j++) {
+            if ((records[j].flags & flags) == flags) {
+                sum += records[j].sizeInBytes;
+            }
+        }
+    }
+
+    return sum;
+}
+
+ssize_t memtrack_proc_graphics_total(memtrack_proc *p)
+{
+    std::vector<MemtrackType> types = {MemtrackType::GRAPHICS};
+    return memtrack_proc_sum(p, types, 0);
+}
+
+ssize_t memtrack_proc_graphics_pss(memtrack_proc *p)
+{
+    std::vector<MemtrackType> types = { MemtrackType::GRAPHICS };
+    return memtrack_proc_sum(p, types,
+            (uint32_t)MemtrackFlag::SMAPS_UNACCOUNTED);
+}
+
+ssize_t memtrack_proc_gl_total(memtrack_proc *p)
+{
+    std::vector<MemtrackType> types = { MemtrackType::GL };
+    return memtrack_proc_sum(p, types, 0);
+}
+
+ssize_t memtrack_proc_gl_pss(memtrack_proc *p)
+{
+    std::vector<MemtrackType> types = { MemtrackType::GL };
+    return memtrack_proc_sum(p, types,
+            (uint32_t)MemtrackFlag::SMAPS_UNACCOUNTED);
+}
+
+ssize_t memtrack_proc_other_total(memtrack_proc *p)
+{
+    std::vector<MemtrackType> types = { MemtrackType::MULTIMEDIA,
+            MemtrackType::CAMERA, MemtrackType::OTHER };
+    return memtrack_proc_sum(p, types, 0);
+}
+
+ssize_t memtrack_proc_other_pss(memtrack_proc *p)
+{
+    std::vector<MemtrackType> types = { MemtrackType::MULTIMEDIA,
+            MemtrackType::CAMERA, MemtrackType::OTHER };
+    return memtrack_proc_sum(p, types,
+            (uint32_t)MemtrackFlag::SMAPS_UNACCOUNTED);
+}
diff --git a/libmemtrack/memtrack_test.c b/libmemtrack/memtrack_test.c
index eaadfa7..77c935e 100644
--- a/libmemtrack/memtrack_test.c
+++ b/libmemtrack/memtrack_test.c
@@ -82,12 +82,6 @@
     (void)argc;
     (void)argv;
 
-    ret = memtrack_init();
-    if (ret < 0) {
-        fprintf(stderr, "failed to initialize HAL: %s (%d)\n", strerror(-ret), ret);
-        exit(EXIT_FAILURE);
-    }
-
     ret = pm_kernel_create(&ker);
     if (ret) {
         fprintf(stderr, "Error creating kernel interface -- "
diff --git a/libmemunreachable/Android.bp b/libmemunreachable/Android.bp
index cdac76b..248a9d2 100644
--- a/libmemunreachable/Android.bp
+++ b/libmemunreachable/Android.bp
@@ -6,7 +6,6 @@
         "-Wextra",
         "-Werror",
     ],
-    clang: true,
     shared_libs: [
         "libbase",
     ],
@@ -17,19 +16,19 @@
         },
         host: {
             shared_libs: ["liblog"],
-        }
+        },
     },
 }
 
-cc_library_shared {
+cc_library {
     name: "libmemunreachable",
     defaults: ["libmemunreachable_defaults"],
     srcs: [
         "Allocator.cpp",
+        "Binder.cpp",
         "HeapWalker.cpp",
         "LeakFolding.cpp",
         "LeakPipe.cpp",
-        "LineBuffer.cpp",
         "MemUnreachable.cpp",
         "ProcessMappings.cpp",
         "PtracerThread.cpp",
@@ -38,6 +37,7 @@
 
     static_libs: [
         "libc_malloc_debug_backtrace",
+        "libprocinfo",
     ],
     // Only need this for arm since libc++ uses its own unwind code that
     // doesn't mix with the other default unwind code.
@@ -46,6 +46,12 @@
             static_libs: ["libunwind_llvm"],
         },
     },
+
+    // TODO(b/78118944), clang lld link flags do not work with special link
+    // rules for libunwind_llvm yet. Linked aosp_arm-eng image failed to
+    // boot up in the emulator.
+    use_clang_lld: false,
+
     export_include_dirs: ["include"],
     local_include_dirs: ["include"],
 }
@@ -83,4 +89,21 @@
             enabled: false,
         },
     },
+
+    test_suites: ["device-tests"],
+}
+
+cc_test {
+    name: "memunreachable_binder_test",
+    defaults: ["libmemunreachable_defaults"],
+    srcs: [
+        "tests/Binder_test.cpp",
+    ],
+    static_libs: ["libmemunreachable"],
+    shared_libs: [
+        "libbinder",
+        "libhwbinder",
+        "libutils",
+    ],
+    test_suites: ["device-tests"],
 }
diff --git a/libmemunreachable/Binder.cpp b/libmemunreachable/Binder.cpp
new file mode 100644
index 0000000..60512a3
--- /dev/null
+++ b/libmemunreachable/Binder.cpp
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2017 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 <sys/cdefs.h>
+#include <unistd.h>
+
+#include <functional>
+
+#include "Binder.h"
+#include "log.h"
+
+__BEGIN_DECLS
+
+// Weak undefined references to the symbols in libbinder and libhwbinder
+// so that libmemunreachable can call them in processes that have them
+// loaded without requiring libmemunreachable to have dependencies on them.
+ssize_t __attribute__((weak)) getBinderKernelReferences(size_t, uintptr_t*);
+ssize_t __attribute__((weak)) getHWBinderKernelReferences(size_t, uintptr_t*);
+
+__END_DECLS
+
+namespace android {
+
+static bool BinderReferencesToVector(allocator::vector<uintptr_t>& refs,
+                                     std::function<ssize_t(size_t, uintptr_t*)> fn) {
+  if (fn == nullptr) {
+    return true;
+  }
+
+  size_t size = refs.size();
+
+  do {
+    refs.resize(size);
+
+    ssize_t ret = fn(refs.size(), refs.data());
+    if (ret < 0) {
+      return false;
+    }
+
+    size = ret;
+  } while (size > refs.size());
+
+  refs.resize(size);
+  return true;
+}
+
+bool BinderReferences(allocator::vector<uintptr_t>& refs) {
+  refs.clear();
+
+  allocator::vector<uintptr_t> binder_refs{refs.get_allocator()};
+  if (BinderReferencesToVector(refs, getBinderKernelReferences)) {
+    refs.insert(refs.end(), binder_refs.begin(), binder_refs.end());
+  } else {
+    MEM_ALOGE("getBinderKernelReferences failed");
+  }
+
+  allocator::vector<uintptr_t> hwbinder_refs{refs.get_allocator()};
+  if (BinderReferencesToVector(hwbinder_refs, getHWBinderKernelReferences)) {
+    refs.insert(refs.end(), hwbinder_refs.begin(), hwbinder_refs.end());
+  } else {
+    MEM_ALOGE("getHWBinderKernelReferences failed");
+  }
+
+  return true;
+}
+
+}  // namespace android
diff --git a/libmemunreachable/Binder.h b/libmemunreachable/Binder.h
new file mode 100644
index 0000000..bf4fd3e
--- /dev/null
+++ b/libmemunreachable/Binder.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2017 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 LIBMEMUNREACHABLE_BINDER_H_
+#define LIBMEMUNREACHABLE_BINDER_H_
+
+#include "Allocator.h"
+
+namespace android {
+
+bool BinderReferences(allocator::vector<uintptr_t>& refs);
+
+}  // namespace android
+
+#endif  // LIBMEMUNREACHABLE_BINDER_H_
diff --git a/libmemunreachable/LineBuffer.cpp b/libmemunreachable/LineBuffer.cpp
deleted file mode 100644
index 4ea0542..0000000
--- a/libmemunreachable/LineBuffer.cpp
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright (C) 2015 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.
- */
-
-// Copied from system/extras/memory_replay/LineBuffer.cpp
-// TODO(ccross): find a way to share between libmemunreachable and memory_replay?
-
-#include <errno.h>
-#include <string.h>
-#include <unistd.h>
-
-#include "LineBuffer.h"
-
-namespace android {
-
-LineBuffer::LineBuffer(int fd, char* buffer, size_t buffer_len)
-    : fd_(fd), buffer_(buffer), buffer_len_(buffer_len) {}
-
-bool LineBuffer::GetLine(char** line, size_t* line_len) {
-  while (true) {
-    if (bytes_ > 0) {
-      char* newline = reinterpret_cast<char*>(memchr(buffer_ + start_, '\n', bytes_));
-      if (newline != nullptr) {
-        *newline = '\0';
-        *line = buffer_ + start_;
-        start_ = newline - buffer_ + 1;
-        bytes_ -= newline - *line + 1;
-        *line_len = newline - *line;
-        return true;
-      }
-    }
-    if (start_ > 0) {
-      // Didn't find anything, copy the current to the front of the buffer.
-      memmove(buffer_, buffer_ + start_, bytes_);
-      start_ = 0;
-    }
-    ssize_t bytes = TEMP_FAILURE_RETRY(read(fd_, buffer_ + bytes_, buffer_len_ - bytes_ - 1));
-    if (bytes <= 0) {
-      if (bytes_ > 0) {
-        // The read data might not contain a nul terminator, so add one.
-        buffer_[bytes_] = '\0';
-        *line = buffer_ + start_;
-        *line_len = bytes_;
-        bytes_ = 0;
-        start_ = 0;
-        return true;
-      }
-      return false;
-    }
-    bytes_ += bytes;
-  }
-}
-
-}  // namespace android
diff --git a/libmemunreachable/LineBuffer.h b/libmemunreachable/LineBuffer.h
deleted file mode 100644
index cc6cd0c..0000000
--- a/libmemunreachable/LineBuffer.h
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright (C) 2015 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 _LIBMEMUNREACHABLE_LINE_BUFFER_H
-#define _LIBMEMUNREACHABLE_LINE_BUFFER_H
-
-#include <stdint.h>
-
-namespace android {
-
-class LineBuffer {
- public:
-  LineBuffer(int fd, char* buffer, size_t buffer_len);
-
-  bool GetLine(char** line, size_t* line_len);
-
- private:
-  int fd_;
-  char* buffer_ = nullptr;
-  size_t buffer_len_ = 0;
-  size_t start_ = 0;
-  size_t bytes_ = 0;
-};
-
-}  // namespace android
-
-#endif  // _LIBMEMUNREACHABLE_LINE_BUFFER_H
diff --git a/libmemunreachable/MemUnreachable.cpp b/libmemunreachable/MemUnreachable.cpp
index a1f74c3..529a043 100644
--- a/libmemunreachable/MemUnreachable.cpp
+++ b/libmemunreachable/MemUnreachable.cpp
@@ -28,6 +28,7 @@
 #include <backtrace.h>
 
 #include "Allocator.h"
+#include "Binder.h"
 #include "HeapWalker.h"
 #include "Leak.h"
 #include "LeakFolding.h"
@@ -53,7 +54,8 @@
   MemUnreachable(pid_t pid, Allocator<void> allocator)
       : pid_(pid), allocator_(allocator), heap_walker_(allocator_) {}
   bool CollectAllocations(const allocator::vector<ThreadInfo>& threads,
-                          const allocator::vector<Mapping>& mappings);
+                          const allocator::vector<Mapping>& mappings,
+                          const allocator::vector<uintptr_t>& refs);
   bool GetUnreachableMemory(allocator::vector<Leak>& leaks, size_t limit, size_t* num_leaks,
                             size_t* leak_bytes);
   size_t Allocations() { return heap_walker_.Allocations(); }
@@ -82,7 +84,8 @@
 }
 
 bool MemUnreachable::CollectAllocations(const allocator::vector<ThreadInfo>& threads,
-                                        const allocator::vector<Mapping>& mappings) {
+                                        const allocator::vector<Mapping>& mappings,
+                                        const allocator::vector<uintptr_t>& refs) {
   MEM_ALOGI("searching process %d for allocations", pid_);
   allocator::vector<Mapping> heap_mappings{mappings};
   allocator::vector<Mapping> anon_mappings{mappings};
@@ -118,6 +121,8 @@
     heap_walker_.Root(thread_it->regs);
   }
 
+  heap_walker_.Root(refs);
+
   MEM_ALOGI("searching done");
 
   return true;
@@ -282,6 +287,7 @@
     ThreadCapture thread_capture(parent_pid, heap);
     allocator::vector<ThreadInfo> thread_info(heap);
     allocator::vector<Mapping> mappings(heap);
+    allocator::vector<uintptr_t> refs(heap);
 
     // ptrace all the threads
     if (!thread_capture.CaptureThreads()) {
@@ -301,6 +307,11 @@
       return 1;
     }
 
+    if (!BinderReferences(refs)) {
+      continue_parent_sem.Post();
+      return 1;
+    }
+
     // malloc must be enabled to call fork, at_fork handlers take the same
     // locks as ScopedDisableMalloc.  All threads are paused in ptrace, so
     // memory state is still consistent.  Unfreeze the original thread so it
@@ -326,7 +337,7 @@
 
       MemUnreachable unreachable{parent_pid, heap};
 
-      if (!unreachable.CollectAllocations(thread_info, mappings)) {
+      if (!unreachable.CollectAllocations(thread_info, mappings, refs)) {
         _exit(2);
       }
       size_t num_allocations = unreachable.Allocations();
@@ -468,23 +479,6 @@
   return oss.str();
 }
 
-// Figure out the abi based on defined macros.
-#if defined(__arm__)
-#define ABI_STRING "arm"
-#elif defined(__aarch64__)
-#define ABI_STRING "arm64"
-#elif defined(__mips__) && !defined(__LP64__)
-#define ABI_STRING "mips"
-#elif defined(__mips__) && defined(__LP64__)
-#define ABI_STRING "mips64"
-#elif defined(__i386__)
-#define ABI_STRING "x86"
-#elif defined(__x86_64__)
-#define ABI_STRING "x86_64"
-#else
-#error "Unsupported ABI"
-#endif
-
 std::string UnreachableMemoryInfo::ToString(bool log_contents) const {
   std::ostringstream oss;
   oss << "  " << leak_bytes << " bytes in ";
@@ -501,6 +495,21 @@
   return oss.str();
 }
 
+UnreachableMemoryInfo::~UnreachableMemoryInfo() {
+  // Clear the memory that holds the leaks, otherwise the next attempt to
+  // detect leaks may find the old data (for example in the jemalloc tcache)
+  // and consider all the leaks to be referenced.
+  memset(leaks.data(), 0, leaks.capacity() * sizeof(Leak));
+
+  std::vector<Leak> tmp;
+  leaks.swap(tmp);
+
+  // Disable and re-enable malloc to flush the jemalloc tcache to make sure
+  // there are no copies of the leaked pointer addresses there.
+  malloc_disable();
+  malloc_enable();
+}
+
 std::string GetUnreachableMemoryString(bool log_contents, size_t limit) {
   UnreachableMemoryInfo info;
   if (!GetUnreachableMemory(info, limit)) {
diff --git a/libmemunreachable/OWNERS b/libmemunreachable/OWNERS
new file mode 100644
index 0000000..9127a93
--- /dev/null
+++ b/libmemunreachable/OWNERS
@@ -0,0 +1,2 @@
+ccross@google.com
+cferris@google.com
diff --git a/libmemunreachable/ProcessMappings.cpp b/libmemunreachable/ProcessMappings.cpp
index 9a06870..701ce16 100644
--- a/libmemunreachable/ProcessMappings.cpp
+++ b/libmemunreachable/ProcessMappings.cpp
@@ -14,21 +14,30 @@
  * limitations under the License.
  */
 
+#include <errno.h>
 #include <fcntl.h>
 #include <inttypes.h>
 #include <string.h>
 #include <unistd.h>
 
 #include <android-base/unique_fd.h>
+#include <procinfo/process_map.h>
 
-#include "LineBuffer.h"
 #include "ProcessMappings.h"
-#include "log.h"
 
 namespace android {
 
-// This function is not re-entrant since it uses a static buffer for
-// the line data.
+struct ReadMapCallback {
+  ReadMapCallback(allocator::vector<Mapping>& mappings) : mappings_(mappings) {}
+
+  void operator()(uint64_t start, uint64_t end, uint16_t flags, uint64_t, const char* name) const {
+    mappings_.emplace_back(start, end, flags & PROT_READ, flags & PROT_WRITE, flags & PROT_EXEC,
+                           name);
+  }
+
+  allocator::vector<Mapping>& mappings_;
+};
+
 bool ProcessMappings(pid_t pid, allocator::vector<Mapping>& mappings) {
   char map_buffer[1024];
   snprintf(map_buffer, sizeof(map_buffer), "/proc/%d/maps", pid);
@@ -36,35 +45,13 @@
   if (fd == -1) {
     return false;
   }
-
-  LineBuffer line_buf(fd, map_buffer, sizeof(map_buffer));
-  char* line;
-  size_t line_len;
-  while (line_buf.GetLine(&line, &line_len)) {
-    int name_pos;
-    char perms[5];
-    Mapping mapping{};
-    if (sscanf(line, "%" SCNxPTR "-%" SCNxPTR " %4s %*x %*x:%*x %*d %n", &mapping.begin,
-               &mapping.end, perms, &name_pos) == 3) {
-      if (perms[0] == 'r') {
-        mapping.read = true;
-      }
-      if (perms[1] == 'w') {
-        mapping.write = true;
-      }
-      if (perms[2] == 'x') {
-        mapping.execute = true;
-      }
-      if (perms[3] == 'p') {
-        mapping.priv = true;
-      }
-      if ((size_t)name_pos < line_len) {
-        strlcpy(mapping.name, line + name_pos, sizeof(mapping.name));
-      }
-      mappings.emplace_back(mapping);
-    }
+  allocator::string content(mappings.get_allocator());
+  ssize_t n;
+  while ((n = TEMP_FAILURE_RETRY(read(fd, map_buffer, sizeof(map_buffer)))) > 0) {
+    content.append(map_buffer, n);
   }
-  return true;
+  ReadMapCallback callback(mappings);
+  return android::procinfo::ReadMapFileContent(&content[0], callback);
 }
 
 }  // namespace android
diff --git a/libmemunreachable/ProcessMappings.h b/libmemunreachable/ProcessMappings.h
index a0e97e9..94da69b 100644
--- a/libmemunreachable/ProcessMappings.h
+++ b/libmemunreachable/ProcessMappings.h
@@ -17,6 +17,8 @@
 #ifndef LIBMEMUNREACHABLE_PROCESS_MAPPING_H_
 #define LIBMEMUNREACHABLE_PROCESS_MAPPING_H_
 
+#include <string.h>
+
 #include "Allocator.h"
 
 namespace android {
@@ -27,8 +29,13 @@
   bool read;
   bool write;
   bool execute;
-  bool priv;
   char name[96];
+
+  Mapping() {}
+  Mapping(uintptr_t begin, uintptr_t end, bool read, bool write, bool execute, const char* name)
+      : begin(begin), end(end), read(read), write(write), execute(execute) {
+    strlcpy(this->name, name, sizeof(this->name));
+  }
 };
 
 // This function is not re-entrant since it uses a static buffer for
diff --git a/libmemunreachable/PtracerThread.cpp b/libmemunreachable/PtracerThread.cpp
index aca2a82..61a1d24 100644
--- a/libmemunreachable/PtracerThread.cpp
+++ b/libmemunreachable/PtracerThread.cpp
@@ -98,6 +98,7 @@
     return (*reinterpret_cast<std::function<int()>*>(arg))();
   };
 
+  // See README.md for why we create the child process this way
   child_pid_ = clone(proxy, stack_->top(), CLONE_VM | CLONE_FS | CLONE_FILES /*|CLONE_UNTRACED*/,
                      reinterpret_cast<void*>(&func_));
   if (child_pid_ < 0) {
diff --git a/libmemunreachable/README.md b/libmemunreachable/README.md
index 61a47de..9cc0c9b 100644
--- a/libmemunreachable/README.md
+++ b/libmemunreachable/README.md
@@ -12,6 +12,27 @@
 Usage
 -------
 
+### In Android apps ###
+
+libmemunreachble is loaded by zygote and can be triggered with `dumpsys -t 600 meminfo --unreachable [process]`.
+
+To enable malloc\_debug backtraces on allocations for a single app process on a userdebug device, use:
+```
+adb root
+adb shell setprop libc.debug.malloc.program app_process
+adb shell setprop wrap.[process] "\$\@"
+adb shell setprop libc.debug.malloc.options backtrace=4
+```
+
+Kill and restart the app, trigger the leak, and then run `dumpsys -t 600 meminfo --unreachable [process]`.
+
+To disable malloc\_debug:
+```
+adb shell setprop libc.debug.malloc.options "''"
+adb shell setprop libc.debug.malloc.program "''"
+adb shell setprop wrap.[process]  "''"
+```
+
 ### C interface ###
 
 #### `bool LogUnreachableMemory(bool log_contents, size_t limit)` ####
@@ -23,7 +44,7 @@
 
 ### C++ interface ###
 
-####`bool GetUnreachableMemory(UnreachableMemoryInfo& info, size_t limit = 100)`####
+#### `bool GetUnreachableMemory(UnreachableMemoryInfo& info, size_t limit = 100)` ####
 Updates an `UnreachableMemoryInfo` object with information on leaks, including details on up to `limit` leaks.  Returns true if leak detection succeeded.
 
 #### `std::string GetUnreachableMemoryString(bool log_contents = false, size_t limit = 100)` ####
@@ -36,7 +57,7 @@
 
  1. *Original process*: Leak detection is requested by calling `GetUnreachableMemory()`
  2. Allocations are disabled using `malloc_disable()`
- 3. The collection process is spawned.  The collection process is similar to a normal `fork()` child process, except that it shares the address space of the parent - any writes by the original process are visible to the collection process, and vice-versa.
+ 3. The collection process is spawned.  The collection process, created using clone, is similar to a normal `fork()` child process, except that it shares the address space of the parent - any writes by the original process are visible to the collection process, and vice-versa. If we forked instead of using clone, the address space might get out of sync with observed post-ptrace thread state, since it takes some time to pause the parent.
  4. *Collection process*: All threads in the original process are paused with `ptrace()`.
  5. Registers contents, active stack areas, and memory mapping information are collected.
  6. *Original process*: Allocations are re-enabled using `malloc_enable()`, but all threads are still paused with `ptrace()`.
diff --git a/libmemunreachable/include/memunreachable/memunreachable.h b/libmemunreachable/include/memunreachable/memunreachable.h
index 438fcaf..c028eab 100644
--- a/libmemunreachable/include/memunreachable/memunreachable.h
+++ b/libmemunreachable/include/memunreachable/memunreachable.h
@@ -62,12 +62,7 @@
   size_t allocation_bytes;
 
   UnreachableMemoryInfo() {}
-  ~UnreachableMemoryInfo() {
-    // Clear the memory that holds the leaks, otherwise the next attempt to
-    // detect leaks may find the old data (for example in the jemalloc tcache)
-    // and consider all the leaks to be referenced.
-    memset(leaks.data(), 0, leaks.capacity() * sizeof(Leak));
-  }
+  ~UnreachableMemoryInfo();
 
   std::string ToString(bool log_contents) const;
 };
diff --git a/libmemunreachable/tests/Binder_test.cpp b/libmemunreachable/tests/Binder_test.cpp
new file mode 100644
index 0000000..eaf7652
--- /dev/null
+++ b/libmemunreachable/tests/Binder_test.cpp
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2016 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 <signal.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <binder/Binder.h>
+#include <binder/IBinder.h>
+#include <binder/IServiceManager.h>
+#include <binder/Parcel.h>
+#include <binder/ProcessState.h>
+
+#include <gtest/gtest.h>
+
+#include "Allocator.h"
+#include "Binder.h"
+
+namespace android {
+
+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;
+  virtual ~BinderService() = default;
+
+  virtual status_t onTransact(uint32_t /*code*/, const Parcel& data, Parcel* reply,
+                              uint32_t /*flags*/ = 0) {
+    reply->writeStrongBinder(ref);
+    ref = data.readStrongBinder();
+    return 0;
+  }
+
+ private:
+  sp<IBinder> ref;
+};
+
+class BinderObject : public BBinder {
+ public:
+  BinderObject() = default;
+  ~BinderObject() = default;
+};
+
+// Forks a subprocess that registers a BinderService with the global binder
+// servicemanager.  Requires root permissions.
+class ServiceProcess {
+ public:
+  ServiceProcess() : child_(0) {}
+  ~ServiceProcess() { Stop(); }
+
+  bool Run() {
+    pid_t ret = fork();
+    if (ret < 0) {
+      return false;
+    } else if (ret == 0) {
+      // child
+      _exit(Service());
+    } else {
+      // parent
+      child_ = ret;
+      return true;
+    }
+  }
+
+  bool Stop() {
+    if (child_ > 0) {
+      if (kill(child_, SIGTERM)) {
+        return false;
+      }
+      int status = 0;
+      if (TEMP_FAILURE_RETRY(waitpid(child_, &status, 0)) != child_) {
+        return false;
+      }
+      child_ = 0;
+      return WIFEXITED(status) && WEXITSTATUS(status) == 0;
+    }
+
+    return true;
+  }
+
+  int Service() {
+    sp<ProcessState> proc{ProcessState::self()};
+    sp<IServiceManager> sm = defaultServiceManager();
+    if (sm == nullptr) {
+      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;
+    }
+    proc->startThreadPool();
+    pause();
+    return 0;
+  }
+
+ private:
+  pid_t child_;
+};
+
+class MemunreachableBinderTest : public ::testing::Test {
+ protected:
+  ServiceProcess service_process_;
+};
+
+// 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());
+
+  sp<IServiceManager> sm = defaultServiceManager();
+  ASSERT_TRUE(sm != nullptr);
+
+  // A small sleep allows the service to start, which
+  // prevents a longer sleep in getService.
+  usleep(100000);
+
+  sp<IBinder> service = sm->getService(service_name);
+  ASSERT_TRUE(service != nullptr);
+
+  sp<IBinder> binder{new BinderObject()};
+
+  Parcel send;
+  Parcel reply;
+
+  send.writeStrongBinder(binder);
+  status_t rv = service->transact(0, send, &reply);
+  ASSERT_EQ(static_cast<status_t>(OK), rv);
+
+  Heap heap;
+  allocator::vector<uintptr_t> refs{heap};
+
+  ASSERT_TRUE(BinderReferences(refs));
+
+  bool found_ref = false;
+  for (auto ref : refs) {
+    if (ref == reinterpret_cast<uintptr_t>(binder.get())) {
+      found_ref = true;
+    }
+  }
+
+  ASSERT_TRUE(found_ref);
+}
+
+}  // namespace android
diff --git a/libmemunreachable/tests/DisableMalloc_test.cpp b/libmemunreachable/tests/DisableMalloc_test.cpp
index c630049..f446719 100644
--- a/libmemunreachable/tests/DisableMalloc_test.cpp
+++ b/libmemunreachable/tests/DisableMalloc_test.cpp
@@ -73,15 +73,18 @@
 TEST_F(DisableMallocTest, deadlock_new) {
   ASSERT_DEATH(
       {
-        char* ptr = new (char);
+        // C++ allows `new Foo` to be replaced with a stack allocation or merged
+        // with future `new Foo` expressions, provided certain conditions are
+        // met [expr.new/10]. None of this applies to `operator new(size_t)`.
+        void* ptr = ::operator new(1);
         ASSERT_NE(ptr, nullptr);
-        delete (ptr);
+        ::operator delete(ptr);
         {
           alarm(100ms);
           ScopedDisableMalloc disable_malloc;
-          char* ptr = new (std::nothrow)(char);
+          void* ptr = ::operator new(1);
           ASSERT_NE(ptr, nullptr);
-          delete (ptr);
+          ::operator delete(ptr);
         }
       },
       "");
@@ -90,14 +93,12 @@
 TEST_F(DisableMallocTest, deadlock_delete) {
   ASSERT_DEATH(
       {
-        char* ptr = new (char);
+        void* ptr = ::operator new(1);
         ASSERT_NE(ptr, nullptr);
         {
           alarm(250ms);
           ScopedDisableMalloc disable_malloc;
-          delete (ptr);
-          // Force ptr usage or this code gets optimized away by the arm64 compiler.
-          ASSERT_NE(ptr, nullptr);
+          ::operator delete(ptr);
         }
       },
       "");
diff --git a/libmemunreachable/tests/MemUnreachable_test.cpp b/libmemunreachable/tests/MemUnreachable_test.cpp
index ec89388..bba0c6d 100644
--- a/libmemunreachable/tests/MemUnreachable_test.cpp
+++ b/libmemunreachable/tests/MemUnreachable_test.cpp
@@ -23,10 +23,15 @@
 
 #include <memunreachable/memunreachable.h>
 
+#include "bionic.h"
+
 namespace android {
 
 class HiddenPointer {
  public:
+  // Since we're doing such a good job of hiding it, the static analyzer
+  // thinks that we're leaking this `malloc`. This is probably related to
+  // https://bugs.llvm.org/show_bug.cgi?id=34198. NOLINTNEXTLINE
   explicit HiddenPointer(size_t size = 256) { Set(malloc(size)); }
   ~HiddenPointer() { Free(); }
   void* Get() { return reinterpret_cast<void*>(~ptr_); }
@@ -45,7 +50,35 @@
   write(0, ptr, 0);
 }
 
-TEST(MemunreachableTest, clean) {
+class MemunreachableTest : public ::testing::Test {
+ protected:
+  virtual void SetUp() {
+    CleanStack(8192);
+    CleanTcache();
+  }
+
+  virtual void TearDown() {
+    CleanStack(8192);
+    CleanTcache();
+  }
+
+  // Allocate a buffer on the stack and zero it to make sure there are no
+  // stray pointers from old test runs.
+  void __attribute__((noinline)) CleanStack(size_t size) {
+    void* buf = alloca(size);
+    memset(buf, 0, size);
+    Ref(&buf);
+  }
+
+  // Disable and re-enable malloc to flush the jemalloc tcache to make sure
+  // there are stray pointers from old test runs there.
+  void CleanTcache() {
+    malloc_disable();
+    malloc_enable();
+  }
+};
+
+TEST_F(MemunreachableTest, clean) {
   UnreachableMemoryInfo info;
 
   ASSERT_TRUE(LogUnreachableMemory(true, 100));
@@ -54,7 +87,7 @@
   ASSERT_EQ(0U, info.leaks.size());
 }
 
-TEST(MemunreachableTest, stack) {
+TEST_F(MemunreachableTest, stack) {
   HiddenPointer hidden_ptr;
 
   {
@@ -88,7 +121,7 @@
 
 void* g_ptr;
 
-TEST(MemunreachableTest, global) {
+TEST_F(MemunreachableTest, global) {
   HiddenPointer hidden_ptr;
 
   g_ptr = hidden_ptr.Get();
@@ -119,7 +152,7 @@
   }
 }
 
-TEST(MemunreachableTest, tls) {
+TEST_F(MemunreachableTest, tls) {
   HiddenPointer hidden_ptr;
   pthread_key_t key;
   pthread_key_create(&key, nullptr);
@@ -154,10 +187,22 @@
   pthread_key_delete(key);
 }
 
-TEST(MemunreachableTest, twice) {
+TEST_F(MemunreachableTest, twice) {
   HiddenPointer hidden_ptr;
 
   {
+    void* ptr = hidden_ptr.Get();
+    Ref(&ptr);
+
+    UnreachableMemoryInfo info;
+
+    ASSERT_TRUE(GetUnreachableMemory(info));
+    ASSERT_EQ(0U, info.leaks.size());
+
+    ptr = nullptr;
+  }
+
+  {
     UnreachableMemoryInfo info;
 
     ASSERT_TRUE(GetUnreachableMemory(info));
@@ -181,7 +226,7 @@
   }
 }
 
-TEST(MemunreachableTest, log) {
+TEST_F(MemunreachableTest, log) {
   HiddenPointer hidden_ptr;
 
   ASSERT_TRUE(LogUnreachableMemory(true, 100));
@@ -196,17 +241,23 @@
   }
 }
 
-TEST(MemunreachableTest, notdumpable) {
+TEST_F(MemunreachableTest, notdumpable) {
+  if (getuid() == 0) {
+    // TODO(ccross): make this a skipped test when gtest supports them
+    printf("[ SKIP     ] Not testable when running as root\n");
+    return;
+  }
+
   ASSERT_EQ(0, prctl(PR_SET_DUMPABLE, 0));
 
   HiddenPointer hidden_ptr;
 
-  ASSERT_TRUE(LogUnreachableMemory(true, 100));
+  EXPECT_FALSE(LogUnreachableMemory(true, 100));
 
   ASSERT_EQ(0, prctl(PR_SET_DUMPABLE, 1));
 }
 
-TEST(MemunreachableTest, leak_lots) {
+TEST_F(MemunreachableTest, leak_lots) {
   std::vector<HiddenPointer> hidden_ptrs;
   hidden_ptrs.resize(1024);
 
diff --git a/libmemunreachable/tests/ThreadCapture_test.cpp b/libmemunreachable/tests/ThreadCapture_test.cpp
index 4fbf729..933d65a 100644
--- a/libmemunreachable/tests/ThreadCapture_test.cpp
+++ b/libmemunreachable/tests/ThreadCapture_test.cpp
@@ -32,6 +32,8 @@
 #include "ScopedDisableMalloc.h"
 #include "ScopedPipe.h"
 
+#include <android-base/threads.h>
+
 using namespace std::chrono_literals;
 
 namespace android {
@@ -260,7 +262,7 @@
 
       ThreadCapture thread_capture(ret, heap);
       thread_capture.InjectTestFunc([&](pid_t tid) {
-        syscall(SYS_tgkill, ret, tid, SIGKILL);
+        tgkill(ret, tid, SIGKILL);
         usleep(10000);
       });
       auto list_tids = allocator::vector<pid_t>(heap);
@@ -319,7 +321,7 @@
 
           ThreadCapture thread_capture(child, heap);
           thread_capture.InjectTestFunc([&](pid_t tid) {
-            syscall(SYS_tgkill, child, tid, sig);
+            tgkill(child, tid, sig);
             usleep(10000);
           });
           auto list_tids = allocator::vector<pid_t>(heap);
diff --git a/libmetricslogger/Android.bp b/libmetricslogger/Android.bp
index 38859d1..e6e17ce 100644
--- a/libmetricslogger/Android.bp
+++ b/libmetricslogger/Android.bp
@@ -7,7 +7,6 @@
 cc_defaults {
     name: "metricslogger_defaults",
 
-    clang: true,
     host_supported: true,
 
     export_include_dirs: ["include"],
@@ -19,11 +18,6 @@
         "-Wall",
         "-Wextra",
         "-Werror",
-
-        // 524291 corresponds to sysui_histogram, from
-        // frameworks/base/core/java/com/android/internal/logging/EventLogTags.logtags
-        "-DHISTOGRAM_LOG_TAG=524292",
-        "-DCOUNT_LOG_TAG=524290",
     ],
 }
 
@@ -31,7 +25,13 @@
 // -----------------------------------------------------------------------------
 cc_library_shared {
     name: "libmetricslogger",
-    vendor_available: true,
+    srcs: metricslogger_lib_src_files,
+    defaults: ["metricslogger_defaults"],
+}
+
+// static version of libmetricslogger, needed by a few art static binaries
+cc_library_static {
+    name: "libmetricslogger_static",
     srcs: metricslogger_lib_src_files,
     defaults: ["metricslogger_defaults"],
 }
diff --git a/libmetricslogger/OWNERS b/libmetricslogger/OWNERS
new file mode 100644
index 0000000..7fe0443
--- /dev/null
+++ b/libmetricslogger/OWNERS
@@ -0,0 +1 @@
+jhawkins@google.com
diff --git a/libmetricslogger/include/metricslogger/metrics_logger.h b/libmetricslogger/include/metricslogger/metrics_logger.h
index 36e124d..2c76869 100644
--- a/libmetricslogger/include/metricslogger/metrics_logger.h
+++ b/libmetricslogger/include/metricslogger/metrics_logger.h
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
+#include <log/log_event_list.h>
 #include <cstdint>
 #include <string>
 
@@ -28,14 +29,66 @@
 // log buffer.
 void LogCounter(const std::string& name, int32_t val);
 
+// Logs a Tron multi_action with category|category| containing the string
+// |value| in the field |field|.
+void LogMultiAction(int32_t category, int32_t field, const std::string& value);
+
+// Logs a Tron complex event.
+//
+// A complex event can include data in a structure not suppored by the other
+// log event types above.
+//
+// Note that instances of this class are single use. You must call Record()
+// to write the event to the event log.
+class ComplexEventLogger {
+  private:
+    android_log_event_list logger;
+
+  public:
+    // Create a complex event with category|category|.
+    explicit ComplexEventLogger(int category);
+    // Add tagged data to the event, with the given tag and integer value.
+    void AddTaggedData(int tag, int32_t value);
+    // Add tagged data to the event, with the given tag and string value.
+    void AddTaggedData(int tag, const std::string& value);
+    // Add tagged data to the event, with the given tag and integer value.
+    void AddTaggedData(int tag, int64_t value);
+    // Add tagged data to the event, with the given tag and float value.
+    void AddTaggedData(int tag, float value);
+    // Record this event. This method can only be used once per instance
+    // of ComplexEventLogger. Do not made any subsequent calls to AddTaggedData
+    // after recording an event.
+    void Record();
+};
+
 // TODO: replace these with the metric_logger.proto definitions
 enum {
     LOGBUILDER_CATEGORY = 757,
+    LOGBUILDER_TYPE = 758,
     LOGBUILDER_NAME = 799,
     LOGBUILDER_BUCKET = 801,
     LOGBUILDER_VALUE = 802,
     LOGBUILDER_COUNTER = 803,
     LOGBUILDER_HISTOGRAM = 804,
+
+    ACTION_BOOT = 1098,
+    FIELD_PLATFORM_REASON = 1099,
+
+    ACTION_HIDDEN_API_ACCESSED = 1391,
+    FIELD_HIDDEN_API_ACCESS_METHOD = 1392,
+    FIELD_HIDDEN_API_ACCESS_DENIED = 1393,
+    FIELD_HIDDEN_API_SIGNATURE = 1394,
+};
+
+enum {
+    TYPE_ACTION = 4,
+};
+
+enum {
+    ACCESS_METHOD_NONE = 0,
+    ACCESS_METHOD_REFLECTION = 1,
+    ACCESS_METHOD_JNI = 2,
+    ACCESS_METHOD_LINKING = 3,
 };
 
 }  // namespace metricslogger
diff --git a/libmetricslogger/metrics_logger.cpp b/libmetricslogger/metrics_logger.cpp
index 6f65e10..912fa12 100644
--- a/libmetricslogger/metrics_logger.cpp
+++ b/libmetricslogger/metrics_logger.cpp
@@ -18,24 +18,69 @@
 
 #include <cstdlib>
 
+#include <log/event_tag_map.h>
 #include <log/log_event_list.h>
 
+namespace {
+
+#ifdef __ANDROID__
+EventTagMap* kEventTagMap = android_openEventTagMap(nullptr);
+const int kSysuiMultiActionTag = android_lookupEventTagNum(
+    kEventTagMap, "sysui_multi_action", "(content|4)", ANDROID_LOG_UNKNOWN);
+#else
+// android_openEventTagMap does not work on host builds.
+const int kSysuiMultiActionTag = 0;
+#endif
+
+}  // namespace
+
 namespace android {
 namespace metricslogger {
 
 // Mirror com.android.internal.logging.MetricsLogger#histogram().
 void LogHistogram(const std::string& event, int32_t data) {
-    android_log_event_list log(HISTOGRAM_LOG_TAG);
+    android_log_event_list log(kSysuiMultiActionTag);
     log << LOGBUILDER_CATEGORY << LOGBUILDER_HISTOGRAM << LOGBUILDER_NAME << event
         << LOGBUILDER_BUCKET << data << LOGBUILDER_VALUE << 1 << LOG_ID_EVENTS;
 }
 
 // Mirror com.android.internal.logging.MetricsLogger#count().
 void LogCounter(const std::string& name, int32_t val) {
-    android_log_event_list log(COUNT_LOG_TAG);
+    android_log_event_list log(kSysuiMultiActionTag);
     log << LOGBUILDER_CATEGORY << LOGBUILDER_COUNTER << LOGBUILDER_NAME << name << LOGBUILDER_VALUE
         << val << LOG_ID_EVENTS;
 }
 
+// Mirror com.android.internal.logging.MetricsLogger#action().
+void LogMultiAction(int32_t category, int32_t field, const std::string& value) {
+    android_log_event_list log(kSysuiMultiActionTag);
+    log << LOGBUILDER_CATEGORY << category << LOGBUILDER_TYPE << TYPE_ACTION
+        << field << value << LOG_ID_EVENTS;
+}
+
+ComplexEventLogger::ComplexEventLogger(int category) : logger(kSysuiMultiActionTag) {
+    logger << LOGBUILDER_CATEGORY << category;
+}
+
+void ComplexEventLogger::AddTaggedData(int tag, int32_t value) {
+    logger << tag << value;
+}
+
+void ComplexEventLogger::AddTaggedData(int tag, const std::string& value) {
+    logger << tag << value;
+}
+
+void ComplexEventLogger::AddTaggedData(int tag, int64_t value) {
+    logger << tag << value;
+}
+
+void ComplexEventLogger::AddTaggedData(int tag, float value) {
+    logger << tag << value;
+}
+
+void ComplexEventLogger::Record() {
+    logger << LOG_ID_EVENTS;
+}
+
 }  // namespace metricslogger
 }  // namespace android
diff --git a/libnativebridge/Android.bp b/libnativebridge/Android.bp
index 1cea4cd..6e63b74 100644
--- a/libnativebridge/Android.bp
+++ b/libnativebridge/Android.bp
@@ -1,9 +1,8 @@
-
 cc_library_headers {
     name: "libnativebridge-dummy-headers",
 
     host_supported: true,
-    export_include_dirs=["include"],
+    export_include_dirs: ["include"],
 }
 
 cc_library {
@@ -11,10 +10,12 @@
 
     host_supported: true,
     srcs: ["native_bridge.cc"],
-    shared_libs: ["liblog"],
-    clang: true,
+    shared_libs: [
+        "liblog",
+        "libbase",
+    ],
 
-    export_include_dirs=["include"],
+    export_include_dirs: ["include"],
 
     cflags: [
         "-Werror",
@@ -23,13 +24,6 @@
     cppflags: [
         "-fvisibility=protected",
     ],
-
-    host_ldlibs: ["-ldl"],
-    target: {
-        android: {
-            shared_libs: ["libdl"],
-        },
-    },
 }
 
 subdirs = ["tests"]
diff --git a/libnativebridge/OWNERS b/libnativebridge/OWNERS
new file mode 100644
index 0000000..f2cc942
--- /dev/null
+++ b/libnativebridge/OWNERS
@@ -0,0 +1 @@
+dimitry@google.com
diff --git a/libnativebridge/native_bridge.cc b/libnativebridge/native_bridge.cc
index 02b4fe7..e24307a 100644
--- a/libnativebridge/native_bridge.cc
+++ b/libnativebridge/native_bridge.cc
@@ -28,6 +28,7 @@
 
 #include <cstring>
 
+#include <android-base/macros.h>
 #include <log/log.h>
 
 namespace android {
@@ -243,29 +244,12 @@
   }
 }
 
-#if defined(__arm__)
-static const char* kRuntimeISA = "arm";
-#elif defined(__aarch64__)
-static const char* kRuntimeISA = "arm64";
-#elif defined(__mips__) && !defined(__LP64__)
-static const char* kRuntimeISA = "mips";
-#elif defined(__mips__) && defined(__LP64__)
-static const char* kRuntimeISA = "mips64";
-#elif defined(__i386__)
-static const char* kRuntimeISA = "x86";
-#elif defined(__x86_64__)
-static const char* kRuntimeISA = "x86_64";
-#else
-static const char* kRuntimeISA = "unknown";
-#endif
-
-
 bool NeedsNativeBridge(const char* instruction_set) {
   if (instruction_set == nullptr) {
     ALOGE("Null instruction set in NeedsNativeBridge.");
     return false;
   }
-  return strncmp(instruction_set, kRuntimeISA, strlen(kRuntimeISA) + 1) != 0;
+  return strncmp(instruction_set, ABI_STRING, strlen(ABI_STRING) + 1) != 0;
 }
 
 #ifdef __APPLE__
diff --git a/libnativebridge/tests/Android.bp b/libnativebridge/tests/Android.bp
index e31dae0..9e2e641 100644
--- a/libnativebridge/tests/Android.bp
+++ b/libnativebridge/tests/Android.bp
@@ -25,14 +25,6 @@
     ],
     header_libs: ["libnativebridge-dummy-headers"],
     cppflags: ["-fvisibility=protected"],
-    target: {
-        android: {
-            shared_libs: ["libdl"],
-        },
-        host: {
-            host_ldlibs: ["-ldl"],
-        },
-    },
 }
 
 cc_library_shared {
diff --git a/libnativebridge/tests/Android.mk b/libnativebridge/tests/Android.mk
index 70b3fcc..5b9ba1c 100644
--- a/libnativebridge/tests/Android.mk
+++ b/libnativebridge/tests/Android.mk
@@ -29,23 +29,28 @@
 
 shared_libraries := \
     liblog \
+    libbase \
     libnativebridge \
     libnativebridge-dummy
 
+libnativebridge_tests_common_cflags := \
+    -Wall \
+    -Werror \
+
 $(foreach file,$(test_src_files), \
     $(eval include $(CLEAR_VARS)) \
-    $(eval LOCAL_CLANG := true) \
     $(eval LOCAL_SHARED_LIBRARIES := $(shared_libraries)) \
     $(eval LOCAL_SRC_FILES := $(file)) \
+    $(eval LOCAL_CFLAGS := $(libnativebridge_tests_common_cflags)) \
     $(eval LOCAL_MODULE := $(notdir $(file:%.cpp=%))) \
     $(eval include $(BUILD_NATIVE_TEST)) \
 )
 
 $(foreach file,$(test_src_files), \
     $(eval include $(CLEAR_VARS)) \
-    $(eval LOCAL_CLANG := true) \
     $(eval LOCAL_SHARED_LIBRARIES := $(shared_libraries)) \
     $(eval LOCAL_SRC_FILES := $(file)) \
+    $(eval LOCAL_CFLAGS := $(libnativebridge_tests_common_cflags)) \
     $(eval LOCAL_MODULE := $(notdir $(file:%.cpp=%))) \
     $(eval include $(BUILD_HOST_NATIVE_TEST)) \
 )
diff --git a/libnativebridge/tests/NeedsNativeBridge_test.cpp b/libnativebridge/tests/NeedsNativeBridge_test.cpp
index 2067ed2..c8ff743 100644
--- a/libnativebridge/tests/NeedsNativeBridge_test.cpp
+++ b/libnativebridge/tests/NeedsNativeBridge_test.cpp
@@ -16,34 +16,20 @@
 
 #include "NativeBridgeTest.h"
 
+#include <android-base/macros.h>
+
 namespace android {
 
 static const char* kISAs[] = { "arm", "arm64", "mips", "mips64", "x86", "x86_64", "random", "64arm",
                                "64_x86", "64_x86_64", "", "reallylongstringabcd", nullptr };
 
-#if defined(__arm__)
-static const char* kRuntimeISA = "arm";
-#elif defined(__aarch64__)
-static const char* kRuntimeISA = "arm64";
-#elif defined(__mips__) && !defined(__LP64__)
-static const char* kRuntimeISA = "mips";
-#elif defined(__mips__) && defined(__LP64__)
-static const char* kRuntimeISA = "mips64";
-#elif defined(__i386__)
-static const char* kRuntimeISA = "x86";
-#elif defined(__x86_64__)
-static const char* kRuntimeISA = "x86_64";
-#else
-static const char* kRuntimeISA = "unknown";
-#endif
-
 TEST_F(NativeBridgeTest, NeedsNativeBridge) {
-    EXPECT_EQ(false, NeedsNativeBridge(kRuntimeISA));
+  EXPECT_EQ(false, NeedsNativeBridge(ABI_STRING));
 
-    const size_t kISACount = sizeof(kISAs)/sizeof(kISAs[0]);
-    for (size_t i = 0; i < kISACount; i++) {
-        EXPECT_EQ(kISAs[i] == nullptr ? false : strcmp(kISAs[i], kRuntimeISA) != 0,
-                  NeedsNativeBridge(kISAs[i]));
+  const size_t kISACount = sizeof(kISAs) / sizeof(kISAs[0]);
+  for (size_t i = 0; i < kISACount; i++) {
+    EXPECT_EQ(kISAs[i] == nullptr ? false : strcmp(kISAs[i], ABI_STRING) != 0,
+              NeedsNativeBridge(kISAs[i]));
     }
 }
 
diff --git a/libnativebridge/tests/PreInitializeNativeBridge_test.cpp b/libnativebridge/tests/PreInitializeNativeBridge_test.cpp
index f3e5f38..cd5a8e2 100644
--- a/libnativebridge/tests/PreInitializeNativeBridge_test.cpp
+++ b/libnativebridge/tests/PreInitializeNativeBridge_test.cpp
@@ -30,12 +30,12 @@
 
 namespace android {
 
-static constexpr const char* kTestData = "PreInitializeNativeBridge test.";
-
 TEST_F(NativeBridgeTest, PreInitializeNativeBridge) {
     ASSERT_TRUE(LoadNativeBridge(kNativeBridgeLibrary, nullptr));
 #if !defined(__APPLE__)         // Mac OS does not support bind-mount.
 #if !defined(__ANDROID__)       // Cannot write into the hard-wired location.
+    static constexpr const char* kTestData = "PreInitializeNativeBridge test.";
+
     // Try to create our mount namespace.
     if (unshare(CLONE_NEWNS) != -1) {
         // Create a dummy file.
diff --git a/libnativeloader/Android.bp b/libnativeloader/Android.bp
index c1133fb..17983bc 100644
--- a/libnativeloader/Android.bp
+++ b/libnativeloader/Android.bp
@@ -11,15 +11,6 @@
         "libnativebridge",
         "libbase",
     ],
-    target: {
-        android: {
-            shared_libs: ["libdl"],
-        },
-        host: {
-            host_ldlibs: ["-ldl"],
-        },
-    },
-    clang: true,
     cflags: [
         "-Werror",
         "-Wall",
@@ -28,5 +19,8 @@
         "-fvisibility=hidden",
     ],
     export_include_dirs: ["include"],
-    local_include_dirs: ["include"],
+    required: [
+        "llndk.libraries.txt",
+        "vndksp.libraries.txt",
+    ],
 }
diff --git a/libnativeloader/OWNERS b/libnativeloader/OWNERS
new file mode 100644
index 0000000..5c28a9f
--- /dev/null
+++ b/libnativeloader/OWNERS
@@ -0,0 +1,2 @@
+dimitry@google.com
+jiyong@google.com
diff --git a/libnativeloader/include/nativeloader/native_loader.h b/libnativeloader/include/nativeloader/native_loader.h
index 99ae3a7..19a1783 100644
--- a/libnativeloader/include/nativeloader/native_loader.h
+++ b/libnativeloader/include/nativeloader/native_loader.h
@@ -34,6 +34,7 @@
                                    int32_t target_sdk_version,
                                    jobject class_loader,
                                    bool is_shared,
+                                   bool is_for_vendor,
                                    jstring library_path,
                                    jstring permitted_path);
 
@@ -52,8 +53,21 @@
 #if defined(__ANDROID__)
 // Look up linker namespace by class_loader. Returns nullptr if
 // there is no namespace associated with the class_loader.
+// TODO(b/79940628): move users to FindNativeLoaderNamespaceByClassLoader and remove this function.
 __attribute__((visibility("default")))
 android_namespace_t* FindNamespaceByClassLoader(JNIEnv* env, jobject class_loader);
+// That version works with native bridge namespaces, but requires use of OpenNativeLibrary.
+class NativeLoaderNamespace;
+__attribute__((visibility("default")))
+NativeLoaderNamespace* FindNativeLoaderNamespaceByClassLoader(
+    JNIEnv* env, jobject class_loader);
+// Load library.  Unlinke OpenNativeLibrary above couldn't create namespace on demand, but does
+// not require access to JNIEnv either.
+__attribute__((visibility("default")))
+void* OpenNativeLibrary(NativeLoaderNamespace* ns,
+                        const char* path,
+                        bool* needs_native_bridge,
+                        std::string* error_msg);
 #endif
 
 __attribute__((visibility("default")))
diff --git a/libnativeloader/native_loader.cpp b/libnativeloader/native_loader.cpp
index 36a2e44..67c1c10 100644
--- a/libnativeloader/native_loader.cpp
+++ b/libnativeloader/native_loader.cpp
@@ -15,7 +15,7 @@
  */
 
 #include "nativeloader/native_loader.h"
-#include "ScopedUtfChars.h"
+#include <nativehelper/ScopedUtfChars.h>
 
 #include <dlfcn.h>
 #ifdef __ANDROID__
@@ -24,21 +24,31 @@
 #include "cutils/properties.h"
 #include "log/log.h"
 #endif
+#include <dirent.h>
+#include <sys/types.h>
 #include "nativebridge/native_bridge.h"
 
 #include <algorithm>
-#include <vector>
-#include <string>
+#include <list>
+#include <memory>
 #include <mutex>
+#include <string>
+#include <vector>
 
 #include <android-base/file.h>
 #include <android-base/macros.h>
 #include <android-base/strings.h>
 
+#ifdef __BIONIC__
+#include <android-base/properties.h>
+#endif
+
 #define CHECK(predicate) LOG_ALWAYS_FATAL_IF(!(predicate),\
                                              "%s:%d: %s CHECK '" #predicate "' failed.",\
                                              __FILE__, __LINE__, __FUNCTION__)
 
+using namespace std::string_literals;
+
 namespace android {
 
 #if defined(__ANDROID__)
@@ -78,10 +88,20 @@
   native_bridge_namespace_t* native_bridge_ns_;
 };
 
-static constexpr const char* kPublicNativeLibrariesSystemConfigPathFromRoot =
-                                  "/etc/public.libraries.txt";
-static constexpr const char* kPublicNativeLibrariesVendorConfig =
-                                  "/vendor/etc/public.libraries.txt";
+static constexpr const char kPublicNativeLibrariesSystemConfigPathFromRoot[] =
+    "/etc/public.libraries.txt";
+static constexpr const char kPublicNativeLibrariesExtensionConfigPrefix[] = "public.libraries-";
+static constexpr const size_t kPublicNativeLibrariesExtensionConfigPrefixLen =
+    sizeof(kPublicNativeLibrariesExtensionConfigPrefix) - 1;
+static constexpr const char kPublicNativeLibrariesExtensionConfigSuffix[] = ".txt";
+static constexpr const size_t kPublicNativeLibrariesExtensionConfigSuffixLen =
+    sizeof(kPublicNativeLibrariesExtensionConfigSuffix) - 1;
+static constexpr const char kPublicNativeLibrariesVendorConfig[] =
+    "/vendor/etc/public.libraries.txt";
+static constexpr const char kLlndkNativeLibrariesSystemConfigPathFromRoot[] =
+    "/etc/llndk.libraries.txt";
+static constexpr const char kVndkspNativeLibrariesSystemConfigPathFromRoot[] =
+    "/etc/vndksp.libraries.txt";
 
 // The device may be configured to have the vendor libraries loaded to a separate namespace.
 // For historical reasons this namespace was named sphal but effectively it is intended
@@ -89,6 +109,11 @@
 // vendor and system namespaces.
 static constexpr const char* kVendorNamespaceName = "sphal";
 
+static constexpr const char* kVndkNamespaceName = "vndk";
+
+static constexpr const char* kClassloaderNamespaceName = "classloader-namespace";
+static constexpr const char* kVendorClassloaderNamespaceName = "vendor-classloader-namespace";
+
 // (http://b/27588281) This is a workaround for apps using custom classloaders and calling
 // System.load() with an absolute path which is outside of the classloader library search path.
 // This list includes all directories app is allowed to access this way.
@@ -100,18 +125,40 @@
   return std::string(debuggable) == "1";
 }
 
+static std::string vndk_version_str() {
+#ifdef __BIONIC__
+  std::string version = android::base::GetProperty("ro.vndk.version", "");
+  if (version != "" && version != "current") {
+    return "." + version;
+  }
+#endif
+  return "";
+}
+
+static void insert_vndk_version_str(std::string* file_name) {
+  CHECK(file_name != nullptr);
+  size_t insert_pos = file_name->find_last_of(".");
+  if (insert_pos == std::string::npos) {
+    insert_pos = file_name->length();
+  }
+  file_name->insert(insert_pos, vndk_version_str());
+}
+
+static const std::function<bool(const std::string&, std::string*)> always_true =
+    [](const std::string&, std::string*) { return true; };
+
 class LibraryNamespaces {
  public:
   LibraryNamespaces() : initialized_(false) { }
 
-  bool Create(JNIEnv* env,
-              uint32_t target_sdk_version,
-              jobject class_loader,
-              bool is_shared,
-              jstring java_library_path,
-              jstring java_permitted_path,
-              NativeLoaderNamespace* ns,
-              std::string* error_msg) {
+  NativeLoaderNamespace* Create(JNIEnv* env,
+                                uint32_t target_sdk_version,
+                                jobject class_loader,
+                                bool is_shared,
+                                bool is_for_vendor,
+                                jstring java_library_path,
+                                jstring java_permitted_path,
+                                std::string* error_msg) {
     std::string library_path; // empty string by default.
 
     if (java_library_path != nullptr) {
@@ -135,10 +182,10 @@
     }
 
     if (!initialized_ && !InitPublicNamespace(library_path.c_str(), error_msg)) {
-      return false;
+      return nullptr;
     }
 
-    bool found = FindNamespaceByClassLoader(env, class_loader, nullptr);
+    bool found = FindNamespaceByClassLoader(env, class_loader);
 
     LOG_ALWAYS_FATAL_IF(found,
                         "There is already a namespace associated with this classloader");
@@ -152,28 +199,68 @@
       namespace_type |= ANDROID_NAMESPACE_TYPE_GREYLIST_ENABLED;
     }
 
-    NativeLoaderNamespace parent_ns;
-    bool found_parent_namespace = FindParentNamespaceByClassLoader(env, class_loader, &parent_ns);
+    NativeLoaderNamespace* parent_ns = FindParentNamespaceByClassLoader(env, class_loader);
 
     bool is_native_bridge = false;
 
-    if (found_parent_namespace) {
-      is_native_bridge = !parent_ns.is_android_namespace();
+    if (parent_ns != nullptr) {
+      is_native_bridge = !parent_ns->is_android_namespace();
     } else if (!library_path.empty()) {
       is_native_bridge = NativeBridgeIsPathSupported(library_path.c_str());
     }
 
+    std::string system_exposed_libraries = system_public_libraries_;
+    const char* namespace_name = kClassloaderNamespaceName;
+    android_namespace_t* vndk_ns = nullptr;
+    if (is_for_vendor && !is_shared) {
+      LOG_FATAL_IF(is_native_bridge, "Unbundled vendor apk must not use translated architecture");
+
+      // For vendor apks, give access to the vendor lib even though
+      // they are treated as unbundled; the libs and apks are still bundled
+      // together in the vendor partition.
+#if defined(__LP64__)
+      std::string vendor_lib_path = "/vendor/lib64";
+#else
+      std::string vendor_lib_path = "/vendor/lib";
+#endif
+      library_path = library_path + ":" + vendor_lib_path.c_str();
+      permitted_path = permitted_path + ":" + vendor_lib_path.c_str();
+
+      // Also give access to LLNDK libraries since they are available to vendors
+      system_exposed_libraries = system_exposed_libraries + ":" + system_llndk_libraries_.c_str();
+
+      // Give access to VNDK-SP libraries from the 'vndk' namespace.
+      vndk_ns = android_get_exported_namespace(kVndkNamespaceName);
+      LOG_ALWAYS_FATAL_IF(vndk_ns == nullptr,
+                          "Cannot find \"%s\" namespace for vendor apks", kVndkNamespaceName);
+
+      // Different name is useful for debugging
+      namespace_name = kVendorClassloaderNamespaceName;
+      ALOGD("classloader namespace configured for unbundled vendor apk. library_path=%s", library_path.c_str());
+    } else {
+      // oem and product public libraries are NOT available to vendor apks, otherwise it
+      // would be system->vendor violation.
+      if (!oem_public_libraries_.empty()) {
+        system_exposed_libraries = system_exposed_libraries + ':' + oem_public_libraries_;
+      }
+      if (!product_public_libraries_.empty()) {
+        system_exposed_libraries = system_exposed_libraries + ':' + product_public_libraries_;
+      }
+    }
+
     NativeLoaderNamespace native_loader_ns;
     if (!is_native_bridge) {
-      android_namespace_t* ns = android_create_namespace("classloader-namespace",
+      android_namespace_t* android_parent_ns =
+          parent_ns == nullptr ? nullptr : parent_ns->get_android_ns();
+      android_namespace_t* ns = android_create_namespace(namespace_name,
                                                          nullptr,
                                                          library_path.c_str(),
                                                          namespace_type,
                                                          permitted_path.c_str(),
-                                                         parent_ns.get_android_ns());
+                                                         android_parent_ns);
       if (ns == nullptr) {
         *error_msg = dlerror();
-        return false;
+        return nullptr;
       }
 
       // Note that when vendor_ns is not configured this function will return nullptr
@@ -181,43 +268,52 @@
       // which is expected behavior in this case.
       android_namespace_t* vendor_ns = android_get_exported_namespace(kVendorNamespaceName);
 
-      if (!android_link_namespaces(ns, nullptr, system_public_libraries_.c_str())) {
+      if (!android_link_namespaces(ns, nullptr, system_exposed_libraries.c_str())) {
         *error_msg = dlerror();
-        return false;
+        return nullptr;
+      }
+
+      if (vndk_ns != nullptr && !system_vndksp_libraries_.empty()) {
+        // vendor apks are allowed to use VNDK-SP libraries.
+        if (!android_link_namespaces(ns, vndk_ns, system_vndksp_libraries_.c_str())) {
+          *error_msg = dlerror();
+          return nullptr;
+        }
       }
 
       if (!vendor_public_libraries_.empty()) {
         if (!android_link_namespaces(ns, vendor_ns, vendor_public_libraries_.c_str())) {
           *error_msg = dlerror();
-          return false;
+          return nullptr;
         }
       }
 
       native_loader_ns = NativeLoaderNamespace(ns);
     } else {
-      native_bridge_namespace_t* ns = NativeBridgeCreateNamespace("classloader-namespace",
+      native_bridge_namespace_t* native_bridge_parent_namespace =
+          parent_ns == nullptr ? nullptr : parent_ns->get_native_bridge_ns();
+      native_bridge_namespace_t* ns = NativeBridgeCreateNamespace(namespace_name,
                                                                   nullptr,
                                                                   library_path.c_str(),
                                                                   namespace_type,
                                                                   permitted_path.c_str(),
-                                                                  parent_ns.get_native_bridge_ns());
-
+                                                                  native_bridge_parent_namespace);
       if (ns == nullptr) {
         *error_msg = NativeBridgeGetError();
-        return false;
+        return nullptr;
       }
 
       native_bridge_namespace_t* vendor_ns = NativeBridgeGetVendorNamespace();
 
-      if (!NativeBridgeLinkNamespaces(ns, nullptr, system_public_libraries_.c_str())) {
+      if (!NativeBridgeLinkNamespaces(ns, nullptr, system_exposed_libraries.c_str())) {
         *error_msg = NativeBridgeGetError();
-        return false;
+        return nullptr;
       }
 
       if (!vendor_public_libraries_.empty()) {
         if (!NativeBridgeLinkNamespaces(ns, vendor_ns, vendor_public_libraries_.c_str())) {
           *error_msg = NativeBridgeGetError();
-          return false;
+          return nullptr;
         }
       }
 
@@ -226,24 +322,19 @@
 
     namespaces_.push_back(std::make_pair(env->NewWeakGlobalRef(class_loader), native_loader_ns));
 
-    *ns = native_loader_ns;
-    return true;
+    return &(namespaces_.back().second);
   }
 
-  bool FindNamespaceByClassLoader(JNIEnv* env, jobject class_loader, NativeLoaderNamespace* ns) {
+  NativeLoaderNamespace* FindNamespaceByClassLoader(JNIEnv* env, jobject class_loader) {
     auto it = std::find_if(namespaces_.begin(), namespaces_.end(),
                 [&](const std::pair<jweak, NativeLoaderNamespace>& value) {
                   return env->IsSameObject(value.first, class_loader);
                 });
     if (it != namespaces_.end()) {
-      if (ns != nullptr) {
-        *ns = it->second;
-      }
-
-      return true;
+      return &it->second;
     }
 
-    return false;
+    return nullptr;
   }
 
   void Initialize() {
@@ -259,11 +350,18 @@
     std::string root_dir = android_root_env != nullptr ? android_root_env : "/system";
     std::string public_native_libraries_system_config =
             root_dir + kPublicNativeLibrariesSystemConfigPathFromRoot;
+    std::string llndk_native_libraries_system_config =
+            root_dir + kLlndkNativeLibrariesSystemConfigPathFromRoot;
+    std::string vndksp_native_libraries_system_config =
+            root_dir + kVndkspNativeLibrariesSystemConfigPathFromRoot;
+
+    std::string product_public_native_libraries_dir = "/product/etc";
 
     std::string error_msg;
-    LOG_ALWAYS_FATAL_IF(!ReadConfig(public_native_libraries_system_config, &sonames, &error_msg),
-                        "Error reading public native library list from \"%s\": %s",
-                        public_native_libraries_system_config.c_str(), error_msg.c_str());
+    LOG_ALWAYS_FATAL_IF(
+        !ReadConfig(public_native_libraries_system_config, &sonames, always_true, &error_msg),
+        "Error reading public native library list from \"%s\": %s",
+        public_native_libraries_system_config.c_str(), error_msg.c_str());
 
     // For debuggable platform builds use ANDROID_ADDITIONAL_PUBLIC_LIBRARIES environment
     // variable to add libraries to the list. This is intended for platform tests only.
@@ -271,8 +369,7 @@
       const char* additional_libs = getenv("ANDROID_ADDITIONAL_PUBLIC_LIBRARIES");
       if (additional_libs != nullptr && additional_libs[0] != '\0') {
         std::vector<std::string> additional_libs_vector = base::Split(additional_libs, ":");
-        std::copy(additional_libs_vector.begin(),
-                  additional_libs_vector.end(),
+        std::copy(additional_libs_vector.begin(), additional_libs_vector.end(),
                   std::back_inserter(sonames));
       }
     }
@@ -282,30 +379,99 @@
     //
     // TODO(dimitry): this is a bit misleading since we do not know
     // if the vendor public library is going to be opened from /vendor/lib
-    // we might as well end up loading them from /system/lib
+    // we might as well end up loading them from /system/lib or /product/lib
     // For now we rely on CTS test to catch things like this but
     // it should probably be addressed in the future.
     for (const auto& soname : sonames) {
       LOG_ALWAYS_FATAL_IF(dlopen(soname.c_str(), RTLD_NOW | RTLD_NODELETE) == nullptr,
-                          "Error preloading public library %s: %s",
-                          soname.c_str(), dlerror());
+                          "Error preloading public library %s: %s", soname.c_str(), dlerror());
     }
 
     system_public_libraries_ = base::Join(sonames, ':');
 
+    // read /system/etc/public.libraries-<companyname>.txt which contain partner defined
+    // system libs that are exposed to apps. The libs in the txt files must be
+    // named as lib<name>.<companyname>.so.
+    sonames.clear();
+    ReadExtensionLibraries(base::Dirname(public_native_libraries_system_config).c_str(), &sonames);
+    oem_public_libraries_ = base::Join(sonames, ':');
+
+    // read /product/etc/public.libraries-<companyname>.txt which contain partner defined
+    // product libs that are exposed to apps.
+    sonames.clear();
+    ReadExtensionLibraries(product_public_native_libraries_dir.c_str(), &sonames);
+    product_public_libraries_ = base::Join(sonames, ':');
+
+    // Insert VNDK version to llndk and vndksp config file names.
+    insert_vndk_version_str(&llndk_native_libraries_system_config);
+    insert_vndk_version_str(&vndksp_native_libraries_system_config);
+
+    sonames.clear();
+    ReadConfig(llndk_native_libraries_system_config, &sonames, always_true);
+    system_llndk_libraries_ = base::Join(sonames, ':');
+
+    sonames.clear();
+    ReadConfig(vndksp_native_libraries_system_config, &sonames, always_true);
+    system_vndksp_libraries_ = base::Join(sonames, ':');
+
     sonames.clear();
     // This file is optional, quietly ignore if the file does not exist.
-    ReadConfig(kPublicNativeLibrariesVendorConfig, &sonames);
+    ReadConfig(kPublicNativeLibrariesVendorConfig, &sonames, always_true, nullptr);
 
     vendor_public_libraries_ = base::Join(sonames, ':');
   }
 
-  void Reset() {
-    namespaces_.clear();
-  }
+  void Reset() { namespaces_.clear(); }
 
  private:
+  void ReadExtensionLibraries(const char* dirname, std::vector<std::string>* sonames) {
+    std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(dirname), closedir);
+    if (dir != nullptr) {
+      // Failing to opening the dir is not an error, which can happen in
+      // webview_zygote.
+      while (struct dirent* ent = readdir(dir.get())) {
+        if (ent->d_type != DT_REG && ent->d_type != DT_LNK) {
+          continue;
+        }
+        const std::string filename(ent->d_name);
+        if (android::base::StartsWith(filename, kPublicNativeLibrariesExtensionConfigPrefix) &&
+            android::base::EndsWith(filename, kPublicNativeLibrariesExtensionConfigSuffix)) {
+          const size_t start = kPublicNativeLibrariesExtensionConfigPrefixLen;
+          const size_t end = filename.size() - kPublicNativeLibrariesExtensionConfigSuffixLen;
+          const std::string company_name = filename.substr(start, end - start);
+          const std::string config_file_path = dirname + "/"s + filename;
+          LOG_ALWAYS_FATAL_IF(
+              company_name.empty(),
+              "Error extracting company name from public native library list file path \"%s\"",
+              config_file_path.c_str());
+
+          std::string error_msg;
+
+          LOG_ALWAYS_FATAL_IF(
+              !ReadConfig(
+                  config_file_path, sonames,
+                  [&company_name](const std::string& soname, std::string* error_msg) {
+                    if (android::base::StartsWith(soname, "lib") &&
+                        android::base::EndsWith(soname, "." + company_name + ".so")) {
+                      return true;
+                    } else {
+                      *error_msg = "Library name \"" + soname +
+                                   "\" does not end with the company name: " + company_name + ".";
+                      return false;
+                    }
+                  },
+                  &error_msg),
+              "Error reading public native library list from \"%s\": %s", config_file_path.c_str(),
+              error_msg.c_str());
+        }
+      }
+    }
+  }
+
+
   bool ReadConfig(const std::string& configFile, std::vector<std::string>* sonames,
+                  const std::function<bool(const std::string& /* soname */,
+                                           std::string* /* error_msg */)>& check_soname,
                   std::string* error_msg = nullptr) {
     // Read list of public native libraries from the config file.
     std::string file_content;
@@ -342,7 +508,11 @@
         trimmed_line.resize(space_pos);
       }
 
-      sonames->push_back(trimmed_line);
+      if (check_soname(trimmed_line, error_msg)) {
+        sonames->push_back(trimmed_line);
+      } else {
+        return false;
+      }
     }
 
     return true;
@@ -384,26 +554,29 @@
     return env->CallObjectMethod(class_loader, get_parent);
   }
 
-  bool FindParentNamespaceByClassLoader(JNIEnv* env,
-                                        jobject class_loader,
-                                        NativeLoaderNamespace* ns) {
+  NativeLoaderNamespace* FindParentNamespaceByClassLoader(JNIEnv* env, jobject class_loader) {
     jobject parent_class_loader = GetParentClassLoader(env, class_loader);
 
     while (parent_class_loader != nullptr) {
-      if (FindNamespaceByClassLoader(env, parent_class_loader, ns)) {
-        return true;
+      NativeLoaderNamespace* ns;
+      if ((ns = FindNamespaceByClassLoader(env, parent_class_loader)) != nullptr) {
+        return ns;
       }
 
       parent_class_loader = GetParentClassLoader(env, parent_class_loader);
     }
 
-    return false;
+    return nullptr;
   }
 
   bool initialized_;
-  std::vector<std::pair<jweak, NativeLoaderNamespace>> namespaces_;
+  std::list<std::pair<jweak, NativeLoaderNamespace>> namespaces_;
   std::string system_public_libraries_;
   std::string vendor_public_libraries_;
+  std::string oem_public_libraries_;
+  std::string product_public_libraries_;
+  std::string system_llndk_libraries_;
+  std::string system_vndksp_libraries_;
 
   DISALLOW_COPY_AND_ASSIGN(LibraryNamespaces);
 };
@@ -430,26 +603,26 @@
                                    int32_t target_sdk_version,
                                    jobject class_loader,
                                    bool is_shared,
+                                   bool is_for_vendor,
                                    jstring library_path,
                                    jstring permitted_path) {
 #if defined(__ANDROID__)
   std::lock_guard<std::mutex> guard(g_namespaces_mutex);
 
   std::string error_msg;
-  NativeLoaderNamespace ns;
   bool success = g_namespaces->Create(env,
                                       target_sdk_version,
                                       class_loader,
                                       is_shared,
+                                      is_for_vendor,
                                       library_path,
                                       permitted_path,
-                                      &ns,
-                                      &error_msg);
+                                      &error_msg) != nullptr;
   if (!success) {
     return env->NewStringUTF(error_msg.c_str());
   }
 #else
-  UNUSED(env, target_sdk_version, class_loader, is_shared,
+  UNUSED(env, target_sdk_version, class_loader, is_shared, is_for_vendor,
          library_path, permitted_path);
 #endif
   return nullptr;
@@ -470,59 +643,70 @@
   }
 
   std::lock_guard<std::mutex> guard(g_namespaces_mutex);
-  NativeLoaderNamespace ns;
+  NativeLoaderNamespace* ns;
 
-  if (!g_namespaces->FindNamespaceByClassLoader(env, class_loader, &ns)) {
+  if ((ns = g_namespaces->FindNamespaceByClassLoader(env, class_loader)) == nullptr) {
     // This is the case where the classloader was not created by ApplicationLoaders
     // In this case we create an isolated not-shared namespace for it.
-    if (!g_namespaces->Create(env,
-                              target_sdk_version,
-                              class_loader,
-                              false,
-                              library_path,
-                              nullptr,
-                              &ns,
-                              error_msg)) {
+    if ((ns = g_namespaces->Create(env,
+                                   target_sdk_version,
+                                   class_loader,
+                                   false /* is_shared */,
+                                   false /* is_for_vendor */,
+                                   library_path,
+                                   nullptr,
+                                   error_msg)) == nullptr) {
       return nullptr;
     }
   }
 
-  if (ns.is_android_namespace()) {
-    android_dlextinfo extinfo;
-    extinfo.flags = ANDROID_DLEXT_USE_NAMESPACE;
-    extinfo.library_namespace = ns.get_android_ns();
-
-    void* handle = android_dlopen_ext(path, RTLD_NOW, &extinfo);
-    if (handle == nullptr) {
-      *error_msg = dlerror();
-    }
-    *needs_native_bridge = false;
-    return handle;
-  } else {
-    void* handle = NativeBridgeLoadLibraryExt(path, RTLD_NOW, ns.get_native_bridge_ns());
-    if (handle == nullptr) {
-      *error_msg = NativeBridgeGetError();
-    }
-    *needs_native_bridge = true;
-    return handle;
-  }
+  return OpenNativeLibrary(ns, path, needs_native_bridge, error_msg);
 #else
-  UNUSED(env, target_sdk_version, class_loader, library_path);
-  *needs_native_bridge = false;
-  void* handle = dlopen(path, RTLD_NOW);
-  if (handle == nullptr) {
-    if (NativeBridgeIsSupported(path)) {
-      *needs_native_bridge = true;
-      handle = NativeBridgeLoadLibrary(path, RTLD_NOW);
-      if (handle == nullptr) {
-        *error_msg = NativeBridgeGetError();
-      }
+  UNUSED(env, target_sdk_version, class_loader);
+
+  // Do some best effort to emulate library-path support. It will not
+  // work for dependencies.
+  //
+  // Note: null has a special meaning and must be preserved.
+  std::string c_library_path;  // Empty string by default.
+  if (library_path != nullptr && path != nullptr && path[0] != '/') {
+    ScopedUtfChars library_path_utf_chars(env, library_path);
+    c_library_path = library_path_utf_chars.c_str();
+  }
+
+  std::vector<std::string> library_paths = base::Split(c_library_path, ":");
+
+  for (const std::string& lib_path : library_paths) {
+    *needs_native_bridge = false;
+    const char* path_arg;
+    std::string complete_path;
+    if (path == nullptr) {
+      // Preserve null.
+      path_arg = nullptr;
     } else {
-      *needs_native_bridge = false;
+      complete_path = lib_path;
+      if (!complete_path.empty()) {
+        complete_path.append("/");
+      }
+      complete_path.append(path);
+      path_arg = complete_path.c_str();
+    }
+    void* handle = dlopen(path_arg, RTLD_NOW);
+    if (handle != nullptr) {
+      return handle;
+    }
+    if (NativeBridgeIsSupported(path_arg)) {
+      *needs_native_bridge = true;
+      handle = NativeBridgeLoadLibrary(path_arg, RTLD_NOW);
+      if (handle != nullptr) {
+        return handle;
+      }
+      *error_msg = NativeBridgeGetError();
+    } else {
       *error_msg = dlerror();
     }
   }
-  return handle;
+  return nullptr;
 #endif
 }
 
@@ -532,18 +716,45 @@
 }
 
 #if defined(__ANDROID__)
+void* OpenNativeLibrary(NativeLoaderNamespace* ns, const char* path, bool* needs_native_bridge,
+                        std::string* error_msg) {
+  if (ns->is_android_namespace()) {
+    android_dlextinfo extinfo;
+    extinfo.flags = ANDROID_DLEXT_USE_NAMESPACE;
+    extinfo.library_namespace = ns->get_android_ns();
+
+    void* handle = android_dlopen_ext(path, RTLD_NOW, &extinfo);
+    if (handle == nullptr) {
+      *error_msg = dlerror();
+    }
+    *needs_native_bridge = false;
+    return handle;
+  } else {
+    void* handle = NativeBridgeLoadLibraryExt(path, RTLD_NOW, ns->get_native_bridge_ns());
+    if (handle == nullptr) {
+      *error_msg = NativeBridgeGetError();
+    }
+    *needs_native_bridge = true;
+    return handle;
+  }
+}
+
 // native_bridge_namespaces are not supported for callers of this function.
 // This function will return nullptr in the case when application is running
 // on native bridge.
 android_namespace_t* FindNamespaceByClassLoader(JNIEnv* env, jobject class_loader) {
   std::lock_guard<std::mutex> guard(g_namespaces_mutex);
-  NativeLoaderNamespace ns;
-  if (g_namespaces->FindNamespaceByClassLoader(env, class_loader, &ns)) {
-    return ns.is_android_namespace() ? ns.get_android_ns() : nullptr;
+  NativeLoaderNamespace* ns = g_namespaces->FindNamespaceByClassLoader(env, class_loader);
+  if (ns != nullptr) {
+    return ns->is_android_namespace() ? ns->get_android_ns() : nullptr;
   }
 
   return nullptr;
 }
+NativeLoaderNamespace* FindNativeLoaderNamespaceByClassLoader(JNIEnv* env, jobject class_loader) {
+  std::lock_guard<std::mutex> guard(g_namespaces_mutex);
+  return g_namespaces->FindNamespaceByClassLoader(env, class_loader);
+}
 #endif
 
 }; //  android namespace
diff --git a/libnativeloader/test/Android.bp b/libnativeloader/test/Android.bp
new file mode 100644
index 0000000..d528f30
--- /dev/null
+++ b/libnativeloader/test/Android.bp
@@ -0,0 +1,71 @@
+//
+// Copyright (C) 2017 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_library {
+    name: "libfoo.oem1",
+    srcs: ["test.cpp"],
+    cflags: ["-DLIBNAME=\"libfoo.oem1.so\""],
+    shared_libs: [
+        "libbase",
+    ],
+}
+
+cc_library {
+    name: "libbar.oem1",
+    srcs: ["test.cpp"],
+    cflags: ["-DLIBNAME=\"libbar.oem1.so\""],
+    shared_libs: [
+        "libbase",
+    ],
+}
+
+cc_library {
+    name: "libfoo.oem2",
+    srcs: ["test.cpp"],
+    cflags: ["-DLIBNAME=\"libfoo.oem2.so\""],
+    shared_libs: [
+        "libbase",
+    ],
+}
+
+cc_library {
+    name: "libbar.oem2",
+    srcs: ["test.cpp"],
+    cflags: ["-DLIBNAME=\"libbar.oem2.so\""],
+    shared_libs: [
+        "libbase",
+    ],
+}
+
+cc_library {
+    name: "libfoo.product1",
+    srcs: ["test.cpp"],
+    cflags: ["-DLIBNAME=\"libfoo.product1.so\""],
+    product_specific: true,
+    shared_libs: [
+        "libbase",
+    ],
+}
+
+cc_library {
+    name: "libbar.product1",
+    srcs: ["test.cpp"],
+    cflags: ["-DLIBNAME=\"libbar.product1.so\""],
+    product_specific: true,
+    shared_libs: [
+        "libbase",
+    ],
+}
diff --git a/libnativeloader/test/Android.mk b/libnativeloader/test/Android.mk
new file mode 100644
index 0000000..65e7b09
--- /dev/null
+++ b/libnativeloader/test/Android.mk
@@ -0,0 +1,57 @@
+#
+# Copyright (C) 2017 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.
+#
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := public.libraries-oem1.txt
+LOCAL_SRC_FILES:= $(LOCAL_MODULE)
+LOCAL_MODULE_CLASS := ETC
+LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)
+include $(BUILD_PREBUILT)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := public.libraries-oem2.txt
+LOCAL_SRC_FILES:= $(LOCAL_MODULE)
+LOCAL_MODULE_CLASS := ETC
+LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)
+include $(BUILD_PREBUILT)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := public.libraries-product1.txt
+LOCAL_SRC_FILES:= $(LOCAL_MODULE)
+LOCAL_MODULE_CLASS := ETC
+LOCAL_MODULE_PATH := $(TARGET_OUT_PRODUCT_ETC)
+include $(BUILD_PREBUILT)
+
+include $(CLEAR_VARS)
+LOCAL_PACKAGE_NAME := oemlibrarytest-system
+LOCAL_MODULE_TAGS := tests
+LOCAL_MANIFEST_FILE := system/AndroidManifest.xml
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_SDK_VERSION := current
+LOCAL_PROGUARD_ENABLED := disabled
+LOCAL_MODULE_PATH := $(TARGET_OUT_APPS)
+include $(BUILD_PACKAGE)
+
+include $(CLEAR_VARS)
+LOCAL_PACKAGE_NAME := oemlibrarytest-vendor
+LOCAL_MODULE_TAGS := tests
+LOCAL_MANIFEST_FILE := vendor/AndroidManifest.xml
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_SDK_VERSION := current
+LOCAL_PROGUARD_ENABLED := disabled
+LOCAL_MODULE_PATH := $(TARGET_OUT_VENDOR_APPS)
+include $(BUILD_PACKAGE)
diff --git a/libnativeloader/test/public.libraries-oem1.txt b/libnativeloader/test/public.libraries-oem1.txt
new file mode 100644
index 0000000..f9433e2
--- /dev/null
+++ b/libnativeloader/test/public.libraries-oem1.txt
@@ -0,0 +1,2 @@
+libfoo.oem1.so
+libbar.oem1.so
diff --git a/libnativeloader/test/public.libraries-oem2.txt b/libnativeloader/test/public.libraries-oem2.txt
new file mode 100644
index 0000000..de6bdb0
--- /dev/null
+++ b/libnativeloader/test/public.libraries-oem2.txt
@@ -0,0 +1,2 @@
+libfoo.oem2.so
+libbar.oem2.so
diff --git a/libnativeloader/test/public.libraries-product1.txt b/libnativeloader/test/public.libraries-product1.txt
new file mode 100644
index 0000000..358154c
--- /dev/null
+++ b/libnativeloader/test/public.libraries-product1.txt
@@ -0,0 +1,2 @@
+libfoo.product1.so
+libbar.product1.so
diff --git a/libnativeloader/test/runtest.sh b/libnativeloader/test/runtest.sh
new file mode 100755
index 0000000..40beb5b
--- /dev/null
+++ b/libnativeloader/test/runtest.sh
@@ -0,0 +1,11 @@
+#!/bin/bash
+adb root
+adb remount
+adb sync
+adb shell stop
+adb shell start
+sleep 5 # wait until device reboots
+adb logcat -c;
+adb shell am start -n android.test.app.system/android.test.app.TestActivity
+adb shell am start -n android.test.app.vendor/android.test.app.TestActivity
+adb logcat | grep android.test.app
diff --git a/libnativeloader/test/src/android/test/app/TestActivity.java b/libnativeloader/test/src/android/test/app/TestActivity.java
new file mode 100644
index 0000000..a7a455d
--- /dev/null
+++ b/libnativeloader/test/src/android/test/app/TestActivity.java
@@ -0,0 +1,44 @@
+/*
+ * 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.
+ */
+
+package android.test.app;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.util.Log;
+
+public class TestActivity extends Activity {
+
+    @Override
+    public void onCreate(Bundle icicle) {
+         super.onCreate(icicle);
+         tryLoadingLib("foo.oem1");
+         tryLoadingLib("bar.oem1");
+         tryLoadingLib("foo.oem2");
+         tryLoadingLib("bar.oem2");
+         tryLoadingLib("foo.product1");
+         tryLoadingLib("bar.product1");
+    }
+
+    private void tryLoadingLib(String name) {
+        try {
+            System.loadLibrary(name);
+            Log.d(getPackageName(), "library " + name + " is successfully loaded");
+        } catch (UnsatisfiedLinkError e) {
+            Log.d(getPackageName(), "failed to load libarary " + name, e);
+        }
+    }
+}
diff --git a/libnativeloader/test/system/AndroidManifest.xml b/libnativeloader/test/system/AndroidManifest.xml
new file mode 100644
index 0000000..c304889
--- /dev/null
+++ b/libnativeloader/test/system/AndroidManifest.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * 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.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="android.test.app.system">
+
+    <application>
+        <activity android:name="android.test.app.TestActivity" >
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+    </application>
+
+</manifest>
+
diff --git a/libnativeloader/test/test.cpp b/libnativeloader/test/test.cpp
new file mode 100644
index 0000000..b166928
--- /dev/null
+++ b/libnativeloader/test/test.cpp
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+#define LOG_TAG "oemlib"
+#include <android-base/logging.h>
+
+static __attribute__((constructor)) void test_lib_init() {
+  LOG(DEBUG) << LIBNAME << " loaded";
+}
diff --git a/libnativeloader/test/vendor/AndroidManifest.xml b/libnativeloader/test/vendor/AndroidManifest.xml
new file mode 100644
index 0000000..c4c1a9c
--- /dev/null
+++ b/libnativeloader/test/vendor/AndroidManifest.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * 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.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="android.test.app.vendor">
+
+    <application>
+        <activity android:name="android.test.app.TestActivity" >
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+    </application>
+
+</manifest>
+
diff --git a/libnetutils/Android.bp b/libnetutils/Android.bp
index 9967ef8..1d43775 100644
--- a/libnetutils/Android.bp
+++ b/libnetutils/Android.bp
@@ -1,6 +1,9 @@
 cc_library_shared {
     name: "libnetutils",
     vendor_available: true,
+    vndk: {
+        enabled: true,
+    },
 
     srcs: [
         "dhcpclient.c",
diff --git a/libnetutils/OWNERS b/libnetutils/OWNERS
new file mode 100644
index 0000000..e3ec950
--- /dev/null
+++ b/libnetutils/OWNERS
@@ -0,0 +1,3 @@
+# TODO: should this be in system/netd?
+ek@google.com
+lorenzo@google.com
diff --git a/libpackagelistparser/Android.bp b/libpackagelistparser/Android.bp
index 70ff528..c38594a 100644
--- a/libpackagelistparser/Android.bp
+++ b/libpackagelistparser/Android.bp
@@ -1,12 +1,16 @@
 cc_library {
 
     name: "libpackagelistparser",
+    recovery_available: true,
     srcs: ["packagelistparser.c"],
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
     shared_libs: ["liblog"],
     local_include_dirs: ["include"],
     export_include_dirs: ["include"],
 
-    clang: true,
     sanitize: {
         misc_undefined: ["integer"],
     },
diff --git a/libpixelflinger/Android.mk b/libpixelflinger/Android.mk
index 55891db..c7306cd 100644
--- a/libpixelflinger/Android.mk
+++ b/libpixelflinger/Android.mk
@@ -25,6 +25,8 @@
 	buffer.cpp
 
 PIXELFLINGER_CFLAGS := -fstrict-aliasing -fomit-frame-pointer
+PIXELFLINGER_CFLAGS += -Wall -Werror
+PIXELFLINGER_CFLAGS += -Wno-unused-function
 
 PIXELFLINGER_SRC_FILES_arm := \
 	codeflinger/ARMAssembler.cpp \
diff --git a/libpixelflinger/codeflinger/Arm64Assembler.cpp b/libpixelflinger/codeflinger/Arm64Assembler.cpp
index bff87bb..aebc129 100644
--- a/libpixelflinger/codeflinger/Arm64Assembler.cpp
+++ b/libpixelflinger/codeflinger/Arm64Assembler.cpp
@@ -151,11 +151,11 @@
 
 namespace android {
 
-static const char* shift_codes[] =
+static __unused const char* shift_codes[] =
 {
     "LSL", "LSR", "ASR", "ROR"
 };
-static const char *cc_codes[] =
+static __unused const char *cc_codes[] =
 {
     "EQ", "NE", "CS", "CC", "MI",
     "PL", "VS", "VC", "HI", "LS",
@@ -984,7 +984,7 @@
 // A64 instructions
 // ----------------------------------------------------------------------------
 
-static const char * dataTransferOpName[] =
+static __unused const char * dataTransferOpName[] =
 {
     "LDR","LDRB","LDRH","STR","STRB","STRH"
 };
diff --git a/libpixelflinger/codeflinger/GGLAssembler.cpp b/libpixelflinger/codeflinger/GGLAssembler.cpp
index 91fbd53..04e285d 100644
--- a/libpixelflinger/codeflinger/GGLAssembler.cpp
+++ b/libpixelflinger/codeflinger/GGLAssembler.cpp
@@ -94,8 +94,6 @@
 
 int GGLAssembler::scanline_core(const needs_t& needs, context_t const* c)
 {
-    int64_t duration = ggl_system_time();
-
     mBlendFactorCached = 0;
     mBlending = 0;
     mMasking = 0;
@@ -353,7 +351,6 @@
     fragment_parts_t& parts, const needs_t& needs)
 {
     Scratch scratches(registerFile());
-    int Rctx = mBuilderContext.Rctx;
 
     // compute count
     comment("compute ct (# of pixels to process)");
diff --git a/libpixelflinger/codeflinger/MIPS64Assembler.cpp b/libpixelflinger/codeflinger/MIPS64Assembler.cpp
index d5e4cea..d6d2156 100644
--- a/libpixelflinger/codeflinger/MIPS64Assembler.cpp
+++ b/libpixelflinger/codeflinger/MIPS64Assembler.cpp
@@ -39,6 +39,7 @@
 #include "mips64_disassem.h"
 
 #define NOT_IMPLEMENTED()  LOG_ALWAYS_FATAL("Arm instruction %s not yet implemented\n", __func__)
+#define __unused __attribute__((__unused__))
 
 // ----------------------------------------------------------------------------
 
@@ -146,7 +147,7 @@
     mMips->MOVE(R_v0, R_a0);    // move context * passed in a0 to v0 (arm r0)
 }
 
-void ArmToMips64Assembler::epilog(uint32_t touched)
+void ArmToMips64Assembler::epilog(uint32_t touched __unused)
 {
     mArmPC[mInum++] = pc();  // save starting PC for this instr
 
@@ -205,7 +206,7 @@
 
 // shifters...
 
-bool ArmToMips64Assembler::isValidImmediate(uint32_t immediate)
+bool ArmToMips64Assembler::isValidImmediate(uint32_t immediate __unused)
 {
     // for MIPS, any 32-bit immediate is OK
     return true;
@@ -225,13 +226,14 @@
     return AMODE_REG_IMM;
 }
 
-uint32_t ArmToMips64Assembler::reg_rrx(int Rm)
+uint32_t ArmToMips64Assembler::reg_rrx(int Rm __unused)
 {
     // reg_rrx mode is not used in the GLLAssember code at this time
     return AMODE_UNSUPPORTED;
 }
 
-uint32_t ArmToMips64Assembler::reg_reg(int Rm, int type, int Rs)
+uint32_t ArmToMips64Assembler::reg_reg(int Rm __unused, int type __unused,
+                                       int Rs __unused)
 {
     // reg_reg mode is not used in the GLLAssember code at this time
     return AMODE_UNSUPPORTED;
@@ -272,14 +274,15 @@
     return AMODE_REG_SCALE_PRE;
 }
 
-uint32_t ArmToMips64Assembler::reg_scale_post(int Rm, int type, uint32_t shift)
+uint32_t ArmToMips64Assembler::reg_scale_post(int Rm __unused, int type __unused,
+                                              uint32_t shift __unused)
 {
     LOG_ALWAYS_FATAL("adr mode reg_scale_post not yet implemented\n");
     return AMODE_UNSUPPORTED;
 }
 
 // LDRH/LDRSB/LDRSH/STRH (immediate and Rm can be negative, which indicate U=0)
-uint32_t ArmToMips64Assembler::immed8_pre(int32_t immed8, int W)
+uint32_t ArmToMips64Assembler::immed8_pre(int32_t immed8, int W __unused)
 {
     LOG_ALWAYS_FATAL("adr mode immed8_pre not yet implemented\n");
 
@@ -305,7 +308,7 @@
     return AMODE_REG_PRE;
 }
 
-uint32_t ArmToMips64Assembler::reg_post(int Rm)
+uint32_t ArmToMips64Assembler::reg_post(int Rm __unused)
 {
     LOG_ALWAYS_FATAL("adr mode reg_post not yet implemented\n");
     return AMODE_UNSUPPORTED;
@@ -320,12 +323,6 @@
 #pragma mark Data Processing...
 #endif
 
-
-static const char * const dpOpNames[] = {
-    "AND", "EOR", "SUB", "RSB", "ADD", "ADC", "SBC", "RSC",
-    "TST", "TEQ", "CMP", "CMN", "ORR", "MOV", "BIC", "MVN"
-};
-
 // check if the operand registers from a previous CMP or S-bit instruction
 // would be overwritten by this instruction. If so, move the value to a
 // safe register.
@@ -594,7 +591,7 @@
 #endif
 
 // multiply, accumulate
-void ArmToMips64Assembler::MLA(int cc, int s,
+void ArmToMips64Assembler::MLA(int cc __unused, int s,
         int Rd, int Rm, int Rs, int Rn) {
 
     //ALOGW("MLA");
@@ -608,7 +605,7 @@
     }
 }
 
-void ArmToMips64Assembler::MUL(int cc, int s,
+void ArmToMips64Assembler::MUL(int cc __unused, int s,
         int Rd, int Rm, int Rs) {
     mArmPC[mInum++] = pc();
     mMips->MUL(Rd, Rm, Rs);
@@ -618,7 +615,7 @@
     }
 }
 
-void ArmToMips64Assembler::UMULL(int cc, int s,
+void ArmToMips64Assembler::UMULL(int cc __unused, int s,
         int RdLo, int RdHi, int Rm, int Rs) {
     mArmPC[mInum++] = pc();
     mMips->MUH(RdHi, Rm, Rs);
@@ -631,8 +628,8 @@
     }
 }
 
-void ArmToMips64Assembler::UMUAL(int cc, int s,
-        int RdLo, int RdHi, int Rm, int Rs) {
+void ArmToMips64Assembler::UMUAL(int cc __unused, int s,
+        int RdLo __unused, int RdHi, int Rm __unused, int Rs __unused) {
     LOG_FATAL_IF(RdLo==Rm || RdHi==Rm || RdLo==RdHi,
                         "UMUAL(r%u,r%u,r%u,r%u)", RdLo,RdHi,Rm,Rs);
     // *mPC++ =    (cc<<28) | (1<<23) | (1<<21) | (s<<20) |
@@ -647,8 +644,8 @@
     }
 }
 
-void ArmToMips64Assembler::SMULL(int cc, int s,
-        int RdLo, int RdHi, int Rm, int Rs) {
+void ArmToMips64Assembler::SMULL(int cc __unused, int s,
+        int RdLo __unused, int RdHi, int Rm __unused, int Rs __unused) {
     LOG_FATAL_IF(RdLo==Rm || RdHi==Rm || RdLo==RdHi,
                         "SMULL(r%u,r%u,r%u,r%u)", RdLo,RdHi,Rm,Rs);
     // *mPC++ =    (cc<<28) | (1<<23) | (1<<22) | (s<<20) |
@@ -662,8 +659,8 @@
         LOG_ALWAYS_FATAL("Condition on SMULL must be on 64-bit result\n");
     }
 }
-void ArmToMips64Assembler::SMUAL(int cc, int s,
-        int RdLo, int RdHi, int Rm, int Rs) {
+void ArmToMips64Assembler::SMUAL(int cc __unused, int s,
+        int RdLo __unused, int RdHi, int Rm __unused, int Rs __unused) {
     LOG_FATAL_IF(RdLo==Rm || RdHi==Rm || RdLo==RdHi,
                         "SMUAL(r%u,r%u,r%u,r%u)", RdLo,RdHi,Rm,Rs);
     // *mPC++ =    (cc<<28) | (1<<23) | (1<<22) | (1<<21) | (s<<20) |
@@ -717,26 +714,26 @@
     }
 }
 
-void ArmToMips64Assembler::BL(int cc, const char* label)
+void ArmToMips64Assembler::BL(int cc __unused, const char* label __unused)
 {
     LOG_ALWAYS_FATAL("branch-and-link not supported yet\n");
     mArmPC[mInum++] = pc();
 }
 
 // no use for Branches with integer PC, but they're in the Interface class ....
-void ArmToMips64Assembler::B(int cc, uint32_t* to_pc)
+void ArmToMips64Assembler::B(int cc __unused, uint32_t* to_pc __unused)
 {
     LOG_ALWAYS_FATAL("branch to absolute PC not supported, use Label\n");
     mArmPC[mInum++] = pc();
 }
 
-void ArmToMips64Assembler::BL(int cc, uint32_t* to_pc)
+void ArmToMips64Assembler::BL(int cc __unused, uint32_t* to_pc __unused)
 {
     LOG_ALWAYS_FATAL("branch to absolute PC not supported, use Label\n");
     mArmPC[mInum++] = pc();
 }
 
-void ArmToMips64Assembler::BX(int cc, int Rn)
+void ArmToMips64Assembler::BX(int cc __unused, int Rn __unused)
 {
     LOG_ALWAYS_FATAL("branch to absolute PC not supported, use Label\n");
     mArmPC[mInum++] = pc();
@@ -750,7 +747,7 @@
 #endif
 
 // data transfer...
-void ArmToMips64Assembler::LDR(int cc, int Rd, int Rn, uint32_t offset)
+void ArmToMips64Assembler::LDR(int cc __unused, int Rd, int Rn, uint32_t offset)
 {
     mArmPC[mInum++] = pc();
     // work-around for ARM default address mode of immed12_pre(0)
@@ -784,7 +781,7 @@
     }
 }
 
-void ArmToMips64Assembler::LDRB(int cc, int Rd, int Rn, uint32_t offset)
+void ArmToMips64Assembler::LDRB(int cc __unused, int Rd, int Rn, uint32_t offset)
 {
     mArmPC[mInum++] = pc();
     // work-around for ARM default address mode of immed12_pre(0)
@@ -813,7 +810,7 @@
 
 }
 
-void ArmToMips64Assembler::STR(int cc, int Rd, int Rn, uint32_t offset)
+void ArmToMips64Assembler::STR(int cc __unused, int Rd, int Rn, uint32_t offset)
 {
     mArmPC[mInum++] = pc();
     // work-around for ARM default address mode of immed12_pre(0)
@@ -849,7 +846,7 @@
     }
 }
 
-void ArmToMips64Assembler::STRB(int cc, int Rd, int Rn, uint32_t offset)
+void ArmToMips64Assembler::STRB(int cc __unused, int Rd, int Rn, uint32_t offset)
 {
     mArmPC[mInum++] = pc();
     // work-around for ARM default address mode of immed12_pre(0)
@@ -877,7 +874,7 @@
     }
 }
 
-void ArmToMips64Assembler::LDRH(int cc, int Rd, int Rn, uint32_t offset)
+void ArmToMips64Assembler::LDRH(int cc __unused, int Rd, int Rn, uint32_t offset)
 {
     mArmPC[mInum++] = pc();
     // work-around for ARM default address mode of immed8_pre(0)
@@ -905,21 +902,23 @@
     }
 }
 
-void ArmToMips64Assembler::LDRSB(int cc, int Rd, int Rn, uint32_t offset)
+void ArmToMips64Assembler::LDRSB(int cc __unused, int Rd __unused,
+                                 int Rn __unused, uint32_t offset __unused)
 {
     mArmPC[mInum++] = pc();
     mMips->NOP2();
     NOT_IMPLEMENTED();
 }
 
-void ArmToMips64Assembler::LDRSH(int cc, int Rd, int Rn, uint32_t offset)
+void ArmToMips64Assembler::LDRSH(int cc __unused, int Rd __unused,
+                                 int Rn __unused, uint32_t offset __unused)
 {
     mArmPC[mInum++] = pc();
     mMips->NOP2();
     NOT_IMPLEMENTED();
 }
 
-void ArmToMips64Assembler::STRH(int cc, int Rd, int Rn, uint32_t offset)
+void ArmToMips64Assembler::STRH(int cc __unused, int Rd, int Rn, uint32_t offset)
 {
     mArmPC[mInum++] = pc();
     // work-around for ARM default address mode of immed8_pre(0)
@@ -955,8 +954,8 @@
 #endif
 
 // block data transfer...
-void ArmToMips64Assembler::LDM(int cc, int dir,
-        int Rn, int W, uint32_t reg_list)
+void ArmToMips64Assembler::LDM(int cc __unused, int dir __unused,
+        int Rn __unused, int W __unused, uint32_t reg_list __unused)
 {   //                        ED FD EA FA      IB IA DB DA
     // const uint8_t P[8] = { 1, 0, 1, 0,      1, 0, 1, 0 };
     // const uint8_t U[8] = { 1, 1, 0, 0,      1, 1, 0, 0 };
@@ -967,8 +966,8 @@
     NOT_IMPLEMENTED();
 }
 
-void ArmToMips64Assembler::STM(int cc, int dir,
-        int Rn, int W, uint32_t reg_list)
+void ArmToMips64Assembler::STM(int cc __unused, int dir __unused,
+        int Rn __unused, int W __unused, uint32_t reg_list __unused)
 {   //                        FA EA FD ED      IB IA DB DA
     // const uint8_t P[8] = { 0, 1, 0, 1,      1, 0, 1, 0 };
     // const uint8_t U[8] = { 0, 0, 1, 1,      1, 1, 0, 0 };
@@ -987,21 +986,23 @@
 #endif
 
 // special...
-void ArmToMips64Assembler::SWP(int cc, int Rn, int Rd, int Rm) {
+void ArmToMips64Assembler::SWP(int cc __unused, int Rn __unused,
+                               int Rd __unused, int Rm __unused) {
     // *mPC++ = (cc<<28) | (2<<23) | (Rn<<16) | (Rd << 12) | 0x90 | Rm;
     mArmPC[mInum++] = pc();
     mMips->NOP2();
     NOT_IMPLEMENTED();
 }
 
-void ArmToMips64Assembler::SWPB(int cc, int Rn, int Rd, int Rm) {
+void ArmToMips64Assembler::SWPB(int cc __unused, int Rn __unused,
+                                int Rd __unused, int Rm __unused) {
     // *mPC++ = (cc<<28) | (2<<23) | (1<<22) | (Rn<<16) | (Rd << 12) | 0x90 | Rm;
     mArmPC[mInum++] = pc();
     mMips->NOP2();
     NOT_IMPLEMENTED();
 }
 
-void ArmToMips64Assembler::SWI(int cc, uint32_t comment) {
+void ArmToMips64Assembler::SWI(int cc __unused, uint32_t comment __unused) {
     // *mPC++ = (cc<<28) | (0xF<<24) | comment;
     mArmPC[mInum++] = pc();
     mMips->NOP2();
@@ -1015,7 +1016,7 @@
 #endif
 
 // DSP instructions...
-void ArmToMips64Assembler::PLD(int Rn, uint32_t offset) {
+void ArmToMips64Assembler::PLD(int Rn __unused, uint32_t offset) {
     LOG_ALWAYS_FATAL_IF(!((offset&(1<<24)) && !(offset&(1<<21))),
                         "PLD only P=1, W=0");
     // *mPC++ = 0xF550F000 | (Rn<<16) | offset;
@@ -1024,13 +1025,14 @@
     NOT_IMPLEMENTED();
 }
 
-void ArmToMips64Assembler::CLZ(int cc, int Rd, int Rm)
+void ArmToMips64Assembler::CLZ(int cc __unused, int Rd, int Rm)
 {
     mArmPC[mInum++] = pc();
     mMips->CLZ(Rd, Rm);
 }
 
-void ArmToMips64Assembler::QADD(int cc,  int Rd, int Rm, int Rn)
+void ArmToMips64Assembler::QADD(int cc __unused, int Rd __unused,
+                                int Rm __unused, int Rn __unused)
 {
     // *mPC++ = (cc<<28) | 0x1000050 | (Rn<<16) | (Rd<<12) | Rm;
     mArmPC[mInum++] = pc();
@@ -1038,7 +1040,8 @@
     NOT_IMPLEMENTED();
 }
 
-void ArmToMips64Assembler::QDADD(int cc,  int Rd, int Rm, int Rn)
+void ArmToMips64Assembler::QDADD(int cc __unused, int Rd __unused,
+                                 int Rm __unused, int Rn __unused)
 {
     // *mPC++ = (cc<<28) | 0x1400050 | (Rn<<16) | (Rd<<12) | Rm;
     mArmPC[mInum++] = pc();
@@ -1046,7 +1049,8 @@
     NOT_IMPLEMENTED();
 }
 
-void ArmToMips64Assembler::QSUB(int cc,  int Rd, int Rm, int Rn)
+void ArmToMips64Assembler::QSUB(int cc __unused, int Rd __unused,
+                                int Rm __unused, int Rn __unused)
 {
     // *mPC++ = (cc<<28) | 0x1200050 | (Rn<<16) | (Rd<<12) | Rm;
     mArmPC[mInum++] = pc();
@@ -1054,7 +1058,8 @@
     NOT_IMPLEMENTED();
 }
 
-void ArmToMips64Assembler::QDSUB(int cc,  int Rd, int Rm, int Rn)
+void ArmToMips64Assembler::QDSUB(int cc __unused, int Rd __unused,
+                                 int Rm __unused, int Rn __unused)
 {
     // *mPC++ = (cc<<28) | 0x1600050 | (Rn<<16) | (Rd<<12) | Rm;
     mArmPC[mInum++] = pc();
@@ -1063,7 +1068,7 @@
 }
 
 // 16 x 16 signed multiply (like SMLAxx without the accumulate)
-void ArmToMips64Assembler::SMUL(int cc, int xy,
+void ArmToMips64Assembler::SMUL(int cc __unused, int xy,
                 int Rd, int Rm, int Rs)
 {
     mArmPC[mInum++] = pc();
@@ -1092,7 +1097,7 @@
 }
 
 // signed 32b x 16b multiple, save top 32-bits of 48-bit result
-void ArmToMips64Assembler::SMULW(int cc, int y,
+void ArmToMips64Assembler::SMULW(int cc __unused, int y,
                 int Rd, int Rm, int Rs)
 {
     mArmPC[mInum++] = pc();
@@ -1111,7 +1116,7 @@
 }
 
 // 16 x 16 signed multiply, accumulate: Rd = Rm{16} * Rs{16} + Rn
-void ArmToMips64Assembler::SMLA(int cc, int xy,
+void ArmToMips64Assembler::SMLA(int cc __unused, int xy,
                 int Rd, int Rm, int Rs, int Rn)
 {
     mArmPC[mInum++] = pc();
@@ -1141,8 +1146,9 @@
     mMips->ADDU(Rd, R_at, Rn);
 }
 
-void ArmToMips64Assembler::SMLAL(int cc, int xy,
-                int RdHi, int RdLo, int Rs, int Rm)
+void ArmToMips64Assembler::SMLAL(int cc __unused, int xy __unused,
+                                 int RdHi __unused, int RdLo __unused,
+                                 int Rs __unused, int Rm __unused)
 {
     // *mPC++ = (cc<<28) | 0x1400080 | (RdHi<<16) | (RdLo<<12) | (Rs<<8) | (xy<<4) | Rm;
     mArmPC[mInum++] = pc();
@@ -1150,8 +1156,9 @@
     NOT_IMPLEMENTED();
 }
 
-void ArmToMips64Assembler::SMLAW(int cc, int y,
-                int Rd, int Rm, int Rs, int Rn)
+void ArmToMips64Assembler::SMLAW(int cc __unused, int y __unused,
+                                 int Rd __unused, int Rm __unused,
+                                 int Rs __unused, int Rn __unused)
 {
     // *mPC++ = (cc<<28) | 0x1200080 | (Rd<<16) | (Rn<<12) | (Rs<<8) | (y<<4) | Rm;
     mArmPC[mInum++] = pc();
@@ -1160,7 +1167,7 @@
 }
 
 // used by ARMv6 version of GGLAssembler::filter32
-void ArmToMips64Assembler::UXTB16(int cc, int Rd, int Rm, int rotate)
+void ArmToMips64Assembler::UXTB16(int cc __unused, int Rd, int Rm, int rotate)
 {
     mArmPC[mInum++] = pc();
 
@@ -1173,7 +1180,8 @@
     mMips->AND(Rd, R_at2, R_at);
 }
 
-void ArmToMips64Assembler::UBFX(int cc, int Rd, int Rn, int lsb, int width)
+void ArmToMips64Assembler::UBFX(int cc __unused, int Rd __unused, int Rn __unused,
+                                int lsb __unused, int width __unused)
 {
      /* Placeholder for UBFX */
      mArmPC[mInum++] = pc();
@@ -1202,7 +1210,8 @@
     dataProcessing(opSUB64, cc, s, Rd, Rn, Op2);
 }
 
-void ArmToMips64Assembler::ADDR_LDR(int cc, int Rd, int Rn, uint32_t offset) {
+void ArmToMips64Assembler::ADDR_LDR(int cc __unused, int Rd,
+                                    int Rn, uint32_t offset) {
     mArmPC[mInum++] = pc();
     // work-around for ARM default address mode of immed12_pre(0)
     if (offset > AMODE_UNSUPPORTED) offset = 0;
@@ -1235,7 +1244,8 @@
     }
 }
 
-void ArmToMips64Assembler::ADDR_STR(int cc, int Rd, int Rn, uint32_t offset) {
+void ArmToMips64Assembler::ADDR_STR(int cc __unused, int Rd,
+                                    int Rn, uint32_t offset) {
     mArmPC[mInum++] = pc();
     // work-around for ARM default address mode of immed12_pre(0)
     if (offset > AMODE_UNSUPPORTED) offset = 0;
@@ -1290,14 +1300,12 @@
 */
 
 MIPS64Assembler::MIPS64Assembler(const sp<Assembly>& assembly, ArmToMips64Assembler *parent)
-    : mParent(parent),
-    MIPSAssembler::MIPSAssembler(assembly, NULL)
+    : MIPSAssembler::MIPSAssembler(assembly, NULL), mParent(parent)
 {
 }
 
 MIPS64Assembler::MIPS64Assembler(void* assembly, ArmToMips64Assembler *parent)
-    : mParent(parent),
-    MIPSAssembler::MIPSAssembler(assembly)
+    : MIPSAssembler::MIPSAssembler(assembly), mParent(parent)
 {
 }
 
@@ -1319,7 +1327,7 @@
 }
 
 
-void MIPS64Assembler::disassemble(const char* name)
+void MIPS64Assembler::disassemble(const char* name __unused)
 {
     char di_buf[140];
 
@@ -1334,11 +1342,6 @@
         }
     }
 
-    // iArm is an index to Arm instructions 1...n for this assembly sequence
-    // mArmPC[iArm] holds the value of the Mips-PC for the first MIPS
-    // instruction corresponding to that Arm instruction number
-
-    int iArm = 0;
     size_t count = pc()-base();
     uint32_t* mipsPC = base();
 
diff --git a/libpixelflinger/codeflinger/MIPSAssembler.cpp b/libpixelflinger/codeflinger/MIPSAssembler.cpp
index 865a568..039a725 100644
--- a/libpixelflinger/codeflinger/MIPSAssembler.cpp
+++ b/libpixelflinger/codeflinger/MIPSAssembler.cpp
@@ -51,6 +51,7 @@
 
 #include <stdio.h>
 #include <stdlib.h>
+#include <inttypes.h>
 
 #include <cutils/properties.h>
 #include <log/log.h>
@@ -60,6 +61,8 @@
 #include "MIPSAssembler.h"
 #include "mips_disassem.h"
 
+#define __unused __attribute__((__unused__))
+
 // Choose MIPS arch variant following gcc flags
 #if defined(__mips__) && __mips==32 && __mips_isa_rev>=2
 #define mips32r2 1
@@ -167,7 +170,7 @@
     mMips->MOVE(R_v0, R_a0);    // move context * passed in a0 to v0 (arm r0)
 }
 
-void ArmToMipsAssembler::epilog(uint32_t touched)
+void ArmToMipsAssembler::epilog(uint32_t touched __unused)
 {
     mArmPC[mInum++] = pc();  // save starting PC for this instr
 
@@ -213,7 +216,7 @@
 
 // shifters...
 
-bool ArmToMipsAssembler::isValidImmediate(uint32_t immediate)
+bool ArmToMipsAssembler::isValidImmediate(uint32_t immediate __unused)
 {
     // for MIPS, any 32-bit immediate is OK
     return true;
@@ -234,13 +237,14 @@
     return AMODE_REG_IMM;
 }
 
-uint32_t ArmToMipsAssembler::reg_rrx(int Rm)
+uint32_t ArmToMipsAssembler::reg_rrx(int Rm __unused)
 {
     // reg_rrx mode is not used in the GLLAssember code at this time
     return AMODE_UNSUPPORTED;
 }
 
-uint32_t ArmToMipsAssembler::reg_reg(int Rm, int type, int Rs)
+uint32_t ArmToMipsAssembler::reg_reg(int Rm __unused, int type __unused,
+                                     int Rs __unused)
 {
     // reg_reg mode is not used in the GLLAssember code at this time
     return AMODE_UNSUPPORTED;
@@ -281,14 +285,15 @@
     return AMODE_REG_SCALE_PRE;
 }
 
-uint32_t ArmToMipsAssembler::reg_scale_post(int Rm, int type, uint32_t shift)
+uint32_t ArmToMipsAssembler::reg_scale_post(int Rm __unused, int type __unused,
+                                            uint32_t shift __unused)
 {
     LOG_ALWAYS_FATAL("adr mode reg_scale_post not yet implemented\n");
     return AMODE_UNSUPPORTED;
 }
 
 // LDRH/LDRSB/LDRSH/STRH (immediate and Rm can be negative, which indicate U=0)
-uint32_t ArmToMipsAssembler::immed8_pre(int32_t immed8, int W)
+uint32_t ArmToMipsAssembler::immed8_pre(int32_t immed8, int W __unused)
 {
     // uint32_t offset = abs(immed8);
 
@@ -318,7 +323,7 @@
     return AMODE_REG_PRE;
 }
 
-uint32_t ArmToMipsAssembler::reg_post(int Rm)
+uint32_t ArmToMipsAssembler::reg_post(int Rm __unused)
 {
     LOG_ALWAYS_FATAL("adr mode reg_post not yet implemented\n");
     return AMODE_UNSUPPORTED;
@@ -333,12 +338,6 @@
 #pragma mark Data Processing...
 #endif
 
-
-static const char * const dpOpNames[] = {
-    "AND", "EOR", "SUB", "RSB", "ADD", "ADC", "SBC", "RSC",
-    "TST", "TEQ", "CMP", "CMN", "ORR", "MOV", "BIC", "MVN"
-};
-
 // check if the operand registers from a previous CMP or S-bit instruction
 // would be overwritten by this instruction. If so, move the value to a
 // safe register.
@@ -605,7 +604,7 @@
 #endif
 
 // multiply, accumulate
-void ArmToMipsAssembler::MLA(int cc, int s,
+void ArmToMipsAssembler::MLA(int cc __unused, int s,
         int Rd, int Rm, int Rs, int Rn) {
 
     mArmPC[mInum++] = pc();  // save starting PC for this instr
@@ -618,7 +617,7 @@
     }
 }
 
-void ArmToMipsAssembler::MUL(int cc, int s,
+void ArmToMipsAssembler::MUL(int cc __unused, int s,
         int Rd, int Rm, int Rs) {
     mArmPC[mInum++] = pc();
     mMips->MUL(Rd, Rm, Rs);
@@ -628,7 +627,7 @@
     }
 }
 
-void ArmToMipsAssembler::UMULL(int cc, int s,
+void ArmToMipsAssembler::UMULL(int cc __unused, int s,
         int RdLo, int RdHi, int Rm, int Rs) {
     mArmPC[mInum++] = pc();
     mMips->MULT(Rm, Rs);
@@ -641,8 +640,8 @@
     }
 }
 
-void ArmToMipsAssembler::UMUAL(int cc, int s,
-        int RdLo, int RdHi, int Rm, int Rs) {
+void ArmToMipsAssembler::UMUAL(int cc __unused, int s,
+        int RdLo __unused, int RdHi, int Rm __unused, int Rs __unused) {
     LOG_FATAL_IF(RdLo==Rm || RdHi==Rm || RdLo==RdHi,
                         "UMUAL(r%u,r%u,r%u,r%u)", RdLo,RdHi,Rm,Rs);
     // *mPC++ =    (cc<<28) | (1<<23) | (1<<21) | (s<<20) |
@@ -657,8 +656,8 @@
     }
 }
 
-void ArmToMipsAssembler::SMULL(int cc, int s,
-        int RdLo, int RdHi, int Rm, int Rs) {
+void ArmToMipsAssembler::SMULL(int cc __unused, int s,
+        int RdLo __unused, int RdHi, int Rm __unused, int Rs __unused) {
     LOG_FATAL_IF(RdLo==Rm || RdHi==Rm || RdLo==RdHi,
                         "SMULL(r%u,r%u,r%u,r%u)", RdLo,RdHi,Rm,Rs);
     // *mPC++ =    (cc<<28) | (1<<23) | (1<<22) | (s<<20) |
@@ -672,8 +671,8 @@
         LOG_ALWAYS_FATAL("Condition on SMULL must be on 64-bit result\n");
     }
 }
-void ArmToMipsAssembler::SMUAL(int cc, int s,
-        int RdLo, int RdHi, int Rm, int Rs) {
+void ArmToMipsAssembler::SMUAL(int cc __unused, int s,
+        int RdLo __unused, int RdHi, int Rm __unused, int Rs __unused) {
     LOG_FATAL_IF(RdLo==Rm || RdHi==Rm || RdLo==RdHi,
                         "SMUAL(r%u,r%u,r%u,r%u)", RdLo,RdHi,Rm,Rs);
     // *mPC++ =    (cc<<28) | (1<<23) | (1<<22) | (1<<21) | (s<<20) |
@@ -727,26 +726,26 @@
     }
 }
 
-void ArmToMipsAssembler::BL(int cc, const char* label)
+void ArmToMipsAssembler::BL(int cc __unused, const char* label __unused)
 {
     LOG_ALWAYS_FATAL("branch-and-link not supported yet\n");
     mArmPC[mInum++] = pc();
 }
 
 // no use for Branches with integer PC, but they're in the Interface class ....
-void ArmToMipsAssembler::B(int cc, uint32_t* to_pc)
+void ArmToMipsAssembler::B(int cc __unused, uint32_t* to_pc __unused)
 {
     LOG_ALWAYS_FATAL("branch to absolute PC not supported, use Label\n");
     mArmPC[mInum++] = pc();
 }
 
-void ArmToMipsAssembler::BL(int cc, uint32_t* to_pc)
+void ArmToMipsAssembler::BL(int cc __unused, uint32_t* to_pc __unused)
 {
     LOG_ALWAYS_FATAL("branch to absolute PC not supported, use Label\n");
     mArmPC[mInum++] = pc();
 }
 
-void ArmToMipsAssembler::BX(int cc, int Rn)
+void ArmToMipsAssembler::BX(int cc __unused, int Rn __unused)
 {
     LOG_ALWAYS_FATAL("branch to absolute PC not supported, use Label\n");
     mArmPC[mInum++] = pc();
@@ -760,7 +759,7 @@
 #endif
 
 // data transfer...
-void ArmToMipsAssembler::LDR(int cc, int Rd, int Rn, uint32_t offset)
+void ArmToMipsAssembler::LDR(int cc __unused, int Rd, int Rn, uint32_t offset)
 {
     mArmPC[mInum++] = pc();
     // work-around for ARM default address mode of immed12_pre(0)
@@ -794,7 +793,7 @@
     }
 }
 
-void ArmToMipsAssembler::LDRB(int cc, int Rd, int Rn, uint32_t offset)
+void ArmToMipsAssembler::LDRB(int cc __unused, int Rd, int Rn, uint32_t offset)
 {
     mArmPC[mInum++] = pc();
     // work-around for ARM default address mode of immed12_pre(0)
@@ -823,7 +822,7 @@
 
 }
 
-void ArmToMipsAssembler::STR(int cc, int Rd, int Rn, uint32_t offset)
+void ArmToMipsAssembler::STR(int cc __unused, int Rd, int Rn, uint32_t offset)
 {
     mArmPC[mInum++] = pc();
     // work-around for ARM default address mode of immed12_pre(0)
@@ -859,7 +858,7 @@
     }
 }
 
-void ArmToMipsAssembler::STRB(int cc, int Rd, int Rn, uint32_t offset)
+void ArmToMipsAssembler::STRB(int cc __unused, int Rd, int Rn, uint32_t offset)
 {
     mArmPC[mInum++] = pc();
     // work-around for ARM default address mode of immed12_pre(0)
@@ -887,7 +886,7 @@
     }
 }
 
-void ArmToMipsAssembler::LDRH(int cc, int Rd, int Rn, uint32_t offset)
+void ArmToMipsAssembler::LDRH(int cc __unused, int Rd, int Rn, uint32_t offset)
 {
     mArmPC[mInum++] = pc();
     // work-around for ARM default address mode of immed8_pre(0)
@@ -915,21 +914,23 @@
     }
 }
 
-void ArmToMipsAssembler::LDRSB(int cc, int Rd, int Rn, uint32_t offset)
+void ArmToMipsAssembler::LDRSB(int cc __unused, int Rd __unused,
+                               int Rn __unused, uint32_t offset __unused)
 {
     mArmPC[mInum++] = pc();
     mMips->NOP2();
     NOT_IMPLEMENTED();
 }
 
-void ArmToMipsAssembler::LDRSH(int cc, int Rd, int Rn, uint32_t offset)
+void ArmToMipsAssembler::LDRSH(int cc __unused, int Rd __unused,
+                               int Rn __unused, uint32_t offset __unused)
 {
     mArmPC[mInum++] = pc();
     mMips->NOP2();
     NOT_IMPLEMENTED();
 }
 
-void ArmToMipsAssembler::STRH(int cc, int Rd, int Rn, uint32_t offset)
+void ArmToMipsAssembler::STRH(int cc __unused, int Rd, int Rn, uint32_t offset)
 {
     mArmPC[mInum++] = pc();
     // work-around for ARM default address mode of immed8_pre(0)
@@ -965,8 +966,8 @@
 #endif
 
 // block data transfer...
-void ArmToMipsAssembler::LDM(int cc, int dir,
-        int Rn, int W, uint32_t reg_list)
+void ArmToMipsAssembler::LDM(int cc __unused, int dir __unused,
+        int Rn __unused, int W __unused, uint32_t reg_list __unused)
 {   //                        ED FD EA FA      IB IA DB DA
     // const uint8_t P[8] = { 1, 0, 1, 0,      1, 0, 1, 0 };
     // const uint8_t U[8] = { 1, 1, 0, 0,      1, 1, 0, 0 };
@@ -977,8 +978,8 @@
     NOT_IMPLEMENTED();
 }
 
-void ArmToMipsAssembler::STM(int cc, int dir,
-        int Rn, int W, uint32_t reg_list)
+void ArmToMipsAssembler::STM(int cc __unused, int dir __unused,
+        int Rn __unused, int W __unused, uint32_t reg_list __unused)
 {   //                        FA EA FD ED      IB IA DB DA
     // const uint8_t P[8] = { 0, 1, 0, 1,      1, 0, 1, 0 };
     // const uint8_t U[8] = { 0, 0, 1, 1,      1, 1, 0, 0 };
@@ -997,21 +998,23 @@
 #endif
 
 // special...
-void ArmToMipsAssembler::SWP(int cc, int Rn, int Rd, int Rm) {
+void ArmToMipsAssembler::SWP(int cc __unused, int Rn __unused,
+                             int Rd __unused, int Rm __unused) {
     // *mPC++ = (cc<<28) | (2<<23) | (Rn<<16) | (Rd << 12) | 0x90 | Rm;
     mArmPC[mInum++] = pc();
     mMips->NOP2();
     NOT_IMPLEMENTED();
 }
 
-void ArmToMipsAssembler::SWPB(int cc, int Rn, int Rd, int Rm) {
+void ArmToMipsAssembler::SWPB(int cc __unused, int Rn __unused,
+                              int Rd __unused, int Rm __unused) {
     // *mPC++ = (cc<<28) | (2<<23) | (1<<22) | (Rn<<16) | (Rd << 12) | 0x90 | Rm;
     mArmPC[mInum++] = pc();
     mMips->NOP2();
     NOT_IMPLEMENTED();
 }
 
-void ArmToMipsAssembler::SWI(int cc, uint32_t comment) {
+void ArmToMipsAssembler::SWI(int cc __unused, uint32_t comment __unused) {
     // *mPC++ = (cc<<28) | (0xF<<24) | comment;
     mArmPC[mInum++] = pc();
     mMips->NOP2();
@@ -1025,7 +1028,7 @@
 #endif
 
 // DSP instructions...
-void ArmToMipsAssembler::PLD(int Rn, uint32_t offset) {
+void ArmToMipsAssembler::PLD(int Rn __unused, uint32_t offset) {
     LOG_ALWAYS_FATAL_IF(!((offset&(1<<24)) && !(offset&(1<<21))),
                         "PLD only P=1, W=0");
     // *mPC++ = 0xF550F000 | (Rn<<16) | offset;
@@ -1034,13 +1037,14 @@
     NOT_IMPLEMENTED();
 }
 
-void ArmToMipsAssembler::CLZ(int cc, int Rd, int Rm)
+void ArmToMipsAssembler::CLZ(int cc __unused, int Rd, int Rm)
 {
     mArmPC[mInum++] = pc();
     mMips->CLZ(Rd, Rm);
 }
 
-void ArmToMipsAssembler::QADD(int cc,  int Rd, int Rm, int Rn)
+void ArmToMipsAssembler::QADD(int cc __unused,  int Rd __unused,
+                              int Rm __unused, int Rn __unused)
 {
     // *mPC++ = (cc<<28) | 0x1000050 | (Rn<<16) | (Rd<<12) | Rm;
     mArmPC[mInum++] = pc();
@@ -1048,7 +1052,8 @@
     NOT_IMPLEMENTED();
 }
 
-void ArmToMipsAssembler::QDADD(int cc,  int Rd, int Rm, int Rn)
+void ArmToMipsAssembler::QDADD(int cc __unused,  int Rd __unused,
+                               int Rm __unused, int Rn __unused)
 {
     // *mPC++ = (cc<<28) | 0x1400050 | (Rn<<16) | (Rd<<12) | Rm;
     mArmPC[mInum++] = pc();
@@ -1056,7 +1061,8 @@
     NOT_IMPLEMENTED();
 }
 
-void ArmToMipsAssembler::QSUB(int cc,  int Rd, int Rm, int Rn)
+void ArmToMipsAssembler::QSUB(int cc __unused,  int Rd __unused,
+                              int Rm __unused, int Rn __unused)
 {
     // *mPC++ = (cc<<28) | 0x1200050 | (Rn<<16) | (Rd<<12) | Rm;
     mArmPC[mInum++] = pc();
@@ -1064,7 +1070,8 @@
     NOT_IMPLEMENTED();
 }
 
-void ArmToMipsAssembler::QDSUB(int cc,  int Rd, int Rm, int Rn)
+void ArmToMipsAssembler::QDSUB(int cc __unused,  int Rd __unused,
+                               int Rm __unused, int Rn __unused)
 {
     // *mPC++ = (cc<<28) | 0x1600050 | (Rn<<16) | (Rd<<12) | Rm;
     mArmPC[mInum++] = pc();
@@ -1073,7 +1080,7 @@
 }
 
 // 16 x 16 signed multiply (like SMLAxx without the accumulate)
-void ArmToMipsAssembler::SMUL(int cc, int xy,
+void ArmToMipsAssembler::SMUL(int cc __unused, int xy,
                 int Rd, int Rm, int Rs)
 {
     mArmPC[mInum++] = pc();
@@ -1112,7 +1119,7 @@
 }
 
 // signed 32b x 16b multiple, save top 32-bits of 48-bit result
-void ArmToMipsAssembler::SMULW(int cc, int y,
+void ArmToMipsAssembler::SMULW(int cc __unused, int y,
                 int Rd, int Rm, int Rs)
 {
     mArmPC[mInum++] = pc();
@@ -1132,7 +1139,7 @@
 }
 
 // 16 x 16 signed multiply, accumulate: Rd = Rm{16} * Rs{16} + Rn
-void ArmToMipsAssembler::SMLA(int cc, int xy,
+void ArmToMipsAssembler::SMLA(int cc __unused, int xy,
                 int Rd, int Rm, int Rs, int Rn)
 {
     mArmPC[mInum++] = pc();
@@ -1172,8 +1179,9 @@
     mMips->ADDU(Rd, R_at, Rn);
 }
 
-void ArmToMipsAssembler::SMLAL(int cc, int xy,
-                int RdHi, int RdLo, int Rs, int Rm)
+void ArmToMipsAssembler::SMLAL(int cc __unused, int xy __unused,
+                               int RdHi __unused, int RdLo __unused,
+                               int Rs __unused, int Rm __unused)
 {
     // *mPC++ = (cc<<28) | 0x1400080 | (RdHi<<16) | (RdLo<<12) | (Rs<<8) | (xy<<4) | Rm;
     mArmPC[mInum++] = pc();
@@ -1181,8 +1189,9 @@
     NOT_IMPLEMENTED();
 }
 
-void ArmToMipsAssembler::SMLAW(int cc, int y,
-                int Rd, int Rm, int Rs, int Rn)
+void ArmToMipsAssembler::SMLAW(int cc __unused, int y __unused,
+                               int Rd __unused, int Rm __unused,
+                               int Rs __unused, int Rn __unused)
 {
     // *mPC++ = (cc<<28) | 0x1200080 | (Rd<<16) | (Rn<<12) | (Rs<<8) | (y<<4) | Rm;
     mArmPC[mInum++] = pc();
@@ -1191,7 +1200,7 @@
 }
 
 // used by ARMv6 version of GGLAssembler::filter32
-void ArmToMipsAssembler::UXTB16(int cc, int Rd, int Rm, int rotate)
+void ArmToMipsAssembler::UXTB16(int cc __unused, int Rd, int Rm, int rotate)
 {
     mArmPC[mInum++] = pc();
 
@@ -1202,7 +1211,9 @@
     mMips->AND(Rd, Rm, 0x00FF00FF);
 }
 
-void ArmToMipsAssembler::UBFX(int cc, int Rd, int Rn, int lsb, int width)
+void ArmToMipsAssembler::UBFX(int cc __unused, int Rd __unused,
+                              int Rn __unused, int lsb __unused,
+                              int width __unused)
 {
      /* Placeholder for UBFX */
      mArmPC[mInum++] = pc();
@@ -1339,11 +1350,6 @@
         }
     }
 
-    // iArm is an index to Arm instructions 1...n for this assembly sequence
-    // mArmPC[iArm] holds the value of the Mips-PC for the first MIPS
-    // instruction corresponding to that Arm instruction number
-
-    int iArm = 0;
     size_t count = pc()-base();
     uint32_t* mipsPC = base();
     while (count--) {
@@ -1359,7 +1365,7 @@
         ::mips_disassem(mipsPC, di_buf, arm_disasm_fmt);
         string_detab(di_buf);
         string_pad(di_buf, 30);
-        ALOGW("%08x:    %08x    %s", uintptr_t(mipsPC), uint32_t(*mipsPC), di_buf);
+        ALOGW("0x%p:    %08x    %s", mipsPC, uint32_t(*mipsPC), di_buf);
         mipsPC++;
     }
 }
@@ -1381,7 +1387,7 @@
     // empty - done in ArmToMipsAssembler
 }
 
-void MIPSAssembler::epilog(uint32_t touched)
+void MIPSAssembler::epilog(uint32_t touched __unused)
 {
     // empty - done in ArmToMipsAssembler
 }
@@ -1403,7 +1409,7 @@
 
     // the instruction & data caches are flushed by CodeCache
     const int64_t duration = ggl_system_time() - mDuration;
-    const char * const format = "generated %s (%d ins) at [%p:%p] in %lld ns\n";
+    const char * const format = "generated %s (%d ins) at [%p:%p] in %" PRId64 " ns\n";
     ALOGI(format, name, int(pc()-base()), base(), pc(), duration);
 
     char value[PROPERTY_VALUE_MAX];
@@ -1864,7 +1870,7 @@
     BEQ(Rs, R_zero, label);
 }
 
-void MIPSAssembler::BNEZ(int Rs, const char* label)
+void MIPSAssembler::BNEZ(int Rs __unused, const char* label)
 {
     BNE(R_at, R_zero, label);
 }
diff --git a/libpixelflinger/codeflinger/load_store.cpp b/libpixelflinger/codeflinger/load_store.cpp
index da21e1d..4db0a49 100644
--- a/libpixelflinger/codeflinger/load_store.cpp
+++ b/libpixelflinger/codeflinger/load_store.cpp
@@ -232,7 +232,6 @@
 void GGLAssembler::downshift(
         pixel_t& d, int component, component_t s, const reg_t& dither)
 {
-    const needs_t& needs = mBuilderContext.needs;
     Scratch scratches(registerFile());
 
     int sh = s.h;
diff --git a/libpixelflinger/codeflinger/mips64_disassem.c b/libpixelflinger/codeflinger/mips64_disassem.c
index 1856e5c..8528299 100644
--- a/libpixelflinger/codeflinger/mips64_disassem.c
+++ b/libpixelflinger/codeflinger/mips64_disassem.c
@@ -45,6 +45,8 @@
 
 #include "mips_opcode.h"
 
+#define __unused __attribute__((__unused__))
+
 static char *sprintf_buffer;
 static int sprintf_buf_len;
 
@@ -114,7 +116,7 @@
     "t8",   "t9",   "k0",   "k1",   "gp",   "sp",   "s8",   "ra"
 };
 
-static char ** reg_name =  &mips_reg_name[0];
+static char * const * reg_name =  &mips_reg_name[0];
 
 static const char * const c0_opname[64] = {
     "c0op00","tlbr",  "tlbwi", "c0op03","c0op04","c0op05","tlbwr", "c0op07",
@@ -147,7 +149,7 @@
  * 'loc' may in fact contain a breakpoint instruction.
  */
 static db_addr_t
-db_disasm_insn(int insn, db_addr_t loc, bool altfmt)
+db_disasm_insn(int insn, db_addr_t loc, bool altfmt __unused)
 {
     bool bdslot = false;
     InstFmt i;
diff --git a/libpixelflinger/codeflinger/mips_disassem.c b/libpixelflinger/codeflinger/mips_disassem.c
index 83a9740..1fe6806 100644
--- a/libpixelflinger/codeflinger/mips_disassem.c
+++ b/libpixelflinger/codeflinger/mips_disassem.c
@@ -57,6 +57,7 @@
 // #include <ddb/db_extern.h>
 // #include <ddb/db_sym.h>
 
+#define __unused __attribute__((__unused__))
 
 static char *sprintf_buffer;
 static int sprintf_buf_len;
@@ -183,7 +184,7 @@
  * 'loc' may in fact contain a breakpoint instruction.
  */
 static db_addr_t
-db_disasm_insn(int insn, db_addr_t loc, bool altfmt)
+db_disasm_insn(int insn, db_addr_t loc, bool altfmt __unused)
 {
     bool bdslot = false;
     InstFmt i;
diff --git a/libpixelflinger/codeflinger/texturing.cpp b/libpixelflinger/codeflinger/texturing.cpp
index 4c357af..e6997bd 100644
--- a/libpixelflinger/codeflinger/texturing.cpp
+++ b/libpixelflinger/codeflinger/texturing.cpp
@@ -41,7 +41,6 @@
 void GGLAssembler::init_iterated_color(fragment_parts_t& parts, const reg_t& x)
 {
     context_t const* c = mBuilderContext.c;
-    const needs_t& needs = mBuilderContext.needs;
 
     if (mSmooth) {
         // NOTE: we could take this case in the mDithering + !mSmooth case,
@@ -324,9 +323,7 @@
         tex_coord_t* coords,
         const reg_t& x, const reg_t& y)
 {
-    context_t const* c = mBuilderContext.c;
     const needs_t& needs = mBuilderContext.needs;
-    int Rctx = mBuilderContext.Rctx;
     int Rx = x.reg;
     int Ry = y.reg;
 
@@ -402,10 +399,6 @@
 void GGLAssembler::build_textures(  fragment_parts_t& parts,
                                     Scratch& regs)
 {
-    context_t const* c = mBuilderContext.c;
-    const needs_t& needs = mBuilderContext.needs;
-    int Rctx = mBuilderContext.Rctx;
-
     // We don't have a way to spill registers automatically
     // spill depth and AA regs, when we know we may have to.
     // build the spill list...
@@ -434,7 +427,6 @@
 
     Spill spill(registerFile(), *this, spill_list);
 
-    const bool multiTexture = mTextureMachine.activeUnits > 1;
     for (int i=0 ; i<GGL_TEXTURE_UNIT_COUNT; i++) {
         const texture_unit_t& tmu = mTextureMachine.tmu[i];
         if (tmu.format_idx == 0)
@@ -442,7 +434,7 @@
 
         pointer_t& txPtr = parts.coords[i].ptr;
         pixel_t& texel = parts.texel[i];
-            
+
         // repeat...
         if ((tmu.swrap == GGL_NEEDS_WRAP_11) &&
             (tmu.twrap == GGL_NEEDS_WRAP_11))
@@ -656,7 +648,6 @@
 void GGLAssembler::build_iterate_texture_coordinates(
     const fragment_parts_t& parts)
 {
-    const bool multiTexture = mTextureMachine.activeUnits > 1;
     for (int i=0 ; i<GGL_TEXTURE_UNIT_COUNT; i++) {
         const texture_unit_t& tmu = mTextureMachine.tmu[i];
         if (tmu.format_idx == 0)
diff --git a/libpixelflinger/include/private/pixelflinger/ggl_fixed.h b/libpixelflinger/include/private/pixelflinger/ggl_fixed.h
index 17b85dd..51e9e26 100644
--- a/libpixelflinger/include/private/pixelflinger/ggl_fixed.h
+++ b/libpixelflinger/include/private/pixelflinger/ggl_fixed.h
@@ -497,7 +497,6 @@
 {
 
     GGLfixed result;
-    int rshift;
 
     asm("smull  %x[result], %w[x], %w[y]                     \n"
         "lsr    %x[result], %x[result], %x[shift]            \n"
diff --git a/libpixelflinger/raster.cpp b/libpixelflinger/raster.cpp
index 26d8e45..e95c2c8 100644
--- a/libpixelflinger/raster.cpp
+++ b/libpixelflinger/raster.cpp
@@ -153,7 +153,6 @@
      GGLint h = where[3];
 
     // exclsively enable this tmu
-    const GGLSurface& cbSurface = c->state.buffers.color.s;
     c->procs.activeTexture(c, tmu);
     c->procs.disable(c, GGL_W_LERP);
 
diff --git a/libpixelflinger/scanline.cpp b/libpixelflinger/scanline.cpp
index c6cf5bf..4cc23c7 100644
--- a/libpixelflinger/scanline.cpp
+++ b/libpixelflinger/scanline.cpp
@@ -2144,7 +2144,6 @@
     const int32_t u = (c->state.texture[0].shade.is0>>16) + x;
     const int32_t v = (c->state.texture[0].shade.it0>>16) + y;
     uint32_t *src = reinterpret_cast<uint32_t*>(tex->data)+(u+(tex->stride*v));
-    int sR, sG, sB;
     uint32_t s, d;
 
     if (ct==1 || uintptr_t(dst)&2) {
diff --git a/libpixelflinger/tests/arch-arm64/assembler/Android.mk b/libpixelflinger/tests/arch-arm64/assembler/Android.mk
index bd0f24b..db5dc4d 100644
--- a/libpixelflinger/tests/arch-arm64/assembler/Android.mk
+++ b/libpixelflinger/tests/arch-arm64/assembler/Android.mk
@@ -14,6 +14,8 @@
 
 LOCAL_MODULE:= test-pixelflinger-arm64-assembler-test
 
+LOCAL_CFLAGS := -Wall -Werror
+
 LOCAL_MODULE_TAGS := tests
 
 LOCAL_MULTILIB := 64
diff --git a/libpixelflinger/tests/arch-arm64/col32cb16blend/Android.mk b/libpixelflinger/tests/arch-arm64/col32cb16blend/Android.mk
index 3368eb0..3096232 100644
--- a/libpixelflinger/tests/arch-arm64/col32cb16blend/Android.mk
+++ b/libpixelflinger/tests/arch-arm64/col32cb16blend/Android.mk
@@ -11,6 +11,8 @@
 
 LOCAL_MODULE:= test-pixelflinger-arm64-col32cb16blend
 
+LOCAL_CFLAGS := -Wall -Werror
+
 LOCAL_MODULE_TAGS := tests
 
 LOCAL_MULTILIB := 64
diff --git a/libpixelflinger/tests/arch-arm64/disassembler/Android.mk b/libpixelflinger/tests/arch-arm64/disassembler/Android.mk
index d8f7e69..78f12af 100644
--- a/libpixelflinger/tests/arch-arm64/disassembler/Android.mk
+++ b/libpixelflinger/tests/arch-arm64/disassembler/Android.mk
@@ -9,6 +9,8 @@
 
 LOCAL_MODULE:= test-pixelflinger-arm64-disassembler-test
 
+LOCAL_CFLAGS := -Wall -Werror
+
 LOCAL_MODULE_TAGS := tests
 
 LOCAL_MULTILIB := 64
diff --git a/libpixelflinger/tests/arch-arm64/t32cb16blend/Android.mk b/libpixelflinger/tests/arch-arm64/t32cb16blend/Android.mk
index 8e5ec5e..664347f 100644
--- a/libpixelflinger/tests/arch-arm64/t32cb16blend/Android.mk
+++ b/libpixelflinger/tests/arch-arm64/t32cb16blend/Android.mk
@@ -11,6 +11,8 @@
 
 LOCAL_MODULE:= test-pixelflinger-arm64-t32cb16blend
 
+LOCAL_CFLAGS := -Wall -Werror
+
 LOCAL_MODULE_TAGS := tests
 
 LOCAL_MULTILIB := 64
diff --git a/libpixelflinger/tests/codegen/Android.mk b/libpixelflinger/tests/codegen/Android.mk
index 2f9ca2f..72d71ef 100644
--- a/libpixelflinger/tests/codegen/Android.mk
+++ b/libpixelflinger/tests/codegen/Android.mk
@@ -13,6 +13,8 @@
 
 LOCAL_MODULE:= test-opengl-codegen
 
+LOCAL_CFLAGS:= -Wall -Werror
+
 LOCAL_MODULE_TAGS := tests
 
 include $(BUILD_NATIVE_TEST)
diff --git a/libpixelflinger/tests/codegen/codegen.cpp b/libpixelflinger/tests/codegen/codegen.cpp
index efa6d87..dce4ed7 100644
--- a/libpixelflinger/tests/codegen/codegen.cpp
+++ b/libpixelflinger/tests/codegen/codegen.cpp
@@ -40,9 +40,9 @@
     const AssemblyKey<needs_t>& key() const { return mKey; }
 };
 
+#if ANDROID_ARM_CODEGEN
 static void ggl_test_codegen(uint32_t n, uint32_t p, uint32_t t0, uint32_t t1)
 {
-#if ANDROID_ARM_CODEGEN
     GGLContext* c;
     gglInit(&c);
     needs_t needs;
@@ -73,10 +73,12 @@
         printf("error %08x (%s)\n", err, strerror(-err));
     }
     gglUninit(c);
-#else
-    printf("This test runs only on ARM, Arm64 or MIPS\n");
-#endif
 }
+#else
+static void ggl_test_codegen(uint32_t, uint32_t, uint32_t, uint32_t) {
+    printf("This test runs only on ARM, Arm64 or MIPS\n");
+}
+#endif
 
 int main(int argc, char** argv)
 {
diff --git a/libpixelflinger/tests/gglmul/Android.mk b/libpixelflinger/tests/gglmul/Android.mk
index 75bd39e..67f358f 100644
--- a/libpixelflinger/tests/gglmul/Android.mk
+++ b/libpixelflinger/tests/gglmul/Android.mk
@@ -11,6 +11,8 @@
 
 LOCAL_MODULE:= test-pixelflinger-gglmul
 
+LOCAL_CFLAGS:= -Wall -Werror
+
 LOCAL_MODULE_TAGS := tests
 
 include $(BUILD_NATIVE_TEST)
diff --git a/libpixelflinger/trap.cpp b/libpixelflinger/trap.cpp
index 234bfdd..06ad237 100644
--- a/libpixelflinger/trap.cpp
+++ b/libpixelflinger/trap.cpp
@@ -349,7 +349,6 @@
 
 static void linex(void *con, const GGLcoord* v0, const GGLcoord* v1, GGLcoord width)
 {
-    GGL_CONTEXT(c, con);
     GGLcoord v[4][2];
     v[0][0] = v0[0];    v[0][1] = v0[1];
     v[1][0] = v1[0];    v[1][1] = v1[1];
@@ -377,7 +376,6 @@
 
 static void aa_linex(void *con, const GGLcoord* v0, const GGLcoord* v1, GGLcoord width)
 {
-    GGL_CONTEXT(c, con);
     GGLcoord v[4][2];
     v[0][0] = v0[0];    v[0][1] = v0[1];
     v[1][0] = v1[0];    v[1][1] = v1[1];
diff --git a/libprocessgroup/Android.bp b/libprocessgroup/Android.bp
index 1974f2c..c38279d 100644
--- a/libprocessgroup/Android.bp
+++ b/libprocessgroup/Android.bp
@@ -1,7 +1,8 @@
 cc_library {
     srcs: ["processgroup.cpp"],
     name: "libprocessgroup",
-    defaults: ["linux_bionic_supported"],
+    host_supported: true,
+    recovery_available: true,
     shared_libs: ["libbase"],
     export_include_dirs: ["include"],
     cflags: [
diff --git a/libprocessgroup/include/processgroup/processgroup.h b/libprocessgroup/include/processgroup/processgroup.h
index f0c3795..9fa4154 100644
--- a/libprocessgroup/include/processgroup/processgroup.h
+++ b/libprocessgroup/include/processgroup/processgroup.h
@@ -33,6 +33,10 @@
 
 int createProcessGroup(uid_t uid, int initialPid);
 
+bool setProcessGroupSwappiness(uid_t uid, int initialPid, int swappiness);
+bool setProcessGroupSoftLimit(uid_t uid, int initialPid, int64_t softLimitInBytes);
+bool setProcessGroupLimit(uid_t uid, int initialPid, int64_t limitInBytes);
+
 void removeAllProcessGroups(void);
 
 __END_DECLS
diff --git a/libprocessgroup/processgroup.cpp b/libprocessgroup/processgroup.cpp
index f5d4e1c..1cebb5d 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,192 +34,89 @@
 #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/properties.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
 #include <private/android_filesystem_config.h>
 
 #include <processgroup/processgroup.h>
 
-using namespace std::chrono_literals;
+using android::base::GetBoolProperty;
+using android::base::StartsWith;
+using android::base::StringPrintf;
+using android::base::WriteStringToFile;
 
-// Uncomment line below use memory cgroups for keeping track of (forked) PIDs
-// #define USE_MEMCG 1
+using namespace std::chrono_literals;
 
 #define MEM_CGROUP_PATH "/dev/memcg/apps"
 #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() {
-#ifdef USE_MEMCG
-    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;
-            });
+        // low-ram devices use per-app memcg by default, unlike high-end ones
+        bool low_ram_device = GetBoolProperty("ro.config.low_ram", false);
+        bool per_app_memcg =
+            GetBoolProperty("ro.config.per_app_memcg", low_ram_device);
+        if (per_app_memcg) {
+            // 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);
+        } else {
+            cgroup_root_path = ACCT_CGROUP_PATH;
+        }
+    });
     return cgroup_root_path;
-#else
-    return ACCT_CGROUP_PATH;
-#endif
 }
 
-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;
         }
     }
 }
@@ -226,38 +124,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
@@ -266,10 +164,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
@@ -316,15 +213,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);
@@ -354,7 +251,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
@@ -366,22 +263,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;
     }
@@ -391,40 +287,54 @@
 
 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));
-
-    int fd = open(path, O_WRONLY);
-    if (fd == -1) {
-        int ret = -errno;
-        PLOG(ERROR) << "Failed to open " << path;
-        return ret;
-    }
-
-    char pid[PROCESSGROUP_MAX_PID_LEN + 1] = {0};
-    int len = snprintf(pid, sizeof(pid), "%d", initialPid);
+    auto uid_pid_procs_file = uid_pid_path + PROCESSGROUP_CGROUP_PROCS_FILE;
 
     int ret = 0;
-    if (write(fd, pid, len) < 0) {
+    if (!WriteStringToFile(std::to_string(initialPid), uid_pid_procs_file)) {
         ret = -errno;
-        PLOG(ERROR) << "Failed to write '" << pid << "' to " << path;
+        PLOG(ERROR) << "Failed to write '" << initialPid << "' to " << uid_pid_procs_file;
     }
 
-    close(fd);
     return ret;
 }
+
+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;
+    }
+
+    auto path = ConvertUidPidToPath(uid, pid) + file_name;
+
+    if (!WriteStringToFile(std::to_string(value), path)) {
+        PLOG(ERROR) << "Failed to write '" << value << "' to " << path;
+        return false;
+    }
+    return true;
+}
+
+bool setProcessGroupSwappiness(uid_t uid, int pid, int 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);
+}
+
+bool setProcessGroupLimit(uid_t uid, int pid, int64_t limit_in_bytes) {
+    return SetProcessGroupValue(uid, pid, "/memory.limit_in_bytes", limit_in_bytes);
+}
diff --git a/libprocinfo/Android.bp b/libprocinfo/Android.bp
index aedaa38..15f03d0 100644
--- a/libprocinfo/Android.bp
+++ b/libprocinfo/Android.bp
@@ -14,20 +14,27 @@
 // limitations under the License.
 //
 
-libprocinfo_cppflags = [
-    "-Wall",
-    "-Wextra",
-    "-Werror",
-]
+cc_defaults {
+    name: "libprocinfo_defaults",
+    cflags: [
+        "-Wall",
+        "-Werror",
+        "-Wextra",
+    ],
+}
 
 cc_library {
     name: "libprocinfo",
+    defaults: ["libprocinfo_defaults"],
     vendor_available: true,
+    recovery_available: true,
+    vndk: {
+        enabled: true,
+    },
     host_supported: true,
     srcs: [
         "process.cpp",
     ],
-    cppflags: libprocinfo_cppflags,
 
     local_include_dirs: ["include"],
     export_include_dirs: ["include"],
@@ -49,9 +56,11 @@
 // ------------------------------------------------------------------------------
 cc_test {
     name: "libprocinfo_test",
+    defaults: ["libprocinfo_defaults"],
     host_supported: true,
     srcs: [
         "process_test.cpp",
+        "process_map_test.cpp",
     ],
     target: {
         darwin: {
@@ -62,8 +71,10 @@
         },
     },
 
-    cppflags: libprocinfo_cppflags,
-    shared_libs: ["libbase", "libprocinfo"],
+    shared_libs: [
+        "libbase",
+        "libprocinfo",
+    ],
 
     compile_multilib: "both",
     multilib: {
@@ -74,4 +85,37 @@
             suffix: "64",
         },
     },
+
+    data: [
+        "testdata/*",
+    ],
+
+    test_suites: ["device-tests"],
+}
+
+cc_benchmark {
+    name: "libprocinfo_benchmark",
+    defaults: ["libprocinfo_defaults"],
+    srcs: [
+        "process_map_benchmark.cpp",
+    ],
+    shared_libs: [
+        "libbacktrace",
+        "libbase",
+        "libprocinfo",
+        "libunwindstack",
+    ],
+    compile_multilib: "both",
+    multilib: {
+        lib32: {
+            suffix: "32",
+        },
+        lib64: {
+            suffix: "64",
+        },
+    },
+
+    data: [
+        "testdata/*",
+    ],
 }
diff --git a/libprocinfo/OWNERS b/libprocinfo/OWNERS
new file mode 100644
index 0000000..a70cc57
--- /dev/null
+++ b/libprocinfo/OWNERS
@@ -0,0 +1 @@
+jmgao@google.com
diff --git a/libprocinfo/include/procinfo/process.h b/libprocinfo/include/procinfo/process.h
index db56fc1..9278e18 100644
--- a/libprocinfo/include/procinfo/process.h
+++ b/libprocinfo/include/procinfo/process.h
@@ -56,23 +56,25 @@
 };
 
 // Parse the contents of /proc/<tid>/status into |process_info|.
-bool GetProcessInfo(pid_t tid, ProcessInfo* process_info);
+bool GetProcessInfo(pid_t tid, ProcessInfo* process_info, std::string* error = nullptr);
 
 // Parse the contents of <fd>/status into |process_info|.
 // |fd| should be an fd pointing at a /proc/<pid> directory.
-bool GetProcessInfoFromProcPidFd(int fd, ProcessInfo* process_info);
+bool GetProcessInfoFromProcPidFd(int fd, ProcessInfo* process_info, std::string* error = nullptr);
 
 // Fetch the list of threads from a given process's /proc/<pid> directory.
 // |fd| should be an fd pointing at a /proc/<pid> directory.
 template <typename Collection>
-auto GetProcessTidsFromProcPidFd(int fd, Collection* out) ->
+auto GetProcessTidsFromProcPidFd(int fd, Collection* out, std::string* error = nullptr) ->
     typename std::enable_if<sizeof(typename Collection::value_type) >= sizeof(pid_t), bool>::type {
   out->clear();
 
   int task_fd = openat(fd, "task", O_DIRECTORY | O_RDONLY | O_CLOEXEC);
   std::unique_ptr<DIR, int (*)(DIR*)> dir(fdopendir(task_fd), closedir);
   if (!dir) {
-    PLOG(ERROR) << "failed to open task directory";
+    if (error != nullptr) {
+      *error = "failed to open task directory";
+    }
     return false;
   }
 
@@ -81,7 +83,9 @@
     if (strcmp(dent->d_name, ".") != 0 && strcmp(dent->d_name, "..") != 0) {
       pid_t tid;
       if (!android::base::ParseInt(dent->d_name, &tid, 1, std::numeric_limits<pid_t>::max())) {
-        LOG(ERROR) << "failed to parse task id: " << dent->d_name;
+        if (error != nullptr) {
+          *error = std::string("failed to parse task id: ") + dent->d_name;
+        }
         return false;
       }
 
@@ -93,21 +97,25 @@
 }
 
 template <typename Collection>
-auto GetProcessTids(pid_t pid, Collection* out) ->
+auto GetProcessTids(pid_t pid, Collection* out, std::string* error = nullptr) ->
     typename std::enable_if<sizeof(typename Collection::value_type) >= sizeof(pid_t), bool>::type {
   char task_path[PATH_MAX];
   if (snprintf(task_path, PATH_MAX, "/proc/%d", pid) >= PATH_MAX) {
-    LOG(ERROR) << "task path overflow (pid = " << pid << ")";
+    if (error != nullptr) {
+      *error = "task path overflow (pid = " + std::to_string(pid) + ")";
+    }
     return false;
   }
 
   android::base::unique_fd fd(open(task_path, O_DIRECTORY | O_RDONLY | O_CLOEXEC));
   if (fd == -1) {
-    PLOG(ERROR) << "failed to open " << task_path;
+    if (error != nullptr) {
+      *error = std::string("failed to open ") + task_path;
+    }
     return false;
   }
 
-  return GetProcessTidsFromProcPidFd(fd.get(), out);
+  return GetProcessTidsFromProcPidFd(fd.get(), out, error);
 }
 
 #endif
diff --git a/libprocinfo/include/procinfo/process_map.h b/libprocinfo/include/procinfo/process_map.h
new file mode 100644
index 0000000..3771f9f
--- /dev/null
+++ b/libprocinfo/include/procinfo/process_map.h
@@ -0,0 +1,151 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <stdlib.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+
+#include <functional>
+#include <string>
+
+#include <android-base/file.h>
+
+namespace android {
+namespace procinfo {
+
+template <class CallbackType>
+bool ReadMapFileContent(char* content, const CallbackType& callback) {
+  uint64_t start_addr;
+  uint64_t end_addr;
+  uint16_t flags;
+  uint64_t pgoff;
+  char* next_line = content;
+  char* p;
+
+  auto pass_space = [&]() {
+    if (*p != ' ') {
+      return false;
+    }
+    while (*p == ' ') {
+      p++;
+    }
+    return true;
+  };
+
+  auto pass_xdigit = [&]() {
+    if (!isxdigit(*p)) {
+      return false;
+    }
+    do {
+      p++;
+    } while (isxdigit(*p));
+    return true;
+  };
+
+  while (next_line != nullptr && *next_line != '\0') {
+    p = next_line;
+    next_line = strchr(next_line, '\n');
+    if (next_line != nullptr) {
+      *next_line = '\0';
+      next_line++;
+    }
+    // Parse line like: 00400000-00409000 r-xp 00000000 fc:00 426998  /usr/lib/gvfs/gvfsd-http
+    char* end;
+    // start_addr
+    start_addr = strtoull(p, &end, 16);
+    if (end == p || *end != '-') {
+      return false;
+    }
+    p = end + 1;
+    // end_addr
+    end_addr = strtoull(p, &end, 16);
+    if (end == p) {
+      return false;
+    }
+    p = end;
+    if (!pass_space()) {
+      return false;
+    }
+    // flags
+    flags = 0;
+    if (*p == 'r') {
+      flags |= PROT_READ;
+    } else if (*p != '-') {
+      return false;
+    }
+    p++;
+    if (*p == 'w') {
+      flags |= PROT_WRITE;
+    } else if (*p != '-') {
+      return false;
+    }
+    p++;
+    if (*p == 'x') {
+      flags |= PROT_EXEC;
+    } else if (*p != '-') {
+      return false;
+    }
+    p++;
+    if (*p != 'p' && *p != 's') {
+      return false;
+    }
+    p++;
+    if (!pass_space()) {
+      return false;
+    }
+    // pgoff
+    pgoff = strtoull(p, &end, 16);
+    if (end == p) {
+      return false;
+    }
+    p = end;
+    if (!pass_space()) {
+      return false;
+    }
+    // major:minor
+    if (!pass_xdigit() || *p++ != ':' || !pass_xdigit() || !pass_space()) {
+      return false;
+    }
+    // inode
+    if (!pass_xdigit() || (*p != '\0' && !pass_space())) {
+      return false;
+    }
+    // filename
+    callback(start_addr, end_addr, flags, pgoff, p);
+  }
+  return true;
+}
+
+inline bool ReadMapFile(
+    const std::string& map_file,
+    const std::function<void(uint64_t, uint64_t, uint16_t, uint64_t, const char*)>& callback) {
+  std::string content;
+  if (!android::base::ReadFileToString(map_file, &content)) {
+    return false;
+  }
+  return ReadMapFileContent(&content[0], callback);
+}
+
+inline bool ReadProcessMaps(
+    pid_t pid,
+    const std::function<void(uint64_t, uint64_t, uint16_t, uint64_t, const char*)>& callback) {
+  return ReadMapFile("/proc/" + std::to_string(pid) + "/maps", callback);
+}
+
+} /* namespace procinfo */
+} /* namespace android */
diff --git a/libprocinfo/process.cpp b/libprocinfo/process.cpp
index 6e5be6e..9194cf3 100644
--- a/libprocinfo/process.cpp
+++ b/libprocinfo/process.cpp
@@ -31,17 +31,19 @@
 namespace android {
 namespace procinfo {
 
-bool GetProcessInfo(pid_t tid, ProcessInfo* process_info) {
+bool GetProcessInfo(pid_t tid, ProcessInfo* process_info, std::string* error) {
   char path[PATH_MAX];
   snprintf(path, sizeof(path), "/proc/%d", tid);
 
   unique_fd dirfd(open(path, O_DIRECTORY | O_RDONLY));
   if (dirfd == -1) {
-    PLOG(ERROR) << "failed to open " << path;
+    if (error != nullptr) {
+      *error = std::string("failed to open ") + path;
+    }
     return false;
   }
 
-  return GetProcessInfoFromProcPidFd(dirfd.get(), process_info);
+  return GetProcessInfoFromProcPidFd(dirfd.get(), process_info, error);
 }
 
 static ProcessState parse_state(const char* state) {
@@ -62,17 +64,21 @@
   }
 }
 
-bool GetProcessInfoFromProcPidFd(int fd, ProcessInfo* process_info) {
+bool GetProcessInfoFromProcPidFd(int fd, ProcessInfo* process_info, std::string* error) {
   int status_fd = openat(fd, "status", O_RDONLY | O_CLOEXEC);
 
   if (status_fd == -1) {
-    PLOG(ERROR) << "failed to open status fd in GetProcessInfoFromProcPidFd";
+    if (error != nullptr) {
+      *error = "failed to open status fd in GetProcessInfoFromProcPidFd";
+    }
     return false;
   }
 
   std::unique_ptr<FILE, decltype(&fclose)> fp(fdopen(status_fd, "r"), fclose);
   if (!fp) {
-    PLOG(ERROR) << "failed to open status file in GetProcessInfoFromProcPidFd";
+    if (error != nullptr) {
+      *error = "failed to open status file in GetProcessInfoFromProcPidFd";
+    }
     close(status_fd);
     return false;
   }
diff --git a/libprocinfo/process_map_benchmark.cpp b/libprocinfo/process_map_benchmark.cpp
new file mode 100644
index 0000000..d9e8a4d
--- /dev/null
+++ b/libprocinfo/process_map_benchmark.cpp
@@ -0,0 +1,85 @@
+/*
+ * 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 <procinfo/process_map.h>
+
+#include <string.h>
+
+#include <string>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <backtrace/BacktraceMap.h>
+#include <unwindstack/Maps.h>
+
+#include <benchmark/benchmark.h>
+
+struct MapInfo {
+  uint64_t start;
+  uint64_t end;
+  uint16_t flags;
+  uint64_t pgoff;
+  const std::string name;
+
+  MapInfo(uint64_t start, uint64_t end, uint16_t flags, uint64_t pgoff, const char* name)
+      : start(start), end(end), flags(flags), pgoff(pgoff), name(name) {}
+};
+
+static void BM_ReadMapFile(benchmark::State& state) {
+  std::string map_file = android::base::GetExecutableDirectory() + "/testdata/maps";
+  for (auto _ : state) {
+    std::vector<MapInfo> maps;
+    android::procinfo::ReadMapFile(
+        map_file, [&](uint64_t start, uint64_t end, uint16_t flags, uint64_t pgoff,
+                      const char* name) { maps.emplace_back(start, end, flags, pgoff, name); });
+    CHECK_EQ(maps.size(), 2043u);
+  }
+}
+BENCHMARK(BM_ReadMapFile);
+
+static void BM_unwindstack_FileMaps(benchmark::State& state) {
+  std::string map_file = android::base::GetExecutableDirectory() + "/testdata/maps";
+  for (auto _ : state) {
+    unwindstack::FileMaps maps(map_file);
+    maps.Parse();
+    CHECK_EQ(maps.Total(), 2043u);
+  }
+}
+BENCHMARK(BM_unwindstack_FileMaps);
+
+static void BM_unwindstack_BufferMaps(benchmark::State& state) {
+  std::string map_file = android::base::GetExecutableDirectory() + "/testdata/maps";
+  std::string content;
+  CHECK(android::base::ReadFileToString(map_file, &content));
+  for (auto _ : state) {
+    unwindstack::BufferMaps maps(content.c_str());
+    maps.Parse();
+    CHECK_EQ(maps.Total(), 2043u);
+  }
+}
+BENCHMARK(BM_unwindstack_BufferMaps);
+
+static void BM_backtrace_BacktraceMap(benchmark::State& state) {
+  pid_t pid = getpid();
+  for (auto _ : state) {
+    BacktraceMap* map = BacktraceMap::Create(pid, true);
+    CHECK(map != nullptr);
+    delete map;
+  }
+}
+BENCHMARK(BM_backtrace_BacktraceMap);
+
+BENCHMARK_MAIN();
diff --git a/libprocinfo/process_map_test.cpp b/libprocinfo/process_map_test.cpp
new file mode 100644
index 0000000..900fd85
--- /dev/null
+++ b/libprocinfo/process_map_test.cpp
@@ -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.
+ */
+
+#include <procinfo/process_map.h>
+
+#include <string>
+
+#include <android-base/file.h>
+
+#include <gtest/gtest.h>
+
+struct MapInfo {
+  uint64_t start;
+  uint64_t end;
+  uint16_t flags;
+  uint64_t pgoff;
+  const std::string name;
+
+  MapInfo(uint64_t start, uint64_t end, uint16_t flags, uint64_t pgoff, const char* name)
+      : start(start), end(end), flags(flags), pgoff(pgoff), name(name) {}
+};
+
+TEST(process_map, smoke) {
+  std::string map_file = android::base::GetExecutableDirectory() + "/testdata/maps";
+  std::vector<MapInfo> maps;
+  ASSERT_TRUE(android::procinfo::ReadMapFile(
+      map_file, [&](uint64_t start, uint64_t end, uint16_t flags, uint64_t pgoff,
+                    const char* name) { maps.emplace_back(start, end, flags, pgoff, name); }));
+  ASSERT_EQ(2043u, maps.size());
+  ASSERT_EQ(maps[0].start, 0x12c00000ULL);
+  ASSERT_EQ(maps[0].end, 0x2ac00000ULL);
+  ASSERT_EQ(maps[0].flags, PROT_READ | PROT_WRITE);
+  ASSERT_EQ(maps[0].pgoff, 0ULL);
+  ASSERT_EQ(maps[0].name, "/dev/ashmem/dalvik-main space (region space) (deleted)");
+  ASSERT_EQ(maps[876].start, 0x70e6c4f000ULL);
+  ASSERT_EQ(maps[876].end, 0x70e6c6b000ULL);
+  ASSERT_EQ(maps[876].flags, PROT_READ | PROT_EXEC);
+  ASSERT_EQ(maps[876].pgoff, 0ULL);
+  ASSERT_EQ(maps[876].name, "/system/lib64/libutils.so");
+  ASSERT_EQ(maps[1260].start, 0x70e96fa000ULL);
+  ASSERT_EQ(maps[1260].end, 0x70e96fb000ULL);
+  ASSERT_EQ(maps[1260].flags, PROT_READ);
+  ASSERT_EQ(maps[1260].pgoff, 0ULL);
+  ASSERT_EQ(maps[1260].name,
+            "/dev/ashmem/dalvik-classes.dex extracted in memory from "
+            "/data/app/com.google.sample.tunnel-HGGRU03Gu1Mwkf_-RnFmvw==/base.apk (deleted)");
+}
diff --git a/libprocinfo/testdata/maps b/libprocinfo/testdata/maps
new file mode 100644
index 0000000..3b312e3
--- /dev/null
+++ b/libprocinfo/testdata/maps
@@ -0,0 +1,2043 @@
+12c00000-2ac00000 rw-p 00000000 00:05 10267643                           /dev/ashmem/dalvik-main space (region space) (deleted)
+6fb5d000-6fd6e000 rw-p 00000000 103:1d 639511                            /data/dalvik-cache/arm64/system@framework@boot.art
+6fd6e000-6fd82000 r--p 00211000 103:1d 639511                            /data/dalvik-cache/arm64/system@framework@boot.art
+6fd82000-6fe47000 rw-p 00000000 103:1d 639514                            /data/dalvik-cache/arm64/system@framework@boot-core-libart.art
+6fe47000-6fe52000 r--p 000c5000 103:1d 639514                            /data/dalvik-cache/arm64/system@framework@boot-core-libart.art
+6fe52000-6fe84000 rw-p 00000000 103:1d 639517                            /data/dalvik-cache/arm64/system@framework@boot-conscrypt.art
+6fe84000-6fe87000 r--p 00032000 103:1d 639517                            /data/dalvik-cache/arm64/system@framework@boot-conscrypt.art
+6fe87000-6feb2000 rw-p 00000000 103:1d 639520                            /data/dalvik-cache/arm64/system@framework@boot-okhttp.art
+6feb2000-6feb5000 r--p 0002b000 103:1d 639520                            /data/dalvik-cache/arm64/system@framework@boot-okhttp.art
+6feb5000-6fef4000 rw-p 00000000 103:1d 639523                            /data/dalvik-cache/arm64/system@framework@boot-bouncycastle.art
+6fef4000-6fefb000 r--p 0003f000 103:1d 639523                            /data/dalvik-cache/arm64/system@framework@boot-bouncycastle.art
+6fefb000-6ff3f000 rw-p 00000000 103:1d 639526                            /data/dalvik-cache/arm64/system@framework@boot-apache-xml.art
+6ff3f000-6ff45000 r--p 00044000 103:1d 639526                            /data/dalvik-cache/arm64/system@framework@boot-apache-xml.art
+6ff45000-6ff7a000 rw-p 00000000 103:1d 639529                            /data/dalvik-cache/arm64/system@framework@boot-ext.art
+6ff7a000-6ff85000 r--p 00035000 103:1d 639529                            /data/dalvik-cache/arm64/system@framework@boot-ext.art
+6ff85000-70594000 rw-p 00000000 103:1d 639532                            /data/dalvik-cache/arm64/system@framework@boot-framework.art
+70594000-705cb000 r--p 0060f000 103:1d 639532                            /data/dalvik-cache/arm64/system@framework@boot-framework.art
+705cb000-7061f000 rw-p 00000000 103:1d 639535                            /data/dalvik-cache/arm64/system@framework@boot-telephony-common.art
+7061f000-70629000 r--p 00054000 103:1d 639535                            /data/dalvik-cache/arm64/system@framework@boot-telephony-common.art
+70629000-70635000 rw-p 00000000 103:1d 639538                            /data/dalvik-cache/arm64/system@framework@boot-voip-common.art
+70635000-70636000 r--p 0000c000 103:1d 639538                            /data/dalvik-cache/arm64/system@framework@boot-voip-common.art
+70636000-70644000 rw-p 00000000 103:1d 639541                            /data/dalvik-cache/arm64/system@framework@boot-ims-common.art
+70644000-70645000 r--p 0000e000 103:1d 639541                            /data/dalvik-cache/arm64/system@framework@boot-ims-common.art
+70645000-70648000 rw-p 00000000 103:1d 639544                            /data/dalvik-cache/arm64/system@framework@boot-android.hidl.base-V1.0-java.art
+70648000-7064c000 rw-p 00000000 103:1d 639547                            /data/dalvik-cache/arm64/system@framework@boot-android.hidl.manager-V1.0-java.art
+7064c000-7064d000 r--p 00004000 103:1d 639547                            /data/dalvik-cache/arm64/system@framework@boot-android.hidl.manager-V1.0-java.art
+7064d000-7064e000 rw-p 00000000 103:1d 639550                            /data/dalvik-cache/arm64/system@framework@boot-framework-oahl-backward-compatibility.art
+7064e000-70652000 rw-p 00000000 103:1d 639553                            /data/dalvik-cache/arm64/system@framework@boot-android.test.base.art
+70652000-70653000 r--p 00004000 103:1d 639553                            /data/dalvik-cache/arm64/system@framework@boot-android.test.base.art
+70653000-70654000 rw-p 00000000 103:1d 639556                            /data/dalvik-cache/arm64/system@framework@boot-com.google.vr.platform.art
+70654000-70655000 r--p 00001000 103:1d 639556                            /data/dalvik-cache/arm64/system@framework@boot-com.google.vr.platform.art
+70655000-70731000 r--p 00000000 fc:00 940                                /system/framework/arm64/boot.oat
+70731000-709ca000 r-xp 000dc000 fc:00 940                                /system/framework/arm64/boot.oat
+709ca000-709cb000 rw-p 00000000 00:00 0                                  [anon:.bss]
+709cb000-70e4c000 r--s 00000000 fc:00 961                                /system/framework/boot.vdex
+70e4c000-70e4d000 r--p 00375000 fc:00 940                                /system/framework/arm64/boot.oat
+70e4d000-70e4e000 rw-p 00376000 fc:00 940                                /system/framework/arm64/boot.oat
+70e4e000-70eab000 r--p 00000000 fc:00 916                                /system/framework/arm64/boot-core-libart.oat
+70eab000-70fad000 r-xp 0005d000 fc:00 916                                /system/framework/arm64/boot-core-libart.oat
+70fad000-70fae000 rw-p 00000000 00:00 0                                  [anon:.bss]
+70fae000-712a9000 r--s 00000000 fc:00 702                                /system/framework/boot-core-libart.vdex
+712a9000-712aa000 r--p 0015f000 fc:00 916                                /system/framework/arm64/boot-core-libart.oat
+712aa000-712ab000 rw-p 00160000 fc:00 916                                /system/framework/arm64/boot-core-libart.oat
+712ab000-712bb000 r--p 00000000 fc:00 941                                /system/framework/arm64/boot-conscrypt.oat
+712bb000-712e4000 r-xp 00010000 fc:00 941                                /system/framework/arm64/boot-conscrypt.oat
+712e4000-712e5000 rw-p 00000000 00:00 0                                  [anon:.bss]
+712e5000-71346000 r--s 00000000 fc:00 970                                /system/framework/boot-conscrypt.vdex
+71346000-71347000 r--p 00039000 fc:00 941                                /system/framework/arm64/boot-conscrypt.oat
+71347000-71348000 rw-p 0003a000 fc:00 941                                /system/framework/arm64/boot-conscrypt.oat
+71348000-71361000 r--p 00000000 fc:00 908                                /system/framework/arm64/boot-okhttp.oat
+71361000-713a3000 r-xp 00019000 fc:00 908                                /system/framework/arm64/boot-okhttp.oat
+713a3000-713a4000 rw-p 00000000 00:00 0                                  [anon:.bss]
+713a4000-71403000 r--s 00000000 fc:00 886                                /system/framework/boot-okhttp.vdex
+71403000-71404000 r--p 0005b000 fc:00 908                                /system/framework/arm64/boot-okhttp.oat
+71404000-71405000 rw-p 0005c000 fc:00 908                                /system/framework/arm64/boot-okhttp.oat
+71405000-71415000 r--p 00000000 fc:00 936                                /system/framework/arm64/boot-bouncycastle.oat
+71415000-71437000 r-xp 00010000 fc:00 936                                /system/framework/arm64/boot-bouncycastle.oat
+71437000-71438000 rw-p 00000000 00:00 0                                  [anon:.bss]
+71438000-7157b000 r--s 00000000 fc:00 1006                               /system/framework/boot-bouncycastle.vdex
+7157b000-7157c000 r--p 00032000 fc:00 936                                /system/framework/arm64/boot-bouncycastle.oat
+7157c000-7157d000 rw-p 00033000 fc:00 936                                /system/framework/arm64/boot-bouncycastle.oat
+7157d000-71583000 r--p 00000000 fc:00 932                                /system/framework/arm64/boot-apache-xml.oat
+71583000-71584000 r-xp 00006000 fc:00 932                                /system/framework/arm64/boot-apache-xml.oat
+71584000-716a7000 r--s 00000000 fc:00 883                                /system/framework/boot-apache-xml.vdex
+716a7000-716a8000 r--p 00007000 fc:00 932                                /system/framework/arm64/boot-apache-xml.oat
+716a8000-716a9000 rw-p 00008000 fc:00 932                                /system/framework/arm64/boot-apache-xml.oat
+716a9000-716b5000 r--p 00000000 fc:00 891                                /system/framework/arm64/boot-ext.oat
+716b5000-716cc000 r-xp 0000c000 fc:00 891                                /system/framework/arm64/boot-ext.oat
+716cc000-716cd000 rw-p 00000000 00:00 0                                  [anon:.bss]
+716cd000-717b8000 r--s 00000000 fc:00 879                                /system/framework/boot-ext.vdex
+717b8000-717b9000 r--p 00023000 fc:00 891                                /system/framework/arm64/boot-ext.oat
+717b9000-717ba000 rw-p 00024000 fc:00 891                                /system/framework/arm64/boot-ext.oat
+717ba000-71aeb000 r--p 00000000 fc:00 943                                /system/framework/arm64/boot-framework.oat
+71aeb000-72390000 r-xp 00331000 fc:00 943                                /system/framework/arm64/boot-framework.oat
+72390000-72396000 rw-p 00000000 00:00 0                                  [anon:.bss]
+72396000-73746000 r--s 00000000 fc:00 985                                /system/framework/boot-framework.vdex
+73746000-73747000 r--p 00bd6000 fc:00 943                                /system/framework/arm64/boot-framework.oat
+73747000-73748000 rw-p 00bd7000 fc:00 943                                /system/framework/arm64/boot-framework.oat
+73748000-73780000 r--p 00000000 fc:00 893                                /system/framework/arm64/boot-telephony-common.oat
+73780000-73818000 r-xp 00038000 fc:00 893                                /system/framework/arm64/boot-telephony-common.oat
+73818000-7381a000 rw-p 00000000 00:00 0                                  [anon:.bss]
+7381a000-73af0000 r--s 00000000 fc:00 697                                /system/framework/boot-telephony-common.vdex
+73af0000-73af1000 r--p 000d0000 fc:00 893                                /system/framework/arm64/boot-telephony-common.oat
+73af1000-73af2000 rw-p 000d1000 fc:00 893                                /system/framework/arm64/boot-telephony-common.oat
+73af2000-73af6000 r--p 00000000 fc:00 922                                /system/framework/arm64/boot-voip-common.oat
+73af6000-73af8000 r-xp 00004000 fc:00 922                                /system/framework/arm64/boot-voip-common.oat
+73af8000-73af9000 rw-p 00000000 00:00 0                                  [anon:.bss]
+73af9000-73b1e000 r--s 00000000 fc:00 959                                /system/framework/boot-voip-common.vdex
+73b1e000-73b1f000 r--p 00006000 fc:00 922                                /system/framework/arm64/boot-voip-common.oat
+73b1f000-73b20000 rw-p 00007000 fc:00 922                                /system/framework/arm64/boot-voip-common.oat
+73b20000-73b23000 r--p 00000000 fc:00 918                                /system/framework/arm64/boot-ims-common.oat
+73b23000-73b25000 r-xp 00003000 fc:00 918                                /system/framework/arm64/boot-ims-common.oat
+73b25000-73b26000 rw-p 00000000 00:00 0                                  [anon:.bss]
+73b26000-73b48000 r--s 00000000 fc:00 957                                /system/framework/boot-ims-common.vdex
+73b48000-73b49000 r--p 00005000 fc:00 918                                /system/framework/arm64/boot-ims-common.oat
+73b49000-73b4a000 rw-p 00006000 fc:00 918                                /system/framework/arm64/boot-ims-common.oat
+73b4a000-73b4d000 r--p 00000000 fc:00 909                                /system/framework/arm64/boot-android.hidl.base-V1.0-java.oat
+73b4d000-73b4e000 r-xp 00003000 fc:00 909                                /system/framework/arm64/boot-android.hidl.base-V1.0-java.oat
+73b4e000-73b55000 r--s 00000000 fc:00 972                                /system/framework/boot-android.hidl.base-V1.0-java.vdex
+73b55000-73b56000 r--p 00004000 fc:00 909                                /system/framework/arm64/boot-android.hidl.base-V1.0-java.oat
+73b56000-73b57000 rw-p 00005000 fc:00 909                                /system/framework/arm64/boot-android.hidl.base-V1.0-java.oat
+73b57000-73b5a000 r--p 00000000 fc:00 954                                /system/framework/arm64/boot-android.hidl.manager-V1.0-java.oat
+73b5a000-73b5c000 r-xp 00003000 fc:00 954                                /system/framework/arm64/boot-android.hidl.manager-V1.0-java.oat
+73b5c000-73b5d000 rw-p 00000000 00:00 0                                  [anon:.bss]
+73b5d000-73b68000 r--s 00000000 fc:00 704                                /system/framework/boot-android.hidl.manager-V1.0-java.vdex
+73b68000-73b69000 r--p 00005000 fc:00 954                                /system/framework/arm64/boot-android.hidl.manager-V1.0-java.oat
+73b69000-73b6a000 rw-p 00006000 fc:00 954                                /system/framework/arm64/boot-android.hidl.manager-V1.0-java.oat
+73b6a000-73b6d000 r--p 00000000 fc:00 904                                /system/framework/arm64/boot-framework-oahl-backward-compatibility.oat
+73b6d000-73b6e000 r-xp 00003000 fc:00 904                                /system/framework/arm64/boot-framework-oahl-backward-compatibility.oat
+73b6e000-73b6f000 r--s 00000000 fc:00 994                                /system/framework/boot-framework-oahl-backward-compatibility.vdex
+73b6f000-73b70000 r--p 00004000 fc:00 904                                /system/framework/arm64/boot-framework-oahl-backward-compatibility.oat
+73b70000-73b71000 rw-p 00005000 fc:00 904                                /system/framework/arm64/boot-framework-oahl-backward-compatibility.oat
+73b71000-73b75000 r--p 00000000 fc:00 896                                /system/framework/arm64/boot-android.test.base.oat
+73b75000-73b79000 r-xp 00004000 fc:00 896                                /system/framework/arm64/boot-android.test.base.oat
+73b79000-73b7a000 rw-p 00000000 00:00 0                                  [anon:.bss]
+73b7a000-73b82000 r--s 00000000 fc:00 706                                /system/framework/boot-android.test.base.vdex
+73b82000-73b83000 r--p 00008000 fc:00 896                                /system/framework/arm64/boot-android.test.base.oat
+73b83000-73b84000 rw-p 00009000 fc:00 896                                /system/framework/arm64/boot-android.test.base.oat
+73b84000-73b87000 r--p 00000000 fc:00 899                                /system/framework/arm64/boot-com.google.vr.platform.oat
+73b87000-73b88000 r-xp 00003000 fc:00 899                                /system/framework/arm64/boot-com.google.vr.platform.oat
+73b88000-73b89000 r--s 00000000 fc:00 884                                /system/framework/boot-com.google.vr.platform.vdex
+73b89000-73b8a000 r--p 00004000 fc:00 899                                /system/framework/arm64/boot-com.google.vr.platform.oat
+73b8a000-73b8b000 rw-p 00005000 fc:00 899                                /system/framework/arm64/boot-com.google.vr.platform.oat
+73b8b000-73b93000 rw-p 00000000 00:05 10267640                           /dev/ashmem/dalvik-non moving space (deleted)
+73b93000-77b8b000 ---p 00008000 00:05 10267640                           /dev/ashmem/dalvik-non moving space (deleted)
+77b8b000-97b8b000 rw-p 00000000 00:05 10267645                           /dev/ashmem/dalvik-free list large object space (deleted)
+97b8b000-99b8b000 rw-p 00000000 00:05 10270989                           /dev/ashmem/dalvik-data-code-cache (deleted)
+99b8b000-9bb8b000 r-xp 00000000 00:05 10270990                           /dev/ashmem/dalvik-jit-code-cache (deleted)
+ebad6000-ebad7000 ---p 00000000 00:05 10269717                           /dev/ashmem/dalvik-Sentinel fault page (deleted)
+7ffb6e000-7ffb76000 rw-s 000e5000 00:10 20630                            /dev/kgsl-3d0
+7ffb76000-7ffb78000 rw-s 000e0000 00:10 20630                            /dev/kgsl-3d0
+7ffbc3000-7ffbc4000 rw-s 000e8000 00:10 20630                            /dev/kgsl-3d0
+7ffbc4000-7ffbc5000 rw-s 000e7000 00:10 20630                            /dev/kgsl-3d0
+7ffbc6000-7ffbce000 rw-s 000e4000 00:10 20630                            /dev/kgsl-3d0
+7ffbd0000-7ffbd2000 rw-s 000df000 00:10 20630                            /dev/kgsl-3d0
+7ffbd2000-7ffbd4000 rw-s 000de000 00:10 20630                            /dev/kgsl-3d0
+7ffbd4000-7ffbd6000 rw-s 000dd000 00:10 20630                            /dev/kgsl-3d0
+7ffbd6000-7ffbd8000 rw-s 000dc000 00:10 20630                            /dev/kgsl-3d0
+7ffbd8000-7ffbda000 rw-s 000db000 00:10 20630                            /dev/kgsl-3d0
+7ffbda000-7ffbdc000 rw-s 000da000 00:10 20630                            /dev/kgsl-3d0
+7ffbdd000-7ffbde000 rw-s 000ec000 00:10 20630                            /dev/kgsl-3d0
+7ffbde000-7ffbe0000 rw-s 000d8000 00:10 20630                            /dev/kgsl-3d0
+7ffce1000-7ffce2000 rw-s 000e6000 00:10 20630                            /dev/kgsl-3d0
+7ffce2000-7ffce4000 rw-s 000d9000 00:10 20630                            /dev/kgsl-3d0
+7ffce4000-7ffce8000 rw-s 000d4000 00:10 20630                            /dev/kgsl-3d0
+7ffce8000-7ffcf8000 rw-s 000d2000 00:10 20630                            /dev/kgsl-3d0
+7ffcf8000-7ffd08000 rw-s 000d1000 00:10 20630                            /dev/kgsl-3d0
+7ffd08000-7ffd10000 rw-s 000d0000 00:10 20630                            /dev/kgsl-3d0
+7ffd14000-7ffd18000 rw-s 000cd000 00:10 20630                            /dev/kgsl-3d0
+7ffd18000-7ffd28000 rw-s 000cc000 00:10 20630                            /dev/kgsl-3d0
+7ffd28000-7ffd38000 rw-s 000cb000 00:10 20630                            /dev/kgsl-3d0
+7ffd38000-7ffd48000 rw-s 000ca000 00:10 20630                            /dev/kgsl-3d0
+7ffd48000-7ffd58000 rw-s 000c9000 00:10 20630                            /dev/kgsl-3d0
+7ffd58000-7ffd68000 rw-s 000c8000 00:10 20630                            /dev/kgsl-3d0
+7ffd68000-7ffd6c000 rw-s 000c7000 00:10 20630                            /dev/kgsl-3d0
+7ffdb1000-7ffdb2000 rw-s 000e3000 00:10 20630                            /dev/kgsl-3d0
+7ffdb4000-7ffdb5000 rw-s 000e2000 00:10 20630                            /dev/kgsl-3d0
+7ffdb5000-7ffdb7000 rw-s 000d7000 00:10 20630                            /dev/kgsl-3d0
+7ffdb7000-7ffdb8000 rw-s 000c2000 00:10 20630                            /dev/kgsl-3d0
+7ffdb8000-7ffdbc000 rw-s 000c0000 00:10 20630                            /dev/kgsl-3d0
+7ffdbc000-7ffdc0000 rw-s 000be000 00:10 20630                            /dev/kgsl-3d0
+7ffdc0000-7ffe00000 rw-s 000bb000 00:10 20630                            /dev/kgsl-3d0
+7ffe00000-7ffe20000 rw-s 000ba000 00:10 20630                            /dev/kgsl-3d0
+7ffe20000-7ffee0000 rw-s 000b9000 00:10 20630                            /dev/kgsl-3d0
+7ffee1000-7ffee3000 rw-s 000c1000 00:10 20630                            /dev/kgsl-3d0
+7ffee3000-7ffee4000 rw-s 000bf000 00:10 20630                            /dev/kgsl-3d0
+7ffee4000-7ffee8000 rw-s 000bd000 00:10 20630                            /dev/kgsl-3d0
+7ffee8000-7ffee9000 rw-s 000bc000 00:10 20630                            /dev/kgsl-3d0
+7ffeea000-7ffeeb000 rw-s 000e1000 00:10 20630                            /dev/kgsl-3d0
+7ffeeb000-7ffeec000 rw-s 000b6000 00:10 20630                            /dev/kgsl-3d0
+7ffeec000-7ffeed000 rw-s 000b5000 00:10 20630                            /dev/kgsl-3d0
+7ffeed000-7ffeee000 rw-s 000b4000 00:10 20630                            /dev/kgsl-3d0
+7ffeee000-7ffeef000 rw-s 000b3000 00:10 20630                            /dev/kgsl-3d0
+7ffeef000-7ffef0000 rw-s 000b2000 00:10 20630                            /dev/kgsl-3d0
+7ffef0000-7ffef1000 rw-s 000b1000 00:10 20630                            /dev/kgsl-3d0
+7ffef1000-7ffef2000 rw-s 000b0000 00:10 20630                            /dev/kgsl-3d0
+7ffef2000-7ffef3000 rw-s 000af000 00:10 20630                            /dev/kgsl-3d0
+7ffef3000-7ffef4000 rw-s 000ae000 00:10 20630                            /dev/kgsl-3d0
+7ffef4000-7ffef5000 rw-s 000ad000 00:10 20630                            /dev/kgsl-3d0
+7ffef5000-7ffef6000 rw-s 000ac000 00:10 20630                            /dev/kgsl-3d0
+7ffef6000-7ffef7000 rw-s 000ab000 00:10 20630                            /dev/kgsl-3d0
+7ffef7000-7ffef8000 rw-s 000aa000 00:10 20630                            /dev/kgsl-3d0
+7ffef8000-7ffef9000 rw-s 000a9000 00:10 20630                            /dev/kgsl-3d0
+7ffef9000-7ffefa000 rw-s 000a8000 00:10 20630                            /dev/kgsl-3d0
+7ffefa000-7ffefb000 rw-s 000a7000 00:10 20630                            /dev/kgsl-3d0
+7ffefb000-7ffefc000 rw-s 000a6000 00:10 20630                            /dev/kgsl-3d0
+7ffefc000-7ffefd000 rw-s 000a5000 00:10 20630                            /dev/kgsl-3d0
+7ffefd000-7ffefe000 rw-s 000a4000 00:10 20630                            /dev/kgsl-3d0
+7ffefe000-7ffeff000 rw-s 000a3000 00:10 20630                            /dev/kgsl-3d0
+7ffeff000-7fff00000 rw-s 000a2000 00:10 20630                            /dev/kgsl-3d0
+7fff00000-7fff01000 rw-s 000a1000 00:10 20630                            /dev/kgsl-3d0
+7fff01000-7fff02000 rw-s 000a0000 00:10 20630                            /dev/kgsl-3d0
+7fff02000-7fff03000 rw-s 0009f000 00:10 20630                            /dev/kgsl-3d0
+7fff03000-7fff04000 rw-s 0009e000 00:10 20630                            /dev/kgsl-3d0
+7fff04000-7fff05000 rw-s 0009d000 00:10 20630                            /dev/kgsl-3d0
+7fff05000-7fff06000 rw-s 0009c000 00:10 20630                            /dev/kgsl-3d0
+7fff06000-7fff07000 rw-s 0009b000 00:10 20630                            /dev/kgsl-3d0
+7fff07000-7fff08000 rw-s 0009a000 00:10 20630                            /dev/kgsl-3d0
+7fff08000-7fff09000 rw-s 00099000 00:10 20630                            /dev/kgsl-3d0
+7fff09000-7fff0a000 rw-s 00098000 00:10 20630                            /dev/kgsl-3d0
+7fff0a000-7fff0b000 rw-s 00097000 00:10 20630                            /dev/kgsl-3d0
+7fff0b000-7fff0c000 rw-s 00096000 00:10 20630                            /dev/kgsl-3d0
+7fff0c000-7fff0d000 rw-s 00095000 00:10 20630                            /dev/kgsl-3d0
+7fff0d000-7fff0e000 rw-s 00094000 00:10 20630                            /dev/kgsl-3d0
+7fff0e000-7fff0f000 rw-s 00093000 00:10 20630                            /dev/kgsl-3d0
+7fff0f000-7fff10000 rw-s 00092000 00:10 20630                            /dev/kgsl-3d0
+7fff10000-7fff11000 rw-s 00091000 00:10 20630                            /dev/kgsl-3d0
+7fff11000-7fff12000 rw-s 00090000 00:10 20630                            /dev/kgsl-3d0
+7fff12000-7fff13000 rw-s 0008f000 00:10 20630                            /dev/kgsl-3d0
+7fff13000-7fff14000 rw-s 0008e000 00:10 20630                            /dev/kgsl-3d0
+7fff14000-7fff15000 rw-s 0008d000 00:10 20630                            /dev/kgsl-3d0
+7fff15000-7fff16000 rw-s 0008c000 00:10 20630                            /dev/kgsl-3d0
+7fff16000-7fff17000 rw-s 0008b000 00:10 20630                            /dev/kgsl-3d0
+7fff17000-7fff18000 rw-s 0008a000 00:10 20630                            /dev/kgsl-3d0
+7fff18000-7fff19000 rw-s 00089000 00:10 20630                            /dev/kgsl-3d0
+7fff19000-7fff1a000 rw-s 00088000 00:10 20630                            /dev/kgsl-3d0
+7fff1a000-7fff1b000 rw-s 00087000 00:10 20630                            /dev/kgsl-3d0
+7fff1b000-7fff1c000 rw-s 00086000 00:10 20630                            /dev/kgsl-3d0
+7fff1c000-7fff1d000 rw-s 00085000 00:10 20630                            /dev/kgsl-3d0
+7fff1d000-7fff1e000 rw-s 00084000 00:10 20630                            /dev/kgsl-3d0
+7fff1e000-7fff1f000 rw-s 00083000 00:10 20630                            /dev/kgsl-3d0
+7fff1f000-7fff20000 rw-s 00082000 00:10 20630                            /dev/kgsl-3d0
+7fff20000-7fff21000 rw-s 00081000 00:10 20630                            /dev/kgsl-3d0
+7fff21000-7fff22000 rw-s 00080000 00:10 20630                            /dev/kgsl-3d0
+7fff22000-7fff23000 rw-s 0007f000 00:10 20630                            /dev/kgsl-3d0
+7fff23000-7fff24000 rw-s 0007e000 00:10 20630                            /dev/kgsl-3d0
+7fff24000-7fff25000 rw-s 0007d000 00:10 20630                            /dev/kgsl-3d0
+7fff25000-7fff26000 rw-s 0007c000 00:10 20630                            /dev/kgsl-3d0
+7fff26000-7fff27000 rw-s 0007b000 00:10 20630                            /dev/kgsl-3d0
+7fff27000-7fff28000 rw-s 0007a000 00:10 20630                            /dev/kgsl-3d0
+7fff28000-7fff29000 rw-s 00079000 00:10 20630                            /dev/kgsl-3d0
+7fff29000-7fff2a000 rw-s 00078000 00:10 20630                            /dev/kgsl-3d0
+7fff2a000-7fff2b000 rw-s 00077000 00:10 20630                            /dev/kgsl-3d0
+7fff2b000-7fff2c000 rw-s 00076000 00:10 20630                            /dev/kgsl-3d0
+7fff2c000-7fff2d000 rw-s 00075000 00:10 20630                            /dev/kgsl-3d0
+7fff2d000-7fff2e000 rw-s 00074000 00:10 20630                            /dev/kgsl-3d0
+7fff2e000-7fff2f000 rw-s 00073000 00:10 20630                            /dev/kgsl-3d0
+7fff2f000-7fff30000 rw-s 00072000 00:10 20630                            /dev/kgsl-3d0
+7fff30000-7fff31000 rw-s 00071000 00:10 20630                            /dev/kgsl-3d0
+7fff31000-7fff32000 rw-s 00070000 00:10 20630                            /dev/kgsl-3d0
+7fff32000-7fff33000 rw-s 0006f000 00:10 20630                            /dev/kgsl-3d0
+7fff33000-7fff34000 rw-s 0006e000 00:10 20630                            /dev/kgsl-3d0
+7fff34000-7fff35000 rw-s 0006d000 00:10 20630                            /dev/kgsl-3d0
+7fff35000-7fff36000 rw-s 0006c000 00:10 20630                            /dev/kgsl-3d0
+7fff36000-7fff37000 rw-s 0006b000 00:10 20630                            /dev/kgsl-3d0
+7fff37000-7fff38000 rw-s 0006a000 00:10 20630                            /dev/kgsl-3d0
+7fff38000-7fff39000 rw-s 00069000 00:10 20630                            /dev/kgsl-3d0
+7fff39000-7fff3a000 rw-s 00068000 00:10 20630                            /dev/kgsl-3d0
+7fff3a000-7fff3b000 rw-s 00067000 00:10 20630                            /dev/kgsl-3d0
+7fff3b000-7fff3c000 rw-s 00066000 00:10 20630                            /dev/kgsl-3d0
+7fff3c000-7fff3d000 rw-s 00065000 00:10 20630                            /dev/kgsl-3d0
+7fff3d000-7fff3e000 rw-s 00064000 00:10 20630                            /dev/kgsl-3d0
+7fff3e000-7fff3f000 rw-s 00063000 00:10 20630                            /dev/kgsl-3d0
+7fff3f000-7fff40000 rw-s 00062000 00:10 20630                            /dev/kgsl-3d0
+7fff40000-7fff41000 rw-s 00061000 00:10 20630                            /dev/kgsl-3d0
+7fff41000-7fff42000 rw-s 00060000 00:10 20630                            /dev/kgsl-3d0
+7fff42000-7fff43000 rw-s 0005f000 00:10 20630                            /dev/kgsl-3d0
+7fff43000-7fff44000 rw-s 0005e000 00:10 20630                            /dev/kgsl-3d0
+7fff44000-7fff45000 rw-s 0005d000 00:10 20630                            /dev/kgsl-3d0
+7fff45000-7fff46000 rw-s 0005c000 00:10 20630                            /dev/kgsl-3d0
+7fff46000-7fff47000 rw-s 0005b000 00:10 20630                            /dev/kgsl-3d0
+7fff47000-7fff48000 rw-s 0005a000 00:10 20630                            /dev/kgsl-3d0
+7fff48000-7fff49000 rw-s 00059000 00:10 20630                            /dev/kgsl-3d0
+7fff49000-7fff4a000 rw-s 00058000 00:10 20630                            /dev/kgsl-3d0
+7fff4a000-7fff4b000 rw-s 00057000 00:10 20630                            /dev/kgsl-3d0
+7fff4b000-7fff4c000 rw-s 00056000 00:10 20630                            /dev/kgsl-3d0
+7fff4c000-7fff4d000 rw-s 00055000 00:10 20630                            /dev/kgsl-3d0
+7fff4d000-7fff4e000 rw-s 00054000 00:10 20630                            /dev/kgsl-3d0
+7fff4e000-7fff4f000 rw-s 00053000 00:10 20630                            /dev/kgsl-3d0
+7fff4f000-7fff50000 rw-s 00052000 00:10 20630                            /dev/kgsl-3d0
+7fff50000-7fff51000 rw-s 00051000 00:10 20630                            /dev/kgsl-3d0
+7fff51000-7fff52000 rw-s 00050000 00:10 20630                            /dev/kgsl-3d0
+7fff52000-7fff53000 rw-s 0004f000 00:10 20630                            /dev/kgsl-3d0
+7fff53000-7fff54000 rw-s 0004e000 00:10 20630                            /dev/kgsl-3d0
+7fff54000-7fff55000 rw-s 0004d000 00:10 20630                            /dev/kgsl-3d0
+7fff55000-7fff56000 rw-s 0004c000 00:10 20630                            /dev/kgsl-3d0
+7fff56000-7fff57000 rw-s 0004b000 00:10 20630                            /dev/kgsl-3d0
+7fff57000-7fff58000 rw-s 0004a000 00:10 20630                            /dev/kgsl-3d0
+7fff58000-7fff59000 rw-s 00049000 00:10 20630                            /dev/kgsl-3d0
+7fff59000-7fff5a000 rw-s 00048000 00:10 20630                            /dev/kgsl-3d0
+7fff5a000-7fff5b000 rw-s 00047000 00:10 20630                            /dev/kgsl-3d0
+7fff5b000-7fff5c000 rw-s 00046000 00:10 20630                            /dev/kgsl-3d0
+7fff5c000-7fff5d000 rw-s 00045000 00:10 20630                            /dev/kgsl-3d0
+7fff5d000-7fff5e000 rw-s 00044000 00:10 20630                            /dev/kgsl-3d0
+7fff5e000-7fff5f000 rw-s 00043000 00:10 20630                            /dev/kgsl-3d0
+7fff5f000-7fff60000 rw-s 00042000 00:10 20630                            /dev/kgsl-3d0
+7fff60000-7fff61000 rw-s 00041000 00:10 20630                            /dev/kgsl-3d0
+7fff61000-7fff62000 rw-s 00040000 00:10 20630                            /dev/kgsl-3d0
+7fff62000-7fff63000 rw-s 0003f000 00:10 20630                            /dev/kgsl-3d0
+7fff63000-7fff64000 rw-s 0003e000 00:10 20630                            /dev/kgsl-3d0
+7fff64000-7fff65000 rw-s 0003d000 00:10 20630                            /dev/kgsl-3d0
+7fff65000-7fff66000 rw-s 0003c000 00:10 20630                            /dev/kgsl-3d0
+7fff66000-7fff67000 rw-s 0003b000 00:10 20630                            /dev/kgsl-3d0
+7fff67000-7fff68000 rw-s 0003a000 00:10 20630                            /dev/kgsl-3d0
+7fff68000-7fff69000 rw-s 00039000 00:10 20630                            /dev/kgsl-3d0
+7fff69000-7fff6a000 rw-s 00038000 00:10 20630                            /dev/kgsl-3d0
+7fff6a000-7fff6b000 rw-s 00037000 00:10 20630                            /dev/kgsl-3d0
+7fff6b000-7fff6c000 rw-s 00036000 00:10 20630                            /dev/kgsl-3d0
+7fff6c000-7fff6d000 rw-s 00035000 00:10 20630                            /dev/kgsl-3d0
+7fff6d000-7fff6e000 rw-s 00034000 00:10 20630                            /dev/kgsl-3d0
+7fff6e000-7fff6f000 rw-s 00033000 00:10 20630                            /dev/kgsl-3d0
+7fff6f000-7fff70000 rw-s 00032000 00:10 20630                            /dev/kgsl-3d0
+7fff70000-7fff71000 rw-s 00031000 00:10 20630                            /dev/kgsl-3d0
+7fff71000-7fff72000 rw-s 00030000 00:10 20630                            /dev/kgsl-3d0
+7fff72000-7fff73000 rw-s 0002f000 00:10 20630                            /dev/kgsl-3d0
+7fff73000-7fff74000 rw-s 0002e000 00:10 20630                            /dev/kgsl-3d0
+7fff74000-7fff75000 rw-s 0002d000 00:10 20630                            /dev/kgsl-3d0
+7fff75000-7fff76000 rw-s 0002c000 00:10 20630                            /dev/kgsl-3d0
+7fff76000-7fff77000 rw-s 0002b000 00:10 20630                            /dev/kgsl-3d0
+7fff77000-7fff78000 rw-s 0002a000 00:10 20630                            /dev/kgsl-3d0
+7fff78000-7fff79000 rw-s 00029000 00:10 20630                            /dev/kgsl-3d0
+7fff79000-7fff7a000 rw-s 00028000 00:10 20630                            /dev/kgsl-3d0
+7fff7a000-7fff7b000 rw-s 00027000 00:10 20630                            /dev/kgsl-3d0
+7fff7b000-7fff7c000 rw-s 00026000 00:10 20630                            /dev/kgsl-3d0
+7fff7c000-7fff7d000 rw-s 00025000 00:10 20630                            /dev/kgsl-3d0
+7fff7d000-7fff7e000 rw-s 00024000 00:10 20630                            /dev/kgsl-3d0
+7fff7e000-7fff7f000 rw-s 00023000 00:10 20630                            /dev/kgsl-3d0
+7fff7f000-7fff80000 rw-s 00022000 00:10 20630                            /dev/kgsl-3d0
+7fff80000-7fff90000 rw-s 00019000 00:10 20630                            /dev/kgsl-3d0
+7fff90000-7fffb0000 rw-s 00018000 00:10 20630                            /dev/kgsl-3d0
+7fffb1000-7fffb2000 rw-s 00021000 00:10 20630                            /dev/kgsl-3d0
+7fffb2000-7fffb3000 rw-s 00020000 00:10 20630                            /dev/kgsl-3d0
+7fffb3000-7fffb4000 rw-s 0001f000 00:10 20630                            /dev/kgsl-3d0
+7fffba000-7fffbe000 rw-s 0001b000 00:10 20630                            /dev/kgsl-3d0
+7fffbe000-7fffbf000 rw-s 0001a000 00:10 20630                            /dev/kgsl-3d0
+7fffbf000-7fffc0000 rw-s 00017000 00:10 20630                            /dev/kgsl-3d0
+7fffc0000-7fffe0000 rw-s 00016000 00:10 20630                            /dev/kgsl-3d0
+7fffe0000-7fffe1000 rw-s 00014000 00:10 20630                            /dev/kgsl-3d0
+7fffe1000-7fffe5000 rw-s 00013000 00:10 20630                            /dev/kgsl-3d0
+7fffe5000-7fffe6000 rw-s 00012000 00:10 20630                            /dev/kgsl-3d0
+7fffe6000-7fffe7000 rw-s 00011000 00:10 20630                            /dev/kgsl-3d0
+7fffe7000-7fffe8000 rw-s 00010000 00:10 20630                            /dev/kgsl-3d0
+7fffe8000-7fffe9000 rw-s 0000f000 00:10 20630                            /dev/kgsl-3d0
+7fffe9000-7fffea000 rw-s 0000e000 00:10 20630                            /dev/kgsl-3d0
+7fffea000-7fffeb000 rw-s 0000d000 00:10 20630                            /dev/kgsl-3d0
+7fffeb000-7fffec000 rw-s 0000c000 00:10 20630                            /dev/kgsl-3d0
+7fffec000-7ffff0000 rw-s 0000b000 00:10 20630                            /dev/kgsl-3d0
+7ffff0000-7ffff1000 rw-s 0000a000 00:10 20630                            /dev/kgsl-3d0
+7ffff1000-7ffff5000 rw-s 00009000 00:10 20630                            /dev/kgsl-3d0
+7ffff5000-7ffff6000 rw-s 00008000 00:10 20630                            /dev/kgsl-3d0
+7ffff6000-7ffff7000 rw-s 00007000 00:10 20630                            /dev/kgsl-3d0
+7ffff7000-7ffff8000 rw-s 00006000 00:10 20630                            /dev/kgsl-3d0
+7ffff8000-7ffff9000 rw-s 00005000 00:10 20630                            /dev/kgsl-3d0
+7ffff9000-7ffffa000 rw-s 00004000 00:10 20630                            /dev/kgsl-3d0
+7ffffa000-7ffffb000 rw-s 00003000 00:10 20630                            /dev/kgsl-3d0
+7ffffb000-7ffffc000 rw-s 00002000 00:10 20630                            /dev/kgsl-3d0
+7ffffc000-800000000 rw-s 00001000 00:10 20630                            /dev/kgsl-3d0
+5ff1d4f000-5ff1d54000 r-xp 00000000 fc:00 3419                           /system/bin/app_process64
+5ff1d6e000-5ff1d6f000 r--p 0000f000 fc:00 3419                           /system/bin/app_process64
+5ff1d6f000-5ff1d71000 rw-p 00000000 00:00 0 
+704defa000-704defb000 ---p 00000000 00:00 0                              [anon:thread stack guard]
+704defb000-704defc000 ---p 00000000 00:00 0 
+704defc000-704e000000 rw-p 00000000 00:00 0 
+704e000000-704e400000 rw-p 00000000 00:00 0                              [anon:libc_malloc]
+704e455000-704e456000 ---p 00000000 00:00 0                              [anon:thread stack guard]
+704e456000-704e457000 ---p 00000000 00:00 0 
+704e457000-704e553000 rw-p 00000000 00:00 0 
+704e553000-704e651000 r--p 00000000 00:10 16029                          /dev/hwbinder
+704e651000-704e65f000 r-xp 00000000 fc:01 1040                           /vendor/lib64/egl/eglSubDriverAndroid.so
+704e65f000-704e660000 r--p 0000e000 fc:01 1040                           /vendor/lib64/egl/eglSubDriverAndroid.so
+704e660000-704e661000 rw-p 0000f000 fc:01 1040                           /vendor/lib64/egl/eglSubDriverAndroid.so
+704e69d000-704e69e000 ---p 00000000 00:00 0                              [anon:thread stack guard]
+704e69e000-704e79b000 rw-p 00000000 00:00 0 
+704e79b000-704f79b000 rw-s 00000000 00:05 10271021                       /dev/ashmem/AudioFlinger::Client(29312) (deleted)
+704f79b000-704f79c000 ---p 00000000 00:00 0                              [anon:thread stack guard]
+704f79c000-704f899000 rw-p 00000000 00:00 0 
+704f899000-704f89a000 ---p 00000000 00:00 0                              [anon:thread stack guard]
+704f89a000-704f89b000 ---p 00000000 00:00 0 
+704f89b000-704f997000 rw-p 00000000 00:00 0 
+704f997000-704f9ee000 r-xp 00000000 103:1d 1737338                       /data/app/com.google.sample.tunnel-HGGRU03Gu1Mwkf_-RnFmvw==/lib/arm64/libgame.so
+704f9ee000-704f9fd000 ---p 00000000 00:00 0 
+704f9fd000-704fa00000 r--p 00056000 103:1d 1737338                       /data/app/com.google.sample.tunnel-HGGRU03Gu1Mwkf_-RnFmvw==/lib/arm64/libgame.so
+704fa00000-704fa01000 rw-p 00059000 103:1d 1737338                       /data/app/com.google.sample.tunnel-HGGRU03Gu1Mwkf_-RnFmvw==/lib/arm64/libgame.so
+704fa01000-704fa19000 rw-p 00000000 00:00 0                              [anon:.bss]
+704fa40000-70507e7000 r-xp 00000000 fc:01 1026                           /vendor/lib64/libllvm-glnext.so
+70507e7000-70507fc000 ---p 00000000 00:00 0 
+70507fc000-7050835000 r--p 00da7000 fc:01 1026                           /vendor/lib64/libllvm-glnext.so
+7050835000-705083a000 rw-p 00de0000 fc:01 1026                           /vendor/lib64/libllvm-glnext.so
+705083a000-7050855000 rw-p 00000000 00:00 0                              [anon:.bss]
+705089b000-7050f19000 r-xp 00000000 fc:01 1039                           /vendor/lib64/egl/libGLESv2_adreno.so
+7050f19000-7050f22000 r--p 0067e000 fc:01 1039                           /vendor/lib64/egl/libGLESv2_adreno.so
+7050f22000-7050f29000 rw-p 00687000 fc:01 1039                           /vendor/lib64/egl/libGLESv2_adreno.so
+7050f29000-7050f2c000 rw-p 00000000 00:00 0                              [anon:.bss]
+7050f83000-7050fbc000 r-xp 00000000 fc:01 1041                           /vendor/lib64/egl/libGLESv1_CM_adreno.so
+7050fbc000-7050fbd000 r--p 00039000 fc:01 1041                           /vendor/lib64/egl/libGLESv1_CM_adreno.so
+7050fbd000-7050fbe000 rw-p 0003a000 fc:01 1041                           /vendor/lib64/egl/libGLESv1_CM_adreno.so
+7050fbe000-7050fbf000 rw-p 00000000 00:00 0                              [anon:.bss]
+7050fc6000-705111d000 r-xp 00000000 fc:01 865                            /vendor/lib64/libgsl.so
+705111d000-705111e000 r--p 00157000 fc:01 865                            /vendor/lib64/libgsl.so
+705111e000-705111f000 rw-p 00158000 fc:01 865                            /vendor/lib64/libgsl.so
+705111f000-7051120000 rw-p 00000000 00:00 0                              [anon:.bss]
+7051146000-705115d000 r-xp 00000000 fc:00 2587                           /system/lib64/vndk-sp-28/libz.so
+705115d000-7051175000 ---p 00000000 00:00 0 
+7051175000-7051176000 r--p 0001f000 fc:00 2587                           /system/lib64/vndk-sp-28/libz.so
+7051176000-7051177000 rw-p 00020000 fc:00 2587                           /system/lib64/vndk-sp-28/libz.so
+705119f000-70511ac000 r-xp 00000000 fc:01 886                            /vendor/lib64/libadreno_utils.so
+70511ac000-70511ad000 r--p 0000d000 fc:01 886                            /vendor/lib64/libadreno_utils.so
+70511ad000-70511ae000 rw-p 0000e000 fc:01 886                            /vendor/lib64/libadreno_utils.so
+70511ae000-70511b0000 rw-p 00000000 00:00 0                              [anon:.bss]
+70511c0000-70511d7000 r-xp 00000000 fc:01 1044                           /vendor/lib64/egl/libEGL_adreno.so
+70511d7000-70511d8000 r--p 00017000 fc:01 1044                           /vendor/lib64/egl/libEGL_adreno.so
+70511d8000-70511d9000 rw-p 00018000 fc:01 1044                           /vendor/lib64/egl/libEGL_adreno.so
+70511d9000-70511da000 rw-p 00000000 00:00 0                              [anon:.bss]
+705120a000-705120d000 r-xp 00000000 fc:01 972                            /vendor/lib64/libdrmutils.so
+705120d000-7051229000 ---p 00000000 00:00 0 
+7051229000-705122a000 r--p 0000f000 fc:01 972                            /vendor/lib64/libdrmutils.so
+705122a000-705122b000 rw-p 00010000 fc:01 972                            /vendor/lib64/libdrmutils.so
+705125a000-705125c000 r-xp 00000000 fc:01 1046                           /vendor/lib64/libqdMetaData.so
+705125c000-7051279000 ---p 00000000 00:00 0 
+7051279000-705127a000 r--p 0000f000 fc:01 1046                           /vendor/lib64/libqdMetaData.so
+705127a000-705127b000 rw-p 00010000 fc:01 1046                           /vendor/lib64/libqdMetaData.so
+7051286000-7051297000 r-xp 00000000 fc:01 1024                           /vendor/lib64/libdrm.so
+7051297000-70512b5000 ---p 00000000 00:00 0 
+70512b5000-70512b6000 r--p 0001f000 fc:01 1024                           /vendor/lib64/libdrm.so
+70512b6000-70512b7000 rw-p 00020000 fc:01 1024                           /vendor/lib64/libdrm.so
+70512cb000-70512de000 r-xp 00000000 fc:01 1008                           /vendor/lib64/hw/gralloc.msm8998.so
+70512de000-70512fa000 ---p 00000000 00:00 0 
+70512fa000-70512fb000 r--p 0001f000 fc:01 1008                           /vendor/lib64/hw/gralloc.msm8998.so
+70512fb000-70512fc000 rw-p 00020000 fc:01 1008                           /vendor/lib64/hw/gralloc.msm8998.so
+7051326000-7051327000 ---p 00000000 00:00 0                              [anon:thread stack guard]
+7051327000-7051328000 ---p 00000000 00:00 0 
+7051328000-7051424000 rw-p 00000000 00:00 0 
+7051424000-705143d000 r--p 00000000 fc:00 739                            /system/framework/oat/arm64/org.apache.http.legacy.boot.odex
+705143d000-7051480000 r-xp 00019000 fc:00 739                            /system/framework/oat/arm64/org.apache.http.legacy.boot.odex
+7051480000-7051494000 r--p 00211000 103:1d 639511                        /data/dalvik-cache/arm64/system@framework@boot.art
+7051494000-705149f000 r--p 000c5000 103:1d 639514                        /data/dalvik-cache/arm64/system@framework@boot-core-libart.art
+705149f000-70514a2000 r--p 00032000 103:1d 639517                        /data/dalvik-cache/arm64/system@framework@boot-conscrypt.art
+70514a2000-70514a5000 r--p 0002b000 103:1d 639520                        /data/dalvik-cache/arm64/system@framework@boot-okhttp.art
+70514a5000-70514ac000 r--p 0003f000 103:1d 639523                        /data/dalvik-cache/arm64/system@framework@boot-bouncycastle.art
+70514ac000-70514b2000 r--p 00044000 103:1d 639526                        /data/dalvik-cache/arm64/system@framework@boot-apache-xml.art
+70514b2000-70514bd000 r--p 00035000 103:1d 639529                        /data/dalvik-cache/arm64/system@framework@boot-ext.art
+70514bd000-70514f4000 r--p 0060f000 103:1d 639532                        /data/dalvik-cache/arm64/system@framework@boot-framework.art
+70514f4000-70514fe000 r--p 00054000 103:1d 639535                        /data/dalvik-cache/arm64/system@framework@boot-telephony-common.art
+70514fe000-70514ff000 r--p 0000c000 103:1d 639538                        /data/dalvik-cache/arm64/system@framework@boot-voip-common.art
+70514ff000-7051500000 r--p 0000e000 103:1d 639541                        /data/dalvik-cache/arm64/system@framework@boot-ims-common.art
+7051500000-7051501000 r--p 00004000 103:1d 639547                        /data/dalvik-cache/arm64/system@framework@boot-android.hidl.manager-V1.0-java.art
+7051501000-7051502000 r--p 00004000 103:1d 639553                        /data/dalvik-cache/arm64/system@framework@boot-android.test.base.art
+7051502000-7051503000 r--p 00001000 103:1d 639556                        /data/dalvik-cache/arm64/system@framework@boot-com.google.vr.platform.art
+7051503000-7051504000 rw-p 00000000 00:00 0                              [anon:.bss]
+7051504000-7051579000 r--s 00000000 fc:00 790                            /system/framework/oat/arm64/org.apache.http.legacy.boot.vdex
+7051579000-705157a000 r--p 0005c000 fc:00 739                            /system/framework/oat/arm64/org.apache.http.legacy.boot.odex
+705157a000-705157b000 rw-p 0005d000 fc:00 739                            /system/framework/oat/arm64/org.apache.http.legacy.boot.odex
+705158b000-7057f4d000 ---p 00000000 00:00 0 
+7057f4d000-7057f4f000 r-xp 00000000 fc:00 2646                           /system/lib64/libwebviewchromium_loader.so
+7057f4f000-7057f6c000 ---p 00000000 00:00 0 
+7057f6c000-7057f6d000 r--p 0000f000 fc:00 2646                           /system/lib64/libwebviewchromium_loader.so
+7057f6d000-7057f6e000 rw-p 00010000 fc:00 2646                           /system/lib64/libwebviewchromium_loader.so
+7057f76000-7057f96000 r--s 00000000 00:10 16615                          /dev/__properties__/u:object_r:hwservicemanager_prop:s0
+7057f96000-7057fb6000 r--s 00000000 00:10 16639                          /dev/__properties__/u:object_r:public_vendor_default_prop:s0
+7057fb6000-7058004000 r--s 00000000 fc:00 1112                           /system/usr/hyphen-data/hyph-hu.hyb
+7058004000-7058024000 r-xp 00000000 fc:00 2354                           /system/lib64/libcompiler_rt.so
+7058024000-7058043000 ---p 00000000 00:00 0 
+7058043000-7058044000 r--p 0002f000 fc:00 2354                           /system/lib64/libcompiler_rt.so
+7058044000-7058045000 rw-p 00030000 fc:00 2354                           /system/lib64/libcompiler_rt.so
+7058045000-70580b2000 rw-p 00000000 00:00 0                              [anon:.bss]
+70580bd000-70580dd000 rw-p 00000000 00:05 10265386                       /dev/ashmem/dalvik-LinearAlloc (deleted)
+70580dd000-70580df000 r-xp 00000000 fc:00 2597                           /system/lib64/vndk-sp-28/libhardware.so
+70580df000-70580fc000 ---p 00000000 00:00 0 
+70580fc000-70580fd000 r--p 0000f000 fc:00 2597                           /system/lib64/vndk-sp-28/libhardware.so
+70580fd000-70580fe000 rw-p 00010000 fc:00 2597                           /system/lib64/vndk-sp-28/libhardware.so
+705810e000-705811f000 r-xp 00000000 fc:00 2589                           /system/lib64/vndk-sp-28/libbase.so
+705811f000-705813d000 ---p 00000000 00:00 0 
+705813d000-705813e000 r--p 0001f000 fc:00 2589                           /system/lib64/vndk-sp-28/libbase.so
+705813e000-705813f000 rw-p 00020000 fc:00 2589                           /system/lib64/vndk-sp-28/libbase.so
+7058140000-7058167000 r-xp 00000000 fc:00 2572                           /system/lib64/vndk-sp-28/libhwbinder.so
+7058167000-705817d000 ---p 00000000 00:00 0 
+705817d000-705817f000 r--p 0002e000 fc:00 2572                           /system/lib64/vndk-sp-28/libhwbinder.so
+705817f000-7058180000 rw-p 00030000 fc:00 2572                           /system/lib64/vndk-sp-28/libhwbinder.so
+705818c000-705818d000 r-xp 00000000 fc:00 2584                           /system/lib64/vndk-sp-28/android.hardware.graphics.common@1.0.so
+705818d000-70581ab000 ---p 00000000 00:00 0 
+70581ab000-70581ac000 r--p 0000f000 fc:00 2584                           /system/lib64/vndk-sp-28/android.hardware.graphics.common@1.0.so
+70581ac000-70581ad000 rw-p 00010000 fc:00 2584                           /system/lib64/vndk-sp-28/android.hardware.graphics.common@1.0.so
+70581b7000-70581d7000 r--s 00000000 00:10 16619                          /dev/__properties__/u:object_r:log_prop:s0
+70581d7000-7058237000 r-xp 00000000 fc:00 2574                           /system/lib64/vndk-sp-28/libhidltransport.so
+7058237000-7058255000 ---p 00000000 00:00 0 
+7058255000-705825d000 r--p 00068000 fc:00 2574                           /system/lib64/vndk-sp-28/libhidltransport.so
+705825d000-705825e000 rw-p 00070000 fc:00 2574                           /system/lib64/vndk-sp-28/libhidltransport.so
+7058260000-7058284000 r--s 00000000 fc:00 1138                           /system/usr/hyphen-data/hyph-nn.hyb
+7058284000-70582a0000 r-xp 00000000 fc:00 2576                           /system/lib64/vndk-sp-28/libutils.so
+70582a0000-70582b3000 ---p 00000000 00:00 0 
+70582b3000-70582b4000 r--p 0001f000 fc:00 2576                           /system/lib64/vndk-sp-28/libutils.so
+70582b4000-70582b5000 rw-p 00020000 fc:00 2576                           /system/lib64/vndk-sp-28/libutils.so
+70582c4000-7058391000 r-xp 00000000 fc:00 2568                           /system/lib64/vndk-sp-28/libc++.so
+7058391000-70583ad000 ---p 00000000 00:00 0 
+70583ad000-70583b7000 r--p 000d6000 fc:00 2568                           /system/lib64/vndk-sp-28/libc++.so
+70583b7000-70583b8000 rw-p 000e0000 fc:00 2568                           /system/lib64/vndk-sp-28/libc++.so
+70583b8000-70583bb000 rw-p 00000000 00:00 0                              [anon:.bss]
+70583cd000-70583e4000 r-xp 00000000 fc:00 2580                           /system/lib64/vndk-sp-28/android.hardware.graphics.mapper@2.0.so
+70583e4000-70583f9000 ---p 00000000 00:00 0 
+70583f9000-70583fb000 r--p 0001e000 fc:00 2580                           /system/lib64/vndk-sp-28/android.hardware.graphics.mapper@2.0.so
+70583fb000-70583fc000 rw-p 00020000 fc:00 2580                           /system/lib64/vndk-sp-28/android.hardware.graphics.mapper@2.0.so
+705841b000-7058421000 r-xp 00000000 fc:01 1001                           /vendor/lib64/hw/android.hardware.graphics.mapper@2.0-impl.so
+7058421000-705843a000 ---p 00000000 00:00 0 
+705843a000-705843b000 r--p 0000f000 fc:01 1001                           /vendor/lib64/hw/android.hardware.graphics.mapper@2.0-impl.so
+705843b000-705843c000 rw-p 00010000 fc:01 1001                           /vendor/lib64/hw/android.hardware.graphics.mapper@2.0-impl.so
+705844f000-7058473000 r--s 00000000 fc:00 1150                           /system/usr/hyphen-data/hyph-nb.hyb
+7058473000-7058495000 r-xp 00000000 fc:00 2582                           /system/lib64/vndk-sp-28/libhidlbase.so
+7058495000-70584b1000 ---p 00000000 00:00 0 
+70584b1000-70584b3000 r--p 0002e000 fc:00 2582                           /system/lib64/vndk-sp-28/libhidlbase.so
+70584b3000-70584b4000 rw-p 00030000 fc:00 2582                           /system/lib64/vndk-sp-28/libhidlbase.so
+70584cd000-70584df000 r-xp 00000000 fc:00 2595                           /system/lib64/vndk-sp-28/libcutils.so
+70584df000-70584fb000 ---p 00000000 00:00 0 
+70584fb000-70584fd000 r--p 0001e000 fc:00 2595                           /system/lib64/vndk-sp-28/libcutils.so
+70584fd000-70584fe000 rw-p 00020000 fc:00 2595                           /system/lib64/vndk-sp-28/libcutils.so
+7058519000-7058537000 r--s 00000000 fc:00 1124                           /system/usr/hyphen-data/hyph-de-ch-1901.hyb
+7058537000-7059fd1000 r--s 0070b000 fc:00 989                            /system/framework/framework-res.apk
+7059fd1000-705a013000 r-xp 00000000 fc:00 2610                           /system/lib64/libjavacrypto.so
+705a013000-705a02b000 ---p 00000000 00:00 0 
+705a02b000-705a02d000 r--p 0004e000 fc:00 2610                           /system/lib64/libjavacrypto.so
+705a02d000-705a02f000 rw-p 00050000 fc:00 2610                           /system/lib64/libjavacrypto.so
+705a041000-705a05f000 r--s 00000000 fc:00 1128                           /system/usr/hyphen-data/hyph-de-1996.hyb
+705a05f000-705a06a000 r-xp 00000000 fc:00 2917                           /system/lib64/libsoundpool.so
+705a06a000-705a07e000 ---p 00000000 00:00 0 
+705a07e000-705a07f000 r--p 0000f000 fc:00 2917                           /system/lib64/libsoundpool.so
+705a07f000-705a080000 rw-p 00010000 fc:00 2917                           /system/lib64/libsoundpool.so
+705a087000-705a102000 r--s 00000000 fc:00 1246                           /system/usr/share/zoneinfo/tzdata
+705a102000-705a863000 r--s 00000000 fc:00 101                            /system/fonts/NotoColorEmoji.ttf
+705a863000-705c000000 r--s 00000000 fc:00 251                            /system/fonts/NotoSerifCJK-Regular.ttc
+705c000000-705c200000 rw-p 00000000 00:00 0                              [anon:libc_malloc]
+705c209000-705c227000 r--s 00000000 fc:00 1077                           /system/usr/hyphen-data/hyph-de-1901.hyb
+705c227000-705c26e000 r--s 02284000 fc:00 989                            /system/framework/framework-res.apk
+705c26e000-705d43e000 r--s 00000000 fc:00 95                             /system/fonts/NotoSansCJK-Regular.ttc
+705d43e000-705d4ec000 r--s 00000000 fc:00 278                            /system/fonts/NotoSansSymbols-Regular-Subsetted.ttf
+705d4ec000-705d548000 r--s 00000000 fc:00 233                            /system/fonts/NotoSansTibetan-Bold.ttf
+705d548000-705d5ab000 r--s 00000000 fc:00 177                            /system/fonts/NotoSansTibetan-Regular.ttf
+705d5ab000-705d627000 r--s 00000000 fc:00 197                            /system/fonts/NotoSansEgyptianHieroglyphs-Regular.ttf
+705d627000-705d6a2000 r--s 00000000 fc:00 76                             /system/fonts/NotoSansCuneiform-Regular.ttf
+705d6a2000-705d6f3000 r--s 00000000 fc:00 67                             /system/fonts/RobotoCondensed-BoldItalic.ttf
+705d6f3000-705d73e000 r--s 00000000 fc:00 199                            /system/fonts/RobotoCondensed-Bold.ttf
+705d73e000-705d78f000 r--s 00000000 fc:00 230                            /system/fonts/RobotoCondensed-MediumItalic.ttf
+705d78f000-705d7da000 r--s 00000000 fc:00 92                             /system/fonts/RobotoCondensed-Medium.ttf
+705d7da000-705d82b000 r--s 00000000 fc:00 128                            /system/fonts/RobotoCondensed-Italic.ttf
+705d82b000-705d875000 r--s 00000000 fc:00 164                            /system/fonts/RobotoCondensed-Regular.ttf
+705d875000-705d8c7000 r--s 00000000 fc:00 292                            /system/fonts/RobotoCondensed-LightItalic.ttf
+705d8c7000-705d919000 r--s 00000000 fc:00 85                             /system/fonts/Roboto-BoldItalic.ttf
+705d919000-705d964000 r--s 00000000 fc:00 175                            /system/fonts/Roboto-Bold.ttf
+705d964000-705d9b5000 r--s 00000000 fc:00 266                            /system/fonts/Roboto-BlackItalic.ttf
+705d9b5000-705da00000 r--s 00000000 fc:00 187                            /system/fonts/Roboto-Black.ttf
+705da00000-705dc00000 rw-p 00000000 00:00 0                              [anon:libc_malloc]
+705dc1d000-705dc6e000 r--s 00000000 fc:00 148                            /system/fonts/Roboto-MediumItalic.ttf
+705dc6e000-705dcb9000 r--s 00000000 fc:00 284                            /system/fonts/Roboto-Medium.ttf
+705dcb9000-705dd0a000 r--s 00000000 fc:00 105                            /system/fonts/Roboto-Italic.ttf
+705dd0a000-705dd55000 r--s 00000000 fc:00 156                            /system/fonts/Roboto-Regular.ttf
+705dd55000-705dda7000 r--s 00000000 fc:00 217                            /system/fonts/Roboto-LightItalic.ttf
+705dda7000-705ddf8000 r--s 00000000 fc:00 166                            /system/fonts/Roboto-ThinItalic.ttf
+705ddf8000-705ddf9000 ---p 00000000 00:00 0                              [anon:thread stack guard]
+705ddf9000-705ddfa000 ---p 00000000 00:00 0 
+705ddfa000-705def6000 rw-p 00000000 00:00 0 
+705def6000-705f5ec000 r--s 00000000 fc:00 1350                           /system/usr/icu/icudt60l.dat
+705f5ec000-705f5ed000 ---p 00000000 00:00 0                              [anon:thread stack guard]
+705f5ed000-705f5ee000 ---p 00000000 00:00 0 
+705f5ee000-705f6ea000 rw-p 00000000 00:00 0 
+705f6ea000-705f7e8000 r--p 00000000 00:10 20636                          /dev/binder
+705f7e8000-705f7e9000 ---p 00000000 00:00 0                              [anon:thread stack guard]
+705f7e9000-705f7ea000 ---p 00000000 00:00 0 
+705f7ea000-705f8ee000 rw-p 00000000 00:00 0 
+705f8ee000-705f8ef000 ---p 00000000 00:00 0                              [anon:thread stack guard]
+705f8ef000-705f8f0000 ---p 00000000 00:00 0 
+705f8f0000-705f9f4000 rw-p 00000000 00:00 0 
+705f9f4000-705f9f5000 ---p 00000000 00:00 0                              [anon:thread stack guard]
+705f9f5000-705f9f6000 ---p 00000000 00:00 0 
+705f9f6000-705fafa000 rw-p 00000000 00:00 0 
+705fafa000-705fafb000 ---p 00000000 00:00 0                              [anon:thread stack guard]
+705fafb000-705fafc000 ---p 00000000 00:00 0 
+705fafc000-705fc00000 rw-p 00000000 00:00 0 
+705fc00000-705fe00000 rw-p 00000000 00:00 0                              [anon:libc_malloc]
+705fe01000-705fe4c000 r--s 00000000 fc:00 97                             /system/fonts/Roboto-Light.ttf
+705fe4c000-705fe4d000 ---p 00000000 00:00 0                              [anon:thread stack guard]
+705fe4d000-705fe4e000 ---p 00000000 00:00 0 
+705fe4e000-705ff4a000 rw-p 00000000 00:00 0 
+705ff4a000-705ff4b000 ---p 00000000 00:05 10270991                       /dev/ashmem/dalvik-Jit thread pool worker thread 0 (deleted)
+705ff4b000-705ff4c000 ---p 00001000 00:05 10270991                       /dev/ashmem/dalvik-Jit thread pool worker thread 0 (deleted)
+705ff4c000-706004b000 rw-p 00002000 00:05 10270991                       /dev/ashmem/dalvik-Jit thread pool worker thread 0 (deleted)
+706004b000-706010f000 r-xp 00000000 fc:00 2390                           /system/lib64/libvixl-arm64.so
+706010f000-7060120000 ---p 00000000 00:00 0 
+7060120000-7060125000 r--p 000cb000 fc:00 2390                           /system/lib64/libvixl-arm64.so
+7060125000-7060126000 rw-p 000d0000 fc:00 2390                           /system/lib64/libvixl-arm64.so
+7060126000-706012d000 rw-p 00000000 00:00 0                              [anon:.bss]
+7060135000-7060151000 r--s 00000000 fc:01 1180                           /vendor/overlay/framework-res__auto_generated_rro.apk
+7060151000-7060263000 r-xp 00000000 fc:00 2669                           /system/lib64/libvixl-arm.so
+7060263000-7060275000 ---p 00000000 00:00 0 
+7060275000-706027a000 r--p 0011b000 fc:00 2669                           /system/lib64/libvixl-arm.so
+706027a000-706027b000 rw-p 00120000 fc:00 2669                           /system/lib64/libvixl-arm.so
+706028b000-706056c000 r-xp 00000000 fc:00 2972                           /system/lib64/libart-compiler.so
+706056c000-7060580000 ---p 00000000 00:00 0 
+7060580000-7060598000 r--p 002e8000 fc:00 2972                           /system/lib64/libart-compiler.so
+7060598000-7060599000 rw-p 00300000 fc:00 2972                           /system/lib64/libart-compiler.so
+7060599000-70605a0000 rw-p 00000000 00:00 0                              [anon:.bss]
+70605b0000-70605d0000 r--s 00000000 00:10 16571                          /dev/__properties__/u:object_r:config_prop:s0
+70605d0000-7060619000 r-xp 00000000 fc:00 2702                           /system/lib64/libssl.so
+7060619000-706062d000 ---p 00000000 00:00 0 
+706062d000-7060630000 r--p 0004d000 fc:00 2702                           /system/lib64/libssl.so
+7060630000-7060631000 rw-p 00050000 fc:00 2702                           /system/lib64/libssl.so
+7060647000-7060667000 r--s 00000000 00:10 16595                          /dev/__properties__/u:object_r:exported3_radio_prop:s0
+7060667000-706069d000 r-xp 00000000 fc:00 2371                           /system/lib64/libopenjdk.so
+706069d000-70606b2000 ---p 00000000 00:00 0 
+70606b2000-70606b4000 r--p 0003e000 fc:00 2371                           /system/lib64/libopenjdk.so
+70606b4000-70606b6000 rw-p 00040000 fc:00 2371                           /system/lib64/libopenjdk.so
+70606bb000-70606db000 r--s 00000000 00:10 16608                          /dev/__properties__/u:object_r:exported_system_prop:s0
+70606db000-70606e3000 r-xp 00000000 fc:00 2538                           /system/lib64/libopenjdkjvm.so
+70606e3000-70606fa000 ---p 00000000 00:00 0 
+70606fa000-70606fb000 r--p 0000f000 fc:00 2538                           /system/lib64/libopenjdkjvm.so
+70606fb000-70606fc000 rw-p 00010000 fc:00 2538                           /system/lib64/libopenjdkjvm.so
+7060701000-7060722000 r--s 00000000 fc:00 227                            /system/fonts/NotoSansAnatolianHieroglyphs-Regular.otf
+7060722000-7061e18000 r--s 00000000 fc:00 1350                           /system/usr/icu/icudt60l.dat
+7061e18000-7061e5d000 r-xp 00000000 fc:00 2368                           /system/lib64/libjavacore.so
+7061e5d000-7061e71000 ---p 00000000 00:00 0 
+7061e71000-7061e73000 r--p 0004e000 fc:00 2368                           /system/lib64/libjavacore.so
+7061e73000-7061e75000 rw-p 00050000 fc:00 2368                           /system/lib64/libjavacore.so
+7061e75000-7061e76000 rw-p 00000000 00:00 0                              [anon:.bss]
+7061e77000-7061e96000 r--s 00000000 fc:00 186                            /system/fonts/NotoSansYi-Regular.ttf
+7061e96000-7061e99000 r-xp 00000000 fc:00 2953                           /system/lib64/libwebviewchromium_plat_support.so
+7061e99000-7061eb5000 ---p 00000000 00:00 0 
+7061eb5000-7061eb6000 r--p 0000f000 fc:00 2953                           /system/lib64/libwebviewchromium_plat_support.so
+7061eb6000-7061eb7000 rw-p 00010000 fc:00 2953                           /system/lib64/libwebviewchromium_plat_support.so
+7061ebc000-7061edd000 r--s 00000000 fc:00 100                            /system/fonts/NotoSansBamum-Regular.ttf
+7061edd000-7061eed000 r-xp 00000000 fc:00 2945                           /system/lib64/libRS.so
+7061eed000-7061efc000 ---p 00000000 00:00 0 
+7061efc000-7061efd000 r--p 0000f000 fc:00 2945                           /system/lib64/libRS.so
+7061efd000-7061efe000 rw-p 00010000 fc:00 2945                           /system/lib64/libRS.so
+7061f05000-7061f6b000 r-xp 00000000 fc:00 2423                           /system/lib64/android.hardware.renderscript@1.0.so
+7061f6b000-7061f7a000 ---p 00000000 00:00 0 
+7061f7a000-7061f7f000 r--p 0006b000 fc:00 2423                           /system/lib64/android.hardware.renderscript@1.0.so
+7061f7f000-7061f80000 rw-p 00070000 fc:00 2423                           /system/lib64/android.hardware.renderscript@1.0.so
+7061f99000-7061f9b000 r-xp 00000000 fc:00 2614                           /system/lib64/libOpenSLES.so
+7061f9b000-7061fb8000 ---p 00000000 00:00 0 
+7061fb8000-7061fb9000 r--p 0000f000 fc:00 2614                           /system/lib64/libOpenSLES.so
+7061fb9000-7061fba000 rw-p 00010000 fc:00 2614                           /system/lib64/libOpenSLES.so
+7061fc6000-7061fc8000 r-xp 00000000 fc:00 2963                           /system/lib64/libOpenMAXAL.so
+7061fc8000-7061fe5000 ---p 00000000 00:00 0 
+7061fe5000-7061fe6000 r--p 0000f000 fc:00 2963                           /system/lib64/libOpenMAXAL.so
+7061fe6000-7061fe7000 rw-p 00010000 fc:00 2963                           /system/lib64/libOpenMAXAL.so
+7061fe7000-7062000000 r--s 00000000 fc:00 143                            /system/fonts/NotoSansBhaiksuki-Regular.otf
+7062000000-7062003000 r-xp 00000000 fc:00 2447                           /system/lib64/libtextclassifier_hash.so
+7062003000-706201f000 ---p 00000000 00:00 0 
+706201f000-7062020000 r--p 0000f000 fc:00 2447                           /system/lib64/libtextclassifier_hash.so
+7062020000-7062021000 rw-p 00010000 fc:00 2447                           /system/lib64/libtextclassifier_hash.so
+7062022000-7062042000 rw-p 00000000 00:05 10269731                       /dev/ashmem/dalvik-CompilerMetadata (deleted)
+7062042000-7062077000 r-xp 00000000 fc:00 2372                           /system/lib64/android.hardware.neuralnetworks@1.0.so
+7062077000-7062095000 ---p 00000000 00:00 0 
+7062095000-706209b000 r--p 0003a000 fc:00 2372                           /system/lib64/android.hardware.neuralnetworks@1.0.so
+706209b000-706209c000 rw-p 00040000 fc:00 2372                           /system/lib64/android.hardware.neuralnetworks@1.0.so
+70620a9000-70620c9000 rw-p 00000000 00:05 10269730                       /dev/ashmem/dalvik-CompilerMetadata (deleted)
+70620c9000-70620e3000 r-xp 00000000 fc:00 2956                           /system/lib64/android.hardware.neuralnetworks@1.1.so
+70620e3000-70620f4000 ---p 00000000 00:00 0 
+70620f4000-70620f7000 r--p 0001d000 fc:00 2956                           /system/lib64/android.hardware.neuralnetworks@1.1.so
+70620f7000-70620f8000 rw-p 00020000 fc:00 2956                           /system/lib64/android.hardware.neuralnetworks@1.1.so
+706210b000-70621d0000 r-xp 00000000 fc:00 2387                           /system/lib64/libneuralnetworks.so
+70621d0000-70621e3000 ---p 00000000 00:00 0 
+70621e3000-70621e5000 r--p 000ce000 fc:00 2387                           /system/lib64/libneuralnetworks.so
+70621e5000-70621e7000 rw-p 000d0000 fc:00 2387                           /system/lib64/libneuralnetworks.so
+70621e7000-7062372000 rw-p 00000000 00:00 0                              [anon:.bss]
+7062373000-7062395000 r--s 00000000 fc:00 274                            /system/fonts/NotoSerifMyanmar-Bold.otf
+7062395000-7062398000 r-xp 00000000 fc:00 2937                           /system/lib64/libjnigraphics.so
+7062398000-70623b4000 ---p 00000000 00:00 0 
+70623b4000-70623b5000 r--p 0000f000 fc:00 2937                           /system/lib64/libjnigraphics.so
+70623b5000-70623b6000 rw-p 00010000 fc:00 2937                           /system/lib64/libjnigraphics.so
+70623c8000-70623e0000 r-xp 00000000 fc:00 2662                           /system/lib64/libGLESv3.so
+70623e0000-70623f7000 ---p 00000000 00:00 0 
+70623f7000-70623f8000 r--p 0001f000 fc:00 2662                           /system/lib64/libGLESv3.so
+70623f8000-70623f9000 rw-p 00020000 fc:00 2662                           /system/lib64/libGLESv3.so
+70623fc000-706241c000 rw-p 00000000 00:05 10269729                       /dev/ashmem/dalvik-CompilerMetadata (deleted)
+706241c000-7062444000 r-xp 00000000 fc:00 2603                           /system/lib64/libexif.so
+7062444000-706245f000 ---p 00000000 00:00 0 
+706245f000-7062472000 r--p 0002d000 fc:00 2603                           /system/lib64/libexif.so
+7062472000-7062473000 rw-p 00040000 fc:00 2603                           /system/lib64/libexif.so
+7062474000-7062490000 r--s 00000000 fc:00 286                            /system/fonts/NotoSansMongolian-Regular.ttf
+7062490000-7062491000 r-xp 00000000 fc:00 2357                           /system/lib64/libasyncio.so
+7062491000-70624af000 ---p 00000000 00:00 0 
+70624af000-70624b0000 r--p 0000f000 fc:00 2357                           /system/lib64/libasyncio.so
+70624b0000-70624b1000 rw-p 00010000 fc:00 2357                           /system/lib64/libasyncio.so
+70624b5000-70624cf000 r--s 00000000 fc:00 221                            /system/fonts/NotoSansMyanmarUI-Bold.ttf
+70624cf000-7062508000 r-xp 00000000 fc:00 2401                           /system/lib64/libmtp.so
+7062508000-7062522000 ---p 00000000 00:00 0 
+7062522000-7062525000 r--p 0003d000 fc:00 2401                           /system/lib64/libmtp.so
+7062525000-706252c000 rw-p 00040000 fc:00 2401                           /system/lib64/libmtp.so
+7062530000-7062550000 rw-p 00000000 00:05 10269728                       /dev/ashmem/dalvik-CompilerMetadata (deleted)
+7062550000-7062572000 r--s 00000000 fc:00 234                            /system/fonts/NotoSerifMyanmar-Regular.otf
+7062572000-706259e000 r-xp 00000000 fc:00 2620                           /system/lib64/libmediandk.so
+706259e000-70625b9000 ---p 00000000 00:00 0 
+70625b9000-70625bc000 r--p 0002d000 fc:00 2620                           /system/lib64/libmediandk.so
+70625bc000-70625c0000 rw-p 00030000 fc:00 2620                           /system/lib64/libmediandk.so
+70625c2000-70625d1000 r-xp 00000000 fc:00 2613                           /system/lib64/libmidi.so
+70625d1000-70625ef000 ---p 00000000 00:00 0 
+70625ef000-70625f1000 r--p 0000e000 fc:00 2613                           /system/lib64/libmidi.so
+70625f1000-70625f2000 rw-p 00010000 fc:00 2613                           /system/lib64/libmidi.so
+7062600000-7062621000 r-xp 00000000 fc:00 2366                           /system/lib64/libmediadrmmetrics_lite.so
+7062621000-706263d000 ---p 00000000 00:00 0 
+706263d000-706263f000 r--p 0002e000 fc:00 2366                           /system/lib64/libmediadrmmetrics_lite.so
+706263f000-7062640000 rw-p 00030000 fc:00 2366                           /system/lib64/libmediadrmmetrics_lite.so
+706264b000-706266b000 rw-p 00000000 00:05 10269727                       /dev/ashmem/dalvik-CompilerMetadata (deleted)
+706266b000-70626d4000 r-xp 00000000 fc:00 2727                           /system/lib64/libmedia_jni.so
+70626d4000-70626eb000 ---p 00000000 00:00 0 
+70626eb000-70626f2000 r--p 00069000 fc:00 2727                           /system/lib64/libmedia_jni.so
+70626f2000-70626f3000 rw-p 00070000 fc:00 2727                           /system/lib64/libmedia_jni.so
+7062703000-7062732000 r-xp 00000000 fc:00 2399                           /system/lib64/libcamera2ndk.so
+7062732000-7062748000 ---p 00000000 00:00 0 
+7062748000-706274b000 r--p 0003d000 fc:00 2399                           /system/lib64/libcamera2ndk.so
+706274b000-7062750000 rw-p 00040000 fc:00 2399                           /system/lib64/libcamera2ndk.so
+7062768000-7062788000 rw-p 00000000 00:05 10269726                       /dev/ashmem/dalvik-CompilerMetadata (deleted)
+7062788000-70627ee000 r-xp 00000000 fc:00 2974                           /system/lib64/android.hardware.drm@1.0.so
+70627ee000-7062805000 ---p 00000000 00:00 0 
+7062805000-706280d000 r--p 00068000 fc:00 2974                           /system/lib64/android.hardware.drm@1.0.so
+706280d000-706280e000 rw-p 00070000 fc:00 2974                           /system/lib64/android.hardware.drm@1.0.so
+706281a000-706281b000 ---p 00000000 00:00 0                              [anon:thread signal stack guard]
+706281b000-706281f000 rw-p 00000000 00:00 0                              [anon:thread signal stack]
+706281f000-7062843000 r--s 00000000 fc:00 142                            /system/fonts/NotoSansKhmer-VF.ttf
+7062843000-7062886000 r-xp 00000000 fc:00 2637                           /system/lib64/android.hardware.drm@1.1.so
+7062886000-70628a5000 ---p 00000000 00:00 0 
+70628a5000-70628ab000 r--p 0004a000 fc:00 2637                           /system/lib64/android.hardware.drm@1.1.so
+70628ab000-70628ac000 rw-p 00050000 fc:00 2637                           /system/lib64/android.hardware.drm@1.1.so
+70628b0000-70628b1000 ---p 00000000 00:00 0                              [anon:thread signal stack guard]
+70628b1000-70628b5000 rw-p 00000000 00:00 0                              [anon:thread signal stack]
+70628b5000-70628db000 r--s 00000000 fc:00 137                            /system/fonts/NotoSansSinhala-Bold.ttf
+70628db000-7062907000 r-xp 00000000 fc:00 2478                           /system/lib64/libmediadrm.so
+7062907000-7062918000 ---p 00000000 00:00 0 
+7062918000-7062920000 r--p 00038000 fc:00 2478                           /system/lib64/libmediadrm.so
+7062920000-7062921000 rw-p 00040000 fc:00 2478                           /system/lib64/libmediadrm.so
+7062922000-7062929000 rw-p 00000000 fc:00 583                            /system/etc/event-log-tags
+7062929000-7062951000 r--s 00000000 fc:00 296                            /system/fonts/NotoSansSinhala-Regular.ttf
+7062951000-7062997000 r-xp 00000000 fc:00 2448                           /system/lib64/libaaudio.so
+7062997000-70629ac000 ---p 00000000 00:00 0 
+70629ac000-70629b2000 r--p 0004a000 fc:00 2448                           /system/lib64/libaaudio.so
+70629b2000-70629ba000 rw-p 00050000 fc:00 2448                           /system/lib64/libaaudio.so
+70629ba000-70629bb000 ---p 00000000 00:00 0                              [anon:thread signal stack guard]
+70629bb000-70629bf000 rw-p 00000000 00:00 0                              [anon:thread signal stack]
+70629bf000-70629c0000 ---p 00000000 00:00 0                              [anon:bionic TLS guard]
+70629c0000-70629c3000 rw-p 00000000 00:00 0                              [anon:bionic TLS]
+70629c3000-70629c4000 ---p 00000000 00:00 0                              [anon:bionic TLS guard]
+70629c4000-70629c5000 ---p 00000000 00:00 0                              [anon:thread signal stack guard]
+70629c5000-70629c9000 rw-p 00000000 00:00 0                              [anon:thread signal stack]
+70629c9000-70629e3000 r-xp 00000000 fc:00 2940                           /system/lib64/libandroid.so
+70629e3000-70629f3000 ---p 00000000 00:00 0 
+70629f3000-70629f6000 r--p 0001d000 fc:00 2940                           /system/lib64/libandroid.so
+70629f6000-70629f7000 rw-p 00020000 fc:00 2940                           /system/lib64/libandroid.so
+70629f8000-70629f9000 ---p 00000000 00:00 0                              [anon:bionic TLS guard]
+70629f9000-70629fc000 rw-p 00000000 00:00 0                              [anon:bionic TLS]
+70629fc000-70629fd000 ---p 00000000 00:00 0                              [anon:bionic TLS guard]
+70629fd000-7062a3e000 r--s 00000000 fc:00 216                            /system/fonts/NotoSerif-BoldItalic.ttf
+7062a3e000-7062b06000 rw-p 00000000 00:05 10270984                       /dev/ashmem/dalvik-indirect ref table (deleted)
+7062b06000-7062bce000 rw-p 00000000 00:05 10270983                       /dev/ashmem/dalvik-indirect ref table (deleted)
+7062bce000-7062dce000 rw-p 00000000 00:05 10270726                       /dev/ashmem/dalvik-rb copying gc mark stack (deleted)
+7062dce000-70635ce000 rw-p 00000000 00:05 10270725                       /dev/ashmem/dalvik-concurrent copying gc mark stack (deleted)
+70635ce000-7063dcf000 rw-p 00000000 00:05 10270724                       /dev/ashmem/dalvik-live stack (deleted)
+7063dcf000-70645d0000 rw-p 00000000 00:05 10270723                       /dev/ashmem/dalvik-allocation stack (deleted)
+70645d0000-70649d1000 rw-p 00000000 00:05 10270721                       /dev/ashmem/dalvik-card table (deleted)
+70649d1000-7064ad1000 rw-p 00000000 00:05 10267648                       /dev/ashmem/dalvik-large object free list space allocation info map (deleted)
+7064ad1000-7065ad1000 rw-p 00000000 00:05 10267644                       /dev/ashmem/dalvik-region space live bitmap (deleted)
+7065ad1000-7065bd1000 rw-p 00000000 00:05 10267642                       /dev/ashmem/dalvik-allocspace zygote / non moving space mark-bitmap 0 (deleted)
+7065bd1000-7065cd1000 rw-p 00000000 00:05 10267641                       /dev/ashmem/dalvik-allocspace zygote / non moving space live-bitmap 0 (deleted)
+7065cd1000-7065cd2000 r-xp 00000000 fc:00 2946                           /system/lib64/libsigchain.so
+7065cd2000-7065cf0000 ---p 00000000 00:00 0 
+7065cf0000-7065cf1000 r--p 0000f000 fc:00 2946                           /system/lib64/libsigchain.so
+7065cf1000-7065cf2000 rw-p 00010000 fc:00 2946                           /system/lib64/libsigchain.so
+7065cf4000-7065d0f000 r--s 00000000 fc:00 190                            /system/fonts/NotoSansMyanmar-Bold.ttf
+7065d0f000-7065d22000 r-xp 00000000 fc:00 2405                           /system/lib64/liblz4.so
+7065d22000-7065d3e000 ---p 00000000 00:00 0 
+7065d3e000-7065d3f000 r--p 0001f000 fc:00 2405                           /system/lib64/liblz4.so
+7065d3f000-7065d40000 rw-p 00020000 fc:00 2405                           /system/lib64/liblz4.so
+7065d40000-7065d5a000 r--s 00000000 fc:00 222                            /system/fonts/NotoSansMyanmarUI-Regular.ttf
+7065d5a000-7065d5e000 r-xp 00000000 fc:00 2609                           /system/lib64/libtombstoned_client.so
+7065d5e000-7065d79000 ---p 00000000 00:00 0 
+7065d79000-7065d7a000 r--p 0000f000 fc:00 2609                           /system/lib64/libtombstoned_client.so
+7065d7a000-7065d7b000 rw-p 00010000 fc:00 2609                           /system/lib64/libtombstoned_client.so
+7065d7f000-7065d80000 ---p 00000000 00:00 0                              [anon:thread signal stack guard]
+7065d80000-7065d84000 rw-p 00000000 00:00 0                              [anon:thread signal stack]
+7065d84000-706636e000 r-xp 00000000 fc:00 2671                           /system/lib64/libart.so
+706636e000-706638d000 ---p 00000000 00:00 0 
+706638d000-706639e000 r--p 005ef000 fc:00 2671                           /system/lib64/libart.so
+706639e000-70663a1000 rw-p 00600000 fc:00 2671                           /system/lib64/libart.so
+70663a1000-70663a4000 rw-p 00000000 00:00 0                              [anon:.bss]
+70663a6000-70663c6000 rw-p 00000000 00:05 10269725                       /dev/ashmem/dalvik-CompilerMetadata (deleted)
+70663c6000-70663c8000 r-xp 00000000 fc:00 2673                           /system/lib64/libmetricslogger.so
+70663c8000-70663e5000 ---p 00000000 00:00 0 
+70663e5000-70663e6000 r--p 0000f000 fc:00 2673                           /system/lib64/libmetricslogger.so
+70663e6000-70663e7000 rw-p 00010000 fc:00 2673                           /system/lib64/libmetricslogger.so
+70663e7000-7066400000 r--s 00000000 fc:00 110                            /system/fonts/NotoSansLepcha-Regular.ttf
+7066400000-7066800000 rw-p 00000000 00:00 0                              [anon:libc_malloc]
+7066803000-706681e000 r--s 00000000 fc:00 297                            /system/fonts/NotoSansMyanmar-Regular.ttf
+706681e000-7066821000 r--p 00000000 00:00 0                              [anon:cfi shadow]
+7066821000-7066822000 r--p 00000000 00:00 0                              [anon:cfi shadow]
+7066822000-7066b1d000 r--p 00000000 00:00 0                              [anon:cfi shadow]
+7066b1d000-7066b1e000 r--p 00000000 00:00 0                              [anon:cfi shadow]
+7066b1e000-7066ba0000 r--p 00000000 00:00 0                              [anon:cfi shadow]
+7066ba0000-7066ba1000 r--p 00000000 00:00 0                              [anon:cfi shadow]
+7066ba1000-7066ba2000 r--p 00000000 00:00 0                              [anon:cfi shadow]
+7066ba2000-7066ba5000 r--p 00000000 00:00 0                              [anon:cfi shadow]
+7066ba5000-7066ba6000 r--p 00000000 00:00 0                              [anon:cfi shadow]
+7066ba6000-70e681e000 r--p 00000000 00:00 0                              [anon:cfi shadow]
+70e681e000-70e6854000 r-xp 00000000 fc:00 2431                           /system/lib64/libstagefright_foundation.so
+70e6854000-70e6865000 ---p 00000000 00:00 0 
+70e6865000-70e6867000 r--p 0003e000 fc:00 2431                           /system/lib64/libstagefright_foundation.so
+70e6867000-70e686c000 rw-p 00040000 fc:00 2431                           /system/lib64/libstagefright_foundation.so
+70e686d000-70e686e000 ---p 00000000 00:00 0                              [anon:bionic TLS guard]
+70e686e000-70e6871000 rw-p 00000000 00:00 0                              [anon:bionic TLS]
+70e6871000-70e6873000 ---p 00000000 00:00 0                              [anon:bionic TLS guard]
+70e6873000-70e6876000 rw-p 00000000 00:00 0                              [anon:bionic TLS]
+70e6876000-70e6877000 ---p 00000000 00:00 0                              [anon:bionic TLS guard]
+70e6877000-70e688c000 r--s 00000000 fc:00 301                            /system/fonts/NotoSansSinhalaUI-Bold.otf
+70e688c000-70e688e000 r-xp 00000000 fc:00 2943                           /system/lib64/libion.so
+70e688e000-70e68ab000 ---p 00000000 00:00 0 
+70e68ab000-70e68ac000 r--p 0000f000 fc:00 2943                           /system/lib64/libion.so
+70e68ac000-70e68ad000 rw-p 00010000 fc:00 2943                           /system/lib64/libion.so
+70e68ad000-70e68af000 rw-p 00000000 00:05 10282496                       /dev/ashmem/dalvik-indirect ref table (deleted)
+70e68af000-70e68b1000 rw-p 00000000 00:05 10282493                       /dev/ashmem/dalvik-indirect ref table (deleted)
+70e68b1000-70e68ee000 r--s 00000000 fc:00 256                            /system/fonts/NotoSerif-Italic.ttf
+70e68ee000-70e6910000 r-xp 00000000 fc:00 2502                           /system/lib64/libhidlbase.so
+70e6910000-70e692c000 ---p 00000000 00:00 0 
+70e692c000-70e692e000 r--p 0002e000 fc:00 2502                           /system/lib64/libhidlbase.so
+70e692e000-70e692f000 rw-p 00030000 fc:00 2502                           /system/lib64/libhidlbase.so
+70e6930000-70e693f000 r--s 00000000 fc:00 1082                           /system/usr/hyphen-data/hyph-en-us.hyb
+70e693f000-70e6954000 r--s 00000000 fc:00 138                            /system/fonts/NotoSansSinhalaUI-Regular.otf
+70e6954000-70e6978000 r-xp 00000000 fc:00 2482                           /system/lib64/libui.so
+70e6978000-70e6992000 ---p 00000000 00:00 0 
+70e6992000-70e6994000 r--p 0002e000 fc:00 2482                           /system/lib64/libui.so
+70e6994000-70e6995000 rw-p 00030000 fc:00 2482                           /system/lib64/libui.so
+70e6996000-70e69a2000 r--s 00000000 fc:00 1117                           /system/usr/hyphen-data/hyph-en-gb.hyb
+70e69a2000-70e69b7000 r--s 00000000 fc:00 202                            /system/fonts/NotoSerifSinhala-Bold.otf
+70e69b7000-70e69cb000 r--s 00000000 fc:00 124                            /system/fonts/NotoSansOriyaUI-Bold.ttf
+70e69cb000-70e69e1000 r-xp 00000000 fc:00 2537                           /system/lib64/liblog.so
+70e69e1000-70e69fa000 ---p 00000000 00:00 0 
+70e69fa000-70e69fb000 r--p 0001f000 fc:00 2537                           /system/lib64/liblog.so
+70e69fb000-70e69fc000 rw-p 00020000 fc:00 2537                           /system/lib64/liblog.so
+70e69fc000-70e69fe000 rw-p 00000000 00:05 10266158                       /dev/ashmem/dalvik-indirect ref table (deleted)
+70e69fe000-70e69ff000 ---p 00000000 00:00 0                              [anon:bionic TLS guard]
+70e69ff000-70e6a02000 rw-p 00000000 00:00 0                              [anon:bionic TLS]
+70e6a02000-70e6a03000 ---p 00000000 00:00 0                              [anon:bionic TLS guard]
+70e6a03000-70e6a05000 r-xp 00000000 fc:00 2489                           /system/lib64/android.hidl.token@1.0-utils.so
+70e6a05000-70e6a22000 ---p 00000000 00:00 0 
+70e6a22000-70e6a23000 r--p 0000f000 fc:00 2489                           /system/lib64/android.hidl.token@1.0-utils.so
+70e6a23000-70e6a24000 rw-p 00010000 fc:00 2489                           /system/lib64/android.hidl.token@1.0-utils.so
+70e6a25000-70e6a2e000 r--s 00000000 fc:00 1120                           /system/usr/hyphen-data/hyph-ga.hyb
+70e6a2e000-70e6a42000 r--s 00000000 fc:00 109                            /system/fonts/NotoSansOriyaUI-Regular.ttf
+70e6a42000-70e6a59000 r-xp 00000000 fc:00 2446                           /system/lib64/android.hardware.graphics.mapper@2.0.so
+70e6a59000-70e6a6e000 ---p 00000000 00:00 0 
+70e6a6e000-70e6a70000 r--p 0001e000 fc:00 2446                           /system/lib64/android.hardware.graphics.mapper@2.0.so
+70e6a70000-70e6a71000 rw-p 00020000 fc:00 2446                           /system/lib64/android.hardware.graphics.mapper@2.0.so
+70e6a72000-70e6a78000 r--s 00000000 fc:00 1084                           /system/usr/hyphen-data/hyph-et.hyb
+70e6a78000-70e6a9d000 r--s 00000000 fc:00 207                            /system/fonts/NotoSerifTelugu-Bold.ttf
+70e6a9d000-70e6a9f000 r-xp 00000000 fc:00 2330                           /system/lib64/android.hardware.configstore-utils.so
+70e6a9f000-70e6abc000 ---p 00000000 00:00 0 
+70e6abc000-70e6abd000 r--p 0000f000 fc:00 2330                           /system/lib64/android.hardware.configstore-utils.so
+70e6abd000-70e6abe000 rw-p 00010000 fc:00 2330                           /system/lib64/android.hardware.configstore-utils.so
+70e6abe000-70e6ac0000 r--s f8042000 00:10 20630                          /dev/kgsl-3d0
+70e6ac0000-70e6adc000 r--s 00000000 fc:00 172                            /system/fonts/NotoSansTeluguUI-Bold.ttf
+70e6adc000-70e6ae0000 r-xp 00000000 fc:00 2555                           /system/lib64/libstagefright_omx_utils.so
+70e6ae0000-70e6afb000 ---p 00000000 00:00 0 
+70e6afb000-70e6afc000 r--p 0000f000 fc:00 2555                           /system/lib64/libstagefright_omx_utils.so
+70e6afc000-70e6afd000 rw-p 00010000 fc:00 2555                           /system/lib64/libstagefright_omx_utils.so
+70e6afd000-70e6afe000 ---p 00000000 00:00 0                              [anon:thread signal stack guard]
+70e6afe000-70e6b02000 rw-p 00000000 00:00 0                              [anon:thread signal stack]
+70e6b02000-70e6b27000 r--s 00000000 fc:00 271                            /system/fonts/NotoSerifTelugu-Regular.ttf
+70e6b27000-70e6b61000 r-xp 00000000 fc:00 2695                           /system/lib64/libdexfile.so
+70e6b61000-70e6b73000 ---p 00000000 00:00 0 
+70e6b73000-70e6b75000 r--p 0003e000 fc:00 2695                           /system/lib64/libdexfile.so
+70e6b75000-70e6b76000 rw-p 00040000 fc:00 2695                           /system/lib64/libdexfile.so
+70e6b76000-70e6b78000 rw-p 00000000 00:05 10253452                       /dev/ashmem/dalvik-indirect ref table (deleted)
+70e6b78000-70e6b85000 r--s 00000000 fc:00 1080                           /system/usr/hyphen-data/hyph-cu.hyb
+70e6b85000-70e6b96000 r-xp 00000000 fc:00 2957                           /system/lib64/libaudioutils.so
+70e6b96000-70e6bb4000 ---p 00000000 00:00 0 
+70e6bb4000-70e6bb5000 r--p 0001f000 fc:00 2957                           /system/lib64/libaudioutils.so
+70e6bb5000-70e6bb6000 rw-p 00020000 fc:00 2957                           /system/lib64/libaudioutils.so
+70e6bb6000-70e6bb7000 ---p 00000000 00:00 0                              [anon:bionic TLS guard]
+70e6bb7000-70e6bba000 rw-p 00000000 00:00 0                              [anon:bionic TLS]
+70e6bba000-70e6bbb000 ---p 00000000 00:00 0                              [anon:bionic TLS guard]
+70e6bbb000-70e6bd7000 r--s 00000000 fc:00 132                            /system/fonts/NotoSansTeluguUI-Regular.ttf
+70e6bd7000-70e6bdc000 r-xp 00000000 fc:00 2409                           /system/lib64/libprocessgroup.so
+70e6bdc000-70e6bf6000 ---p 00000000 00:00 0 
+70e6bf6000-70e6bf7000 r--p 0000f000 fc:00 2409                           /system/lib64/libprocessgroup.so
+70e6bf7000-70e6bf8000 rw-p 00010000 fc:00 2409                           /system/lib64/libprocessgroup.so
+70e6bf8000-70e6c09000 r--s 00000000 fc:00 79                             /system/fonts/NotoSansNewa-Regular.otf
+70e6c09000-70e6c1c000 r-xp 00000000 fc:00 2329                           /system/lib64/android.hidl.memory.token@1.0.so
+70e6c1c000-70e6c36000 ---p 00000000 00:00 0 
+70e6c36000-70e6c38000 r--p 0001e000 fc:00 2329                           /system/lib64/android.hidl.memory.token@1.0.so
+70e6c38000-70e6c39000 rw-p 00020000 fc:00 2329                           /system/lib64/android.hidl.memory.token@1.0.so
+70e6c3a000-70e6c4f000 r--s 00000000 fc:00 253                            /system/fonts/NotoSansOriya-Bold.ttf
+70e6c4f000-70e6c6b000 r-xp 00000000 fc:00 2407                           /system/lib64/libutils.so
+70e6c6b000-70e6c7e000 ---p 00000000 00:00 0 
+70e6c7e000-70e6c7f000 r--p 0001f000 fc:00 2407                           /system/lib64/libutils.so
+70e6c7f000-70e6c80000 rw-p 00020000 fc:00 2407                           /system/lib64/libutils.so
+70e6c80000-70e6c9d000 r-xp 00000000 fc:00 2934                           /system/lib64/libtinyxml2.so
+70e6c9d000-70e6cba000 ---p 00000000 00:00 0 
+70e6cba000-70e6cbc000 r--p 0001e000 fc:00 2934                           /system/lib64/libtinyxml2.so
+70e6cbc000-70e6cbf000 rw-p 00020000 fc:00 2934                           /system/lib64/libtinyxml2.so
+70e6cbf000-70e6ccf000 r--s 00000000 fc:00 80                             /system/fonts/NotoSansMarchen-Regular.otf
+70e6ccf000-70e6ce0000 r-xp 00000000 fc:00 2655                           /system/lib64/libbase.so
+70e6ce0000-70e6cfe000 ---p 00000000 00:00 0 
+70e6cfe000-70e6cff000 r--p 0001f000 fc:00 2655                           /system/lib64/libbase.so
+70e6cff000-70e6d00000 rw-p 00020000 fc:00 2655                           /system/lib64/libbase.so
+70e6d00000-70e6d09000 r--s 00000000 fc:00 1113                           /system/usr/hyphen-data/hyph-cy.hyb
+70e6d09000-70e6d50000 r-xp 00000000 fc:00 2495                           /system/lib64/libRScpp.so
+70e6d50000-70e6d68000 ---p 00000000 00:00 0 
+70e6d68000-70e6d69000 r--p 0004f000 fc:00 2495                           /system/lib64/libRScpp.so
+70e6d69000-70e6d6a000 rw-p 00050000 fc:00 2495                           /system/lib64/libRScpp.so
+70e6d6b000-70e6d6d000 r--s 00088000 103:1d 1736830                       /data/app/com.google.sample.tunnel-HGGRU03Gu1Mwkf_-RnFmvw==/base.apk
+70e6d6d000-70e6d7d000 r--s 00000000 fc:00 238                            /system/fonts/NotoSansVai-Regular.ttf
+70e6d7d000-70e6d98000 r--s 00000000 fc:00 276                            /system/fonts/NotoSansTelugu-Bold.ttf
+70e6d98000-70e6f2b000 r-xp 00000000 fc:00 2961                           /system/lib64/libicuuc.so
+70e6f2b000-70e6f47000 ---p 00000000 00:00 0 
+70e6f47000-70e6f5c000 r--p 0019b000 fc:00 2961                           /system/lib64/libicuuc.so
+70e6f5c000-70e6f5d000 rw-p 001b0000 fc:00 2961                           /system/lib64/libicuuc.so
+70e6f5d000-70e6f5e000 rw-p 00000000 00:00 0                              [anon:.bss]
+70e6f5f000-70e6f68000 r--s 00000000 fc:00 159                            /system/fonts/NotoSansLinearA-Regular.otf
+70e6f68000-70e6f84000 r--s 00000000 fc:00 170                            /system/fonts/NotoSansTelugu-Regular.ttf
+70e6f84000-70e7058000 r-xp 00000000 fc:00 2356                           /system/lib64/libc.so
+70e7058000-70e706e000 ---p 00000000 00:00 0 
+70e706e000-70e7074000 r--p 000da000 fc:00 2356                           /system/lib64/libc.so
+70e7074000-70e7076000 rw-p 000e0000 fc:00 2356                           /system/lib64/libc.so
+70e7076000-70e7077000 rw-p 00000000 00:00 0                              [anon:.bss]
+70e7077000-70e7078000 r--p 00000000 00:00 0                              [anon:.bss]
+70e7078000-70e7080000 rw-p 00000000 00:00 0                              [anon:.bss]
+70e7080000-70e7087000 r--s 00000000 fc:00 102                            /system/fonts/NotoSansSharada-Regular.otf
+70e7087000-70e708e000 r-xp 00000000 fc:00 2378                           /system/lib64/libheif.so
+70e708e000-70e70a4000 ---p 00000000 00:00 0 
+70e70a4000-70e70a6000 r--p 0000e000 fc:00 2378                           /system/lib64/libheif.so
+70e70a6000-70e70a7000 rw-p 00010000 fc:00 2378                           /system/lib64/libheif.so
+70e70a7000-70e70a9000 r--s 00000000 fc:00 1116                           /system/usr/hyphen-data/hyph-sl.hyb
+70e70a9000-70e70ab000 r--s 00000000 fc:00 1147                           /system/usr/hyphen-data/hyph-mn-cyrl.hyb
+70e70ab000-70e70c5000 r--s 00000000 fc:00 291                            /system/fonts/NotoSansBengaliUI-Bold.ttf
+70e70c5000-70e70ea000 r-xp 00000000 fc:00 2545                           /system/lib64/libEGL.so
+70e70ea000-70e7109000 ---p 00000000 00:00 0 
+70e7109000-70e710d000 r--p 0002c000 fc:00 2545                           /system/lib64/libEGL.so
+70e710d000-70e710e000 rw-p 00030000 fc:00 2545                           /system/lib64/libEGL.so
+70e710e000-70e7115000 rw-p 00000000 00:00 0                              [anon:.bss]
+70e7115000-70e7119000 r--s 00000000 fc:00 1143                           /system/usr/hyphen-data/hyph-es.hyb
+70e7119000-70e712e000 r--s 00000000 fc:00 71                             /system/fonts/NotoSansOriya-Regular.ttf
+70e712e000-70e717a000 r--s 00000000 fc:00 272                            /system/fonts/Roboto-Thin.ttf
+70e717a000-70e71db000 r-xp 00000000 fc:00 2393                           /system/lib64/libpdx_default_transport.so
+70e71db000-70e71f7000 ---p 00000000 00:00 0 
+70e71f7000-70e71f9000 r--p 0006e000 fc:00 2393                           /system/lib64/libpdx_default_transport.so
+70e71f9000-70e71fa000 rw-p 00070000 fc:00 2393                           /system/lib64/libpdx_default_transport.so
+70e71fa000-70e71fb000 rw-p 00000000 00:00 0                              [anon:.bss]
+70e71fc000-70e71fe000 r--s 00000000 fc:00 1107                           /system/usr/hyphen-data/hyph-fr.hyb
+70e71fe000-70e7200000 r--s 00000000 fc:00 1097                           /system/usr/hyphen-data/hyph-da.hyb
+70e7200000-70e7223000 r-xp 00000000 fc:00 2380                           /system/lib64/libminikin.so
+70e7223000-70e723e000 ---p 00000000 00:00 0 
+70e723e000-70e723f000 r--p 0002f000 fc:00 2380                           /system/lib64/libminikin.so
+70e723f000-70e7240000 rw-p 00030000 fc:00 2380                           /system/lib64/libminikin.so
+70e7241000-70e724d000 r--s 00000000 fc:00 179                            /system/fonts/NotoSansTaiTham-Regular.ttf
+70e724d000-70e725c000 r-xp 00000000 fc:00 2527                           /system/lib64/libmediautils.so
+70e725c000-70e7279000 ---p 00000000 00:00 0 
+70e7279000-70e727b000 r--p 0001e000 fc:00 2527                           /system/lib64/libmediautils.so
+70e727b000-70e727c000 rw-p 00020000 fc:00 2527                           /system/lib64/libmediautils.so
+70e727d000-70e7283000 r--s 00000000 fc:00 136                            /system/fonts/NotoSansMiao-Regular.otf
+70e7283000-70e74d2000 r-xp 00000000 fc:00 2349                           /system/lib64/libicui18n.so
+70e74d2000-70e74e6000 ---p 00000000 00:00 0 
+70e74e6000-70e74fa000 r--p 0025c000 fc:00 2349                           /system/lib64/libicui18n.so
+70e74fa000-70e74fb000 rw-p 00270000 fc:00 2349                           /system/lib64/libicui18n.so
+70e74fc000-70e74ff000 r--s 00000000 103:1d 1474562                       /data/resource-cache/vendor@overlay@framework-res__auto_generated_rro.apk@idmap
+70e74ff000-70e750c000 r--s 00000000 fc:00 126                            /system/fonts/NotoSansSyriacWestern-Regular.ttf
+70e750c000-70e751b000 r-xp 00000000 fc:00 2466                           /system/lib64/libmediaextractor.so
+70e751b000-70e753a000 ---p 00000000 00:00 0 
+70e753a000-70e753b000 r--p 0000f000 fc:00 2466                           /system/lib64/libmediaextractor.so
+70e753b000-70e753c000 rw-p 00010000 fc:00 2466                           /system/lib64/libmediaextractor.so
+70e753d000-70e7540000 r--s 00000000 fc:00 107                            /system/fonts/NotoSansPauCinHau-Regular.otf
+70e7540000-70e7593000 r-xp 00000000 fc:00 2363                           /system/lib64/libandroidfw.so
+70e7593000-70e75ac000 ---p 00000000 00:00 0 
+70e75ac000-70e75af000 r--p 0005d000 fc:00 2363                           /system/lib64/libandroidfw.so
+70e75af000-70e75b0000 rw-p 00060000 fc:00 2363                           /system/lib64/libandroidfw.so
+70e75b0000-70e75b2000 r--s 00000000 fc:00 1083                           /system/usr/hyphen-data/hyph-be.hyb
+70e75b2000-70e75cd000 r--s 00000000 fc:00 270                            /system/fonts/NotoSansBengaliUI-Regular.ttf
+70e75cd000-70e75cf000 r-xp 00000000 fc:00 2701                           /system/lib64/libmemtrack.so
+70e75cf000-70e75ec000 ---p 00000000 00:00 0 
+70e75ec000-70e75ed000 r--p 0000f000 fc:00 2701                           /system/lib64/libmemtrack.so
+70e75ed000-70e75ee000 rw-p 00010000 fc:00 2701                           /system/lib64/libmemtrack.so
+70e75ee000-70e75f0000 r--s 00000000 fc:00 209                            /system/fonts/NotoSansSoraSompeng-Regular.otf
+70e75f0000-70e760d000 r--s 00000000 fc:00 243                            /system/fonts/NotoSerifBengali-Bold.ttf
+70e760d000-70e7613000 r-xp 00000000 fc:00 2667                           /system/lib64/libutilscallstack.so
+70e7613000-70e762c000 ---p 00000000 00:00 0 
+70e762c000-70e762d000 r--p 0000f000 fc:00 2667                           /system/lib64/libutilscallstack.so
+70e762d000-70e762e000 rw-p 00010000 fc:00 2667                           /system/lib64/libutilscallstack.so
+70e762e000-70e7632000 r--s 00000000 fc:00 99                             /system/fonts/NotoSansPahawhHmong-Regular.otf
+70e7632000-70e764f000 r--s 00000000 fc:00 205                            /system/fonts/NotoSerifBengali-Regular.ttf
+70e764f000-70e7661000 r-xp 00000000 fc:00 2710                           /system/lib64/libcutils.so
+70e7661000-70e767d000 ---p 00000000 00:00 0 
+70e767d000-70e767f000 r--p 0001e000 fc:00 2710                           /system/lib64/libcutils.so
+70e767f000-70e7680000 rw-p 00020000 fc:00 2710                           /system/lib64/libcutils.so
+70e7680000-70e7683000 r--s 00000000 fc:00 257                            /system/fonts/NotoSansPalmyrene-Regular.otf
+70e7683000-70e7697000 r--s 00000000 fc:00 78                             /system/fonts/NotoSansKannadaUI-Bold.ttf
+70e7697000-70e769a000 r-xp 00000000 fc:00 2362                           /system/lib64/libstagefright_http_support.so
+70e769a000-70e76b6000 ---p 00000000 00:00 0 
+70e76b6000-70e76b7000 r--p 0000f000 fc:00 2362                           /system/lib64/libstagefright_http_support.so
+70e76b7000-70e76b8000 rw-p 00010000 fc:00 2362                           /system/lib64/libstagefright_http_support.so
+70e76b8000-70e76b9000 rw-p 00000000 00:00 0                              [anon:linker_alloc_lob]
+70e76b9000-70e76be000 r--s 00000000 fc:00 165                            /system/fonts/NotoSansMeroitic-Regular.otf
+70e76be000-70e76cb000 r--s 00000000 fc:00 112                            /system/fonts/NotoSansSyriacEastern-Regular.ttf
+70e76cb000-70e76de000 r-xp 00000000 fc:00 2343                           /system/lib64/libsensor.so
+70e76de000-70e76f5000 ---p 00000000 00:00 0 
+70e76f5000-70e76f8000 r--p 0001d000 fc:00 2343                           /system/lib64/libsensor.so
+70e76f8000-70e76f9000 rw-p 00020000 fc:00 2343                           /system/lib64/libsensor.so
+70e76f9000-70e76fc000 r--s 00000000 fc:00 157                            /system/fonts/NotoSansOldPermic-Regular.otf
+70e76fc000-70e7708000 r--s 00000000 fc:00 189                            /system/fonts/NotoSansSyriacEstrangela-Regular.ttf
+70e7708000-70e771d000 r-xp 00000000 fc:00 2339                           /system/lib64/android.hidl.token@1.0.so
+70e771d000-70e7735000 ---p 00000000 00:00 0 
+70e7735000-70e7737000 r--p 0001e000 fc:00 2339                           /system/lib64/android.hidl.token@1.0.so
+70e7737000-70e7738000 rw-p 00020000 fc:00 2339                           /system/lib64/android.hidl.token@1.0.so
+70e7738000-70e7739000 r--p 00000000 00:00 0                              [anon:linker_alloc]
+70e7739000-70e773b000 r--s 00000000 fc:00 267                            /system/fonts/NotoSansOldNorthArabian-Regular.otf
+70e773b000-70e7740000 r--s 00000000 fc:00 208                            /system/fonts/NotoSansManichaean-Regular.otf
+70e7740000-70e775c000 r--s 00000000 fc:00 118                            /system/fonts/NotoSansGujaratiUI-Bold.ttf
+70e775c000-70e7767000 r-xp 00000000 fc:00 2525                           /system/lib64/libappfuse.so
+70e7767000-70e777b000 ---p 00000000 00:00 0 
+70e777b000-70e777c000 r--p 0000f000 fc:00 2525                           /system/lib64/libappfuse.so
+70e777c000-70e777d000 rw-p 00010000 fc:00 2525                           /system/lib64/libappfuse.so
+70e777e000-70e7795000 r--s 00000000 fc:00 250                            /system/fonts/NotoSerifKannada-Bold.ttf
+70e7795000-70e7798000 r-xp 00000000 fc:00 2413                           /system/lib64/libpackagelistparser.so
+70e7798000-70e77b4000 ---p 00000000 00:00 0 
+70e77b4000-70e77b5000 r--p 0000f000 fc:00 2413                           /system/lib64/libpackagelistparser.so
+70e77b5000-70e77b6000 rw-p 00010000 fc:00 2413                           /system/lib64/libpackagelistparser.so
+70e77b6000-70e77b8000 r--s 00000000 fc:00 147                            /system/fonts/NotoSansNabataean-Regular.otf
+70e77b8000-70e77ba000 r--s 00000000 fc:00 146                            /system/fonts/NotoSansMultani-Regular.otf
+70e77ba000-70e77c1000 r--s 00000000 fc:00 214                            /system/fonts/NotoSansPhagsPa-Regular.ttf
+70e77c1000-70e77de000 r--s 00000000 fc:00 90                             /system/fonts/NotoSansGujaratiUI-Regular.ttf
+70e77de000-70e77e0000 r-xp 00000000 fc:00 2430                           /system/lib64/libdl.so
+70e77e0000-70e77fd000 ---p 00000000 00:00 0 
+70e77fd000-70e77fe000 r--p 0000f000 fc:00 2430                           /system/lib64/libdl.so
+70e77fe000-70e77ff000 r--p 00000000 00:00 0                              [anon:.bss]
+70e7800000-70e7809000 r--s 00000000 fc:00 258                            /system/fonts/NotoSansSymbols-Regular-Subsetted2.ttf
+70e7809000-70e7857000 r-xp 00000000 fc:00 2560                           /system/lib64/libjpeg.so
+70e7857000-70e7868000 ---p 00000000 00:00 0 
+70e7868000-70e7869000 r--p 0004f000 fc:00 2560                           /system/lib64/libjpeg.so
+70e7869000-70e786a000 rw-p 00050000 fc:00 2560                           /system/lib64/libjpeg.so
+70e786a000-70e7887000 r--s 00000000 fc:00 237                            /system/fonts/NotoSansGujarati-Bold.ttf
+70e7887000-70e788a000 r-xp 00000000 fc:00 2608                           /system/lib64/libnetd_client.so
+70e788a000-70e78a6000 ---p 00000000 00:00 0 
+70e78a6000-70e78a7000 r--p 0000f000 fc:00 2608                           /system/lib64/libnetd_client.so
+70e78a7000-70e78a8000 rw-p 00010000 fc:00 2608                           /system/lib64/libnetd_client.so
+70e78a8000-70e78aa000 r--s 00000000 fc:00 290                            /system/fonts/NotoSansMro-Regular.otf
+70e78aa000-70e78b9000 r--s 00000000 fc:00 84                             /system/fonts/NotoSansLinearB-Regular.ttf
+70e78b9000-70e78d7000 r--s 00000000 fc:00 287                            /system/fonts/NotoSansGujarati-Regular.ttf
+70e78d7000-70e78d8000 r-xp 00000000 fc:00 2651                           /system/lib64/libvndksupport.so
+70e78d8000-70e78f6000 ---p 00000000 00:00 0 
+70e78f6000-70e78f7000 r--p 0000f000 fc:00 2651                           /system/lib64/libvndksupport.so
+70e78f7000-70e78f8000 rw-p 00010000 fc:00 2651                           /system/lib64/libvndksupport.so
+70e78f9000-70e78fc000 r--s 00000000 fc:00 178                            /system/fonts/NotoSansTaiLe-Regular.ttf
+70e78fc000-70e7900000 r--s 00000000 fc:00 163                            /system/fonts/NotoSansTifinagh-Regular.ttf
+70e7900000-70e7906000 r-xp 00000000 fc:00 2659                           /system/lib64/libnativeloader.so
+70e7906000-70e791f000 ---p 00000000 00:00 0 
+70e791f000-70e7920000 r--p 0000f000 fc:00 2659                           /system/lib64/libnativeloader.so
+70e7920000-70e7921000 rw-p 00010000 fc:00 2659                           /system/lib64/libnativeloader.so
+70e7921000-70e7923000 r--s 00000000 fc:00 268                            /system/fonts/NotoSansHatran-Regular.otf
+70e7923000-70e7927000 r--s 00000000 fc:00 195                            /system/fonts/NotoSansTaiViet-Regular.ttf
+70e7927000-70e7945000 r--s 00000000 fc:00 139                            /system/fonts/NotoSansDevanagariUI-Bold.ttf
+70e7945000-70e79a3000 r-xp 00000000 fc:00 2450                           /system/lib64/libharfbuzz_ng.so
+70e79a3000-70e79b3000 ---p 00000000 00:00 0 
+70e79b3000-70e79b5000 r--p 0005e000 fc:00 2450                           /system/lib64/libharfbuzz_ng.so
+70e79b5000-70e79b6000 rw-p 00060000 fc:00 2450                           /system/lib64/libharfbuzz_ng.so
+70e79b6000-70e79c5000 r--s 00000000 fc:00 111                            /system/fonts/NotoSansKaithi-Regular.ttf
+70e79c5000-70e7a92000 r-xp 00000000 fc:00 2332                           /system/lib64/libc++.so
+70e7a92000-70e7aae000 ---p 00000000 00:00 0 
+70e7aae000-70e7ab8000 r--p 000d6000 fc:00 2332                           /system/lib64/libc++.so
+70e7ab8000-70e7ab9000 rw-p 000e0000 fc:00 2332                           /system/lib64/libc++.so
+70e7ab9000-70e7abc000 rw-p 00000000 00:00 0                              [anon:.bss]
+70e7abc000-70e7abe000 r--s 00000000 fc:00 73                             /system/fonts/NotoSansBassaVah-Regular.otf
+70e7abe000-70e7ac8000 r--s 00000000 fc:00 254                            /system/fonts/NotoSansJavanese-Regular.ttf
+70e7ac8000-70e7acb000 r-xp 00000000 fc:00 2660                           /system/lib64/libnativebridge.so
+70e7acb000-70e7ae7000 ---p 00000000 00:00 0 
+70e7ae7000-70e7ae8000 r--p 0000f000 fc:00 2660                           /system/lib64/libnativebridge.so
+70e7ae8000-70e7ae9000 rw-p 00010000 fc:00 2660                           /system/lib64/libnativebridge.so
+70e7ae9000-70e7aee000 r--s 00000000 fc:00 158                            /system/fonts/NotoSansSaurashtra-Regular.ttf
+70e7aee000-70e7b0f000 r--s 00000000 fc:00 82                             /system/fonts/NotoSansDevanagari-Bold.ttf
+70e7b0f000-70e7b2e000 r-xp 00000000 fc:00 2612                           /system/lib64/libpcre2.so
+70e7b2e000-70e7b3e000 ---p 00000000 00:00 0 
+70e7b3e000-70e7b3f000 r--p 0001f000 fc:00 2612                           /system/lib64/libpcre2.so
+70e7b3f000-70e7b40000 rw-p 00020000 fc:00 2612                           /system/lib64/libpcre2.so
+70e7b40000-70e7bcc000 r-xp 00000000 fc:00 2975                           /system/lib64/libgui.so
+70e7bcc000-70e7be2000 ---p 00000000 00:00 0 
+70e7be2000-70e7bf5000 r--p 0008d000 fc:00 2975                           /system/lib64/libgui.so
+70e7bf5000-70e7bf6000 rw-p 000a0000 fc:00 2975                           /system/lib64/libgui.so
+70e7bf6000-70e7bf7000 r--p 00000000 00:00 0                              [anon:linker_alloc]
+70e7bf7000-70e7bf9000 r--s 00000000 fc:00 228                            /system/fonts/NotoSansUgaritic-Regular.ttf
+70e7bf9000-70e7c08000 r--s 00000000 fc:00 89                             /system/fonts/NotoSansCherokee-Regular.ttf
+70e7c08000-70e7dea000 r-xp 00000000 fc:00 2441                           /system/lib64/libandroid_runtime.so
+70e7dea000-70e7e04000 ---p 00000000 00:00 0 
+70e7e04000-70e7e23000 r--p 001e1000 fc:00 2441                           /system/lib64/libandroid_runtime.so
+70e7e23000-70e7e24000 rw-p 00200000 fc:00 2441                           /system/lib64/libandroid_runtime.so
+70e7e24000-70e7e28000 rw-p 00000000 00:00 0                              [anon:.bss]
+70e7e29000-70e7e66000 r--s 00000000 fc:00 74                             /system/fonts/NotoSerif-Bold.ttf
+70e7e66000-70e7ed0000 r-xp 00000000 fc:00 2547                           /system/lib64/libclang_rt.ubsan_standalone-aarch64-android.so
+70e7ed0000-70e7edf000 ---p 00000000 00:00 0 
+70e7edf000-70e7ee1000 r--p 00069000 fc:00 2547                           /system/lib64/libclang_rt.ubsan_standalone-aarch64-android.so
+70e7ee1000-70e7ee4000 rw-p 0006b000 fc:00 2547                           /system/lib64/libclang_rt.ubsan_standalone-aarch64-android.so
+70e7ee4000-70e89f6000 rw-p 00000000 00:00 0                              [anon:.bss]
+70e89f6000-70e89fa000 r--s 00000000 fc:00 127                            /system/fonts/NotoSansSylotiNagri-Regular.ttf
+70e89fa000-70e8a06000 r--s 00000000 fc:00 93                             /system/fonts/NotoSansCanadianAboriginal-Regular.ttf
+70e8a06000-70e8a1b000 r-xp 00000000 fc:00 2460                           /system/lib64/android.hardware.graphics.allocator@2.0.so
+70e8a1b000-70e8a33000 ---p 00000000 00:00 0 
+70e8a33000-70e8a35000 r--p 0001e000 fc:00 2460                           /system/lib64/android.hardware.graphics.allocator@2.0.so
+70e8a35000-70e8a36000 rw-p 00020000 fc:00 2460                           /system/lib64/android.hardware.graphics.allocator@2.0.so
+70e8a36000-70e8a55000 r--s 00000000 fc:00 145                            /system/fonts/NotoSansDevanagariUI-Regular.ttf
+70e8a55000-70e8a61000 r-xp 00000000 fc:00 2540                           /system/lib64/libstagefright_xmlparser.so
+70e8a61000-70e8a74000 ---p 00000000 00:00 0 
+70e8a74000-70e8a75000 r--p 0000f000 fc:00 2540                           /system/lib64/libstagefright_xmlparser.so
+70e8a75000-70e8a76000 rw-p 00010000 fc:00 2540                           /system/lib64/libstagefright_xmlparser.so
+70e8a76000-70e8a78000 r--s 00000000 fc:00 293                            /system/fonts/NotoSansTagbanwa-Regular.ttf
+70e8a78000-70e8a8f000 r--s 00000000 fc:00 161                            /system/fonts/NotoSerifKannada-Regular.ttf
+70e8a8f000-70e8b23000 r-xp 00000000 fc:00 2633                           /system/lib64/libaudioclient.so
+70e8b23000-70e8b37000 ---p 00000000 00:00 0 
+70e8b37000-70e8b49000 r--p 0009e000 fc:00 2633                           /system/lib64/libaudioclient.so
+70e8b49000-70e8b55000 rw-p 000b0000 fc:00 2633                           /system/lib64/libaudioclient.so
+70e8b55000-70e8b9f000 r--s 00000000 fc:00 83                             /system/fonts/RobotoCondensed-Light.ttf
+70e8b9f000-70e8ba1000 r-xp 00000000 fc:00 2520                           /system/lib64/libhardware_legacy.so
+70e8ba1000-70e8bbe000 ---p 00000000 00:00 0 
+70e8bbe000-70e8bbf000 r--p 0000f000 fc:00 2520                           /system/lib64/libhardware_legacy.so
+70e8bbf000-70e8bc0000 rw-p 00010000 fc:00 2520                           /system/lib64/libhardware_legacy.so
+70e8bc0000-70e8be0000 r-xp 00000000 fc:00 2410                           /system/lib64/android.hidl.memory@1.0.so
+70e8be0000-70e8bfa000 ---p 00000000 00:00 0 
+70e8bfa000-70e8bfd000 r--p 0002d000 fc:00 2410                           /system/lib64/android.hidl.memory@1.0.so
+70e8bfd000-70e8bfe000 rw-p 00030000 fc:00 2410                           /system/lib64/android.hidl.memory@1.0.so
+70e8bfe000-70e8bff000 r--p 00000000 00:00 0                              [anon:linker_alloc]
+70e8bff000-70e8c02000 r--s 00000000 fc:00 273                            /system/fonts/NotoSansSundanese-Regular.ttf
+70e8c02000-70e8c0f000 r--s 00000000 fc:00 115                            /system/fonts/NotoSansAdlam-Regular.ttf
+70e8c0f000-70e8c18000 r-xp 00000000 fc:00 2350                           /system/lib64/libnetdutils.so
+70e8c18000-70e8c2e000 ---p 00000000 00:00 0 
+70e8c2e000-70e8c2f000 r--p 0000f000 fc:00 2350                           /system/lib64/libnetdutils.so
+70e8c2f000-70e8c30000 rw-p 00010000 fc:00 2350                           /system/lib64/libnetdutils.so
+70e8c30000-70e8c44000 r--s 00000000 fc:00 283                            /system/fonts/NotoSansKannadaUI-Regular.ttf
+70e8c44000-70e8c45000 r-xp 00000000 fc:00 2926                           /system/lib64/libhidlallocatorutils.so
+70e8c45000-70e8c63000 ---p 00000000 00:00 0 
+70e8c63000-70e8c64000 r--p 0000f000 fc:00 2926                           /system/lib64/libhidlallocatorutils.so
+70e8c64000-70e8c65000 rw-p 00010000 fc:00 2926                           /system/lib64/libhidlallocatorutils.so
+70e8c65000-70e8c67000 r--s 00000000 fc:00 65                             /system/fonts/NotoSansTagalog-Regular.ttf
+70e8c67000-70e8c70000 r--s 00000000 fc:00 294                            /system/fonts/NotoSansChakma-Regular.ttf
+70e8c70000-70e8c92000 r--s 00000000 fc:00 116                            /system/fonts/NotoSansDevanagari-Regular.ttf
+70e8c92000-70e8c94000 r-xp 00000000 fc:00 2501                           /system/lib64/libsync.so
+70e8c94000-70e8cb1000 ---p 00000000 00:00 0 
+70e8cb1000-70e8cb2000 r--p 0000f000 fc:00 2501                           /system/lib64/libsync.so
+70e8cb2000-70e8cb3000 rw-p 00010000 fc:00 2501                           /system/lib64/libsync.so
+70e8cb3000-70e8cc6000 r--s 00000000 fc:00 196                            /system/fonts/NotoSerifSinhala-Regular.otf
+70e8cc6000-70e8d5c000 r-xp 00000000 fc:00 2403                           /system/lib64/libmedia.so
+70e8d5c000-70e8d70000 ---p 00000000 00:00 0 
+70e8d70000-70e8d88000 r--p 00098000 fc:00 2403                           /system/lib64/libmedia.so
+70e8d88000-70e8d95000 rw-p 000b0000 fc:00 2403                           /system/lib64/libmedia.so
+70e8d95000-70e8d96000 r--p 00000000 00:00 0                              [anon:atexit handlers]
+70e8d96000-70e8d99000 r--s 00000000 fc:00 247                            /system/fonts/NotoSansSamaritan-Regular.ttf
+70e8d99000-70e8dad000 r--s 00000000 fc:00 108                            /system/fonts/NotoSansKannada-Bold.ttf
+70e8dad000-70e8dcd000 r--s 00000000 fc:00 303                            /system/fonts/NotoSerifEthiopic-Bold.otf
+70e8dcd000-70e8de5000 r-xp 00000000 fc:00 2954                           /system/lib64/libGLESv2.so
+70e8de5000-70e8dfc000 ---p 00000000 00:00 0 
+70e8dfc000-70e8dfd000 r--p 0001f000 fc:00 2954                           /system/lib64/libGLESv2.so
+70e8dfd000-70e8dfe000 rw-p 00020000 fc:00 2954                           /system/lib64/libGLESv2.so
+70e8dfe000-70e8e06000 r--s 00000000 fc:00 265                            /system/fonts/NotoSansBalinese-Regular.ttf
+70e8e06000-70e8e0e000 r--s 00000000 fc:00 219                            /system/fonts/NotoSansLaoUI-Bold.ttf
+70e8e0e000-70e8e12000 r-xp 00000000 fc:00 2617                           /system/lib64/libdebuggerd_client.so
+70e8e12000-70e8e2d000 ---p 00000000 00:00 0 
+70e8e2d000-70e8e2e000 r--p 0000f000 fc:00 2617                           /system/lib64/libdebuggerd_client.so
+70e8e2e000-70e8e2f000 rw-p 00010000 fc:00 2617                           /system/lib64/libdebuggerd_client.so
+70e8e2f000-70e8e4b000 r--s 00000000 fc:00 211                            /system/fonts/NotoSerifEthiopic-Regular.otf
+70e8e4b000-70e8e5e000 r-xp 00000000 fc:00 2484                           /system/lib64/android.hardware.memtrack@1.0.so
+70e8e5e000-70e8e78000 ---p 00000000 00:00 0 
+70e8e78000-70e8e7a000 r--p 0001e000 fc:00 2484                           /system/lib64/android.hardware.memtrack@1.0.so
+70e8e7a000-70e8e7b000 rw-p 00020000 fc:00 2484                           /system/lib64/android.hardware.memtrack@1.0.so
+70e8e7b000-70e8e7d000 r--s 00000000 fc:00 261                            /system/fonts/NotoSansShavian-Regular.ttf
+70e8e7d000-70e8e80000 r--s 00000000 fc:00 204                            /system/fonts/NotoSansRunic-Regular.ttf
+70e8e80000-70e8ea1000 r-xp 00000000 fc:00 2512                           /system/lib64/android.hardware.configstore@1.0.so
+70e8ea1000-70e8ebb000 ---p 00000000 00:00 0 
+70e8ebb000-70e8ebe000 r--p 0002d000 fc:00 2512                           /system/lib64/android.hardware.configstore@1.0.so
+70e8ebe000-70e8ebf000 rw-p 00030000 fc:00 2512                           /system/lib64/android.hardware.configstore@1.0.so
+70e8ebf000-70e8ee3000 r--s 00000000 fc:00 226                            /system/fonts/NotoSansEthiopic-Bold.ttf
+70e8ee3000-70e8f1a000 r-xp 00000000 fc:00 2337                           /system/lib64/libm.so
+70e8f1a000-70e8f32000 ---p 00000000 00:00 0 
+70e8f32000-70e8f33000 r--p 0003f000 fc:00 2337                           /system/lib64/libm.so
+70e8f33000-70e8f34000 rw-p 00040000 fc:00 2337                           /system/lib64/libm.so
+70e8f34000-70e8f36000 r--s 00000000 fc:00 133                            /system/fonts/NotoSansRejang-Regular.ttf
+70e8f36000-70e8f38000 r--s 00000000 fc:00 69                             /system/fonts/NotoSansPhoenician-Regular.ttf
+70e8f38000-70e8f40000 r--s 00000000 fc:00 245                            /system/fonts/NotoSansLaoUI-Regular.ttf
+70e8f40000-70e8f45000 r-xp 00000000 fc:00 2341                           /system/lib64/libstagefright_codecbase.so
+70e8f45000-70e8f5f000 ---p 00000000 00:00 0 
+70e8f5f000-70e8f60000 r--p 0000f000 fc:00 2341                           /system/lib64/libstagefright_codecbase.so
+70e8f60000-70e8f61000 rw-p 00010000 fc:00 2341                           /system/lib64/libstagefright_codecbase.so
+70e8f61000-70e8f62000 r--p 00000000 00:00 0                              [anon:atexit handlers]
+70e8f62000-70e8f66000 r--s 00000000 fc:00 225                            /system/fonts/NotoSansOldPersian-Regular.ttf
+70e8f66000-70e8f89000 r--s 00000000 fc:00 167                            /system/fonts/NotoSansEthiopic-Regular.ttf
+70e8f89000-70e8f92000 r-xp 00000000 fc:00 2515                           /system/lib64/libGLESv1_CM.so
+70e8f92000-70e8fa8000 ---p 00000000 00:00 0 
+70e8fa8000-70e8fa9000 r--p 0000f000 fc:00 2515                           /system/lib64/libGLESv1_CM.so
+70e8fa9000-70e8faa000 rw-p 00010000 fc:00 2515                           /system/lib64/libGLESv1_CM.so
+70e8faa000-70e8fad000 r--s 00000000 fc:00 206                            /system/fonts/NotoSansOsage-Regular.ttf
+70e8fad000-70e8fb5000 r--s 00000000 fc:00 121                            /system/fonts/NotoSerifLao-Bold.ttf
+70e8fb5000-70e8fd3000 r--s 00000000 fc:00 68                             /system/fonts/NotoNaskhArabicUI-Bold.ttf
+70e8fd3000-70e9010000 r-xp 00000000 fc:00 2600                           /system/lib64/android.hardware.cas@1.0.so
+70e9010000-70e9026000 ---p 00000000 00:00 0 
+70e9026000-70e902c000 r--p 0004a000 fc:00 2600                           /system/lib64/android.hardware.cas@1.0.so
+70e902c000-70e902d000 rw-p 00050000 fc:00 2600                           /system/lib64/android.hardware.cas@1.0.so
+70e902d000-70e902e000 r--s 00000000 00:05 31475                          /dev/ashmem/5c7d41a6-003d-45a5-9e3b-2d34c5829a2d (deleted)
+70e902e000-70e9043000 r--s 00000000 fc:00 120                            /system/fonts/NotoSansKannada-Regular.ttf
+70e9043000-70e9067000 r-xp 00000000 fc:00 2729                           /system/lib64/libexpat.so
+70e9067000-70e9081000 ---p 00000000 00:00 0 
+70e9081000-70e9083000 r--p 0002e000 fc:00 2729                           /system/lib64/libexpat.so
+70e9083000-70e9084000 rw-p 00030000 fc:00 2729                           /system/lib64/libexpat.so
+70e9084000-70e9086000 r--s 00000000 fc:00 155                            /system/fonts/NotoSansOsmanya-Regular.ttf
+70e9086000-70e90c3000 r--s 00000000 fc:00 91                             /system/fonts/NotoSerif-Regular.ttf
+70e90c3000-70e91ce000 r-xp 00000000 fc:00 2647                           /system/lib64/libcrypto.so
+70e91ce000-70e91e2000 ---p 00000000 00:00 0 
+70e91e2000-70e91f3000 r--p 0010f000 fc:00 2647                           /system/lib64/libcrypto.so
+70e91f3000-70e91f4000 rw-p 00120000 fc:00 2647                           /system/lib64/libcrypto.so
+70e91f4000-70e91f5000 rw-p 00000000 00:00 0                              [anon:.bss]
+70e91f5000-70e91fa000 r--s 00000000 fc:00 149                            /system/fonts/NotoSansNKo-Regular.ttf
+70e91fa000-70e9202000 r--s 00000000 fc:00 198                            /system/fonts/NotoSerifLao-Regular.ttf
+70e9202000-70e921b000 r-xp 00000000 fc:00 2682                           /system/lib64/libmedia_helper.so
+70e921b000-70e922d000 ---p 00000000 00:00 0 
+70e922d000-70e9230000 r--p 0001d000 fc:00 2682                           /system/lib64/libmedia_helper.so
+70e9230000-70e9231000 rw-p 00020000 fc:00 2682                           /system/lib64/libmedia_helper.so
+70e9231000-70e924a000 r--s 00000000 fc:00 232                            /system/fonts/NotoSansBengali-Bold.ttf
+70e924a000-70e9256000 r-xp 00000000 fc:00 2467                           /system/lib64/libsoundtrigger.so
+70e9256000-70e9272000 ---p 00000000 00:00 0 
+70e9272000-70e9276000 r--p 0000c000 fc:00 2467                           /system/lib64/libsoundtrigger.so
+70e9276000-70e9277000 rw-p 00010000 fc:00 2467                           /system/lib64/libsoundtrigger.so
+70e9277000-70e9278000 r--s 00000000 fc:01 1177                           /vendor/overlay/Pixel/PixelThemeOverlay.apk
+70e9278000-70e927c000 r--s 00000000 fc:00 215                            /system/fonts/NotoSansNewTaiLue-Regular.ttf
+70e927c000-70e929a000 r--s 00000000 fc:00 235                            /system/fonts/NotoNaskhArabicUI-Regular.ttf
+70e929a000-70e929b000 r-xp 00000000 fc:00 2375                           /system/lib64/android.hardware.graphics.common@1.1.so
+70e929b000-70e92b9000 ---p 00000000 00:00 0 
+70e92b9000-70e92ba000 r--p 0000f000 fc:00 2375                           /system/lib64/android.hardware.graphics.common@1.1.so
+70e92ba000-70e92bb000 rw-p 00010000 fc:00 2375                           /system/lib64/android.hardware.graphics.common@1.1.so
+70e92bb000-70e92da000 r--s 00000000 fc:00 281                            /system/fonts/GoogleSans-BoldItalic.ttf
+70e92da000-70e9302000 r-xp 00000000 fc:00 2529                           /system/lib64/libinput.so
+70e9302000-70e931b000 ---p 00000000 00:00 0 
+70e931b000-70e9322000 r--p 00029000 fc:00 2529                           /system/lib64/libinput.so
+70e9322000-70e9323000 rw-p 00030000 fc:00 2529                           /system/lib64/libinput.so
+70e9323000-70e9340000 r--s 00000000 fc:00 130                            /system/fonts/NotoNaskhArabic-Bold.ttf
+70e9340000-70e934b000 r-xp 00000000 fc:00 2451                           /system/lib64/libbpf.so
+70e934b000-70e935f000 ---p 00000000 00:00 0 
+70e935f000-70e9360000 r--p 0000f000 fc:00 2451                           /system/lib64/libbpf.so
+70e9360000-70e9361000 rw-p 00010000 fc:00 2451                           /system/lib64/libbpf.so
+70e9361000-70e9367000 r--s 00000000 fc:00 96                             /system/fonts/NotoSansKharoshthi-Regular.ttf
+70e9367000-70e9385000 r--s 00000000 fc:00 151                            /system/fonts/GoogleSans-Bold.ttf
+70e9385000-70e93b8000 r-xp 00000000 fc:00 2494                           /system/lib64/libpng.so
+70e93b8000-70e93d4000 ---p 00000000 00:00 0 
+70e93d4000-70e93d5000 r--p 0003f000 fc:00 2494                           /system/lib64/libpng.so
+70e93d5000-70e93d6000 rw-p 00040000 fc:00 2494                           /system/lib64/libpng.so
+70e93d6000-70e93d7000 r--s 00004000 fc:01 1177                           /vendor/overlay/Pixel/PixelThemeOverlay.apk
+70e93d7000-70e93d9000 r--s 00000000 fc:00 295                            /system/fonts/NotoSansOldTurkic-Regular.ttf
+70e93d9000-70e93e5000 r--s 00000000 fc:00 86                             /system/fonts/NotoSerifKhmer-Bold.otf
+70e93e5000-70e9404000 r--s 00000000 fc:00 240                            /system/fonts/GoogleSans-MediumItalic.ttf
+70e9404000-70e9409000 r-xp 00000000 fc:00 2404                           /system/lib64/libhidlmemory.so
+70e9409000-70e9423000 ---p 00000000 00:00 0 
+70e9423000-70e9424000 r--p 0000f000 fc:00 2404                           /system/lib64/libhidlmemory.so
+70e9424000-70e9425000 rw-p 00010000 fc:00 2404                           /system/lib64/libhidlmemory.so
+70e9425000-70e943e000 r--s 00000000 fc:00 185                            /system/fonts/NotoSansBengali-Regular.ttf
+70e943e000-70e945c000 r--s 00000000 fc:00 129                            /system/fonts/GoogleSans-Medium.ttf
+70e945c000-70e960c000 r-xp 00000000 fc:00 2398                           /system/lib64/libstagefright.so
+70e960c000-70e9625000 ---p 00000000 00:00 0 
+70e9625000-70e9638000 r--p 001bd000 fc:00 2398                           /system/lib64/libstagefright.so
+70e9638000-70e966c000 rw-p 001d0000 fc:00 2398                           /system/lib64/libstagefright.so
+70e966c000-70e966d000 rw-p 00000000 00:00 0                              [anon:.bss]
+70e966d000-70e966e000 r--s 00000000 103:1d 1474566                       /data/resource-cache/vendor@overlay@Pixel@PixelThemeOverlay.apk@idmap
+70e966e000-70e9672000 r--s 00000000 fc:00 241                            /system/fonts/NotoSansMeeteiMayek-Regular.ttf
+70e9672000-70e9680000 r--s 00000000 fc:00 150                            /system/fonts/NotoSansMalayalamUI-Bold.ttf
+70e9680000-70e96a7000 r-xp 00000000 fc:00 2365                           /system/lib64/libhwbinder.so
+70e96a7000-70e96bd000 ---p 00000000 00:00 0 
+70e96bd000-70e96bf000 r--p 0002e000 fc:00 2365                           /system/lib64/libhwbinder.so
+70e96bf000-70e96c0000 rw-p 00030000 fc:00 2365                           /system/lib64/libhwbinder.so
+70e96c0000-70e96c1000 r--s 00088000 103:1d 1736830                       /data/app/com.google.sample.tunnel-HGGRU03Gu1Mwkf_-RnFmvw==/base.apk
+70e96c1000-70e96cb000 r--s 00000000 fc:00 94                             /system/fonts/NotoSansKhmerUI-Regular.ttf
+70e96cb000-70e96d9000 r--s 00000000 fc:00 275                            /system/fonts/NotoSansMalayalamUI-Regular.ttf
+70e96d9000-70e96dd000 r-xp 00000000 fc:00 2386                           /system/lib64/libusbhost.so
+70e96dd000-70e96f8000 ---p 00000000 00:00 0 
+70e96f8000-70e96f9000 r--p 0000f000 fc:00 2386                           /system/lib64/libusbhost.so
+70e96f9000-70e96fa000 rw-p 00010000 fc:00 2386                           /system/lib64/libusbhost.so
+70e96fa000-70e96fb000 r--p 00000000 00:05 10266154                       /dev/ashmem/dalvik-classes.dex extracted in memory from /data/app/com.google.sample.tunnel-HGGRU03Gu1Mwkf_-RnFmvw==/base.apk (deleted)
+70e96fb000-70e9701000 r--s 00000000 fc:00 280                            /system/fonts/NotoSansCoptic-Regular.ttf
+70e9701000-70e9720000 r-xp 00000000 fc:00 2490                           /system/lib64/libstagefright_bufferqueue_helper.so
+70e9720000-70e973b000 ---p 00000000 00:00 0 
+70e973b000-70e973e000 r--p 0002d000 fc:00 2490                           /system/lib64/libstagefright_bufferqueue_helper.so
+70e973e000-70e9740000 rw-p 00030000 fc:00 2490                           /system/lib64/libstagefright_bufferqueue_helper.so
+70e9740000-70e9742000 r--s 00000000 fc:00 141                            /system/fonts/NotoSansOldSouthArabian-Regular.ttf
+70e9742000-70e974c000 r--s 00000000 fc:00 229                            /system/fonts/NotoSerifKhmer-Regular.otf
+70e974c000-70e9759000 r--s 00000000 fc:00 260                            /system/fonts/NotoSerifMalayalam-Bold.ttf
+70e9759000-70e97c7000 r-xp 00000000 fc:00 2428                           /system/lib64/libstagefright_omx.so
+70e97c7000-70e97dc000 ---p 00000000 00:00 0 
+70e97dc000-70e97e6000 r--p 00076000 fc:00 2428                           /system/lib64/libstagefright_omx.so
+70e97e6000-70e97ed000 rw-p 00080000 fc:00 2428                           /system/lib64/libstagefright_omx.so
+70e97ed000-70e97fa000 r--s 00000000 fc:00 66                             /system/fonts/NotoSerifMalayalam-Regular.ttf
+70e97fa000-70e9819000 r--s 00000000 fc:00 183                            /system/fonts/GoogleSans-Italic.ttf
+70e9819000-70e9856000 r-xp 00000000 fc:00 2434                           /system/lib64/libprotobuf-cpp-lite.so
+70e9856000-70e9867000 ---p 00000000 00:00 0 
+70e9867000-70e9869000 r--p 0003e000 fc:00 2434                           /system/lib64/libprotobuf-cpp-lite.so
+70e9869000-70e986a000 rw-p 00040000 fc:00 2434                           /system/lib64/libprotobuf-cpp-lite.so
+70e986a000-70e9873000 r--s 00000000 fc:00 134                            /system/fonts/NotoSansKhmerUI-Bold.ttf
+70e9873000-70e9891000 r--s 00000000 fc:00 81                             /system/fonts/GoogleSans-Regular.ttf
+70e9891000-70e989e000 r-xp 00000000 fc:00 2377                           /system/lib64/libmediametrics.so
+70e989e000-70e98ae000 ---p 00000000 00:00 0 
+70e98ae000-70e98b0000 r--p 0000e000 fc:00 2377                           /system/lib64/libmediametrics.so
+70e98b0000-70e98b1000 rw-p 00010000 fc:00 2377                           /system/lib64/libmediametrics.so
+70e98b1000-70e98b9000 r--s 00000000 fc:00 152                            /system/fonts/NotoSansLao-Bold.ttf
+70e98b9000-70e98c7000 r--s 00000000 fc:00 279                            /system/fonts/NotoSansMalayalam-Bold.ttf
+70e98c7000-70e98ca000 r-xp 00000000 fc:00 2952                           /system/lib64/libETC1.so
+70e98ca000-70e98e6000 ---p 00000000 00:00 0 
+70e98e6000-70e98e7000 r--p 0000f000 fc:00 2952                           /system/lib64/libETC1.so
+70e98e7000-70e98e8000 rw-p 00010000 fc:00 2952                           /system/lib64/libETC1.so
+70e98e8000-70e98e9000 r--s 00000000 fc:00 1121                           /system/usr/hyphen-data/hyph-und-ethi.hyb
+70e98e9000-70e98ef000 r--s 00000000 fc:00 64                             /system/fonts/NotoSansBrahmi-Regular.ttf
+70e98ef000-70e990f000 rw-p 00000000 00:05 10271012                       /dev/ashmem/dalvik-CompilerMetadata (deleted)
+70e990f000-70e9926000 r-xp 00000000 fc:00 2526                           /system/lib64/libbacktrace.so
+70e9926000-70e993e000 ---p 00000000 00:00 0 
+70e993e000-70e993f000 r--p 0001f000 fc:00 2526                           /system/lib64/libbacktrace.so
+70e993f000-70e9940000 rw-p 00020000 fc:00 2526                           /system/lib64/libbacktrace.so
+70e9940000-70e9941000 r--s 00000000 fc:00 1129                           /system/usr/hyphen-data/hyph-tk.hyb
+70e9941000-70e99a4000 r-xp 00000000 fc:00 2528                           /system/lib64/libcamera_client.so
+70e99a4000-70e99bc000 ---p 00000000 00:00 0 
+70e99bc000-70e99c9000 r--p 00063000 fc:00 2528                           /system/lib64/libcamera_client.so
+70e99c9000-70e99d0000 rw-p 00070000 fc:00 2528                           /system/lib64/libcamera_client.so
+70e99d0000-70e99d1000 r--p 00000000 00:00 0                              [anon:linker_alloc]
+70e99d1000-70e99d3000 r--s 00000000 fc:00 200                            /system/fonts/NotoSansOldItalic-Regular.ttf
+70e99d3000-70e99e1000 r--s 00000000 fc:00 153                            /system/fonts/NotoSansMalayalam-Regular.ttf
+70e99e1000-70e9a01000 rw-p 00000000 00:05 10271011                       /dev/ashmem/dalvik-CompilerMetadata (deleted)
+70e9a01000-70e9a1e000 r-xp 00000000 fc:00 2542                           /system/lib64/libimg_utils.so
+70e9a1e000-70e9a39000 ---p 00000000 00:00 0 
+70e9a39000-70e9a3c000 r--p 0001d000 fc:00 2542                           /system/lib64/libimg_utils.so
+70e9a3c000-70e9a3f000 rw-p 00020000 fc:00 2542                           /system/lib64/libimg_utils.so
+70e9a3f000-70e9a5c000 r--s 00000000 fc:00 262                            /system/fonts/NotoNaskhArabic-Regular.ttf
+70e9a5c000-70e9a69000 r-xp 00000000 fc:00 2706                           /system/lib64/libziparchive.so
+70e9a69000-70e9a7b000 ---p 00000000 00:00 0 
+70e9a7b000-70e9a7c000 r--p 0000f000 fc:00 2706                           /system/lib64/libziparchive.so
+70e9a7c000-70e9a7d000 rw-p 00010000 fc:00 2706                           /system/lib64/libziparchive.so
+70e9a7d000-70e9a85000 r--s 00000000 fc:00 119                            /system/fonts/NotoSansLao-Regular.ttf
+70e9a85000-70e9a8e000 r--s 00000000 fc:00 77                             /system/fonts/NotoSansTamilUI-Bold.ttf
+70e9a8e000-70e9a97000 r--s 00000000 fc:00 160                            /system/fonts/NotoSansTamilUI-Regular.ttf
+70e9a97000-70e9a9d000 r-xp 00000000 fc:00 2536                           /system/lib64/libnativehelper.so
+70e9a9d000-70e9ab6000 ---p 00000000 00:00 0 
+70e9ab6000-70e9ab7000 r--p 0000f000 fc:00 2536                           /system/lib64/libnativehelper.so
+70e9ab7000-70e9ab8000 rw-p 00010000 fc:00 2536                           /system/lib64/libnativehelper.so
+70e9ab8000-70e9ab9000 r--s 00000000 fc:00 1134                           /system/usr/hyphen-data/hyph-te.hyb
+70e9ab9000-70e9ac2000 r--s 00000000 fc:00 246                            /system/fonts/NotoSerifTamil-Bold.ttf
+70e9ac2000-70e9acb000 r--s 00000000 fc:00 302                            /system/fonts/NotoSerifTamil-Regular.ttf
+70e9acb000-70e9af4000 r-xp 00000000 fc:00 2950                           /system/lib64/libmemunreachable.so
+70e9af4000-70e9b09000 ---p 00000000 00:00 0 
+70e9b09000-70e9b0b000 r--p 0002e000 fc:00 2950                           /system/lib64/libmemunreachable.so
+70e9b0b000-70e9b0c000 rw-p 00030000 fc:00 2950                           /system/lib64/libmemunreachable.so
+70e9b0c000-70e9b0d000 r--s 00000000 fc:00 1088                           /system/usr/hyphen-data/hyph-ta.hyb
+70e9b0d000-70e9b0f000 r--s 00000000 fc:00 72                             /system/fonts/NotoSansOlChiki-Regular.ttf
+70e9b0f000-70e9b2f000 rw-p 00000000 00:05 10271010                       /dev/ashmem/dalvik-CompilerMetadata (deleted)
+70e9b2f000-70e9b4f000 r--s 00000000 00:10 16633                          /dev/__properties__/u:object_r:persist_debug_prop:s0
+70e9b4f000-70e9b65000 r-xp 00000000 fc:00 2920                           /system/lib64/android.hardware.cas.native@1.0.so
+70e9b65000-70e9b7b000 ---p 00000000 00:00 0 
+70e9b7b000-70e9b7d000 r--p 0001e000 fc:00 2920                           /system/lib64/android.hardware.cas.native@1.0.so
+70e9b7d000-70e9b7e000 rw-p 00020000 fc:00 2920                           /system/lib64/android.hardware.cas.native@1.0.so
+70e9b7e000-70e9b7f000 r--s 00000000 fc:00 1145                           /system/usr/hyphen-data/hyph-pt.hyb
+70e9b7f000-70e9b83000 r--s 00000000 fc:00 277                            /system/fonts/NotoSansMandaic-Regular.ttf
+70e9b83000-70e9bdb000 r-xp 00000000 fc:00 2334                           /system/lib64/libsonivox.so
+70e9bdb000-70e9bf2000 ---p 00000000 00:00 0 
+70e9bf2000-70e9bf3000 r--p 0005f000 fc:00 2334                           /system/lib64/libsonivox.so
+70e9bf3000-70e9bf4000 rw-p 00060000 fc:00 2334                           /system/lib64/libsonivox.so
+70e9bf4000-70e9bfb000 rw-p 00000000 00:00 0                              [anon:.bss]
+70e9bfb000-70e9c01000 r--s 00000000 fc:00 123                            /system/fonts/NotoSansCham-Bold.ttf
+70e9c01000-70e9c0a000 r--s 00000000 fc:00 255                            /system/fonts/NotoSansTamil-Bold.ttf
+70e9c0a000-70e9c13000 r--s 00000000 fc:00 135                            /system/fonts/NotoSansTamil-Regular.ttf
+70e9c13000-70e9c15000 r-xp 00000000 fc:00 2519                           /system/lib64/libgraphicsenv.so
+70e9c15000-70e9c32000 ---p 00000000 00:00 0 
+70e9c32000-70e9c33000 r--p 0000f000 fc:00 2519                           /system/lib64/libgraphicsenv.so
+70e9c33000-70e9c34000 rw-p 00010000 fc:00 2519                           /system/lib64/libgraphicsenv.so
+70e9c34000-70e9c3a000 r--s 00000000 fc:00 259                            /system/fonts/NotoSansCham-Regular.ttf
+70e9c3a000-70e9c42000 r--s 00000000 fc:00 114                            /system/fonts/NotoSansGurmukhiUI-Bold.ttf
+70e9c42000-70e9c4a000 r--s 00000000 fc:00 122                            /system/fonts/NotoSansGurmukhiUI-Regular.ttf
+70e9c4a000-70e9c5f000 r-xp 00000000 fc:00 2348                           /system/lib64/android.hidl.allocator@1.0.so
+70e9c5f000-70e9c77000 ---p 00000000 00:00 0 
+70e9c77000-70e9c79000 r--p 0001e000 fc:00 2348                           /system/lib64/android.hidl.allocator@1.0.so
+70e9c79000-70e9c7a000 rw-p 00020000 fc:00 2348                           /system/lib64/android.hidl.allocator@1.0.so
+70e9c7a000-70e9c7b000 r--s 00000000 fc:00 1095                           /system/usr/hyphen-data/hyph-pa.hyb
+70e9c7b000-70e9c83000 r--s 00000000 fc:00 298                            /system/fonts/NotoSerifGurmukhi-Bold.otf
+70e9c83000-70e9d01000 r-xp 00000000 fc:00 2665                           /system/lib64/libbinder.so
+70e9d01000-70e9d1e000 ---p 00000000 00:00 0 
+70e9d1e000-70e9d2e000 r--p 00080000 fc:00 2665                           /system/lib64/libbinder.so
+70e9d2e000-70e9d2f000 rw-p 00090000 fc:00 2665                           /system/lib64/libbinder.so
+70e9d2f000-70e9d4f000 rw-p 00000000 00:05 10271009                       /dev/ashmem/dalvik-CompilerMetadata (deleted)
+70e9d4f000-70e9d53000 r-xp 00000000 fc:00 2454                           /system/lib64/libaudiomanager.so
+70e9d53000-70e9d6e000 ---p 00000000 00:00 0 
+70e9d6e000-70e9d6f000 r--p 0000f000 fc:00 2454                           /system/lib64/libaudiomanager.so
+70e9d6f000-70e9d70000 rw-p 00010000 fc:00 2454                           /system/lib64/libaudiomanager.so
+70e9d70000-70e9d71000 r--s 00000000 fc:00 1087                           /system/usr/hyphen-data/hyph-or.hyb
+70e9d71000-70e9d91000 rw-p 00000000 00:05 10271008                       /dev/ashmem/dalvik-CompilerMetadata (deleted)
+70e9d91000-70e9e21000 r-xp 00000000 fc:00 2627                           /system/lib64/libft2.so
+70e9e21000-70e9e37000 ---p 00000000 00:00 0 
+70e9e37000-70e9e3c000 r--p 0009b000 fc:00 2627                           /system/lib64/libft2.so
+70e9e3c000-70e9e3d000 rw-p 000a0000 fc:00 2627                           /system/lib64/libft2.so
+70e9e3d000-70e9e3e000 r--s 00000000 fc:00 1142                           /system/usr/hyphen-data/hyph-mr.hyb
+70e9e3e000-70e9e45000 r--s 00000000 fc:00 88                             /system/fonts/NotoSerifGurmukhi-Regular.otf
+70e9e45000-70e9e65000 r--s 00000000 00:10 16594                          /dev/__properties__/u:object_r:exported3_default_prop:s0
+70e9e65000-70e9e7f000 r-xp 00000000 fc:00 2643                           /system/lib64/libunwind.so
+70e9e7f000-70e9e94000 ---p 00000000 00:00 0 
+70e9e94000-70e9e95000 r--p 0001f000 fc:00 2643                           /system/lib64/libunwind.so
+70e9e95000-70e9e96000 rw-p 00020000 fc:00 2643                           /system/lib64/libunwind.so
+70e9e96000-70e9eff000 rw-p 00000000 00:00 0                              [anon:.bss]
+70e9eff000-70e9f00000 r--s 00000000 fc:00 1130                           /system/usr/hyphen-data/hyph-ml.hyb
+70e9f00000-70e9f02000 r--s 00000000 fc:00 75                             /system/fonts/NotoSansOgham-Regular.ttf
+70e9f02000-70e9f0a000 r--s 00000000 fc:00 193                            /system/fonts/NotoSansGurmukhi-Bold.ttf
+70e9f0a000-70ea022000 r-xp 00000000 fc:00 2328                           /system/lib64/libsqlite.so
+70ea022000-70ea033000 ---p 00000000 00:00 0 
+70ea033000-70ea036000 r--p 0011d000 fc:00 2328                           /system/lib64/libsqlite.so
+70ea036000-70ea038000 rw-p 00120000 fc:00 2328                           /system/lib64/libsqlite.so
+70ea038000-70ea03c000 r--s 00000000 fc:00 285                            /system/fonts/NotoSansGlagolitic-Regular.ttf
+70ea03c000-70ea04c000 r--s 00000000 fc:00 184                            /system/fonts/NotoSerifGujarati-Bold.ttf
+70ea04c000-70ea060000 r-xp 00000000 fc:00 2731                           /system/lib64/libstatslog.so
+70ea060000-70ea07b000 ---p 00000000 00:00 0 
+70ea07b000-70ea07c000 r--p 0001f000 fc:00 2731                           /system/lib64/libstatslog.so
+70ea07c000-70ea07d000 rw-p 00020000 fc:00 2731                           /system/lib64/libstatslog.so
+70ea07d000-70ea081000 r--s 00000000 fc:00 182                            /system/fonts/NotoSansBatak-Regular.ttf
+70ea081000-70ea091000 r--s 00000000 fc:00 264                            /system/fonts/NotoSerifGujarati-Regular.ttf
+70ea091000-70ea160000 r-xp 00000000 fc:00 2728                           /system/lib64/libdng_sdk.so
+70ea160000-70ea173000 ---p 00000000 00:00 0 
+70ea173000-70ea17a000 r--p 000d9000 fc:00 2728                           /system/lib64/libdng_sdk.so
+70ea17a000-70ea17b000 rw-p 000e0000 fc:00 2728                           /system/lib64/libdng_sdk.so
+70ea17b000-70ea198000 r--s 00000000 fc:00 223                            /system/fonts/DancingScript-Bold.ttf
+70ea198000-70ea19c000 r-xp 00000000 fc:00 2344                           /system/lib64/libnativewindow.so
+70ea19c000-70ea1b7000 ---p 00000000 00:00 0 
+70ea1b7000-70ea1b8000 r--p 0000f000 fc:00 2344                           /system/lib64/libnativewindow.so
+70ea1b8000-70ea1b9000 rw-p 00010000 fc:00 2344                           /system/lib64/libnativewindow.so
+70ea1b9000-70ea1bd000 r--s 00000000 fc:00 98                             /system/fonts/NotoSansAhom-Regular.otf
+70ea1bd000-70ea1d1000 r--s 00000000 fc:00 104                            /system/fonts/NotoSerifDevanagari-Bold.ttf
+70ea1d1000-70ea1d4000 r-xp 00000000 fc:00 2923                           /system/lib64/libstdc++.so
+70ea1d4000-70ea1f0000 ---p 00000000 00:00 0 
+70ea1f0000-70ea1f1000 r--p 0000f000 fc:00 2923                           /system/lib64/libstdc++.so
+70ea1f1000-70ea1f2000 rw-p 00010000 fc:00 2923                           /system/lib64/libstdc++.so
+70ea1f2000-70ea1f4000 r--s 00000000 fc:00 117                            /system/fonts/NotoSansLydian-Regular.ttf
+70ea1f4000-70ea211000 r--s 00000000 fc:00 239                            /system/fonts/DancingScript-Regular.ttf
+70ea211000-70ea24a000 r-xp 00000000 fc:00 2933                           /system/lib64/android.hardware.graphics.bufferqueue@1.0.so
+70ea24a000-70ea268000 ---p 00000000 00:00 0 
+70ea268000-70ea26c000 r--p 0003c000 fc:00 2933                           /system/lib64/android.hardware.graphics.bufferqueue@1.0.so
+70ea26c000-70ea26d000 rw-p 00040000 fc:00 2933                           /system/lib64/android.hardware.graphics.bufferqueue@1.0.so
+70ea26d000-70ea26f000 r--s 00000000 fc:00 244                            /system/fonts/NotoSansLycian-Regular.ttf
+70ea26f000-70ea273000 r--s 00000000 fc:00 173                            /system/fonts/NotoSansThaana-Bold.ttf
+70ea273000-70ea287000 r--s 00000000 fc:00 106                            /system/fonts/NotoSerifDevanagari-Regular.ttf
+70ea287000-70ea29e000 r-xp 00000000 fc:00 2938                           /system/lib64/libpiex.so
+70ea29e000-70ea2b6000 ---p 00000000 00:00 0 
+70ea2b6000-70ea2b7000 r--p 0001f000 fc:00 2938                           /system/lib64/libpiex.so
+70ea2b7000-70ea2b8000 rw-p 00020000 fc:00 2938                           /system/lib64/libpiex.so
+70ea2b8000-70ea2c0000 r--s 00000000 fc:00 113                            /system/fonts/NotoSansGurmukhi-Regular.ttf
+70ea2c0000-70ea2c6000 r--s 00000000 fc:00 263                            /system/fonts/NotoSerifGeorgian-Bold.ttf
+70ea2c6000-70ea2cc000 r--s 00000000 fc:00 249                            /system/fonts/NotoSerifGeorgian-Regular.ttf
+70ea2cc000-70ea9b4000 r-xp 00000000 fc:00 2532                           /system/lib64/libhwui.so
+70ea9b4000-70ea9d3000 ---p 00000000 00:00 0 
+70ea9d3000-70eaa0b000 r--p 006e8000 fc:00 2532                           /system/lib64/libhwui.so
+70eaa0b000-70eaa0c000 rw-p 00720000 fc:00 2532                           /system/lib64/libhwui.so
+70eaa0c000-70eaa11000 rw-p 00000000 00:00 0                              [anon:.bss]
+70eaa11000-70eaa12000 r--s 00000000 fc:00 1110                           /system/usr/hyphen-data/hyph-la.hyb
+70eaa12000-70eaa16000 r--s 00000000 fc:00 87                             /system/fonts/NotoSansThaana-Regular.ttf
+70eaa16000-70eaa1b000 r--s 00000000 fc:00 218                            /system/fonts/NotoSansGeorgian-Bold.ttf
+70eaa1b000-70eaa20000 r--s 00000000 fc:00 125                            /system/fonts/NotoSansGeorgian-Regular.ttf
+70eaa20000-70eaa40000 rw-p 00000000 00:05 10271007                       /dev/ashmem/dalvik-CompilerMetadata (deleted)
+70eaa40000-70eaaa0000 r-xp 00000000 fc:00 2384                           /system/lib64/libhidltransport.so
+70eaaa0000-70eaabe000 ---p 00000000 00:00 0 
+70eaabe000-70eaac6000 r--p 00068000 fc:00 2384                           /system/lib64/libhidltransport.so
+70eaac6000-70eaac7000 rw-p 00070000 fc:00 2384                           /system/lib64/libhidltransport.so
+70eaac7000-70eaacb000 r--s 00000000 fc:00 192                            /system/fonts/NotoSerifArmenian-Bold.ttf
+70eaacb000-70eaad0000 r--s 00000000 fc:00 210                            /system/fonts/NotoSansThaiUI-Bold.ttf
+70eaad0000-70eaaf0000 rw-p 00000000 00:05 10271006                       /dev/ashmem/dalvik-CompilerMetadata (deleted)
+70eaaf0000-70eab10000 rw-p 00000000 00:05 10271005                       /dev/ashmem/dalvik-CompilerMetadata (deleted)
+70eab10000-70eab57000 r-xp 00000000 fc:00 2546                           /system/lib64/libmedia_omx.so
+70eab57000-70eab6d000 ---p 00000000 00:00 0 
+70eab6d000-70eab7a000 r--p 00053000 fc:00 2546                           /system/lib64/libmedia_omx.so
+70eab7a000-70eab7f000 rw-p 00060000 fc:00 2546                           /system/lib64/libmedia_omx.so
+70eab7f000-70eab80000 r--s 00000000 fc:00 1119                           /system/usr/hyphen-data/hyph-kn.hyb
+70eab80000-70eab86000 r--s 00000000 fc:00 224                            /system/fonts/NotoSansThaiUI-Regular.ttf
+70eab86000-70eab8b000 r--s 00000000 fc:00 300                            /system/fonts/NotoSerifThai-Bold.ttf
+70eab8b000-70eabab000 rw-p 00000000 00:05 10271004                       /dev/ashmem/dalvik-CompilerMetadata (deleted)
+70eabab000-70eac21000 r-xp 00000000 fc:00 2385                           /system/lib64/libvintf.so
+70eac21000-70eac31000 ---p 00000000 00:00 0 
+70eac31000-70eac36000 r--p 0007b000 fc:00 2385                           /system/lib64/libvintf.so
+70eac36000-70eac37000 rw-p 00080000 fc:00 2385                           /system/lib64/libvintf.so
+70eac37000-70eac39000 rw-p 00000000 00:00 0                              [anon:.bss]
+70eac39000-70eac3a000 r--s 00000000 fc:00 1104                           /system/usr/hyphen-data/hyph-hy.hyb
+70eac3a000-70eac3f000 r--s 00000000 fc:00 212                            /system/fonts/NotoSerifThai-Regular.ttf
+70eac3f000-70eac44000 r--s 00000000 fc:00 220                            /system/fonts/NotoSansThai-Bold.ttf
+70eac44000-70eacb2000 r-xp 00000000 fc:00 2606                           /system/lib64/android.hardware.media.omx@1.0.so
+70eacb2000-70eaccf000 ---p 00000000 00:00 0 
+70eaccf000-70eacd8000 r--p 00077000 fc:00 2606                           /system/lib64/android.hardware.media.omx@1.0.so
+70eacd8000-70eacd9000 rw-p 00080000 fc:00 2606                           /system/lib64/android.hardware.media.omx@1.0.so
+70eacd9000-70eacdf000 r--s 00000000 fc:00 169                            /system/fonts/NotoSansThai-Regular.ttf
+70eacdf000-70eace9000 r--s 00000000 fc:00 140                            /system/fonts/CarroisGothicSC-Regular.ttf
+70eace9000-70ead09000 rw-p 00000000 00:05 10271003                       /dev/ashmem/dalvik-CompilerMetadata (deleted)
+70ead09000-70ead22000 r-xp 00000000 fc:00 2539                           /system/lib64/android.hardware.graphics.mapper@2.1.so
+70ead22000-70ead34000 ---p 00000000 00:00 0 
+70ead34000-70ead37000 r--p 0001d000 fc:00 2539                           /system/lib64/android.hardware.graphics.mapper@2.1.so
+70ead37000-70ead38000 rw-p 00020000 fc:00 2539                           /system/lib64/android.hardware.graphics.mapper@2.1.so
+70ead38000-70ead47000 r--s 00000000 fc:00 188                            /system/fonts/ComingSoon.ttf
+70ead47000-70ead5d000 r-xp 00000000 fc:00 2379                           /system/lib64/libselinux.so
+70ead5d000-70ead76000 ---p 00000000 00:00 0 
+70ead76000-70ead77000 r--p 0001f000 fc:00 2379                           /system/lib64/libselinux.so
+70ead77000-70ead78000 rw-p 00020000 fc:00 2379                           /system/lib64/libselinux.so
+70ead78000-70ead79000 rw-p 00000000 00:00 0                              [anon:.bss]
+70ead79000-70ead7d000 r--s 00000000 fc:00 282                            /system/fonts/NotoSerifArmenian-Regular.ttf
+70ead7d000-70ead82000 r--s 00000000 fc:00 288                            /system/fonts/NotoSerifHebrew-Bold.ttf
+70ead82000-70ead83000 r-xp 00000000 fc:00 2680                           /system/lib64/android.hardware.media@1.0.so
+70ead83000-70eada1000 ---p 00000000 00:00 0 
+70eada1000-70eada2000 r--p 0000f000 fc:00 2680                           /system/lib64/android.hardware.media@1.0.so
+70eada2000-70eada3000 rw-p 00010000 fc:00 2680                           /system/lib64/android.hardware.media@1.0.so
+70eada3000-70eada8000 r--s 00000000 fc:00 248                            /system/fonts/NotoSerifHebrew-Regular.ttf
+70eada8000-70eadb9000 r--s 00000000 fc:00 252                            /system/fonts/CutiveMono.ttf
+70eadb9000-70eadd9000 r--s 00000000 00:10 16641                          /dev/__properties__/u:object_r:radio_prop:s0
+70eadd9000-70eadda000 r-xp 00000000 fc:00 2533                           /system/lib64/android.hardware.graphics.common@1.0.so
+70eadda000-70eadf8000 ---p 00000000 00:00 0 
+70eadf8000-70eadf9000 r--p 0000f000 fc:00 2533                           /system/lib64/android.hardware.graphics.common@1.0.so
+70eadf9000-70eadfa000 rw-p 00010000 fc:00 2533                           /system/lib64/android.hardware.graphics.common@1.0.so
+70eadfa000-70eadfb000 r--s 00000000 fc:00 1126                           /system/usr/hyphen-data/hyph-hr.hyb
+70eadfb000-70eadfd000 r--s 00000000 fc:00 194                            /system/fonts/NotoSansLisu-Regular.ttf
+70eadfd000-70eae18000 r--s 00000000 fc:00 201                            /system/fonts/DroidSansMono.ttf
+70eae18000-70eae3b000 r-xp 00000000 fc:00 2925                           /system/lib64/liblzma.so
+70eae3b000-70eae57000 ---p 00000000 00:00 0 
+70eae57000-70eae58000 r--p 0002f000 fc:00 2925                           /system/lib64/liblzma.so
+70eae58000-70eae59000 rw-p 00030000 fc:00 2925                           /system/lib64/liblzma.so
+70eae59000-70eae5f000 rw-p 00000000 00:00 0                              [anon:.bss]
+70eae5f000-70eae62000 r--s 00000000 fc:00 103                            /system/fonts/NotoSansLimbu-Regular.ttf
+70eae62000-70eae67000 r--s 00000000 fc:00 236                            /system/fonts/NotoSansHebrew-Bold.ttf
+70eae67000-70eae84000 r--s 001c2000 fc:00 990                            /system/framework/ext.jar
+70eae84000-70eaea4000 rw-p 00000000 00:05 10269720                       /dev/ashmem/dalvik-LinearAlloc (deleted)
+70eaea4000-70eaede000 r-xp 00000000 fc:00 2924                           /system/lib64/libwilhelm.so
+70eaede000-70eaefa000 ---p 00000000 00:00 0 
+70eaefa000-70eaeff000 r--p 0003b000 fc:00 2924                           /system/lib64/libwilhelm.so
+70eaeff000-70eaf00000 rw-p 00040000 fc:00 2924                           /system/lib64/libwilhelm.so
+70eaf00000-70eaf03000 r--s 00000000 fc:00 242                            /system/fonts/NotoSansElbasan-Regular.otf
+70eaf03000-70eaf21000 r-xp 00000000 fc:00 2415                           /system/lib64/libdrmframework.so
+70eaf21000-70eaf38000 ---p 00000000 00:00 0 
+70eaf38000-70eaf3d000 r--p 0002b000 fc:00 2415                           /system/lib64/libdrmframework.so
+70eaf3d000-70eaf3e000 rw-p 00030000 fc:00 2415                           /system/lib64/libdrmframework.so
+70eaf3e000-70eaf43000 r--s 00000000 fc:00 70                             /system/fonts/NotoSansHebrew-Regular.ttf
+70eaf43000-70eaf44000 ---p 00000000 00:00 0                              [anon:thread signal stack guard]
+70eaf44000-70eaf48000 rw-p 00000000 00:00 0                              [anon:thread signal stack]
+70eaf48000-70eaf49000 ---p 00000000 00:00 0                              [anon:bionic TLS guard]
+70eaf49000-70eaf4c000 rw-p 00000000 00:00 0                              [anon:bionic TLS]
+70eaf4c000-70eaf4d000 ---p 00000000 00:00 0                              [anon:bionic TLS guard]
+70eaf4d000-70eaf4e000 ---p 00000000 00:00 0                              [anon:thread signal stack guard]
+70eaf4e000-70eaf52000 rw-p 00000000 00:00 0                              [anon:thread signal stack]
+70eaf52000-70eaf98000 r-xp 00000000 fc:00 2426                           /system/lib64/libunwindstack.so
+70eaf98000-70eafb6000 ---p 00000000 00:00 0 
+70eafb6000-70eafbd000 r--p 00049000 fc:00 2426                           /system/lib64/libunwindstack.so
+70eafbd000-70eafbe000 rw-p 00050000 fc:00 2426                           /system/lib64/libunwindstack.so
+70eafbe000-70eafc0000 r--s 00000000 fc:00 162                            /system/fonts/NotoSansKayahLi-Regular.ttf
+70eafc0000-70eafe4000 r-xp 00000000 fc:00 2944                           /system/lib64/libvulkan.so
+70eafe4000-70eaffc000 ---p 00000000 00:00 0 
+70eaffc000-70eaffe000 r--p 0002e000 fc:00 2944                           /system/lib64/libvulkan.so
+70eaffe000-70eafff000 rw-p 00030000 fc:00 2944                           /system/lib64/libvulkan.so
+70eafff000-70eb001000 r--s 00000000 fc:00 180                            /system/fonts/NotoSansInscriptionalParthian-Regular.ttf
+70eb001000-70eb01d000 r-xp 00000000 fc:00 2400                           /system/lib64/libbufferhubqueue.so
+70eb01d000-70eb030000 ---p 00000000 00:00 0 
+70eb030000-70eb031000 r--p 0001f000 fc:00 2400                           /system/lib64/libbufferhubqueue.so
+70eb031000-70eb032000 rw-p 00020000 fc:00 2400                           /system/lib64/libbufferhubqueue.so
+70eb032000-70eb036000 r--s 00000000 fc:00 269                            /system/fonts/NotoSansArmenian-Bold.ttf
+70eb036000-70eb037000 ---p 00000000 00:00 0                              [anon:bionic TLS guard]
+70eb037000-70eb03a000 rw-p 00000000 00:00 0                              [anon:bionic TLS]
+70eb03a000-70eb03b000 ---p 00000000 00:00 0                              [anon:bionic TLS guard]
+70eb03b000-70eb03c000 ---p 00000000 00:00 0                              [anon:thread signal stack guard]
+70eb03c000-70eb040000 rw-p 00000000 00:00 0                              [anon:thread signal stack]
+70eb040000-70eb042000 r-xp 00000000 fc:00 2935                           /system/lib64/libhardware.so
+70eb042000-70eb05f000 ---p 00000000 00:00 0 
+70eb05f000-70eb060000 r--p 0000f000 fc:00 2935                           /system/lib64/libhardware.so
+70eb060000-70eb061000 rw-p 00010000 fc:00 2935                           /system/lib64/libhardware.so
+70eb061000-70eb063000 r--s 00000000 fc:00 171                            /system/fonts/NotoSansInscriptionalPahlavi-Regular.ttf
+70eb063000-70eb064000 ---p 00000000 00:00 0                              [anon:bionic TLS guard]
+70eb064000-70eb067000 rw-p 00000000 00:00 0                              [anon:bionic TLS]
+70eb067000-70eb068000 ---p 00000000 00:00 0                              [anon:bionic TLS guard]
+70eb068000-70eb069000 ---p 00000000 00:00 0                              [anon:thread signal stack guard]
+70eb069000-70eb06d000 rw-p 00000000 00:00 0                              [anon:thread signal stack]
+70eb06d000-70eb06e000 ---p 00000000 00:00 0                              [anon:bionic TLS guard]
+70eb06e000-70eb071000 rw-p 00000000 00:00 0                              [anon:bionic TLS]
+70eb071000-70eb072000 ---p 00000000 00:00 0                              [anon:bionic TLS guard]
+70eb072000-70eb073000 ---p 00000000 00:00 0                              [anon:thread signal stack guard]
+70eb073000-70eb077000 rw-p 00000000 00:00 0                              [anon:thread signal stack]
+70eb077000-70eb078000 ---p 00000000 00:00 0                              [anon:bionic TLS guard]
+70eb078000-70eb07b000 rw-p 00000000 00:00 0                              [anon:bionic TLS]
+70eb07b000-70eb07c000 ---p 00000000 00:00 0                              [anon:bionic TLS guard]
+70eb07c000-70eb07d000 ---p 00000000 00:00 0                              [anon:thread signal stack guard]
+70eb07d000-70eb081000 rw-p 00000000 00:00 0                              [anon:thread signal stack]
+70eb081000-70eb082000 ---p 00000000 00:00 0                              [anon:bionic TLS guard]
+70eb082000-70eb085000 rw-p 00000000 00:00 0                              [anon:bionic TLS]
+70eb085000-70eb086000 ---p 00000000 00:00 0                              [anon:bionic TLS guard]
+70eb086000-70eb09d000 r-xp 00000000 fc:00 2604                           /system/lib64/libz.so
+70eb09d000-70eb0b5000 ---p 00000000 00:00 0 
+70eb0b5000-70eb0b6000 r--p 0001f000 fc:00 2604                           /system/lib64/libz.so
+70eb0b6000-70eb0b7000 rw-p 00020000 fc:00 2604                           /system/lib64/libz.so
+70eb0b7000-70eb0bb000 r--s 00000000 fc:00 289                            /system/fonts/NotoSansArmenian-Regular.ttf
+70eb0bb000-70eb0bc000 ---p 00000000 00:00 0                              [anon:thread signal stack guard]
+70eb0bc000-70eb0c0000 rw-p 00000000 00:00 0                              [anon:thread signal stack]
+70eb0c0000-70eb0c1000 ---p 00000000 00:00 0                              [anon:bionic TLS guard]
+70eb0c1000-70eb0c4000 rw-p 00000000 00:00 0                              [anon:bionic TLS]
+70eb0c4000-70eb0c5000 ---p 00000000 00:00 0                              [anon:bionic TLS guard]
+70eb0c5000-70eb0c6000 ---p 00000000 00:00 0                              [anon:thread signal stack guard]
+70eb0c6000-70eb0ca000 rw-p 00000000 00:00 0                              [anon:thread signal stack]
+70eb0ca000-70eb0cb000 ---p 00000000 00:00 0                              [anon:bionic TLS guard]
+70eb0cb000-70eb0ce000 rw-p 00000000 00:00 0                              [anon:bionic TLS]
+70eb0ce000-70eb0cf000 ---p 00000000 00:00 0                              [anon:bionic TLS guard]
+70eb0cf000-70eb0ef000 rw-p 00000000 00:05 10270988                       /dev/ashmem/dalvik-LinearAlloc (deleted)
+70eb0ef000-70eb5bb000 r-xp 00000000 fc:00 2374                           /system/lib64/libpdfium.so
+70eb5bb000-70eb5cf000 ---p 00000000 00:00 0 
+70eb5cf000-70eb5e6000 r--p 004d9000 fc:00 2374                           /system/lib64/libpdfium.so
+70eb5e6000-70eb5ea000 rw-p 004f0000 fc:00 2374                           /system/lib64/libpdfium.so
+70eb5ea000-70eb5f1000 rw-p 00000000 00:00 0                              [anon:.bss]
+70eb5f1000-70eb5f2000 r--s 00000000 fc:00 1094                           /system/usr/hyphen-data/hyph-hi.hyb
+70eb5f2000-70eb5f6000 rw-p 00000000 00:05 10270982                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb5f6000-70eb5fa000 rw-p 00000000 00:05 10270981                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb5fa000-70eb5fe000 rw-p 00000000 00:05 10270980                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb5fe000-70eb602000 rw-p 00000000 00:05 10270979                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb602000-70eb606000 rw-p 00000000 00:05 10270978                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb606000-70eb60a000 rw-p 00000000 00:05 10270977                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb60a000-70eb60e000 rw-p 00000000 00:05 10270976                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb60e000-70eb612000 rw-p 00000000 00:05 10270975                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb612000-70eb616000 rw-p 00000000 00:05 10270974                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb616000-70eb61a000 r-xp 00000000 fc:00 2479                           /system/lib64/libspeexresampler.so
+70eb61a000-70eb635000 ---p 00000000 00:00 0 
+70eb635000-70eb636000 r--p 0000f000 fc:00 2479                           /system/lib64/libspeexresampler.so
+70eb636000-70eb637000 rw-p 00010000 fc:00 2479                           /system/lib64/libspeexresampler.so
+70eb637000-70eb639000 r--s 00000000 fc:00 299                            /system/fonts/NotoSansImperialAramaic-Regular.ttf
+70eb639000-70eb63d000 rw-p 00000000 00:05 10270973                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb63d000-70eb641000 rw-p 00000000 00:05 10270972                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb641000-70eb645000 rw-p 00000000 00:05 10270971                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb645000-70eb649000 rw-p 00000000 00:05 10270970                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb649000-70eb64d000 rw-p 00000000 00:05 10270969                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb64d000-70eb651000 rw-p 00000000 00:05 10270968                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb651000-70eb655000 rw-p 00000000 00:05 10270967                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb655000-70eb659000 rw-p 00000000 00:05 10270966                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb659000-70eb65d000 rw-p 00000000 00:05 10270965                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb65d000-70eb661000 rw-p 00000000 00:05 10270964                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb661000-70eb6c5000 r-xp 00000000 fc:00 2461                           /system/lib64/libhidl-gen-utils.so
+70eb6c5000-70eb6df000 ---p 00000000 00:00 0 
+70eb6df000-70eb6e1000 r--p 0006e000 fc:00 2461                           /system/lib64/libhidl-gen-utils.so
+70eb6e1000-70eb6e2000 rw-p 00070000 fc:00 2461                           /system/lib64/libhidl-gen-utils.so
+70eb6e2000-70eb6e6000 rw-p 00000000 00:05 10270963                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb6e6000-70eb6ea000 rw-p 00000000 00:05 10270962                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb6ea000-70eb6ee000 rw-p 00000000 00:05 10270961                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb6ee000-70eb6f2000 rw-p 00000000 00:05 10270960                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb6f2000-70eb6f6000 rw-p 00000000 00:05 10270959                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb6f6000-70eb6fa000 rw-p 00000000 00:05 10270958                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb6fa000-70eb6fe000 rw-p 00000000 00:05 10270957                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb6fe000-70eb702000 rw-p 00000000 00:05 10270956                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb702000-70eb706000 rw-p 00000000 00:05 10270955                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb706000-70eb70a000 rw-p 00000000 00:05 10270954                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb70a000-70eb70e000 rw-p 00000000 00:05 10270953                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb70e000-70eb712000 rw-p 00000000 00:05 10270952                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb712000-70eb71a000 r-xp 00000000 fc:00 2652                           /system/lib64/libcamera_metadata.so
+70eb71a000-70eb72f000 ---p 00000000 00:00 0 
+70eb72f000-70eb730000 r--p 0000f000 fc:00 2652                           /system/lib64/libcamera_metadata.so
+70eb730000-70eb732000 rw-p 00010000 fc:00 2652                           /system/lib64/libcamera_metadata.so
+70eb732000-70eb734000 r--s 00000000 fc:00 131                            /system/fonts/NotoSansHanunoo-Regular.ttf
+70eb734000-70eb738000 rw-p 00000000 00:05 10270951                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb738000-70eb73c000 rw-p 00000000 00:05 10270950                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb73c000-70eb740000 rw-p 00000000 00:05 10270949                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb740000-70eb744000 rw-p 00000000 00:05 10270948                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb744000-70eb748000 rw-p 00000000 00:05 10270947                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb748000-70eb74c000 rw-p 00000000 00:05 10270946                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb74c000-70eb750000 rw-p 00000000 00:05 10270945                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb750000-70eb754000 rw-p 00000000 00:05 10270944                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb754000-70eb758000 rw-p 00000000 00:05 10270943                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb758000-70eb75c000 rw-p 00000000 00:05 10270942                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb75c000-70eb760000 rw-p 00000000 00:05 10270941                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb760000-70eb764000 rw-p 00000000 00:05 10270940                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb764000-70eb767000 rw-p 00000000 00:00 0                              [anon:linker_alloc]
+70eb767000-70eb768000 r--p 00000000 00:00 0                              [anon:linker_alloc]
+70eb768000-70eb76c000 rw-p 00000000 00:05 10270939                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb76c000-70eb770000 rw-p 00000000 00:05 10270938                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb770000-70eb771000 rw-p 00000000 00:00 0                              [anon:linker_alloc]
+70eb771000-70eb774000 r--s 00000000 fc:00 231                            /system/fonts/NotoSansDeseret-Regular.ttf
+70eb774000-70eb778000 rw-p 00000000 00:05 10270937                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb778000-70eb77c000 rw-p 00000000 00:05 10270936                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb77c000-70eb780000 rw-p 00000000 00:05 10270935                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb780000-70eb784000 rw-p 00000000 00:05 10270934                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb784000-70eb788000 rw-p 00000000 00:05 10270933                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb788000-70eb78c000 rw-p 00000000 00:05 10270932                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb78c000-70eb790000 rw-p 00000000 00:05 10270931                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb790000-70eb794000 rw-p 00000000 00:05 10270930                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb794000-70eb798000 rw-p 00000000 00:05 10270929                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb798000-70eb79c000 rw-p 00000000 00:05 10270928                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb79c000-70eb7a0000 rw-p 00000000 00:05 10270927                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb7a0000-70eb7a1000 r--p 00000000 00:00 0                              [anon:linker_alloc]
+70eb7a1000-70eb7a3000 r--s 00000000 fc:00 176                            /system/fonts/NotoSansGothic-Regular.ttf
+70eb7a3000-70eb7a7000 rw-p 00000000 00:05 10270926                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb7a7000-70eb7a8000 r--p 00000000 00:00 0                              [anon:linker_alloc]
+70eb7a8000-70eb7a9000 r--s 00000000 fc:00 1109                           /system/usr/hyphen-data/hyph-gu.hyb
+70eb7a9000-70eb7ad000 rw-p 00000000 00:05 10270925                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb7ad000-70eb7ae000 r--p 00000000 00:00 0                              [anon:linker_alloc]
+70eb7ae000-70eb7af000 rw-p 00000000 00:00 0                              [anon:linker_alloc]
+70eb7af000-70eb7b0000 r--p 00000000 00:00 0                              [anon:linker_alloc]
+70eb7b0000-70eb7b2000 r--s 00000000 fc:00 191                            /system/fonts/NotoSansCypriot-Regular.ttf
+70eb7b2000-70eb7b6000 rw-p 00000000 00:05 10270924                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb7b6000-70eb7ba000 rw-p 00000000 00:05 10270923                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb7ba000-70eb7be000 rw-p 00000000 00:05 10270922                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb7be000-70eb7c2000 rw-p 00000000 00:05 10270921                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb7c2000-70eb7c6000 rw-p 00000000 00:05 10270920                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb7c6000-70eb7ca000 rw-p 00000000 00:05 10270919                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb7ca000-70eb7ce000 rw-p 00000000 00:05 10270918                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb7ce000-70eb7d2000 rw-p 00000000 00:05 10270917                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb7d2000-70eb7d6000 rw-p 00000000 00:05 10270916                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb7d6000-70eb7d7000 rw-p 00000000 00:00 0                              [anon:linker_alloc_small_objects]
+70eb7d7000-70eb7db000 rw-p 00000000 00:05 10270915                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb7db000-70eb7df000 rw-p 00000000 00:05 10270914                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb7df000-70eb7e3000 rw-p 00000000 00:05 10270913                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb7e3000-70eb7e7000 rw-p 00000000 00:05 10270912                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb7e7000-70eb7e8000 r--p 00000000 00:00 0                              [anon:linker_alloc]
+70eb7e8000-70eb7ea000 r--s 00000000 fc:00 174                            /system/fonts/NotoSansCarian-Regular.ttf
+70eb7ea000-70eb7ee000 rw-p 00000000 00:05 10270911                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb7ee000-70eb7f2000 rw-p 00000000 00:05 10270910                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb7f2000-70eb7f6000 rw-p 00000000 00:05 10270909                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb7f6000-70eb7f7000 rw-p 00000000 00:00 0                              [anon:linker_alloc]
+70eb7f7000-70eb7f8000 r--s 00000000 fc:00 1096                           /system/usr/hyphen-data/hyph-eu.hyb
+70eb7f8000-70eb7fc000 rw-p 00000000 00:05 10270908                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb7fc000-70eb800000 rw-p 00000000 00:05 10270907                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb800000-70eb804000 rw-p 00000000 00:05 10270906                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb804000-70eb808000 rw-p 00000000 00:05 10270905                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb808000-70eb80c000 rw-p 00000000 00:05 10270904                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb80c000-70eb810000 rw-p 00000000 00:05 10270903                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb810000-70eb814000 rw-p 00000000 00:05 10270902                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb814000-70eb815000 rw-p 00000000 00:00 0                              [anon:linker_alloc_vector]
+70eb815000-70eb819000 rw-p 00000000 00:05 10270901                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb819000-70eb81d000 rw-p 00000000 00:05 10270900                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb81d000-70eb81e000 rw-p 00000000 00:00 0                              [anon:linker_alloc]
+70eb81e000-70eb822000 rw-p 00000000 00:05 10270899                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb822000-70eb826000 rw-p 00000000 00:05 10270898                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb826000-70eb82a000 rw-p 00000000 00:05 10270897                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb82a000-70eb82e000 rw-p 00000000 00:05 10270896                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb82e000-70eb832000 rw-p 00000000 00:05 10270895                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb832000-70eb836000 rw-p 00000000 00:05 10270894                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb836000-70eb837000 r--p 00000000 00:00 0                              [anon:linker_alloc]
+70eb837000-70eb83b000 rw-p 00000000 00:05 10270893                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb83b000-70eb83f000 rw-p 00000000 00:05 10270892                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb83f000-70eb843000 rw-p 00000000 00:05 10270891                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb843000-70eb847000 rw-p 00000000 00:05 10270890                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb847000-70eb84b000 rw-p 00000000 00:05 10270889                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb84b000-70eb84f000 rw-p 00000000 00:05 10270888                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb84f000-70eb850000 rw-p 00000000 00:00 0                              [anon:linker_alloc]
+70eb850000-70eb854000 rw-p 00000000 00:05 10270887                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb854000-70eb858000 rw-p 00000000 00:05 10270886                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb858000-70eb85c000 rw-p 00000000 00:05 10270885                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb85c000-70eb860000 rw-p 00000000 00:05 10270884                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb860000-70eb864000 rw-p 00000000 00:05 10270883                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb864000-70eb868000 rw-p 00000000 00:05 10270882                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb868000-70eb869000 r--p 00000000 00:00 0                              [anon:linker_alloc]
+70eb869000-70eb86d000 rw-p 00000000 00:05 10270881                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb86d000-70eb871000 rw-p 00000000 00:05 10270880                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb871000-70eb875000 rw-p 00000000 00:05 10270879                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb875000-70eb879000 rw-p 00000000 00:05 10270878                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb879000-70eb87d000 rw-p 00000000 00:05 10270877                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb87d000-70eb881000 rw-p 00000000 00:05 10270876                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb881000-70eb885000 rw-p 00000000 00:05 10270875                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb885000-70eb889000 rw-p 00000000 00:05 10270874                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb889000-70eb88d000 rw-p 00000000 00:05 10270873                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb88d000-70eb891000 rw-p 00000000 00:05 10270872                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb891000-70eb892000 r--p 00000000 00:00 0                              [anon:linker_alloc]
+70eb892000-70eb896000 rw-p 00000000 00:05 10270871                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb896000-70eb89a000 rw-p 00000000 00:05 10270870                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb89a000-70eb89b000 rw-p 00000000 00:00 0                              [anon:linker_alloc_vector]
+70eb89b000-70eb89c000 rw-p 00000000 00:00 0                              [anon:linker_alloc_small_objects]
+70eb89c000-70eb8a0000 rw-p 00000000 00:05 10270869                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb8a0000-70eb8a4000 rw-p 00000000 00:05 10270868                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb8a4000-70eb8a5000 r--p 00000000 00:00 0                              [anon:atexit handlers]
+70eb8a5000-70eb8a9000 rw-p 00000000 00:05 10270867                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb8a9000-70eb8ad000 rw-p 00000000 00:05 10270866                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb8ad000-70eb8b1000 rw-p 00000000 00:05 10270865                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb8b1000-70eb8b5000 rw-p 00000000 00:05 10270864                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb8b5000-70eb8b9000 rw-p 00000000 00:05 10270863                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb8b9000-70eb8bd000 rw-p 00000000 00:05 10270862                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb8bd000-70eb8be000 rw-p 00000000 00:00 0                              [anon:linker_alloc]
+70eb8be000-70eb8c1000 r--s 00000000 fc:00 168                            /system/fonts/NotoSansAvestan-Regular.ttf
+70eb8c1000-70eb8c5000 rw-p 00000000 00:05 10270861                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb8c5000-70eb8c9000 rw-p 00000000 00:05 10270860                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb8c9000-70eb8cd000 rw-p 00000000 00:05 10270859                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb8cd000-70eb8d1000 rw-p 00000000 00:05 10270858                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb8d1000-70eb8d5000 rw-p 00000000 00:05 10270857                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb8d5000-70eb8d7000 r--p 00000000 00:00 0                              [anon:linker_alloc]
+70eb8d7000-70eb8db000 rw-p 00000000 00:05 10270856                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb8db000-70eb8df000 rw-p 00000000 00:05 10270855                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb8df000-70eb8e3000 rw-p 00000000 00:05 10270854                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb8e3000-70eb8e7000 rw-p 00000000 00:05 10270853                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb8e7000-70eb8eb000 rw-p 00000000 00:05 10270852                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb8eb000-70eb8ec000 r--p 00000000 00:00 0                              [anon:linker_alloc]
+70eb8ec000-70eb8ed000 r--s 00000000 fc:00 1099                           /system/usr/hyphen-data/hyph-bn.hyb
+70eb8ed000-70eb8f1000 rw-p 00000000 00:05 10270851                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb8f1000-70eb8f5000 rw-p 00000000 00:05 10270850                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb8f5000-70eb8f9000 rw-p 00000000 00:05 10270849                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb8f9000-70eb8fd000 rw-p 00000000 00:05 10270848                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb8fd000-70eb901000 rw-p 00000000 00:05 10270847                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb901000-70eb905000 rw-p 00000000 00:05 10270846                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb905000-70eb909000 rw-p 00000000 00:05 10270845                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb909000-70eb90d000 rw-p 00000000 00:05 10270844                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb90d000-70eb911000 rw-p 00000000 00:05 10270843                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb911000-70eb915000 rw-p 00000000 00:05 10270842                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb915000-70eb916000 rw-p 00000000 00:00 0                              [anon:linker_alloc]
+70eb916000-70eb917000 r--s 00000000 fc:00 1114                           /system/usr/hyphen-data/hyph-bg.hyb
+70eb917000-70eb91b000 rw-p 00000000 00:05 10270841                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb91b000-70eb91c000 r--p 00000000 00:00 0                              [anon:linker_alloc]
+70eb91c000-70eb91d000 r--s 00000000 fc:00 1133                           /system/usr/hyphen-data/hyph-as.hyb
+70eb91d000-70eb921000 rw-p 00000000 00:05 10270840                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb921000-70eb925000 rw-p 00000000 00:05 10270839                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb925000-70eb926000 rw-p 00000000 00:00 0                              [anon:linker_alloc_small_objects]
+70eb926000-70eb927000 r--p 00000000 00:00 0                              [anon:linker_alloc]
+70eb927000-70eb929000 r--s 00000000 fc:00 203                            /system/fonts/NotoSansBuhid-Regular.ttf
+70eb929000-70eb92d000 rw-p 00000000 00:05 10270838                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb92d000-70eb931000 rw-p 00000000 00:05 10270837                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb931000-70eb935000 rw-p 00000000 00:05 10270836                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb935000-70eb939000 rw-p 00000000 00:05 10270835                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb939000-70eb93d000 rw-p 00000000 00:05 10270834                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb93d000-70eb941000 rw-p 00000000 00:05 10270833                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb941000-70eb945000 rw-p 00000000 00:05 10270832                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb945000-70eb949000 rw-p 00000000 00:05 10270831                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb949000-70eb94d000 rw-p 00000000 00:05 10270830                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb94d000-70eb951000 rw-p 00000000 00:05 10270829                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb951000-70eb991000 rw-p 00000000 00:05 10270722                       /dev/ashmem/dalvik-mark stack (deleted)
+70eb991000-70eb992000 rw-p 00000000 00:00 0                              [anon:linker_alloc]
+70eb992000-70eb996000 rw-p 00000000 00:05 10270828                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb996000-70eb99a000 rw-p 00000000 00:05 10270827                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb99a000-70eb99e000 rw-p 00000000 00:05 10270826                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb99e000-70eb9a2000 rw-p 00000000 00:05 10270825                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb9a2000-70eb9a4000 r--p 00000000 00:00 0                              [anon:linker_alloc]
+70eb9a4000-70eb9a8000 rw-p 00000000 00:05 10270824                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb9a8000-70eb9ac000 rw-p 00000000 00:05 10270823                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb9ac000-70eb9b0000 rw-p 00000000 00:05 10270822                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb9b0000-70eb9b1000 r--p 00000000 00:00 0                              [anon:linker_alloc]
+70eb9b1000-70eb9b2000 r--s 00021000 fc:01 1180                           /vendor/overlay/framework-res__auto_generated_rro.apk
+70eb9b2000-70eb9b6000 rw-p 00000000 00:05 10270821                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb9b6000-70eb9ba000 rw-p 00000000 00:05 10270820                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb9ba000-70eb9be000 rw-p 00000000 00:05 10270819                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb9be000-70eb9c2000 rw-p 00000000 00:05 10270818                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb9c2000-70eb9c6000 rw-p 00000000 00:05 10270817                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb9c6000-70eb9ca000 rw-p 00000000 00:05 10270816                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb9ca000-70eb9ce000 rw-p 00000000 00:05 10270815                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb9ce000-70eb9cf000 rw-p 00000000 00:00 0                              [anon:linker_alloc]
+70eb9cf000-70eb9d1000 r--s 00000000 fc:00 213                            /system/fonts/NotoSansBuginese-Regular.ttf
+70eb9d1000-70eb9d5000 rw-p 00000000 00:05 10270814                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb9d5000-70eb9d9000 rw-p 00000000 00:05 10270813                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb9d9000-70eb9dd000 rw-p 00000000 00:05 10270812                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb9dd000-70eb9e1000 rw-p 00000000 00:05 10270811                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb9e1000-70eb9e5000 rw-p 00000000 00:05 10270810                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb9e5000-70eb9e9000 rw-p 00000000 00:05 10270809                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb9e9000-70eb9ed000 rw-p 00000000 00:05 10270808                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb9ed000-70eb9f1000 rw-p 00000000 00:05 10270807                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb9f1000-70eb9f5000 rw-p 00000000 00:05 10270806                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb9f5000-70eb9f6000 r--p 00000000 00:00 0                              [anon:linker_alloc]
+70eb9f6000-70eb9f8000 rw-p 00000000 00:05 10271002                       /dev/ashmem/dalvik-indirect ref table (deleted)
+70eb9f8000-70eb9fc000 rw-p 00000000 00:05 10270805                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb9fc000-70eb9fd000 rw-p 00000000 00:00 0                              [anon:linker_alloc]
+70eb9fd000-70eb9ff000 rw-p 00000000 00:05 10270999                       /dev/ashmem/dalvik-indirect ref table (deleted)
+70eb9ff000-70eba00000 r--s 00000000 fc:00 983                            /system/framework/com.google.vr.platform.jar
+70eba00000-70eba04000 rw-p 00000000 00:05 10270804                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eba04000-70eba08000 rw-p 00000000 00:05 10270803                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eba08000-70eba0c000 rw-p 00000000 00:05 10270802                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eba0c000-70eba10000 rw-p 00000000 00:05 10270801                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eba10000-70eba14000 rw-p 00000000 00:05 10270800                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eba14000-70eba18000 rw-p 00000000 00:05 10270799                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eba18000-70eba1c000 rw-p 00000000 00:05 10270798                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eba1c000-70eba20000 rw-p 00000000 00:05 10270797                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eba20000-70eba24000 rw-p 00000000 00:05 10270796                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eba24000-70eba28000 rw-p 00000000 00:05 10270795                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eba28000-70eba2c000 rw-p 00000000 00:05 10270794                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eba2c000-70eba2d000 rw-p 00000000 00:00 0                              [anon:linker_alloc]
+70eba2d000-70eba2e000 r--s 00000000 fc:00 881                            /system/framework/android.test.base.jar
+70eba2e000-70eba2f000 r--s 00000000 fc:00 707                            /system/framework/framework-oahl-backward-compatibility.jar
+70eba2f000-70eba30000 r--s 00000000 fc:00 705                            /system/framework/android.hidl.manager-V1.0-java.jar
+70eba30000-70eba34000 rw-p 00000000 00:05 10270793                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eba34000-70eba38000 rw-p 00000000 00:05 10270792                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eba38000-70eba3c000 rw-p 00000000 00:05 10270791                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eba3c000-70eba40000 rw-p 00000000 00:05 10270790                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eba40000-70eba44000 rw-p 00000000 00:05 10270789                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eba44000-70eba48000 rw-p 00000000 00:05 10270788                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eba48000-70eba4c000 rw-p 00000000 00:05 10270787                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eba4c000-70eba50000 rw-p 00000000 00:05 10270786                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eba50000-70eba52000 r--p 00000000 00:00 0                              [anon:linker_alloc]
+70eba52000-70eba53000 r--s 00000000 fc:00 971                            /system/framework/android.hidl.base-V1.0-java.jar
+70eba53000-70eba57000 rw-p 00000000 00:05 10270785                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eba57000-70eba5b000 rw-p 00000000 00:05 10270784                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eba5b000-70eba5f000 rw-p 00000000 00:05 10270783                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eba5f000-70eba63000 rw-p 00000000 00:05 10270782                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eba63000-70eba64000 rw-p 00000000 00:00 0                              [anon:linker_alloc]
+70eba64000-70eba65000 r--s 00000000 fc:00 889                            /system/framework/ims-common.jar
+70eba65000-70eba69000 rw-p 00000000 00:05 10270781                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eba69000-70eba6d000 rw-p 00000000 00:05 10270780                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eba6d000-70eba71000 rw-p 00000000 00:05 10270779                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eba71000-70eba75000 rw-p 00000000 00:05 10270778                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eba75000-70eba95000 rw-p 00000000 00:05 10267647                       /dev/ashmem/dalvik-large marked objects (deleted)
+70eba95000-70ebab5000 rw-p 00000000 00:05 10267646                       /dev/ashmem/dalvik-large live objects (deleted)
+70ebab5000-70ebab6000 r--p 00000000 00:00 0                              [anon:linker_alloc]
+70ebab6000-70ebab7000 rw-p 00000000 00:00 0                              [anon:linker_alloc]
+70ebab7000-70ebabb000 rw-p 00000000 00:05 10270777                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70ebabb000-70ebadb000 r--s 00000000 00:10 16603                          /dev/__properties__/u:object_r:exported_fingerprint_prop:s0
+70ebadb000-70ebadc000 r--p 00000000 00:00 0                              [anon:linker_alloc]
+70ebadc000-70ebadd000 r--s 00000000 fc:00 878                            /system/framework/voip-common.jar
+70ebadd000-70ebadf000 rw-p 00000000 00:05 10270995                       /dev/ashmem/dalvik-indirect ref table (deleted)
+70ebadf000-70ebae3000 rw-p 00000000 00:05 10270776                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70ebae3000-70ebae7000 rw-p 00000000 00:05 10270775                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70ebae7000-70ebaeb000 rw-p 00000000 00:05 10270774                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70ebaeb000-70ebaef000 rw-p 00000000 00:05 10270773                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70ebaef000-70ebb0f000 r--s 00000000 00:10 16582                          /dev/__properties__/u:object_r:debug_prop:s0
+70ebb0f000-70ebb10000 rw-p 00000000 00:00 0                              [anon:linker_alloc]
+70ebb10000-70ebb11000 r--s 00000000 fc:00 703                            /system/framework/telephony-common.jar
+70ebb11000-70ebb13000 rw-p 00000000 00:05 10270994                       /dev/ashmem/dalvik-indirect ref table (deleted)
+70ebb13000-70ebb17000 rw-p 00000000 00:05 10270772                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70ebb17000-70ebb19000 r--p 00000000 00:00 0                              [anon:linker_alloc]
+70ebb19000-70ebb1d000 rw-p 00000000 00:05 10270771                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70ebb1d000-70ebb3d000 r--s 00000000 00:10 16600                          /dev/__properties__/u:object_r:exported_default_prop:s0
+70ebb3d000-70ebb5d000 r--s 00000000 00:10 16650                          /dev/__properties__/u:object_r:system_prop:s0
+70ebb5d000-70ebb7d000 r--s 00000000 00:10 16610                          /dev/__properties__/u:object_r:exported_vold_prop:s0
+70ebb7d000-70ebb9d000 r--s 00000000 00:10 16598                          /dev/__properties__/u:object_r:exported_config_prop:s0
+70ebb9d000-70ebb9e000 rw-p 00000000 00:00 0                              [anon:linker_alloc]
+70ebb9e000-70ebba2000 rw-p 00000000 00:05 10270770                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70ebba2000-70ebba6000 rw-p 00000000 00:05 10270769                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70ebba6000-70ebba7000 r--p 00000000 00:00 0                              [anon:linker_alloc]
+70ebba7000-70ebba8000 r--s 00000000 fc:00 1004                           /system/framework/framework.jar
+70ebba8000-70ebbac000 rw-p 00000000 00:05 10270768                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70ebbac000-70ebbb0000 rw-p 00000000 00:05 10270767                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70ebbb0000-70ebbb4000 rw-p 00000000 00:05 10270766                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70ebbb4000-70ebbb8000 rw-p 00000000 00:05 10270765                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70ebbb8000-70ebbbc000 rw-p 00000000 00:05 10270764                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70ebbbc000-70ebbc0000 rw-p 00000000 00:05 10270763                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70ebbc0000-70ebbc4000 rw-p 00000000 00:05 10270762                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70ebbc4000-70ebbe4000 r--s 00000000 00:10 16581                          /dev/__properties__/u:object_r:dalvik_prop:s0
+70ebbe4000-70ebbe5000 rw-p 00000000 00:00 0                              [anon:linker_alloc]
+70ebbe5000-70ebbe6000 r--s 00004000 fc:00 877                            /system/framework/apache-xml.jar
+70ebbe6000-70ebbe8000 rw-p 00000000 00:05 10270993                       /dev/ashmem/dalvik-indirect ref table (deleted)
+70ebbe8000-70ebbec000 rw-p 00000000 00:05 10270761                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70ebbec000-70ebbf0000 rw-p 00000000 00:05 10270760                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70ebbf0000-70ebbf4000 rw-p 00000000 00:05 10270759                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70ebbf4000-70ebbf8000 rw-p 00000000 00:05 10270758                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70ebbf8000-70ebbf9000 r--p 00000000 00:00 0                              [anon:linker_alloc]
+70ebbf9000-70ebbfa000 r--s 00000000 fc:00 968                            /system/framework/bouncycastle.jar
+70ebbfa000-70ebbfc000 rw-p 00000000 00:05 10270992                       /dev/ashmem/dalvik-indirect ref table (deleted)
+70ebbfc000-70ebc00000 rw-p 00000000 00:05 10270757                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70ebc00000-70ebc04000 rw-p 00000000 00:05 10270756                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70ebc04000-70ebc08000 rw-p 00000000 00:05 10270755                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70ebc08000-70ebc0c000 rw-p 00000000 00:05 10270754                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70ebc0c000-70ebc10000 rw-p 00000000 00:05 10270753                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70ebc10000-70ebc14000 rw-p 00000000 00:05 10270752                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70ebc14000-70ebc15000 rw-p 00000000 00:00 0                              [anon:linker_alloc]
+70ebc15000-70ebc16000 r--s 00000000 fc:00 960                            /system/framework/okhttp.jar
+70ebc16000-70ebc1a000 rw-p 00000000 00:05 10270751                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70ebc1a000-70ebc1e000 rw-p 00000000 00:05 10270750                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70ebc1e000-70ebc3e000 r--s 00000000 00:10 16584                          /dev/__properties__/u:object_r:default_prop:s0
+70ebc3e000-70ebc3f000 r--p 00000000 00:00 0                              [anon:linker_alloc]
+70ebc3f000-70ebc40000 r--s 00000000 fc:00 974                            /system/framework/conscrypt.jar
+70ebc40000-70ebc42000 rw-p 00000000 00:05 10269719                       /dev/ashmem/dalvik-indirect ref table (deleted)
+70ebc42000-70ebc46000 rw-p 00000000 00:05 10270749                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70ebc46000-70ebc4a000 rw-p 00000000 00:05 10270748                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70ebc4a000-70ebc4e000 rw-p 00000000 00:05 10270747                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70ebc4e000-70ebc4f000 rw-p 00000000 00:00 0                              [anon:linker_alloc]
+70ebc4f000-70ebc51000 rw-p 00000000 00:05 10269718                       /dev/ashmem/dalvik-indirect ref table (deleted)
+70ebc51000-70ebc55000 rw-p 00000000 00:05 10270746                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70ebc55000-70ebc59000 rw-p 00000000 00:05 10270745                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70ebc59000-70ebc5d000 rw-p 00000000 00:05 10270744                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70ebc5d000-70ebc7d000 r--s 00000000 00:10 16599                          /dev/__properties__/u:object_r:exported_dalvik_prop:s0
+70ebc7d000-70ebc7e000 rw-p 00000000 00:00 0                              [anon:linker_alloc]
+70ebc7e000-70ebc7f000 r--p 00000000 00:00 0                              [anon:linker_alloc]
+70ebc7f000-70ebc80000 r--s 00004000 fc:00 963                            /system/framework/core-libart.jar
+70ebc80000-70ebc81000 r--p 00000000 00:00 0                              [anon:linker_alloc]
+70ebc81000-70ebc85000 rw-p 00000000 00:05 10270743                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70ebc85000-70ebc89000 rw-p 00000000 00:05 10270742                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70ebc89000-70ebc8a000 rw-p 00000000 00:00 0                              [anon:linker_alloc_small_objects]
+70ebc8a000-70ebc8c000 r--p 00000000 00:00 0                              [anon:linker_alloc]
+70ebc8c000-70ebc8d000 r--s 0001e000 fc:00 699                            /system/framework/core-oj.jar
+70ebc8d000-70ebc91000 rw-p 00000000 00:05 10270741                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70ebc91000-70ebc95000 rw-p 00000000 00:05 10270740                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70ebc95000-70ebc99000 rw-p 00000000 00:05 10270739                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70ebc99000-70ebc9b000 r--p 00000000 00:00 0                              [anon:linker_alloc]
+70ebc9b000-70ebc9c000 rw-p 00000000 00:00 0                              [anon:linker_alloc]
+70ebc9c000-70ebca0000 rw-p 00000000 00:05 10270738                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70ebca0000-70ebca4000 rw-p 00000000 00:05 10270737                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70ebca4000-70ebca8000 rw-p 00000000 00:05 10270736                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70ebca8000-70ebcac000 rw-p 00000000 00:05 10270735                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70ebcac000-70ebcb0000 rw-p 00000000 00:05 10270734                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70ebcb0000-70ebcd0000 r--s 00000000 00:10 16592                          /dev/__properties__/u:object_r:exported2_system_prop:s0
+70ebcd0000-70ebcd1000 rw-p 00000000 00:00 0                              [anon:linker_alloc]
+70ebcd1000-70ebcd5000 rw-p 00000000 00:05 10270733                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70ebcd5000-70ebcd9000 rw-p 00000000 00:05 10270732                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70ebcd9000-70ebcdd000 rw-p 00000000 00:05 10270731                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70ebcdd000-70ebce1000 rw-p 00000000 00:05 10270730                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70ebce1000-70ebce5000 rw-p 00000000 00:05 10270729                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70ebce5000-70ebce9000 rw-p 00000000 00:05 10270728                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70ebce9000-70ebcea000 r--p 00000000 00:00 0                              [anon:linker_alloc]
+70ebcea000-70ebcec000 rw-p 00000000 00:05 10270987                       /dev/ashmem/dalvik-indirect ref table (deleted)
+70ebcec000-70ebcf9000 r--p 00646000 103:1d 639532                        /data/dalvik-cache/arm64/system@framework@boot-framework.art
+70ebcf9000-70ebd19000 r--s 00000000 00:10 16620                          /dev/__properties__/u:object_r:log_tag_prop:s0
+70ebd19000-70ebd39000 r--s 00000000 00:10 16621                          /dev/__properties__/u:object_r:logd_prop:s0
+70ebd39000-70ebd3a000 rw-p 00000000 00:00 0                              [anon:linker_alloc]
+70ebd3a000-70ebd3b000 r--p 00000000 00:00 0                              [anon:linker_alloc]
+70ebd3b000-70ebd3f000 rw-p 00000000 00:05 10270727                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70ebd3f000-70ebd40000 r--p 00002000 103:1d 639556                        /data/dalvik-cache/arm64/system@framework@boot-com.google.vr.platform.art
+70ebd40000-70ebd41000 r--p 00005000 103:1d 639553                        /data/dalvik-cache/arm64/system@framework@boot-android.test.base.art
+70ebd41000-70ebd42000 r--p 00000000 00:00 0                              [anon:linker_alloc]
+70ebd42000-70ebd43000 r--p 00001000 103:1d 639550                        /data/dalvik-cache/arm64/system@framework@boot-framework-oahl-backward-compatibility.art
+70ebd43000-70ebd44000 r--p 00005000 103:1d 639547                        /data/dalvik-cache/arm64/system@framework@boot-android.hidl.manager-V1.0-java.art
+70ebd44000-70ebd45000 r--p 00003000 103:1d 639544                        /data/dalvik-cache/arm64/system@framework@boot-android.hidl.base-V1.0-java.art
+70ebd45000-70ebd46000 r--p 00000000 00:00 0                              [anon:linker_alloc]
+70ebd46000-70ebd47000 r--p 0000f000 103:1d 639541                        /data/dalvik-cache/arm64/system@framework@boot-ims-common.art
+70ebd47000-70ebd48000 r--p 0000d000 103:1d 639538                        /data/dalvik-cache/arm64/system@framework@boot-voip-common.art
+70ebd48000-70ebd4a000 r--p 0005e000 103:1d 639535                        /data/dalvik-cache/arm64/system@framework@boot-telephony-common.art
+70ebd4a000-70ebd4b000 r--p 00040000 103:1d 639529                        /data/dalvik-cache/arm64/system@framework@boot-ext.art
+70ebd4b000-70ebd4c000 r--p 0004a000 103:1d 639526                        /data/dalvik-cache/arm64/system@framework@boot-apache-xml.art
+70ebd4c000-70ebd4d000 r--p 00046000 103:1d 639523                        /data/dalvik-cache/arm64/system@framework@boot-bouncycastle.art
+70ebd4d000-70ebd4e000 r--p 00000000 00:00 0                              [anon:linker_alloc]
+70ebd4e000-70ebd53000 r--p 00225000 103:1d 639511                        /data/dalvik-cache/arm64/system@framework@boot.art
+70ebd53000-70ebd5a000 rw-p 00000000 fc:00 583                            /system/etc/event-log-tags
+70ebd5a000-70ebd5b000 r--p 00000000 00:00 0                              [anon:linker_alloc]
+70ebd5b000-70ebd5c000 r--p 0002e000 103:1d 639520                        /data/dalvik-cache/arm64/system@framework@boot-okhttp.art
+70ebd5c000-70ebd5d000 r--p 00035000 103:1d 639517                        /data/dalvik-cache/arm64/system@framework@boot-conscrypt.art
+70ebd5d000-70ebd5f000 r--p 000d0000 103:1d 639514                        /data/dalvik-cache/arm64/system@framework@boot-core-libart.art
+70ebd5f000-70ebd62000 r--p 00000000 00:00 0                              [anon:atexit handlers]
+70ebd62000-70ebd63000 rw-p 00000000 00:00 0 
+70ebd63000-70ebd83000 r--s 00000000 00:10 16590                          /dev/__properties__/u:object_r:exported2_default_prop:s0
+70ebd83000-70ebd84000 rw-p 00000000 00:00 0                              [anon:linker_alloc]
+70ebd84000-70ebd89000 rw-p 00000000 00:00 0 
+70ebd89000-70ebda9000 r--s 00000000 00:10 16669                          /dev/__properties__/properties_serial
+70ebda9000-70ebdb3000 r--s 00000000 00:10 16560                          /dev/__properties__/property_info
+70ebdb3000-70ebdb4000 rw-p 00000000 00:00 0                              [anon:linker_alloc_vector]
+70ebdb4000-70ebdb5000 rw-p 00000000 00:00 0                              [anon:linker_alloc_small_objects]
+70ebdb5000-70ebdb7000 rw-p 00000000 00:00 0                              [anon:System property context nodes]
+70ebdb7000-70ebdb8000 rw-p 00000000 00:00 0                              [anon:linker_alloc_vector]
+70ebdb8000-70ebdba000 rw-p 00000000 00:00 0                              [anon:linker_alloc_small_objects]
+70ebdba000-70ebdbb000 rw-p 00000000 00:00 0                              [anon:linker_alloc]
+70ebdbb000-70ebdbd000 rw-p 00000000 00:00 0                              [anon:linker_alloc_small_objects]
+70ebdbd000-70ebdbe000 r--p 00000000 00:00 0                              [anon:atexit handlers]
+70ebdbe000-70ebdbf000 r--p 00000000 00:00 0                              [anon:linker_alloc]
+70ebdbf000-70ebdc0000 rw-p 00000000 00:00 0                              [anon:linker_alloc_small_objects]
+70ebdc0000-70ebdc1000 rw-p 00000000 00:00 0                              [anon:linker_alloc_vector]
+70ebdc1000-70ebdc2000 rw-p 00000000 00:00 0                              [anon:linker_alloc_small_objects]
+70ebdc2000-70ebdc3000 rw-p 00000000 00:00 0                              [anon:linker_alloc_vector]
+70ebdc3000-70ebdc5000 rw-p 00000000 00:00 0                              [anon:linker_alloc_small_objects]
+70ebdc5000-70ebde5000 r--s 00000000 00:10 16600                          /dev/__properties__/u:object_r:exported_default_prop:s0
+70ebde5000-70ebde6000 rw-p 00000000 00:00 0                              [anon:linker_alloc_small_objects]
+70ebde6000-70ebde8000 r--p 00000000 00:00 0                              [anon:linker_alloc]
+70ebde8000-70ebe08000 r--s 00000000 00:10 16582                          /dev/__properties__/u:object_r:debug_prop:s0
+70ebe08000-70ebe09000 ---p 00000000 00:00 0 
+70ebe09000-70ebe0a000 rw-p 00000000 00:00 0 
+70ebe0a000-70ebe0b000 ---p 00000000 00:00 0 
+70ebe0b000-70ebe2b000 r--s 00000000 00:10 16669                          /dev/__properties__/properties_serial
+70ebe2b000-70ebe2d000 rw-p 00000000 00:00 0                              [anon:System property context nodes]
+70ebe2d000-70ebf55000 r-xp 00000000 fc:00 3184                           /system/bin/linker64
+70ebf55000-70ebf5f000 r--s 00000000 00:10 16560                          /dev/__properties__/property_info
+70ebf5f000-70ebf60000 r--p 00000000 00:00 0                              [anon:linker_alloc]
+70ebf60000-70ebf61000 rw-p 00000000 00:00 0                              [anon:linker_alloc_vector]
+70ebf61000-70ebf62000 rw-p 00000000 00:00 0                              [anon:linker_alloc_small_objects]
+70ebf62000-70ebf63000 rw-p 00000000 00:00 0                              [anon:arc4random data]
+70ebf63000-70ebf64000 rw-p 00000000 00:00 0                              [anon:linker_alloc_small_objects]
+70ebf64000-70ebf65000 r--p 00000000 00:00 0                              [anon:atexit handlers]
+70ebf65000-70ebf66000 ---p 00000000 00:00 0                              [anon:thread signal stack guard]
+70ebf66000-70ebf6a000 rw-p 00000000 00:00 0                              [anon:thread signal stack]
+70ebf6a000-70ebf6b000 rw-p 00000000 00:00 0                              [anon:arc4random data]
+70ebf6b000-70ebf6c000 ---p 00000000 00:00 0                              [anon:bionic TLS guard]
+70ebf6c000-70ebf6f000 rw-p 00000000 00:00 0                              [anon:bionic TLS]
+70ebf6f000-70ebf70000 ---p 00000000 00:00 0                              [anon:bionic TLS guard]
+70ebf70000-70ebf71000 r--p 00000000 00:00 0                              [vvar]
+70ebf71000-70ebf72000 r-xp 00000000 00:00 0                              [vdso]
+70ebf72000-70ebf7d000 r--p 00135000 fc:00 3184                           /system/bin/linker64
+70ebf7d000-70ebf7e000 rw-p 00140000 fc:00 3184                           /system/bin/linker64
+70ebf7e000-70ebf81000 rw-p 00000000 00:00 0 
+70ebf81000-70ebf82000 r--p 00000000 00:00 0 
+70ebf82000-70ebf89000 rw-p 00000000 00:00 0 
+7fc7df1000-7fc7df2000 ---p 00000000 00:00 0 
+7fc7df2000-7fc85f1000 rw-p 00000000 00:00 0                              [stack]
diff --git a/libqtaguid/Android.bp b/libqtaguid/Android.bp
new file mode 100644
index 0000000..de632ca
--- /dev/null
+++ b/libqtaguid/Android.bp
@@ -0,0 +1,56 @@
+//
+// Copyright (C) 2017 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_library_headers {
+    name: "libqtaguid_headers",
+    vendor_available: false,
+    host_supported: false,
+    export_include_dirs: ["include"],
+    target: {
+        linux_bionic: {
+            enabled: true,
+        },
+    },
+}
+
+cc_library {
+    name: "libqtaguid",
+    vendor_available: false,
+    host_supported: false,
+    target: {
+        android: {
+            srcs: [
+                "qtaguid.c",
+            ],
+            sanitize: {
+                misc_undefined: ["integer"],
+            },
+        },
+    },
+
+    shared_libs: ["liblog"],
+    header_libs: [
+        "libqtaguid_headers",
+    ],
+    export_header_lib_headers: ["libqtaguid_headers"],
+    local_include_dirs: ["include"],
+
+    cflags: [
+        "-Werror",
+        "-Wall",
+        "-Wextra",
+    ],
+}
diff --git a/libqtaguid/include/qtaguid/qtaguid.h b/libqtaguid/include/qtaguid/qtaguid.h
new file mode 100644
index 0000000..72285e5
--- /dev/null
+++ b/libqtaguid/include/qtaguid/qtaguid.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2011 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 __LEGACY_QTAGUID_H
+#define __LEGACY_QTAGUID_H
+
+#include <stdint.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Set tags (and owning UIDs) for network sockets. The socket must be untagged
+ * by calling qtaguid_untagSocket() before closing it, otherwise the qtaguid
+ * module will keep a reference to it even after close.
+ */
+extern int legacy_tagSocket(int sockfd, int tag, uid_t uid);
+
+/*
+ * Untag a network socket before closing.
+ */
+extern int legacy_untagSocket(int sockfd);
+
+/*
+ * For the given uid, switch counter sets.
+ * The kernel only keeps a limited number of sets.
+ * 2 for now.
+ */
+extern int legacy_setCounterSet(int counterSetNum, uid_t uid);
+
+/*
+ * Delete all tag info that relates to the given tag an uid.
+ * If the tag is 0, then ALL info about the uid is freeded.
+ * The delete data also affects active tagged socketd, which are
+ * then untagged.
+ * The calling process can only operate on its own tags.
+ * Unless it is part of the happy AID_NET_BW_ACCT group.
+ * In which case it can clobber everything.
+ */
+extern int legacy_deleteTagData(int tag, uid_t uid);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __LEGACY_QTAGUID_H */
diff --git a/libqtaguid/qtaguid.c b/libqtaguid/qtaguid.c
new file mode 100644
index 0000000..cd38bad
--- /dev/null
+++ b/libqtaguid/qtaguid.c
@@ -0,0 +1,143 @@
+/*
+** Copyright 2011, 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.
+*/
+
+// #define LOG_NDEBUG 0
+
+#define LOG_TAG "qtaguid"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <log/log.h>
+#include <qtaguid/qtaguid.h>
+
+static const char* CTRL_PROCPATH = "/proc/net/xt_qtaguid/ctrl";
+static const int CTRL_MAX_INPUT_LEN = 128;
+
+/*
+ * One per proccess.
+ * Once the device is open, this process will have its socket tags tracked.
+ * And on exit or untimely death, all socket tags will be removed.
+ * A process can only open /dev/xt_qtaguid once.
+ * It should not close it unless it is really done with all the socket tags.
+ * Failure to open it will be visible when socket tagging will be attempted.
+ */
+static int resTrackFd = -1;
+pthread_once_t resTrackInitDone = PTHREAD_ONCE_INIT;
+
+/* Only call once per process. */
+void legacy_resTrack(void) {
+    resTrackFd = TEMP_FAILURE_RETRY(open("/dev/xt_qtaguid", O_RDONLY | O_CLOEXEC));
+}
+
+/*
+ * Returns:
+ *   0 on success.
+ *   -errno on failure.
+ */
+static int write_ctrl(const char* cmd) {
+    int fd, res, savedErrno;
+
+    ALOGV("write_ctrl(%s)", cmd);
+
+    fd = TEMP_FAILURE_RETRY(open(CTRL_PROCPATH, O_WRONLY | O_CLOEXEC));
+    if (fd < 0) {
+        return -errno;
+    }
+
+    res = TEMP_FAILURE_RETRY(write(fd, cmd, strlen(cmd)));
+    if (res < 0) {
+        savedErrno = errno;
+    } else {
+        savedErrno = 0;
+    }
+    if (res < 0) {
+        // ALOGV is enough because all the callers also log failures
+        ALOGV("Failed write_ctrl(%s) res=%d errno=%d", cmd, res, savedErrno);
+    }
+    close(fd);
+    return -savedErrno;
+}
+
+int legacy_tagSocket(int sockfd, int tag, uid_t uid) {
+    char lineBuf[CTRL_MAX_INPUT_LEN];
+    int res;
+    uint64_t kTag = ((uint64_t)tag << 32);
+
+    pthread_once(&resTrackInitDone, legacy_resTrack);
+
+    snprintf(lineBuf, sizeof(lineBuf), "t %d %" PRIu64 " %d", sockfd, kTag, uid);
+
+    ALOGV("Tagging socket %d with tag %" PRIx64 "{%u,0} for uid %d", sockfd, kTag, tag, uid);
+
+    res = write_ctrl(lineBuf);
+    if (res < 0) {
+        ALOGI("Tagging socket %d with tag %" PRIx64 "(%d) for uid %d failed errno=%d", sockfd, kTag,
+              tag, uid, res);
+    }
+
+    return res;
+}
+
+int legacy_untagSocket(int sockfd) {
+    char lineBuf[CTRL_MAX_INPUT_LEN];
+    int res;
+
+    ALOGV("Untagging socket %d", sockfd);
+
+    snprintf(lineBuf, sizeof(lineBuf), "u %d", sockfd);
+    res = write_ctrl(lineBuf);
+    if (res < 0) {
+        ALOGI("Untagging socket %d failed errno=%d", sockfd, res);
+    }
+
+    return res;
+}
+
+int legacy_setCounterSet(int counterSetNum, uid_t uid) {
+    char lineBuf[CTRL_MAX_INPUT_LEN];
+    int res;
+
+    ALOGV("Setting counters to set %d for uid %d", counterSetNum, uid);
+
+    snprintf(lineBuf, sizeof(lineBuf), "s %d %d", counterSetNum, uid);
+    res = write_ctrl(lineBuf);
+    return res;
+}
+
+int legacy_deleteTagData(int tag, uid_t uid) {
+    char lineBuf[CTRL_MAX_INPUT_LEN];
+    int cnt = 0, res = 0;
+    uint64_t kTag = (uint64_t)tag << 32;
+
+    ALOGV("Deleting tag data with tag %" PRIx64 "{%d,0} for uid %d", kTag, tag, uid);
+
+    pthread_once(&resTrackInitDone, legacy_resTrack);
+
+    snprintf(lineBuf, sizeof(lineBuf), "d %" PRIu64 " %d", kTag, uid);
+    res = write_ctrl(lineBuf);
+    if (res < 0) {
+        ALOGI("Deleting tag data with tag %" PRIx64 "/%d for uid %d failed with cnt=%d errno=%d",
+              kTag, tag, uid, cnt, errno);
+    }
+
+    return res;
+}
diff --git a/libsparse/.clang-format b/libsparse/.clang-format
new file mode 120000
index 0000000..fd0645f
--- /dev/null
+++ b/libsparse/.clang-format
@@ -0,0 +1 @@
+../.clang-format-2
\ No newline at end of file
diff --git a/libsparse/Android.bp b/libsparse/Android.bp
index 6ec0991..8ad339f 100644
--- a/libsparse/Android.bp
+++ b/libsparse/Android.bp
@@ -3,31 +3,24 @@
 cc_library {
     name: "libsparse",
     host_supported: true,
+    recovery_available: true,
     unique_host_soname: true,
     srcs: [
-        "backed_block.c",
-        "output_file.c",
-        "sparse.c",
-        "sparse_crc32.c",
-        "sparse_err.c",
+        "backed_block.cpp",
+        "output_file.cpp",
+        "sparse.cpp",
+        "sparse_crc32.cpp",
+        "sparse_err.cpp",
         "sparse_read.cpp",
     ],
     cflags: ["-Werror"],
     local_include_dirs: ["include"],
     export_include_dirs: ["include"],
+    shared_libs: [
+        "libz",
+        "libbase",
+    ],
     target: {
-        host: {
-            shared_libs: [
-                "libz-host",
-                "libbase",
-            ],
-        },
-        android: {
-            shared_libs: [
-                "libz",
-                "libbase",
-            ],
-        },
         windows: {
             enabled: true,
         },
@@ -38,8 +31,8 @@
     name: "simg2img",
     host_supported: true,
     srcs: [
-        "simg2img.c",
-        "sparse_crc32.c",
+        "simg2img.cpp",
+        "sparse_crc32.cpp",
     ],
     static_libs: [
         "libsparse",
@@ -53,7 +46,7 @@
 cc_binary {
     name: "img2simg",
     host_supported: true,
-    srcs: ["img2simg.c"],
+    srcs: ["img2simg.cpp"],
     static_libs: [
         "libsparse",
         "libz",
@@ -65,7 +58,7 @@
 
 cc_binary_host {
     name: "append2simg",
-    srcs: ["append2simg.c"],
+    srcs: ["append2simg.cpp"],
     static_libs: [
         "libsparse",
         "libz",
diff --git a/libsparse/OWNERS b/libsparse/OWNERS
new file mode 100644
index 0000000..70b375f
--- /dev/null
+++ b/libsparse/OWNERS
@@ -0,0 +1 @@
+ccross@google.com
diff --git a/libsparse/append2simg.c b/libsparse/append2simg.c
deleted file mode 100644
index eef8764..0000000
--- a/libsparse/append2simg.c
+++ /dev/null
@@ -1,140 +0,0 @@
-/*
- * Copyright (C) 2013 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.
- */
-
-#define _FILE_OFFSET_BITS 64
-#define _LARGEFILE64_SOURCE 1
-#define _GNU_SOURCE
-
-#include <errno.h>
-#include <fcntl.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-#include <sparse/sparse.h>
-#include "sparse_file.h"
-#include "backed_block.h"
-
-#ifndef O_BINARY
-#define O_BINARY 0
-#endif
-
-#if defined(__APPLE__) && defined(__MACH__)
-#define lseek64 lseek
-#endif
-#if defined(__APPLE__) && defined(__MACH__)
-#define lseek64 lseek
-#define off64_t off_t
-#endif
-
-void usage()
-{
-    fprintf(stderr, "Usage: append2simg <output> <input>\n");
-}
-
-int main(int argc, char *argv[])
-{
-    int output;
-    int output_block;
-    char *output_path;
-    struct sparse_file *sparse_output;
-
-    int input;
-    char *input_path;
-    off64_t input_len;
-
-    int tmp_fd;
-    char *tmp_path;
-
-    int ret;
-
-    if (argc == 3) {
-        output_path = argv[1];
-        input_path = argv[2];
-    } else {
-        usage();
-        exit(-1);
-    }
-
-    ret = asprintf(&tmp_path, "%s.append2simg", output_path);
-    if (ret < 0) {
-        fprintf(stderr, "Couldn't allocate filename\n");
-        exit(-1);
-    }
-
-    output = open(output_path, O_RDWR | O_BINARY);
-    if (output < 0) {
-        fprintf(stderr, "Couldn't open output file (%s)\n", strerror(errno));
-        exit(-1);
-    }
-
-    sparse_output = sparse_file_import_auto(output, false, true);
-    if (!sparse_output) {
-        fprintf(stderr, "Couldn't import output file\n");
-        exit(-1);
-    }
-
-    input = open(input_path, O_RDONLY | O_BINARY);
-    if (input < 0) {
-        fprintf(stderr, "Couldn't open input file (%s)\n", strerror(errno));
-        exit(-1);
-    }
-
-    input_len = lseek64(input, 0, SEEK_END);
-    if (input_len < 0) {
-        fprintf(stderr, "Couldn't get input file length (%s)\n", strerror(errno));
-        exit(-1);
-    } else if (input_len % sparse_output->block_size) {
-        fprintf(stderr, "Input file is not a multiple of the output file's block size");
-        exit(-1);
-    }
-    lseek64(input, 0, SEEK_SET);
-
-    output_block = sparse_output->len / sparse_output->block_size;
-    if (sparse_file_add_fd(sparse_output, input, 0, input_len, output_block) < 0) {
-        fprintf(stderr, "Couldn't add input file\n");
-        exit(-1);
-    }
-    sparse_output->len += input_len;
-
-    tmp_fd = open(tmp_path, O_WRONLY | O_CREAT | O_BINARY, 0664);
-    if (tmp_fd < 0) {
-        fprintf(stderr, "Couldn't open temporary file (%s)\n", strerror(errno));
-        exit(-1);
-    }
-
-    lseek64(output, 0, SEEK_SET);
-    if (sparse_file_write(sparse_output, tmp_fd, false, true, false) < 0) {
-        fprintf(stderr, "Failed to write sparse file\n");
-        exit(-1);
-    }
-
-    sparse_file_destroy(sparse_output);
-    close(tmp_fd);
-    close(output);
-    close(input);
-
-    ret = rename(tmp_path, output_path);
-    if (ret < 0) {
-        fprintf(stderr, "Failed to rename temporary file (%s)\n", strerror(errno));
-        exit(-1);
-    }
-
-    free(tmp_path);
-
-    exit(0);
-}
diff --git a/libsparse/append2simg.cpp b/libsparse/append2simg.cpp
new file mode 100644
index 0000000..99f4339
--- /dev/null
+++ b/libsparse/append2simg.cpp
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2013 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.
+ */
+
+#define _FILE_OFFSET_BITS 64
+#define _LARGEFILE64_SOURCE 1
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <sparse/sparse.h>
+#include "backed_block.h"
+#include "sparse_file.h"
+
+#ifndef O_BINARY
+#define O_BINARY 0
+#endif
+
+#if defined(__APPLE__) && defined(__MACH__)
+#define lseek64 lseek
+#endif
+#if defined(__APPLE__) && defined(__MACH__)
+#define lseek64 lseek
+#define off64_t off_t
+#endif
+
+void usage() {
+  fprintf(stderr, "Usage: append2simg <output> <input>\n");
+}
+
+int main(int argc, char* argv[]) {
+  int output;
+  int output_block;
+  char* output_path;
+  struct sparse_file* sparse_output;
+
+  int input;
+  char* input_path;
+  off64_t input_len;
+
+  int tmp_fd;
+  char* tmp_path;
+
+  int ret;
+
+  if (argc == 3) {
+    output_path = argv[1];
+    input_path = argv[2];
+  } else {
+    usage();
+    exit(-1);
+  }
+
+  ret = asprintf(&tmp_path, "%s.append2simg", output_path);
+  if (ret < 0) {
+    fprintf(stderr, "Couldn't allocate filename\n");
+    exit(-1);
+  }
+
+  output = open(output_path, O_RDWR | O_BINARY);
+  if (output < 0) {
+    fprintf(stderr, "Couldn't open output file (%s)\n", strerror(errno));
+    exit(-1);
+  }
+
+  sparse_output = sparse_file_import_auto(output, false, true);
+  if (!sparse_output) {
+    fprintf(stderr, "Couldn't import output file\n");
+    exit(-1);
+  }
+
+  input = open(input_path, O_RDONLY | O_BINARY);
+  if (input < 0) {
+    fprintf(stderr, "Couldn't open input file (%s)\n", strerror(errno));
+    exit(-1);
+  }
+
+  input_len = lseek64(input, 0, SEEK_END);
+  if (input_len < 0) {
+    fprintf(stderr, "Couldn't get input file length (%s)\n", strerror(errno));
+    exit(-1);
+  } else if (input_len % sparse_output->block_size) {
+    fprintf(stderr, "Input file is not a multiple of the output file's block size");
+    exit(-1);
+  }
+  lseek64(input, 0, SEEK_SET);
+
+  output_block = sparse_output->len / sparse_output->block_size;
+  if (sparse_file_add_fd(sparse_output, input, 0, input_len, output_block) < 0) {
+    fprintf(stderr, "Couldn't add input file\n");
+    exit(-1);
+  }
+  sparse_output->len += input_len;
+
+  tmp_fd = open(tmp_path, O_WRONLY | O_CREAT | O_BINARY, 0664);
+  if (tmp_fd < 0) {
+    fprintf(stderr, "Couldn't open temporary file (%s)\n", strerror(errno));
+    exit(-1);
+  }
+
+  lseek64(output, 0, SEEK_SET);
+  if (sparse_file_write(sparse_output, tmp_fd, false, true, false) < 0) {
+    fprintf(stderr, "Failed to write sparse file\n");
+    exit(-1);
+  }
+
+  sparse_file_destroy(sparse_output);
+  close(tmp_fd);
+  close(output);
+  close(input);
+
+  ret = rename(tmp_path, output_path);
+  if (ret < 0) {
+    fprintf(stderr, "Failed to rename temporary file (%s)\n", strerror(errno));
+    exit(-1);
+  }
+
+  free(tmp_path);
+
+  exit(0);
+}
diff --git a/libsparse/backed_block.c b/libsparse/backed_block.c
deleted file mode 100644
index 794cd6b..0000000
--- a/libsparse/backed_block.c
+++ /dev/null
@@ -1,404 +0,0 @@
-/*
- * Copyright (C) 2010 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 <assert.h>
-#include <errno.h>
-#include <stdint.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include "backed_block.h"
-#include "sparse_defs.h"
-
-struct backed_block {
-	unsigned int block;
-	unsigned int len;
-	enum backed_block_type type;
-	union {
-		struct {
-			void *data;
-		} data;
-		struct {
-			char *filename;
-			int64_t offset;
-		} file;
-		struct {
-			int fd;
-			int64_t offset;
-		} fd;
-		struct {
-			uint32_t val;
-		} fill;
-	};
-	struct backed_block *next;
-};
-
-struct backed_block_list {
-	struct backed_block *data_blocks;
-	struct backed_block *last_used;
-	unsigned int block_size;
-};
-
-struct backed_block *backed_block_iter_new(struct backed_block_list *bbl)
-{
-	return bbl->data_blocks;
-}
-
-struct backed_block *backed_block_iter_next(struct backed_block *bb)
-{
-	return bb->next;
-}
-
-unsigned int backed_block_len(struct backed_block *bb)
-{
-	return bb->len;
-}
-
-unsigned int backed_block_block(struct backed_block *bb)
-{
-	return bb->block;
-}
-
-void *backed_block_data(struct backed_block *bb)
-{
-	assert(bb->type == BACKED_BLOCK_DATA);
-	return bb->data.data;
-}
-
-const char *backed_block_filename(struct backed_block *bb)
-{
-	assert(bb->type == BACKED_BLOCK_FILE);
-	return bb->file.filename;
-}
-
-int backed_block_fd(struct backed_block *bb)
-{
-	assert(bb->type == BACKED_BLOCK_FD);
-	return bb->fd.fd;
-}
-
-int64_t backed_block_file_offset(struct backed_block *bb)
-{
-	assert(bb->type == BACKED_BLOCK_FILE || bb->type == BACKED_BLOCK_FD);
-	if (bb->type == BACKED_BLOCK_FILE) {
-		return bb->file.offset;
-	} else { /* bb->type == BACKED_BLOCK_FD */
-		return bb->fd.offset;
-	}
-}
-
-uint32_t backed_block_fill_val(struct backed_block *bb)
-{
-	assert(bb->type == BACKED_BLOCK_FILL);
-	return bb->fill.val;
-}
-
-enum backed_block_type backed_block_type(struct backed_block *bb)
-{
-	return bb->type;
-}
-
-void backed_block_destroy(struct backed_block *bb)
-{
-	if (bb->type == BACKED_BLOCK_FILE) {
-		free(bb->file.filename);
-	}
-
-	free(bb);
-}
-
-struct backed_block_list *backed_block_list_new(unsigned int block_size)
-{
-	struct backed_block_list *b = calloc(sizeof(struct backed_block_list), 1);
-	b->block_size = block_size;
-	return b;
-}
-
-void backed_block_list_destroy(struct backed_block_list *bbl)
-{
-	if (bbl->data_blocks) {
-		struct backed_block *bb = bbl->data_blocks;
-		while (bb) {
-			struct backed_block *next = bb->next;
-			backed_block_destroy(bb);
-			bb = next;
-		}
-	}
-
-	free(bbl);
-}
-
-void backed_block_list_move(struct backed_block_list *from,
-		struct backed_block_list *to, struct backed_block *start,
-		struct backed_block *end)
-{
-	struct backed_block *bb;
-
-	if (start == NULL) {
-		start = from->data_blocks;
-	}
-
-	if (!end) {
-		for (end = start; end && end->next; end = end->next)
-			;
-	}
-
-	if (start == NULL || end == NULL) {
-		return;
-	}
-
-	from->last_used = NULL;
-	to->last_used = NULL;
-	if (from->data_blocks == start) {
-		from->data_blocks = end->next;
-	} else {
-		for (bb = from->data_blocks; bb; bb = bb->next) {
-			if (bb->next == start) {
-				bb->next = end->next;
-				break;
-			}
-		}
-	}
-
-	if (!to->data_blocks) {
-		to->data_blocks = start;
-		end->next = NULL;
-	} else {
-		for (bb = to->data_blocks; bb; bb = bb->next) {
-			if (!bb->next || bb->next->block > start->block) {
-				end->next = bb->next;
-				bb->next = start;
-				break;
-			}
-		}
-	}
-}
-
-/* may free b */
-static int merge_bb(struct backed_block_list *bbl,
-		struct backed_block *a, struct backed_block *b)
-{
-	unsigned int block_len;
-
-	/* Block doesn't exist (possible if one block is the last block) */
-	if (!a || !b) {
-		return -EINVAL;
-	}
-
-	assert(a->block < b->block);
-
-	/* Blocks are of different types */
-	if (a->type != b->type) {
-		return -EINVAL;
-	}
-
-	/* Blocks are not adjacent */
-	block_len = a->len / bbl->block_size; /* rounds down */
-	if (a->block + block_len != b->block) {
-		return -EINVAL;
-	}
-
-	switch (a->type) {
-	case BACKED_BLOCK_DATA:
-		/* Don't support merging data for now */
-		return -EINVAL;
-	case BACKED_BLOCK_FILL:
-		if (a->fill.val != b->fill.val) {
-			return -EINVAL;
-		}
-		break;
-	case BACKED_BLOCK_FILE:
-		/* Already make sure b->type is BACKED_BLOCK_FILE */
-		if (strcmp(a->file.filename, b->file.filename) ||
-				a->file.offset + a->len != b->file.offset) {
-			return -EINVAL;
-		}
-		break;
-	case BACKED_BLOCK_FD:
-		if (a->fd.fd != b->fd.fd ||
-				a->fd.offset + a->len != b->fd.offset) {
-			return -EINVAL;
-		}
-		break;
-	}
-
-	/* Blocks are compatible and adjacent, with a before b.  Merge b into a,
-	 * and free b */
-	a->len += b->len;
-	a->next = b->next;
-
-	backed_block_destroy(b);
-
-	return 0;
-}
-
-static int queue_bb(struct backed_block_list *bbl, struct backed_block *new_bb)
-{
-	struct backed_block *bb;
-
-	if (bbl->data_blocks == NULL) {
-		bbl->data_blocks = new_bb;
-		return 0;
-	}
-
-	if (bbl->data_blocks->block > new_bb->block) {
-		new_bb->next = bbl->data_blocks;
-		bbl->data_blocks = new_bb;
-		return 0;
-	}
-
-	/* Optimization: blocks are mostly queued in sequence, so save the
-	   pointer to the last bb that was added, and start searching from
-	   there if the next block number is higher */
-	if (bbl->last_used && new_bb->block > bbl->last_used->block)
-		bb = bbl->last_used;
-	else
-		bb = bbl->data_blocks;
-	bbl->last_used = new_bb;
-
-	for (; bb->next && bb->next->block < new_bb->block; bb = bb->next)
-		;
-
-	if (bb->next == NULL) {
-		bb->next = new_bb;
-	} else {
-		new_bb->next = bb->next;
-		bb->next = new_bb;
-	}
-
-	merge_bb(bbl, new_bb, new_bb->next);
-	if (!merge_bb(bbl, bb, new_bb)) {
-		/* new_bb destroyed, point to retained as last_used */
-		bbl->last_used = bb;
-	}
-
-	return 0;
-}
-
-/* Queues a fill block of memory to be written to the specified data blocks */
-int backed_block_add_fill(struct backed_block_list *bbl, unsigned int fill_val,
-		unsigned int len, unsigned int block)
-{
-	struct backed_block *bb = calloc(1, sizeof(struct backed_block));
-	if (bb == NULL) {
-		return -ENOMEM;
-	}
-
-	bb->block = block;
-	bb->len = len;
-	bb->type = BACKED_BLOCK_FILL;
-	bb->fill.val = fill_val;
-	bb->next = NULL;
-
-	return queue_bb(bbl, bb);
-}
-
-/* Queues a block of memory to be written to the specified data blocks */
-int backed_block_add_data(struct backed_block_list *bbl, void *data,
-		unsigned int len, unsigned int block)
-{
-	struct backed_block *bb = calloc(1, sizeof(struct backed_block));
-	if (bb == NULL) {
-		return -ENOMEM;
-	}
-
-	bb->block = block;
-	bb->len = len;
-	bb->type = BACKED_BLOCK_DATA;
-	bb->data.data = data;
-	bb->next = NULL;
-
-	return queue_bb(bbl, bb);
-}
-
-/* Queues a chunk of a file on disk to be written to the specified data blocks */
-int backed_block_add_file(struct backed_block_list *bbl, const char *filename,
-		int64_t offset, unsigned int len, unsigned int block)
-{
-	struct backed_block *bb = calloc(1, sizeof(struct backed_block));
-	if (bb == NULL) {
-		return -ENOMEM;
-	}
-
-	bb->block = block;
-	bb->len = len;
-	bb->type = BACKED_BLOCK_FILE;
-	bb->file.filename = strdup(filename);
-	bb->file.offset = offset;
-	bb->next = NULL;
-
-	return queue_bb(bbl, bb);
-}
-
-/* Queues a chunk of a fd to be written to the specified data blocks */
-int backed_block_add_fd(struct backed_block_list *bbl, int fd, int64_t offset,
-		unsigned int len, unsigned int block)
-{
-	struct backed_block *bb = calloc(1, sizeof(struct backed_block));
-	if (bb == NULL) {
-		return -ENOMEM;
-	}
-
-	bb->block = block;
-	bb->len = len;
-	bb->type = BACKED_BLOCK_FD;
-	bb->fd.fd = fd;
-	bb->fd.offset = offset;
-	bb->next = NULL;
-
-	return queue_bb(bbl, bb);
-}
-
-int backed_block_split(struct backed_block_list *bbl, struct backed_block *bb,
-		unsigned int max_len)
-{
-	struct backed_block *new_bb;
-
-	max_len = ALIGN_DOWN(max_len, bbl->block_size);
-
-	if (bb->len <= max_len) {
-		return 0;
-	}
-
-	new_bb = malloc(sizeof(struct backed_block));
-	if (new_bb == NULL) {
-		return -ENOMEM;
-	}
-
-	*new_bb = *bb;
-
-	new_bb->len = bb->len - max_len;
-	new_bb->block = bb->block + max_len / bbl->block_size;
-	new_bb->next = bb->next;
-	bb->next = new_bb;
-	bb->len = max_len;
-
-	switch (bb->type) {
-	case BACKED_BLOCK_DATA:
-		new_bb->data.data = (char *)bb->data.data + max_len;
-		break;
-	case BACKED_BLOCK_FILE:
-		new_bb->file.offset += max_len;
-		break;
-	case BACKED_BLOCK_FD:
-		new_bb->fd.offset += max_len;
-		break;
-	case BACKED_BLOCK_FILL:
-		break;
-	}
-
-	return 0;
-}
diff --git a/libsparse/backed_block.cpp b/libsparse/backed_block.cpp
new file mode 100644
index 0000000..f3d8022
--- /dev/null
+++ b/libsparse/backed_block.cpp
@@ -0,0 +1,380 @@
+/*
+ * Copyright (C) 2010 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 <assert.h>
+#include <errno.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "backed_block.h"
+#include "sparse_defs.h"
+
+struct backed_block {
+  unsigned int block;
+  unsigned int len;
+  enum backed_block_type type;
+  union {
+    struct {
+      void* data;
+    } data;
+    struct {
+      char* filename;
+      int64_t offset;
+    } file;
+    struct {
+      int fd;
+      int64_t offset;
+    } fd;
+    struct {
+      uint32_t val;
+    } fill;
+  };
+  struct backed_block* next;
+};
+
+struct backed_block_list {
+  struct backed_block* data_blocks;
+  struct backed_block* last_used;
+  unsigned int block_size;
+};
+
+struct backed_block* backed_block_iter_new(struct backed_block_list* bbl) {
+  return bbl->data_blocks;
+}
+
+struct backed_block* backed_block_iter_next(struct backed_block* bb) {
+  return bb->next;
+}
+
+unsigned int backed_block_len(struct backed_block* bb) {
+  return bb->len;
+}
+
+unsigned int backed_block_block(struct backed_block* bb) {
+  return bb->block;
+}
+
+void* backed_block_data(struct backed_block* bb) {
+  assert(bb->type == BACKED_BLOCK_DATA);
+  return bb->data.data;
+}
+
+const char* backed_block_filename(struct backed_block* bb) {
+  assert(bb->type == BACKED_BLOCK_FILE);
+  return bb->file.filename;
+}
+
+int backed_block_fd(struct backed_block* bb) {
+  assert(bb->type == BACKED_BLOCK_FD);
+  return bb->fd.fd;
+}
+
+int64_t backed_block_file_offset(struct backed_block* bb) {
+  assert(bb->type == BACKED_BLOCK_FILE || bb->type == BACKED_BLOCK_FD);
+  if (bb->type == BACKED_BLOCK_FILE) {
+    return bb->file.offset;
+  } else { /* bb->type == BACKED_BLOCK_FD */
+    return bb->fd.offset;
+  }
+}
+
+uint32_t backed_block_fill_val(struct backed_block* bb) {
+  assert(bb->type == BACKED_BLOCK_FILL);
+  return bb->fill.val;
+}
+
+enum backed_block_type backed_block_type(struct backed_block* bb) {
+  return bb->type;
+}
+
+void backed_block_destroy(struct backed_block* bb) {
+  if (bb->type == BACKED_BLOCK_FILE) {
+    free(bb->file.filename);
+  }
+
+  free(bb);
+}
+
+struct backed_block_list* backed_block_list_new(unsigned int block_size) {
+  struct backed_block_list* b =
+      reinterpret_cast<backed_block_list*>(calloc(sizeof(struct backed_block_list), 1));
+  b->block_size = block_size;
+  return b;
+}
+
+void backed_block_list_destroy(struct backed_block_list* bbl) {
+  if (bbl->data_blocks) {
+    struct backed_block* bb = bbl->data_blocks;
+    while (bb) {
+      struct backed_block* next = bb->next;
+      backed_block_destroy(bb);
+      bb = next;
+    }
+  }
+
+  free(bbl);
+}
+
+void backed_block_list_move(struct backed_block_list* from, struct backed_block_list* to,
+                            struct backed_block* start, struct backed_block* end) {
+  struct backed_block* bb;
+
+  if (start == nullptr) {
+    start = from->data_blocks;
+  }
+
+  if (!end) {
+    for (end = start; end && end->next; end = end->next)
+      ;
+  }
+
+  if (start == nullptr || end == nullptr) {
+    return;
+  }
+
+  from->last_used = nullptr;
+  to->last_used = nullptr;
+  if (from->data_blocks == start) {
+    from->data_blocks = end->next;
+  } else {
+    for (bb = from->data_blocks; bb; bb = bb->next) {
+      if (bb->next == start) {
+        bb->next = end->next;
+        break;
+      }
+    }
+  }
+
+  if (!to->data_blocks) {
+    to->data_blocks = start;
+    end->next = nullptr;
+  } else {
+    for (bb = to->data_blocks; bb; bb = bb->next) {
+      if (!bb->next || bb->next->block > start->block) {
+        end->next = bb->next;
+        bb->next = start;
+        break;
+      }
+    }
+  }
+}
+
+/* may free b */
+static int merge_bb(struct backed_block_list* bbl, struct backed_block* a, struct backed_block* b) {
+  unsigned int block_len;
+
+  /* Block doesn't exist (possible if one block is the last block) */
+  if (!a || !b) {
+    return -EINVAL;
+  }
+
+  assert(a->block < b->block);
+
+  /* Blocks are of different types */
+  if (a->type != b->type) {
+    return -EINVAL;
+  }
+
+  /* Blocks are not adjacent */
+  block_len = a->len / bbl->block_size; /* rounds down */
+  if (a->block + block_len != b->block) {
+    return -EINVAL;
+  }
+
+  switch (a->type) {
+    case BACKED_BLOCK_DATA:
+      /* Don't support merging data for now */
+      return -EINVAL;
+    case BACKED_BLOCK_FILL:
+      if (a->fill.val != b->fill.val) {
+        return -EINVAL;
+      }
+      break;
+    case BACKED_BLOCK_FILE:
+      /* Already make sure b->type is BACKED_BLOCK_FILE */
+      if (strcmp(a->file.filename, b->file.filename) || a->file.offset + a->len != b->file.offset) {
+        return -EINVAL;
+      }
+      break;
+    case BACKED_BLOCK_FD:
+      if (a->fd.fd != b->fd.fd || a->fd.offset + a->len != b->fd.offset) {
+        return -EINVAL;
+      }
+      break;
+  }
+
+  /* Blocks are compatible and adjacent, with a before b.  Merge b into a,
+   * and free b */
+  a->len += b->len;
+  a->next = b->next;
+
+  backed_block_destroy(b);
+
+  return 0;
+}
+
+static int queue_bb(struct backed_block_list* bbl, struct backed_block* new_bb) {
+  struct backed_block* bb;
+
+  if (bbl->data_blocks == nullptr) {
+    bbl->data_blocks = new_bb;
+    return 0;
+  }
+
+  if (bbl->data_blocks->block > new_bb->block) {
+    new_bb->next = bbl->data_blocks;
+    bbl->data_blocks = new_bb;
+    return 0;
+  }
+
+  /* Optimization: blocks are mostly queued in sequence, so save the
+     pointer to the last bb that was added, and start searching from
+     there if the next block number is higher */
+  if (bbl->last_used && new_bb->block > bbl->last_used->block)
+    bb = bbl->last_used;
+  else
+    bb = bbl->data_blocks;
+  bbl->last_used = new_bb;
+
+  for (; bb->next && bb->next->block < new_bb->block; bb = bb->next)
+    ;
+
+  if (bb->next == nullptr) {
+    bb->next = new_bb;
+  } else {
+    new_bb->next = bb->next;
+    bb->next = new_bb;
+  }
+
+  merge_bb(bbl, new_bb, new_bb->next);
+  if (!merge_bb(bbl, bb, new_bb)) {
+    /* new_bb destroyed, point to retained as last_used */
+    bbl->last_used = bb;
+  }
+
+  return 0;
+}
+
+/* Queues a fill block of memory to be written to the specified data blocks */
+int backed_block_add_fill(struct backed_block_list* bbl, unsigned int fill_val, unsigned int len,
+                          unsigned int block) {
+  struct backed_block* bb = reinterpret_cast<backed_block*>(calloc(1, sizeof(struct backed_block)));
+  if (bb == nullptr) {
+    return -ENOMEM;
+  }
+
+  bb->block = block;
+  bb->len = len;
+  bb->type = BACKED_BLOCK_FILL;
+  bb->fill.val = fill_val;
+  bb->next = nullptr;
+
+  return queue_bb(bbl, bb);
+}
+
+/* Queues a block of memory to be written to the specified data blocks */
+int backed_block_add_data(struct backed_block_list* bbl, void* data, unsigned int len,
+                          unsigned int block) {
+  struct backed_block* bb = reinterpret_cast<backed_block*>(calloc(1, sizeof(struct backed_block)));
+  if (bb == nullptr) {
+    return -ENOMEM;
+  }
+
+  bb->block = block;
+  bb->len = len;
+  bb->type = BACKED_BLOCK_DATA;
+  bb->data.data = data;
+  bb->next = nullptr;
+
+  return queue_bb(bbl, bb);
+}
+
+/* Queues a chunk of a file on disk to be written to the specified data blocks */
+int backed_block_add_file(struct backed_block_list* bbl, const char* filename, int64_t offset,
+                          unsigned int len, unsigned int block) {
+  struct backed_block* bb = reinterpret_cast<backed_block*>(calloc(1, sizeof(struct backed_block)));
+  if (bb == nullptr) {
+    return -ENOMEM;
+  }
+
+  bb->block = block;
+  bb->len = len;
+  bb->type = BACKED_BLOCK_FILE;
+  bb->file.filename = strdup(filename);
+  bb->file.offset = offset;
+  bb->next = nullptr;
+
+  return queue_bb(bbl, bb);
+}
+
+/* Queues a chunk of a fd to be written to the specified data blocks */
+int backed_block_add_fd(struct backed_block_list* bbl, int fd, int64_t offset, unsigned int len,
+                        unsigned int block) {
+  struct backed_block* bb = reinterpret_cast<backed_block*>(calloc(1, sizeof(struct backed_block)));
+  if (bb == nullptr) {
+    return -ENOMEM;
+  }
+
+  bb->block = block;
+  bb->len = len;
+  bb->type = BACKED_BLOCK_FD;
+  bb->fd.fd = fd;
+  bb->fd.offset = offset;
+  bb->next = nullptr;
+
+  return queue_bb(bbl, bb);
+}
+
+int backed_block_split(struct backed_block_list* bbl, struct backed_block* bb,
+                       unsigned int max_len) {
+  struct backed_block* new_bb;
+
+  max_len = ALIGN_DOWN(max_len, bbl->block_size);
+
+  if (bb->len <= max_len) {
+    return 0;
+  }
+
+  new_bb = reinterpret_cast<backed_block*>(malloc(sizeof(struct backed_block)));
+  if (new_bb == nullptr) {
+    return -ENOMEM;
+  }
+
+  *new_bb = *bb;
+
+  new_bb->len = bb->len - max_len;
+  new_bb->block = bb->block + max_len / bbl->block_size;
+  new_bb->next = bb->next;
+  bb->next = new_bb;
+  bb->len = max_len;
+
+  switch (bb->type) {
+    case BACKED_BLOCK_DATA:
+      new_bb->data.data = (char*)bb->data.data + max_len;
+      break;
+    case BACKED_BLOCK_FILE:
+      new_bb->file.offset += max_len;
+      break;
+    case BACKED_BLOCK_FD:
+      new_bb->fd.offset += max_len;
+      break;
+    case BACKED_BLOCK_FILL:
+      break;
+  }
+
+  return 0;
+}
diff --git a/libsparse/backed_block.h b/libsparse/backed_block.h
index 1a159be..3a75460 100644
--- a/libsparse/backed_block.h
+++ b/libsparse/backed_block.h
@@ -23,42 +23,40 @@
 struct backed_block;
 
 enum backed_block_type {
-	BACKED_BLOCK_DATA,
-	BACKED_BLOCK_FILE,
-	BACKED_BLOCK_FD,
-	BACKED_BLOCK_FILL,
+  BACKED_BLOCK_DATA,
+  BACKED_BLOCK_FILE,
+  BACKED_BLOCK_FD,
+  BACKED_BLOCK_FILL,
 };
 
-int backed_block_add_data(struct backed_block_list *bbl, void *data,
-		unsigned int len, unsigned int block);
-int backed_block_add_fill(struct backed_block_list *bbl, unsigned int fill_val,
-		unsigned int len, unsigned int block);
-int backed_block_add_file(struct backed_block_list *bbl, const char *filename,
-		int64_t offset, unsigned int len, unsigned int block);
-int backed_block_add_fd(struct backed_block_list *bbl, int fd,
-		int64_t offset, unsigned int len, unsigned int block);
+int backed_block_add_data(struct backed_block_list* bbl, void* data, unsigned int len,
+                          unsigned int block);
+int backed_block_add_fill(struct backed_block_list* bbl, unsigned int fill_val, unsigned int len,
+                          unsigned int block);
+int backed_block_add_file(struct backed_block_list* bbl, const char* filename, int64_t offset,
+                          unsigned int len, unsigned int block);
+int backed_block_add_fd(struct backed_block_list* bbl, int fd, int64_t offset, unsigned int len,
+                        unsigned int block);
 
-struct backed_block *backed_block_iter_new(struct backed_block_list *bbl);
-struct backed_block *backed_block_iter_next(struct backed_block *bb);
-unsigned int backed_block_len(struct backed_block *bb);
-unsigned int backed_block_block(struct backed_block *bb);
-void *backed_block_data(struct backed_block *bb);
-const char *backed_block_filename(struct backed_block *bb);
-int backed_block_fd(struct backed_block *bb);
-int64_t backed_block_file_offset(struct backed_block *bb);
-uint32_t backed_block_fill_val(struct backed_block *bb);
-enum backed_block_type backed_block_type(struct backed_block *bb);
-int backed_block_split(struct backed_block_list *bbl, struct backed_block *bb,
-		unsigned int max_len);
+struct backed_block* backed_block_iter_new(struct backed_block_list* bbl);
+struct backed_block* backed_block_iter_next(struct backed_block* bb);
+unsigned int backed_block_len(struct backed_block* bb);
+unsigned int backed_block_block(struct backed_block* bb);
+void* backed_block_data(struct backed_block* bb);
+const char* backed_block_filename(struct backed_block* bb);
+int backed_block_fd(struct backed_block* bb);
+int64_t backed_block_file_offset(struct backed_block* bb);
+uint32_t backed_block_fill_val(struct backed_block* bb);
+enum backed_block_type backed_block_type(struct backed_block* bb);
+int backed_block_split(struct backed_block_list* bbl, struct backed_block* bb, unsigned int max_len);
 
-struct backed_block *backed_block_iter_new(struct backed_block_list *bbl);
-struct backed_block *backed_block_iter_next(struct backed_block *bb);
+struct backed_block* backed_block_iter_new(struct backed_block_list* bbl);
+struct backed_block* backed_block_iter_next(struct backed_block* bb);
 
-struct backed_block_list *backed_block_list_new(unsigned int block_size);
-void backed_block_list_destroy(struct backed_block_list *bbl);
+struct backed_block_list* backed_block_list_new(unsigned int block_size);
+void backed_block_list_destroy(struct backed_block_list* bbl);
 
-void backed_block_list_move(struct backed_block_list *from,
-		struct backed_block_list *to, struct backed_block *start,
-		struct backed_block *end);
+void backed_block_list_move(struct backed_block_list* from, struct backed_block_list* to,
+                            struct backed_block* start, struct backed_block* end);
 
 #endif
diff --git a/libsparse/defs.h b/libsparse/defs.h
index 34e63c5..28e5cab 100644
--- a/libsparse/defs.h
+++ b/libsparse/defs.h
@@ -17,7 +17,7 @@
 #ifndef _LIBSPARSE_DEFS_H_
 
 #ifndef __unused
-#define __unused        __attribute__((__unused__))
+#define __unused __attribute__((__unused__))
 #endif
 
 #endif /* _LIBSPARSE_DEFS_H_ */
diff --git a/libsparse/img2simg.c b/libsparse/img2simg.c
deleted file mode 100644
index a0db36f..0000000
--- a/libsparse/img2simg.c
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
- * Copyright (C) 2012 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.
- */
-
-#define _FILE_OFFSET_BITS 64
-#define _LARGEFILE64_SOURCE 1
-
-#include <fcntl.h>
-#include <stdbool.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include <sparse/sparse.h>
-
-#ifndef O_BINARY
-#define O_BINARY 0
-#endif
-
-#if defined(__APPLE__) && defined(__MACH__)
-#define lseek64 lseek
-#define off64_t off_t
-#endif
-
-void usage()
-{
-    fprintf(stderr, "Usage: img2simg <raw_image_file> <sparse_image_file> [<block_size>]\n");
-}
-
-int main(int argc, char *argv[])
-{
-	int in;
-	int out;
-	int ret;
-	struct sparse_file *s;
-	unsigned int block_size = 4096;
-	off64_t len;
-
-	if (argc < 3 || argc > 4) {
-		usage();
-		exit(-1);
-	}
-
-	if (argc == 4) {
-		block_size = atoi(argv[3]);
-	}
-
-	if (block_size < 1024 || block_size % 4 != 0) {
-		usage();
-		exit(-1);
-	}
-
-	if (strcmp(argv[1], "-") == 0) {
-		in = STDIN_FILENO;
-	} else {
-		in = open(argv[1], O_RDONLY | O_BINARY);
-		if (in < 0) {
-			fprintf(stderr, "Cannot open input file %s\n", argv[1]);
-			exit(-1);
-		}
-	}
-
-	if (strcmp(argv[2], "-") == 0) {
-		out = STDOUT_FILENO;
-	} else {
-		out = open(argv[2], O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0664);
-		if (out < 0) {
-			fprintf(stderr, "Cannot open output file %s\n", argv[2]);
-			exit(-1);
-		}
-	}
-
-	len = lseek64(in, 0, SEEK_END);
-	lseek64(in, 0, SEEK_SET);
-
-	s = sparse_file_new(block_size, len);
-	if (!s) {
-		fprintf(stderr, "Failed to create sparse file\n");
-		exit(-1);
-	}
-
-	sparse_file_verbose(s);
-	ret = sparse_file_read(s, in, false, false);
-	if (ret) {
-		fprintf(stderr, "Failed to read file\n");
-		exit(-1);
-	}
-
-	ret = sparse_file_write(s, out, false, true, false);
-	if (ret) {
-		fprintf(stderr, "Failed to write sparse file\n");
-		exit(-1);
-	}
-
-	close(in);
-	close(out);
-
-	exit(0);
-}
diff --git a/libsparse/img2simg.cpp b/libsparse/img2simg.cpp
new file mode 100644
index 0000000..4c2c6ca
--- /dev/null
+++ b/libsparse/img2simg.cpp
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+#define _FILE_OFFSET_BITS 64
+#define _LARGEFILE64_SOURCE 1
+
+#include <fcntl.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <sparse/sparse.h>
+
+#ifndef O_BINARY
+#define O_BINARY 0
+#endif
+
+#if defined(__APPLE__) && defined(__MACH__)
+#define lseek64 lseek
+#define off64_t off_t
+#endif
+
+void usage() {
+  fprintf(stderr, "Usage: img2simg <raw_image_file> <sparse_image_file> [<block_size>]\n");
+}
+
+int main(int argc, char* argv[]) {
+  int in;
+  int out;
+  int ret;
+  struct sparse_file* s;
+  unsigned int block_size = 4096;
+  off64_t len;
+
+  if (argc < 3 || argc > 4) {
+    usage();
+    exit(-1);
+  }
+
+  if (argc == 4) {
+    block_size = atoi(argv[3]);
+  }
+
+  if (block_size < 1024 || block_size % 4 != 0) {
+    usage();
+    exit(-1);
+  }
+
+  if (strcmp(argv[1], "-") == 0) {
+    in = STDIN_FILENO;
+  } else {
+    in = open(argv[1], O_RDONLY | O_BINARY);
+    if (in < 0) {
+      fprintf(stderr, "Cannot open input file %s\n", argv[1]);
+      exit(-1);
+    }
+  }
+
+  if (strcmp(argv[2], "-") == 0) {
+    out = STDOUT_FILENO;
+  } else {
+    out = open(argv[2], O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0664);
+    if (out < 0) {
+      fprintf(stderr, "Cannot open output file %s\n", argv[2]);
+      exit(-1);
+    }
+  }
+
+  len = lseek64(in, 0, SEEK_END);
+  lseek64(in, 0, SEEK_SET);
+
+  s = sparse_file_new(block_size, len);
+  if (!s) {
+    fprintf(stderr, "Failed to create sparse file\n");
+    exit(-1);
+  }
+
+  sparse_file_verbose(s);
+  ret = sparse_file_read(s, in, false, false);
+  if (ret) {
+    fprintf(stderr, "Failed to read file\n");
+    exit(-1);
+  }
+
+  ret = sparse_file_write(s, out, false, true, false);
+  if (ret) {
+    fprintf(stderr, "Failed to write sparse file\n");
+    exit(-1);
+  }
+
+  close(in);
+  close(out);
+
+  exit(0);
+}
diff --git a/libsparse/include/sparse/sparse.h b/libsparse/include/sparse/sparse.h
index 356f65f..3d5fb0c 100644
--- a/libsparse/include/sparse/sparse.h
+++ b/libsparse/include/sparse/sparse.h
@@ -18,6 +18,7 @@
 #define _LIBSPARSE_SPARSE_H_
 
 #include <stdbool.h>
+#include <stddef.h>
 #include <stdint.h>
 
 #ifdef	__cplusplus
@@ -26,6 +27,11 @@
 
 struct sparse_file;
 
+// The callbacks in sparse_file_callback() and sparse_file_foreach_chunk() take
+// size_t as the length type (was `int` in past). This allows clients to keep
+// their codes compatibile with both versions as needed.
+#define	SPARSE_CALLBACK_USES_SIZE_T
+
 /**
  * sparse_file_new - create a new sparse file cookie
  *
@@ -201,7 +207,7 @@
  * Returns 0 on success, negative errno on error.
  */
 int sparse_file_callback(struct sparse_file *s, bool sparse, bool crc,
-		int (*write)(void *priv, const void *data, int len), void *priv);
+		int (*write)(void *priv, const void *data, size_t len), void *priv);
 
 /**
  * sparse_file_foreach_chunk - call a callback for data blocks in sparse file
@@ -218,7 +224,7 @@
  * Returns 0 on success, negative errno on error.
  */
 int sparse_file_foreach_chunk(struct sparse_file *s, bool sparse, bool crc,
-	int (*write)(void *priv, const void *data, int len, unsigned int block,
+	int (*write)(void *priv, const void *data, size_t len, unsigned int block,
 		     unsigned int nr_blocks),
 	void *priv);
 /**
@@ -240,9 +246,24 @@
 int sparse_file_read(struct sparse_file *s, int fd, bool sparse, bool crc);
 
 /**
- * sparse_file_import - import an existing sparse file
+ * sparse_file_read_buf - read a buffer into a sparse file cookie
  *
  * @s - sparse file cookie
+ * @buf - buffer to read from
+ * @crc - verify the crc of a file in the Android sparse file format
+ *
+ * Reads a buffer into a sparse file cookie. The buffer must remain
+ * valid until the sparse file cookie is freed. If crc is true, the
+ * crc of the sparse file will be verified.
+ *
+ * Returns 0 on success, negative errno on error.
+ */
+int sparse_file_read_buf(struct sparse_file *s, char *buf, bool crc);
+
+/**
+ * sparse_file_import - import an existing sparse file
+ *
+ * @fd - file descriptor to read from
  * @verbose - print verbose errors while reading the sparse file
  * @crc - verify the crc of a file in the Android sparse file format
  *
@@ -255,6 +276,21 @@
 struct sparse_file *sparse_file_import(int fd, bool verbose, bool crc);
 
 /**
+ * sparse_file_import_buf - import an existing sparse file from a buffer
+ *
+ * @buf - buffer to read from
+ * @verbose - print verbose errors while reading the sparse file
+ * @crc - verify the crc of a file in the Android sparse file format
+ *
+ * Reads existing sparse file data into a sparse file cookie, recreating the same
+ * sparse cookie that was used to write it.  If verbose is true, prints verbose
+ * errors when the sparse file is formatted incorrectly.
+ *
+ * Returns a new sparse file cookie on success, NULL on error.
+ */
+struct sparse_file *sparse_file_import_buf(char* buf, bool verbose, bool crc);
+
+/**
  * sparse_file_import_auto - import an existing sparse or normal file
  *
  * @fd - file descriptor to read from
diff --git a/libsparse/output_file.c b/libsparse/output_file.c
deleted file mode 100644
index 51e60ef..0000000
--- a/libsparse/output_file.c
+++ /dev/null
@@ -1,775 +0,0 @@
-/*
- * Copyright (C) 2010 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.
- */
-
-#define _FILE_OFFSET_BITS 64
-#define _LARGEFILE64_SOURCE 1
-
-#include <fcntl.h>
-#include <inttypes.h>
-#include <limits.h>
-#include <stdbool.h>
-#include <stddef.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
-#include <zlib.h>
-
-#include "defs.h"
-#include "output_file.h"
-#include "sparse_crc32.h"
-#include "sparse_format.h"
-
-#ifndef _WIN32
-#include <sys/mman.h>
-#define O_BINARY 0
-#else
-#define ftruncate64 ftruncate
-#endif
-
-#if defined(__APPLE__) && defined(__MACH__)
-#define lseek64 lseek
-#define ftruncate64 ftruncate
-#define mmap64 mmap
-#define off64_t off_t
-#endif
-
-#define min(a, b) \
-	({ typeof(a) _a = (a); typeof(b) _b = (b); (_a < _b) ? _a : _b; })
-
-#define SPARSE_HEADER_MAJOR_VER 1
-#define SPARSE_HEADER_MINOR_VER 0
-#define SPARSE_HEADER_LEN       (sizeof(sparse_header_t))
-#define CHUNK_HEADER_LEN (sizeof(chunk_header_t))
-
-#define container_of(inner, outer_t, elem) \
-	((outer_t *)((char *)(inner) - offsetof(outer_t, elem)))
-
-struct output_file_ops {
-	int (*open)(struct output_file *, int fd);
-	int (*skip)(struct output_file *, int64_t);
-	int (*pad)(struct output_file *, int64_t);
-	int (*write)(struct output_file *, void *, size_t);
-	void (*close)(struct output_file *);
-};
-
-struct sparse_file_ops {
-	int (*write_data_chunk)(struct output_file *out, unsigned int len,
-			void *data);
-	int (*write_fill_chunk)(struct output_file *out, unsigned int len,
-			uint32_t fill_val);
-	int (*write_skip_chunk)(struct output_file *out, int64_t len);
-	int (*write_end_chunk)(struct output_file *out);
-};
-
-struct output_file {
-	int64_t cur_out_ptr;
-	unsigned int chunk_cnt;
-	uint32_t crc32;
-	struct output_file_ops *ops;
-	struct sparse_file_ops *sparse_ops;
-	int use_crc;
-	unsigned int block_size;
-	int64_t len;
-	char *zero_buf;
-	uint32_t *fill_buf;
-	char *buf;
-};
-
-struct output_file_gz {
-	struct output_file out;
-	gzFile gz_fd;
-};
-
-#define to_output_file_gz(_o) \
-	container_of((_o), struct output_file_gz, out)
-
-struct output_file_normal {
-	struct output_file out;
-	int fd;
-};
-
-#define to_output_file_normal(_o) \
-	container_of((_o), struct output_file_normal, out)
-
-struct output_file_callback {
-	struct output_file out;
-	void *priv;
-	int (*write)(void *priv, const void *buf, int len);
-};
-
-#define to_output_file_callback(_o) \
-	container_of((_o), struct output_file_callback, out)
-
-static int file_open(struct output_file *out, int fd)
-{
-	struct output_file_normal *outn = to_output_file_normal(out);
-
-	outn->fd = fd;
-	return 0;
-}
-
-static int file_skip(struct output_file *out, int64_t cnt)
-{
-	off64_t ret;
-	struct output_file_normal *outn = to_output_file_normal(out);
-
-	ret = lseek64(outn->fd, cnt, SEEK_CUR);
-	if (ret < 0) {
-		error_errno("lseek64");
-		return -1;
-	}
-	return 0;
-}
-
-static int file_pad(struct output_file *out, int64_t len)
-{
-	int ret;
-	struct output_file_normal *outn = to_output_file_normal(out);
-
-	ret = ftruncate64(outn->fd, len);
-	if (ret < 0) {
-		return -errno;
-	}
-
-	return 0;
-}
-
-static int file_write(struct output_file *out, void *data, size_t len)
-{
-	ssize_t ret;
-	struct output_file_normal *outn = to_output_file_normal(out);
-
-	while (len > 0) {
-		ret = write(outn->fd, data, len);
-		if (ret < 0) {
-			if (errno == EINTR) {
-				continue;
-			}
-			error_errno("write");
-			return -1;
-		}
-
-		data = (char *)data + ret;
-		len -= ret;
-	}
-
-	return 0;
-}
-
-static void file_close(struct output_file *out)
-{
-	struct output_file_normal *outn = to_output_file_normal(out);
-
-	free(outn);
-}
-
-static struct output_file_ops file_ops = {
-	.open = file_open,
-	.skip = file_skip,
-	.pad = file_pad,
-	.write = file_write,
-	.close = file_close,
-};
-
-static int gz_file_open(struct output_file *out, int fd)
-{
-	struct output_file_gz *outgz = to_output_file_gz(out);
-
-	outgz->gz_fd = gzdopen(fd, "wb9");
-	if (!outgz->gz_fd) {
-		error_errno("gzopen");
-		return -errno;
-	}
-
-	return 0;
-}
-
-
-static int gz_file_skip(struct output_file *out, int64_t cnt)
-{
-	off64_t ret;
-	struct output_file_gz *outgz = to_output_file_gz(out);
-
-	ret = gzseek(outgz->gz_fd, cnt, SEEK_CUR);
-	if (ret < 0) {
-		error_errno("gzseek");
-		return -1;
-	}
-	return 0;
-}
-
-static int gz_file_pad(struct output_file *out, int64_t len)
-{
-	off64_t ret;
-	struct output_file_gz *outgz = to_output_file_gz(out);
-
-	ret = gztell(outgz->gz_fd);
-	if (ret < 0) {
-		return -1;
-	}
-
-	if (ret >= len) {
-		return 0;
-	}
-
-	ret = gzseek(outgz->gz_fd, len - 1, SEEK_SET);
-	if (ret < 0) {
-		return -1;
-	}
-
-	gzwrite(outgz->gz_fd, "", 1);
-
-	return 0;
-}
-
-static int gz_file_write(struct output_file *out, void *data, size_t len)
-{
-	int ret;
-	struct output_file_gz *outgz = to_output_file_gz(out);
-
-	while (len > 0) {
-		ret = gzwrite(outgz->gz_fd, data,
-			      min(len, (unsigned int)INT_MAX));
-		if (ret == 0) {
-			error("gzwrite %s", gzerror(outgz->gz_fd, NULL));
-			return -1;
-		}
-		len -= ret;
-		data = (char *)data + ret;
-	}
-
-	return 0;
-}
-
-static void gz_file_close(struct output_file *out)
-{
-	struct output_file_gz *outgz = to_output_file_gz(out);
-
-	gzclose(outgz->gz_fd);
-	free(outgz);
-}
-
-static struct output_file_ops gz_file_ops = {
-	.open = gz_file_open,
-	.skip = gz_file_skip,
-	.pad = gz_file_pad,
-	.write = gz_file_write,
-	.close = gz_file_close,
-};
-
-static int callback_file_open(struct output_file *out __unused, int fd __unused)
-{
-	return 0;
-}
-
-static int callback_file_skip(struct output_file *out, int64_t off)
-{
-	struct output_file_callback *outc = to_output_file_callback(out);
-	int to_write;
-	int ret;
-
-	while (off > 0) {
-		to_write = min(off, (int64_t)INT_MAX);
-		ret = outc->write(outc->priv, NULL, to_write);
-		if (ret < 0) {
-			return ret;
-		}
-		off -= to_write;
-	}
-
-	return 0;
-}
-
-static int callback_file_pad(struct output_file *out __unused, int64_t len __unused)
-{
-	return -1;
-}
-
-static int callback_file_write(struct output_file *out, void *data, size_t len)
-{
-	struct output_file_callback *outc = to_output_file_callback(out);
-
-	return outc->write(outc->priv, data, len);
-}
-
-static void callback_file_close(struct output_file *out)
-{
-	struct output_file_callback *outc = to_output_file_callback(out);
-
-	free(outc);
-}
-
-static struct output_file_ops callback_file_ops = {
-	.open = callback_file_open,
-	.skip = callback_file_skip,
-	.pad = callback_file_pad,
-	.write = callback_file_write,
-	.close = callback_file_close,
-};
-
-int read_all(int fd, void *buf, size_t len)
-{
-	size_t total = 0;
-	int ret;
-	char *ptr = buf;
-
-	while (total < len) {
-		ret = read(fd, ptr, len - total);
-
-		if (ret < 0)
-			return -errno;
-
-		if (ret == 0)
-			return -EINVAL;
-
-		ptr += ret;
-		total += ret;
-	}
-
-	return 0;
-}
-
-static int write_sparse_skip_chunk(struct output_file *out, int64_t skip_len)
-{
-	chunk_header_t chunk_header;
-	int ret;
-
-	if (skip_len % out->block_size) {
-		error("don't care size %"PRIi64" is not a multiple of the block size %u",
-				skip_len, out->block_size);
-		return -1;
-	}
-
-	/* We are skipping data, so emit a don't care chunk. */
-	chunk_header.chunk_type = CHUNK_TYPE_DONT_CARE;
-	chunk_header.reserved1 = 0;
-	chunk_header.chunk_sz = skip_len / out->block_size;
-	chunk_header.total_sz = CHUNK_HEADER_LEN;
-	ret = out->ops->write(out, &chunk_header, sizeof(chunk_header));
-	if (ret < 0)
-		return -1;
-
-	out->cur_out_ptr += skip_len;
-	out->chunk_cnt++;
-
-	return 0;
-}
-
-static int write_sparse_fill_chunk(struct output_file *out, unsigned int len,
-		uint32_t fill_val)
-{
-	chunk_header_t chunk_header;
-	int rnd_up_len, count;
-	int ret;
-
-	/* Round up the fill length to a multiple of the block size */
-	rnd_up_len = ALIGN(len, out->block_size);
-
-	/* Finally we can safely emit a chunk of data */
-	chunk_header.chunk_type = CHUNK_TYPE_FILL;
-	chunk_header.reserved1 = 0;
-	chunk_header.chunk_sz = rnd_up_len / out->block_size;
-	chunk_header.total_sz = CHUNK_HEADER_LEN + sizeof(fill_val);
-	ret = out->ops->write(out, &chunk_header, sizeof(chunk_header));
-
-	if (ret < 0)
-		return -1;
-	ret = out->ops->write(out, &fill_val, sizeof(fill_val));
-	if (ret < 0)
-		return -1;
-
-	if (out->use_crc) {
-		count = out->block_size / sizeof(uint32_t);
-		while (count--)
-			out->crc32 = sparse_crc32(out->crc32, &fill_val, sizeof(uint32_t));
-	}
-
-	out->cur_out_ptr += rnd_up_len;
-	out->chunk_cnt++;
-
-	return 0;
-}
-
-static int write_sparse_data_chunk(struct output_file *out, unsigned int len,
-		void *data)
-{
-	chunk_header_t chunk_header;
-	int rnd_up_len, zero_len;
-	int ret;
-
-	/* Round up the data length to a multiple of the block size */
-	rnd_up_len = ALIGN(len, out->block_size);
-	zero_len = rnd_up_len - len;
-
-	/* Finally we can safely emit a chunk of data */
-	chunk_header.chunk_type = CHUNK_TYPE_RAW;
-	chunk_header.reserved1 = 0;
-	chunk_header.chunk_sz = rnd_up_len / out->block_size;
-	chunk_header.total_sz = CHUNK_HEADER_LEN + rnd_up_len;
-	ret = out->ops->write(out, &chunk_header, sizeof(chunk_header));
-
-	if (ret < 0)
-		return -1;
-	ret = out->ops->write(out, data, len);
-	if (ret < 0)
-		return -1;
-	if (zero_len) {
-		ret = out->ops->write(out, out->zero_buf, zero_len);
-		if (ret < 0)
-			return -1;
-	}
-
-	if (out->use_crc) {
-		out->crc32 = sparse_crc32(out->crc32, data, len);
-		if (zero_len)
-			out->crc32 = sparse_crc32(out->crc32, out->zero_buf, zero_len);
-	}
-
-	out->cur_out_ptr += rnd_up_len;
-	out->chunk_cnt++;
-
-	return 0;
-}
-
-int write_sparse_end_chunk(struct output_file *out)
-{
-	chunk_header_t chunk_header;
-	int ret;
-
-	if (out->use_crc) {
-		chunk_header.chunk_type = CHUNK_TYPE_CRC32;
-		chunk_header.reserved1 = 0;
-		chunk_header.chunk_sz = 0;
-		chunk_header.total_sz = CHUNK_HEADER_LEN + 4;
-
-		ret = out->ops->write(out, &chunk_header, sizeof(chunk_header));
-		if (ret < 0) {
-			return ret;
-		}
-		out->ops->write(out, &out->crc32, 4);
-		if (ret < 0) {
-			return ret;
-		}
-
-		out->chunk_cnt++;
-	}
-
-	return 0;
-}
-
-static struct sparse_file_ops sparse_file_ops = {
-		.write_data_chunk = write_sparse_data_chunk,
-		.write_fill_chunk = write_sparse_fill_chunk,
-		.write_skip_chunk = write_sparse_skip_chunk,
-		.write_end_chunk = write_sparse_end_chunk,
-};
-
-static int write_normal_data_chunk(struct output_file *out, unsigned int len,
-		void *data)
-{
-	int ret;
-	unsigned int rnd_up_len = ALIGN(len, out->block_size);
-
-	ret = out->ops->write(out, data, len);
-	if (ret < 0) {
-		return ret;
-	}
-
-	if (rnd_up_len > len) {
-		ret = out->ops->skip(out, rnd_up_len - len);
-	}
-
-	return ret;
-}
-
-static int write_normal_fill_chunk(struct output_file *out, unsigned int len,
-		uint32_t fill_val)
-{
-	int ret;
-	unsigned int i;
-	unsigned int write_len;
-
-	/* Initialize fill_buf with the fill_val */
-	for (i = 0; i < out->block_size / sizeof(uint32_t); i++) {
-		out->fill_buf[i] = fill_val;
-	}
-
-	while (len) {
-		write_len = min(len, out->block_size);
-		ret = out->ops->write(out, out->fill_buf, write_len);
-		if (ret < 0) {
-			return ret;
-		}
-
-		len -= write_len;
-	}
-
-	return 0;
-}
-
-static int write_normal_skip_chunk(struct output_file *out, int64_t len)
-{
-	return out->ops->skip(out, len);
-}
-
-int write_normal_end_chunk(struct output_file *out)
-{
-	return out->ops->pad(out, out->len);
-}
-
-static struct sparse_file_ops normal_file_ops = {
-		.write_data_chunk = write_normal_data_chunk,
-		.write_fill_chunk = write_normal_fill_chunk,
-		.write_skip_chunk = write_normal_skip_chunk,
-		.write_end_chunk = write_normal_end_chunk,
-};
-
-void output_file_close(struct output_file *out)
-{
-	out->sparse_ops->write_end_chunk(out);
-	out->ops->close(out);
-}
-
-static int output_file_init(struct output_file *out, int block_size,
-		int64_t len, bool sparse, int chunks, bool crc)
-{
-	int ret;
-
-	out->len = len;
-	out->block_size = block_size;
-	out->cur_out_ptr = 0ll;
-	out->chunk_cnt = 0;
-	out->crc32 = 0;
-	out->use_crc = crc;
-
-	out->zero_buf = calloc(block_size, 1);
-	if (!out->zero_buf) {
-		error_errno("malloc zero_buf");
-		return -ENOMEM;
-	}
-
-	out->fill_buf = calloc(block_size, 1);
-	if (!out->fill_buf) {
-		error_errno("malloc fill_buf");
-		ret = -ENOMEM;
-		goto err_fill_buf;
-	}
-
-	if (sparse) {
-		out->sparse_ops = &sparse_file_ops;
-	} else {
-		out->sparse_ops = &normal_file_ops;
-	}
-
-	if (sparse) {
-		sparse_header_t sparse_header = {
-				.magic = SPARSE_HEADER_MAGIC,
-				.major_version = SPARSE_HEADER_MAJOR_VER,
-				.minor_version = SPARSE_HEADER_MINOR_VER,
-				.file_hdr_sz = SPARSE_HEADER_LEN,
-				.chunk_hdr_sz = CHUNK_HEADER_LEN,
-				.blk_sz = out->block_size,
-				.total_blks = DIV_ROUND_UP(out->len, out->block_size),
-				.total_chunks = chunks,
-				.image_checksum = 0
-		};
-
-		if (out->use_crc) {
-			sparse_header.total_chunks++;
-		}
-
-		ret = out->ops->write(out, &sparse_header, sizeof(sparse_header));
-		if (ret < 0) {
-			goto err_write;
-		}
-	}
-
-	return 0;
-
-err_write:
-	free(out->fill_buf);
-err_fill_buf:
-	free(out->zero_buf);
-	return ret;
-}
-
-static struct output_file *output_file_new_gz(void)
-{
-	struct output_file_gz *outgz = calloc(1, sizeof(struct output_file_gz));
-	if (!outgz) {
-		error_errno("malloc struct outgz");
-		return NULL;
-	}
-
-	outgz->out.ops = &gz_file_ops;
-
-	return &outgz->out;
-}
-
-static struct output_file *output_file_new_normal(void)
-{
-	struct output_file_normal *outn = calloc(1, sizeof(struct output_file_normal));
-	if (!outn) {
-		error_errno("malloc struct outn");
-		return NULL;
-	}
-
-	outn->out.ops = &file_ops;
-
-	return &outn->out;
-}
-
-struct output_file *output_file_open_callback(int (*write)(void *, const void *, int),
-		void *priv, unsigned int block_size, int64_t len,
-		int gz __unused, int sparse, int chunks, int crc)
-{
-	int ret;
-	struct output_file_callback *outc;
-
-	outc = calloc(1, sizeof(struct output_file_callback));
-	if (!outc) {
-		error_errno("malloc struct outc");
-		return NULL;
-	}
-
-	outc->out.ops = &callback_file_ops;
-	outc->priv = priv;
-	outc->write = write;
-
-	ret = output_file_init(&outc->out, block_size, len, sparse, chunks, crc);
-	if (ret < 0) {
-		free(outc);
-		return NULL;
-	}
-
-	return &outc->out;
-}
-
-struct output_file *output_file_open_fd(int fd, unsigned int block_size, int64_t len,
-		int gz, int sparse, int chunks, int crc)
-{
-	int ret;
-	struct output_file *out;
-
-	if (gz) {
-		out = output_file_new_gz();
-	} else {
-		out = output_file_new_normal();
-	}
-	if (!out) {
-		return NULL;
-	}
-
-	out->ops->open(out, fd);
-
-	ret = output_file_init(out, block_size, len, sparse, chunks, crc);
-	if (ret < 0) {
-		free(out);
-		return NULL;
-	}
-
-	return out;
-}
-
-/* Write a contiguous region of data blocks from a memory buffer */
-int write_data_chunk(struct output_file *out, unsigned int len, void *data)
-{
-	return out->sparse_ops->write_data_chunk(out, len, data);
-}
-
-/* Write a contiguous region of data blocks with a fill value */
-int write_fill_chunk(struct output_file *out, unsigned int len,
-		uint32_t fill_val)
-{
-	return out->sparse_ops->write_fill_chunk(out, len, fill_val);
-}
-
-int write_fd_chunk(struct output_file *out, unsigned int len,
-		int fd, int64_t offset)
-{
-	int ret;
-	int64_t aligned_offset;
-	int aligned_diff;
-	uint64_t buffer_size;
-	char *ptr;
-
-	aligned_offset = offset & ~(4096 - 1);
-	aligned_diff = offset - aligned_offset;
-	buffer_size = (uint64_t)len + (uint64_t)aligned_diff;
-
-#ifndef _WIN32
-	if (buffer_size > SIZE_MAX)
-		return -E2BIG;
-	char *data = mmap64(NULL, buffer_size, PROT_READ, MAP_SHARED, fd,
-			aligned_offset);
-	if (data == MAP_FAILED) {
-		return -errno;
-	}
-	ptr = data + aligned_diff;
-#else
-	off64_t pos;
-	char *data = malloc(len);
-	if (!data) {
-		return -errno;
-	}
-	pos = lseek64(fd, offset, SEEK_SET);
-	if (pos < 0) {
-                free(data);
-		return -errno;
-	}
-	ret = read_all(fd, data, len);
-	if (ret < 0) {
-                free(data);
-		return ret;
-	}
-	ptr = data;
-#endif
-
-	ret = out->sparse_ops->write_data_chunk(out, len, ptr);
-
-#ifndef _WIN32
-	munmap(data, buffer_size);
-#else
-	free(data);
-#endif
-
-	return ret;
-}
-
-/* Write a contiguous region of data blocks from a file */
-int write_file_chunk(struct output_file *out, unsigned int len,
-		const char *file, int64_t offset)
-{
-	int ret;
-
-	int file_fd = open(file, O_RDONLY | O_BINARY);
-	if (file_fd < 0) {
-		return -errno;
-	}
-
-	ret = write_fd_chunk(out, len, file_fd, offset);
-
-	close(file_fd);
-
-	return ret;
-}
-
-int write_skip_chunk(struct output_file *out, int64_t len)
-{
-	return out->sparse_ops->write_skip_chunk(out, len);
-}
diff --git a/libsparse/output_file.cpp b/libsparse/output_file.cpp
new file mode 100644
index 0000000..fe314b3
--- /dev/null
+++ b/libsparse/output_file.cpp
@@ -0,0 +1,720 @@
+/*
+ * Copyright (C) 2010 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.
+ */
+
+#define _FILE_OFFSET_BITS 64
+#define _LARGEFILE64_SOURCE 1
+
+#include <fcntl.h>
+#include <inttypes.h>
+#include <limits.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <zlib.h>
+
+#include "defs.h"
+#include "output_file.h"
+#include "sparse_crc32.h"
+#include "sparse_format.h"
+
+#ifndef _WIN32
+#include <sys/mman.h>
+#define O_BINARY 0
+#else
+#define ftruncate64 ftruncate
+#endif
+
+#if defined(__APPLE__) && defined(__MACH__)
+#define lseek64 lseek
+#define ftruncate64 ftruncate
+#define mmap64 mmap
+#define off64_t off_t
+#endif
+
+#define min(a, b)        \
+  ({                     \
+    typeof(a) _a = (a);  \
+    typeof(b) _b = (b);  \
+    (_a < _b) ? _a : _b; \
+  })
+
+#define SPARSE_HEADER_MAJOR_VER 1
+#define SPARSE_HEADER_MINOR_VER 0
+#define SPARSE_HEADER_LEN (sizeof(sparse_header_t))
+#define CHUNK_HEADER_LEN (sizeof(chunk_header_t))
+
+#define container_of(inner, outer_t, elem) ((outer_t*)((char*)(inner)-offsetof(outer_t, elem)))
+
+struct output_file_ops {
+  int (*open)(struct output_file*, int fd);
+  int (*skip)(struct output_file*, int64_t);
+  int (*pad)(struct output_file*, int64_t);
+  int (*write)(struct output_file*, void*, size_t);
+  void (*close)(struct output_file*);
+};
+
+struct sparse_file_ops {
+  int (*write_data_chunk)(struct output_file* out, unsigned int len, void* data);
+  int (*write_fill_chunk)(struct output_file* out, unsigned int len, uint32_t fill_val);
+  int (*write_skip_chunk)(struct output_file* out, int64_t len);
+  int (*write_end_chunk)(struct output_file* out);
+};
+
+struct output_file {
+  int64_t cur_out_ptr;
+  unsigned int chunk_cnt;
+  uint32_t crc32;
+  struct output_file_ops* ops;
+  struct sparse_file_ops* sparse_ops;
+  int use_crc;
+  unsigned int block_size;
+  int64_t len;
+  char* zero_buf;
+  uint32_t* fill_buf;
+  char* buf;
+};
+
+struct output_file_gz {
+  struct output_file out;
+  gzFile gz_fd;
+};
+
+#define to_output_file_gz(_o) container_of((_o), struct output_file_gz, out)
+
+struct output_file_normal {
+  struct output_file out;
+  int fd;
+};
+
+#define to_output_file_normal(_o) container_of((_o), struct output_file_normal, out)
+
+struct output_file_callback {
+  struct output_file out;
+  void* priv;
+  int (*write)(void* priv, const void* buf, size_t len);
+};
+
+#define to_output_file_callback(_o) container_of((_o), struct output_file_callback, out)
+
+static int file_open(struct output_file* out, int fd) {
+  struct output_file_normal* outn = to_output_file_normal(out);
+
+  outn->fd = fd;
+  return 0;
+}
+
+static int file_skip(struct output_file* out, int64_t cnt) {
+  off64_t ret;
+  struct output_file_normal* outn = to_output_file_normal(out);
+
+  ret = lseek64(outn->fd, cnt, SEEK_CUR);
+  if (ret < 0) {
+    error_errno("lseek64");
+    return -1;
+  }
+  return 0;
+}
+
+static int file_pad(struct output_file* out, int64_t len) {
+  int ret;
+  struct output_file_normal* outn = to_output_file_normal(out);
+
+  ret = ftruncate64(outn->fd, len);
+  if (ret < 0) {
+    return -errno;
+  }
+
+  return 0;
+}
+
+static int file_write(struct output_file* out, void* data, size_t len) {
+  ssize_t ret;
+  struct output_file_normal* outn = to_output_file_normal(out);
+
+  while (len > 0) {
+    ret = write(outn->fd, data, len);
+    if (ret < 0) {
+      if (errno == EINTR) {
+        continue;
+      }
+      error_errno("write");
+      return -1;
+    }
+
+    data = (char*)data + ret;
+    len -= ret;
+  }
+
+  return 0;
+}
+
+static void file_close(struct output_file* out) {
+  struct output_file_normal* outn = to_output_file_normal(out);
+
+  free(outn);
+}
+
+static struct output_file_ops file_ops = {
+    .open = file_open,
+    .skip = file_skip,
+    .pad = file_pad,
+    .write = file_write,
+    .close = file_close,
+};
+
+static int gz_file_open(struct output_file* out, int fd) {
+  struct output_file_gz* outgz = to_output_file_gz(out);
+
+  outgz->gz_fd = gzdopen(fd, "wb9");
+  if (!outgz->gz_fd) {
+    error_errno("gzopen");
+    return -errno;
+  }
+
+  return 0;
+}
+
+static int gz_file_skip(struct output_file* out, int64_t cnt) {
+  off64_t ret;
+  struct output_file_gz* outgz = to_output_file_gz(out);
+
+  ret = gzseek(outgz->gz_fd, cnt, SEEK_CUR);
+  if (ret < 0) {
+    error_errno("gzseek");
+    return -1;
+  }
+  return 0;
+}
+
+static int gz_file_pad(struct output_file* out, int64_t len) {
+  off64_t ret;
+  struct output_file_gz* outgz = to_output_file_gz(out);
+
+  ret = gztell(outgz->gz_fd);
+  if (ret < 0) {
+    return -1;
+  }
+
+  if (ret >= len) {
+    return 0;
+  }
+
+  ret = gzseek(outgz->gz_fd, len - 1, SEEK_SET);
+  if (ret < 0) {
+    return -1;
+  }
+
+  gzwrite(outgz->gz_fd, "", 1);
+
+  return 0;
+}
+
+static int gz_file_write(struct output_file* out, void* data, size_t len) {
+  int ret;
+  struct output_file_gz* outgz = to_output_file_gz(out);
+
+  while (len > 0) {
+    ret = gzwrite(outgz->gz_fd, data, min(len, (unsigned int)INT_MAX));
+    if (ret == 0) {
+      error("gzwrite %s", gzerror(outgz->gz_fd, nullptr));
+      return -1;
+    }
+    len -= ret;
+    data = (char*)data + ret;
+  }
+
+  return 0;
+}
+
+static void gz_file_close(struct output_file* out) {
+  struct output_file_gz* outgz = to_output_file_gz(out);
+
+  gzclose(outgz->gz_fd);
+  free(outgz);
+}
+
+static struct output_file_ops gz_file_ops = {
+    .open = gz_file_open,
+    .skip = gz_file_skip,
+    .pad = gz_file_pad,
+    .write = gz_file_write,
+    .close = gz_file_close,
+};
+
+static int callback_file_open(struct output_file* out __unused, int fd __unused) {
+  return 0;
+}
+
+static int callback_file_skip(struct output_file* out, int64_t off) {
+  struct output_file_callback* outc = to_output_file_callback(out);
+  int to_write;
+  int ret;
+
+  while (off > 0) {
+    to_write = min(off, (int64_t)INT_MAX);
+    ret = outc->write(outc->priv, nullptr, to_write);
+    if (ret < 0) {
+      return ret;
+    }
+    off -= to_write;
+  }
+
+  return 0;
+}
+
+static int callback_file_pad(struct output_file* out __unused, int64_t len __unused) {
+  return -1;
+}
+
+static int callback_file_write(struct output_file* out, void* data, size_t len) {
+  struct output_file_callback* outc = to_output_file_callback(out);
+
+  return outc->write(outc->priv, data, len);
+}
+
+static void callback_file_close(struct output_file* out) {
+  struct output_file_callback* outc = to_output_file_callback(out);
+
+  free(outc);
+}
+
+static struct output_file_ops callback_file_ops = {
+    .open = callback_file_open,
+    .skip = callback_file_skip,
+    .pad = callback_file_pad,
+    .write = callback_file_write,
+    .close = callback_file_close,
+};
+
+int read_all(int fd, void* buf, size_t len) {
+  size_t total = 0;
+  int ret;
+  char* ptr = reinterpret_cast<char*>(buf);
+
+  while (total < len) {
+    ret = read(fd, ptr, len - total);
+
+    if (ret < 0) return -errno;
+
+    if (ret == 0) return -EINVAL;
+
+    ptr += ret;
+    total += ret;
+  }
+
+  return 0;
+}
+
+static int write_sparse_skip_chunk(struct output_file* out, int64_t skip_len) {
+  chunk_header_t chunk_header;
+  int ret;
+
+  if (skip_len % out->block_size) {
+    error("don't care size %" PRIi64 " is not a multiple of the block size %u", skip_len,
+          out->block_size);
+    return -1;
+  }
+
+  /* We are skipping data, so emit a don't care chunk. */
+  chunk_header.chunk_type = CHUNK_TYPE_DONT_CARE;
+  chunk_header.reserved1 = 0;
+  chunk_header.chunk_sz = skip_len / out->block_size;
+  chunk_header.total_sz = CHUNK_HEADER_LEN;
+  ret = out->ops->write(out, &chunk_header, sizeof(chunk_header));
+  if (ret < 0) return -1;
+
+  out->cur_out_ptr += skip_len;
+  out->chunk_cnt++;
+
+  return 0;
+}
+
+static int write_sparse_fill_chunk(struct output_file* out, unsigned int len, uint32_t fill_val) {
+  chunk_header_t chunk_header;
+  int rnd_up_len, count;
+  int ret;
+
+  /* Round up the fill length to a multiple of the block size */
+  rnd_up_len = ALIGN(len, out->block_size);
+
+  /* Finally we can safely emit a chunk of data */
+  chunk_header.chunk_type = CHUNK_TYPE_FILL;
+  chunk_header.reserved1 = 0;
+  chunk_header.chunk_sz = rnd_up_len / out->block_size;
+  chunk_header.total_sz = CHUNK_HEADER_LEN + sizeof(fill_val);
+  ret = out->ops->write(out, &chunk_header, sizeof(chunk_header));
+
+  if (ret < 0) return -1;
+  ret = out->ops->write(out, &fill_val, sizeof(fill_val));
+  if (ret < 0) return -1;
+
+  if (out->use_crc) {
+    count = out->block_size / sizeof(uint32_t);
+    while (count--) out->crc32 = sparse_crc32(out->crc32, &fill_val, sizeof(uint32_t));
+  }
+
+  out->cur_out_ptr += rnd_up_len;
+  out->chunk_cnt++;
+
+  return 0;
+}
+
+static int write_sparse_data_chunk(struct output_file* out, unsigned int len, void* data) {
+  chunk_header_t chunk_header;
+  int rnd_up_len, zero_len;
+  int ret;
+
+  /* Round up the data length to a multiple of the block size */
+  rnd_up_len = ALIGN(len, out->block_size);
+  zero_len = rnd_up_len - len;
+
+  /* Finally we can safely emit a chunk of data */
+  chunk_header.chunk_type = CHUNK_TYPE_RAW;
+  chunk_header.reserved1 = 0;
+  chunk_header.chunk_sz = rnd_up_len / out->block_size;
+  chunk_header.total_sz = CHUNK_HEADER_LEN + rnd_up_len;
+  ret = out->ops->write(out, &chunk_header, sizeof(chunk_header));
+
+  if (ret < 0) return -1;
+  ret = out->ops->write(out, data, len);
+  if (ret < 0) return -1;
+  if (zero_len) {
+    ret = out->ops->write(out, out->zero_buf, zero_len);
+    if (ret < 0) return -1;
+  }
+
+  if (out->use_crc) {
+    out->crc32 = sparse_crc32(out->crc32, data, len);
+    if (zero_len) out->crc32 = sparse_crc32(out->crc32, out->zero_buf, zero_len);
+  }
+
+  out->cur_out_ptr += rnd_up_len;
+  out->chunk_cnt++;
+
+  return 0;
+}
+
+int write_sparse_end_chunk(struct output_file* out) {
+  chunk_header_t chunk_header;
+  int ret;
+
+  if (out->use_crc) {
+    chunk_header.chunk_type = CHUNK_TYPE_CRC32;
+    chunk_header.reserved1 = 0;
+    chunk_header.chunk_sz = 0;
+    chunk_header.total_sz = CHUNK_HEADER_LEN + 4;
+
+    ret = out->ops->write(out, &chunk_header, sizeof(chunk_header));
+    if (ret < 0) {
+      return ret;
+    }
+    out->ops->write(out, &out->crc32, 4);
+    if (ret < 0) {
+      return ret;
+    }
+
+    out->chunk_cnt++;
+  }
+
+  return 0;
+}
+
+static struct sparse_file_ops sparse_file_ops = {
+    .write_data_chunk = write_sparse_data_chunk,
+    .write_fill_chunk = write_sparse_fill_chunk,
+    .write_skip_chunk = write_sparse_skip_chunk,
+    .write_end_chunk = write_sparse_end_chunk,
+};
+
+static int write_normal_data_chunk(struct output_file* out, unsigned int len, void* data) {
+  int ret;
+  unsigned int rnd_up_len = ALIGN(len, out->block_size);
+
+  ret = out->ops->write(out, data, len);
+  if (ret < 0) {
+    return ret;
+  }
+
+  if (rnd_up_len > len) {
+    ret = out->ops->skip(out, rnd_up_len - len);
+  }
+
+  return ret;
+}
+
+static int write_normal_fill_chunk(struct output_file* out, unsigned int len, uint32_t fill_val) {
+  int ret;
+  unsigned int i;
+  unsigned int write_len;
+
+  /* Initialize fill_buf with the fill_val */
+  for (i = 0; i < out->block_size / sizeof(uint32_t); i++) {
+    out->fill_buf[i] = fill_val;
+  }
+
+  while (len) {
+    write_len = min(len, out->block_size);
+    ret = out->ops->write(out, out->fill_buf, write_len);
+    if (ret < 0) {
+      return ret;
+    }
+
+    len -= write_len;
+  }
+
+  return 0;
+}
+
+static int write_normal_skip_chunk(struct output_file* out, int64_t len) {
+  return out->ops->skip(out, len);
+}
+
+int write_normal_end_chunk(struct output_file* out) {
+  return out->ops->pad(out, out->len);
+}
+
+static struct sparse_file_ops normal_file_ops = {
+    .write_data_chunk = write_normal_data_chunk,
+    .write_fill_chunk = write_normal_fill_chunk,
+    .write_skip_chunk = write_normal_skip_chunk,
+    .write_end_chunk = write_normal_end_chunk,
+};
+
+void output_file_close(struct output_file* out) {
+  out->sparse_ops->write_end_chunk(out);
+  out->ops->close(out);
+}
+
+static int output_file_init(struct output_file* out, int block_size, int64_t len, bool sparse,
+                            int chunks, bool crc) {
+  int ret;
+
+  out->len = len;
+  out->block_size = block_size;
+  out->cur_out_ptr = 0ll;
+  out->chunk_cnt = 0;
+  out->crc32 = 0;
+  out->use_crc = crc;
+
+  out->zero_buf = reinterpret_cast<char*>(calloc(block_size, 1));
+  if (!out->zero_buf) {
+    error_errno("malloc zero_buf");
+    return -ENOMEM;
+  }
+
+  out->fill_buf = reinterpret_cast<uint32_t*>(calloc(block_size, 1));
+  if (!out->fill_buf) {
+    error_errno("malloc fill_buf");
+    ret = -ENOMEM;
+    goto err_fill_buf;
+  }
+
+  if (sparse) {
+    out->sparse_ops = &sparse_file_ops;
+  } else {
+    out->sparse_ops = &normal_file_ops;
+  }
+
+  if (sparse) {
+    sparse_header_t sparse_header = {
+        .magic = SPARSE_HEADER_MAGIC,
+        .major_version = SPARSE_HEADER_MAJOR_VER,
+        .minor_version = SPARSE_HEADER_MINOR_VER,
+        .file_hdr_sz = SPARSE_HEADER_LEN,
+        .chunk_hdr_sz = CHUNK_HEADER_LEN,
+        .blk_sz = out->block_size,
+        .total_blks = static_cast<unsigned>(DIV_ROUND_UP(out->len, out->block_size)),
+        .total_chunks = static_cast<unsigned>(chunks),
+        .image_checksum = 0};
+
+    if (out->use_crc) {
+      sparse_header.total_chunks++;
+    }
+
+    ret = out->ops->write(out, &sparse_header, sizeof(sparse_header));
+    if (ret < 0) {
+      goto err_write;
+    }
+  }
+
+  return 0;
+
+err_write:
+  free(out->fill_buf);
+err_fill_buf:
+  free(out->zero_buf);
+  return ret;
+}
+
+static struct output_file* output_file_new_gz(void) {
+  struct output_file_gz* outgz =
+      reinterpret_cast<struct output_file_gz*>(calloc(1, sizeof(struct output_file_gz)));
+  if (!outgz) {
+    error_errno("malloc struct outgz");
+    return nullptr;
+  }
+
+  outgz->out.ops = &gz_file_ops;
+
+  return &outgz->out;
+}
+
+static struct output_file* output_file_new_normal(void) {
+  struct output_file_normal* outn =
+      reinterpret_cast<struct output_file_normal*>(calloc(1, sizeof(struct output_file_normal)));
+  if (!outn) {
+    error_errno("malloc struct outn");
+    return nullptr;
+  }
+
+  outn->out.ops = &file_ops;
+
+  return &outn->out;
+}
+
+struct output_file* output_file_open_callback(int (*write)(void*, const void*, size_t), void* priv,
+                                              unsigned int block_size, int64_t len, int gz __unused,
+                                              int sparse, int chunks, int crc) {
+  int ret;
+  struct output_file_callback* outc;
+
+  outc =
+      reinterpret_cast<struct output_file_callback*>(calloc(1, sizeof(struct output_file_callback)));
+  if (!outc) {
+    error_errno("malloc struct outc");
+    return nullptr;
+  }
+
+  outc->out.ops = &callback_file_ops;
+  outc->priv = priv;
+  outc->write = write;
+
+  ret = output_file_init(&outc->out, block_size, len, sparse, chunks, crc);
+  if (ret < 0) {
+    free(outc);
+    return nullptr;
+  }
+
+  return &outc->out;
+}
+
+struct output_file* output_file_open_fd(int fd, unsigned int block_size, int64_t len, int gz,
+                                        int sparse, int chunks, int crc) {
+  int ret;
+  struct output_file* out;
+
+  if (gz) {
+    out = output_file_new_gz();
+  } else {
+    out = output_file_new_normal();
+  }
+  if (!out) {
+    return nullptr;
+  }
+
+  out->ops->open(out, fd);
+
+  ret = output_file_init(out, block_size, len, sparse, chunks, crc);
+  if (ret < 0) {
+    free(out);
+    return nullptr;
+  }
+
+  return out;
+}
+
+/* Write a contiguous region of data blocks from a memory buffer */
+int write_data_chunk(struct output_file* out, unsigned int len, void* data) {
+  return out->sparse_ops->write_data_chunk(out, len, data);
+}
+
+/* Write a contiguous region of data blocks with a fill value */
+int write_fill_chunk(struct output_file* out, unsigned int len, uint32_t fill_val) {
+  return out->sparse_ops->write_fill_chunk(out, len, fill_val);
+}
+
+int write_fd_chunk(struct output_file* out, unsigned int len, int fd, int64_t offset) {
+  int ret;
+  int64_t aligned_offset;
+  int aligned_diff;
+  uint64_t buffer_size;
+  char* ptr;
+
+  aligned_offset = offset & ~(4096 - 1);
+  aligned_diff = offset - aligned_offset;
+  buffer_size = (uint64_t)len + (uint64_t)aligned_diff;
+
+#ifndef _WIN32
+  if (buffer_size > SIZE_MAX) return -E2BIG;
+  char* data =
+      reinterpret_cast<char*>(mmap64(nullptr, buffer_size, PROT_READ, MAP_SHARED, fd, aligned_offset));
+  if (data == MAP_FAILED) {
+    return -errno;
+  }
+  ptr = data + aligned_diff;
+#else
+  off64_t pos;
+  char* data = reinterpret_cast<char*>(malloc(len));
+  if (!data) {
+    return -errno;
+  }
+  pos = lseek64(fd, offset, SEEK_SET);
+  if (pos < 0) {
+    free(data);
+    return -errno;
+  }
+  ret = read_all(fd, data, len);
+  if (ret < 0) {
+    free(data);
+    return ret;
+  }
+  ptr = data;
+#endif
+
+  ret = out->sparse_ops->write_data_chunk(out, len, ptr);
+
+#ifndef _WIN32
+  munmap(data, buffer_size);
+#else
+  free(data);
+#endif
+
+  return ret;
+}
+
+/* Write a contiguous region of data blocks from a file */
+int write_file_chunk(struct output_file* out, unsigned int len, const char* file, int64_t offset) {
+  int ret;
+
+  int file_fd = open(file, O_RDONLY | O_BINARY);
+  if (file_fd < 0) {
+    return -errno;
+  }
+
+  ret = write_fd_chunk(out, len, file_fd, offset);
+
+  close(file_fd);
+
+  return ret;
+}
+
+int write_skip_chunk(struct output_file* out, int64_t len) {
+  return out->sparse_ops->write_skip_chunk(out, len);
+}
diff --git a/libsparse/output_file.h b/libsparse/output_file.h
index b67e94e..278430b 100644
--- a/libsparse/output_file.h
+++ b/libsparse/output_file.h
@@ -25,22 +25,19 @@
 
 struct output_file;
 
-struct output_file *output_file_open_fd(int fd, unsigned int block_size, int64_t len,
-		int gz, int sparse, int chunks, int crc);
-struct output_file *output_file_open_callback(int (*write)(void *, const void *, int),
-		void *priv, unsigned int block_size, int64_t len, int gz, int sparse,
-		int chunks, int crc);
-int write_data_chunk(struct output_file *out, unsigned int len, void *data);
-int write_fill_chunk(struct output_file *out, unsigned int len,
-		uint32_t fill_val);
-int write_file_chunk(struct output_file *out, unsigned int len,
-		const char *file, int64_t offset);
-int write_fd_chunk(struct output_file *out, unsigned int len,
-		int fd, int64_t offset);
-int write_skip_chunk(struct output_file *out, int64_t len);
-void output_file_close(struct output_file *out);
+struct output_file* output_file_open_fd(int fd, unsigned int block_size, int64_t len, int gz,
+                                        int sparse, int chunks, int crc);
+struct output_file* output_file_open_callback(int (*write)(void*, const void*, size_t), void* priv,
+                                              unsigned int block_size, int64_t len, int gz,
+                                              int sparse, int chunks, int crc);
+int write_data_chunk(struct output_file* out, unsigned int len, void* data);
+int write_fill_chunk(struct output_file* out, unsigned int len, uint32_t fill_val);
+int write_file_chunk(struct output_file* out, unsigned int len, const char* file, int64_t offset);
+int write_fd_chunk(struct output_file* out, unsigned int len, int fd, int64_t offset);
+int write_skip_chunk(struct output_file* out, int64_t len);
+void output_file_close(struct output_file* out);
 
-int read_all(int fd, void *buf, size_t len);
+int read_all(int fd, void* buf, size_t len);
 
 #ifdef __cplusplus
 }
diff --git a/libsparse/simg2img.c b/libsparse/simg2img.c
deleted file mode 100644
index b9b438e..0000000
--- a/libsparse/simg2img.c
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * Copyright (C) 2010 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 <sparse/sparse.h>
-
-#include <fcntl.h>
-#include <stdbool.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#ifndef O_BINARY
-#define O_BINARY 0
-#endif
-
-void usage()
-{
-  fprintf(stderr, "Usage: simg2img <sparse_image_files> <raw_image_file>\n");
-}
-
-int main(int argc, char *argv[])
-{
-	int in;
-	int out;
-	int i;
-	struct sparse_file *s;
-
-	if (argc < 3) {
-		usage();
-		exit(-1);
-	}
-
-	out = open(argv[argc - 1], O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0664);
-	if (out < 0) {
-		fprintf(stderr, "Cannot open output file %s\n", argv[argc - 1]);
-		exit(-1);
-	}
-
-	for (i = 1; i < argc - 1; i++) {
-		if (strcmp(argv[i], "-") == 0) {
-			in = STDIN_FILENO;
-		} else {
-			in = open(argv[i], O_RDONLY | O_BINARY);
-			if (in < 0) {
-				fprintf(stderr, "Cannot open input file %s\n", argv[i]);
-				exit(-1);
-			}
-		}
-
-		s = sparse_file_import(in, true, false);
-		if (!s) {
-			fprintf(stderr, "Failed to read sparse file\n");
-			exit(-1);
-		}
-
-		if (lseek(out, 0, SEEK_SET) == -1) {
-			perror("lseek failed");
-			exit(EXIT_FAILURE);
-		}
-
-		if (sparse_file_write(s, out, false, false, false) < 0) {
-			fprintf(stderr, "Cannot write output file\n");
-			exit(-1);
-		}
-		sparse_file_destroy(s);
-		close(in);
-	}
-
-	close(out);
-
-	exit(0);
-}
-
diff --git a/libsparse/simg2img.cpp b/libsparse/simg2img.cpp
new file mode 100644
index 0000000..8ba5f69
--- /dev/null
+++ b/libsparse/simg2img.cpp
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2010 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 <sparse/sparse.h>
+
+#include <fcntl.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#ifndef O_BINARY
+#define O_BINARY 0
+#endif
+
+void usage() {
+  fprintf(stderr, "Usage: simg2img <sparse_image_files> <raw_image_file>\n");
+}
+
+int main(int argc, char* argv[]) {
+  int in;
+  int out;
+  int i;
+  struct sparse_file* s;
+
+  if (argc < 3) {
+    usage();
+    exit(-1);
+  }
+
+  out = open(argv[argc - 1], O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0664);
+  if (out < 0) {
+    fprintf(stderr, "Cannot open output file %s\n", argv[argc - 1]);
+    exit(-1);
+  }
+
+  for (i = 1; i < argc - 1; i++) {
+    if (strcmp(argv[i], "-") == 0) {
+      in = STDIN_FILENO;
+    } else {
+      in = open(argv[i], O_RDONLY | O_BINARY);
+      if (in < 0) {
+        fprintf(stderr, "Cannot open input file %s\n", argv[i]);
+        exit(-1);
+      }
+    }
+
+    s = sparse_file_import(in, true, false);
+    if (!s) {
+      fprintf(stderr, "Failed to read sparse file\n");
+      exit(-1);
+    }
+
+    if (lseek(out, 0, SEEK_SET) == -1) {
+      perror("lseek failed");
+      exit(EXIT_FAILURE);
+    }
+
+    if (sparse_file_write(s, out, false, false, false) < 0) {
+      fprintf(stderr, "Cannot write output file\n");
+      exit(-1);
+    }
+    sparse_file_destroy(s);
+    close(in);
+  }
+
+  close(out);
+
+  exit(0);
+}
diff --git a/libsparse/simg2simg.c b/libsparse/simg2simg.c
deleted file mode 100644
index 5f9ccf6..0000000
--- a/libsparse/simg2simg.c
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
- * Copyright (C) 2012 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.
- */
-
-#define _FILE_OFFSET_BITS 64
-#define _LARGEFILE64_SOURCE 1
-#define _GNU_SOURCE
-
-#include <fcntl.h>
-#include <stdbool.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include <sparse/sparse.h>
-
-#ifndef O_BINARY
-#define O_BINARY 0
-#endif
-
-void usage()
-{
-  fprintf(stderr, "Usage: simg2simg <sparse image file> <sparse_image_file> <max_size>\n");
-}
-
-int main(int argc, char *argv[])
-{
-	int in;
-	int out;
-	int i;
-	int ret;
-	struct sparse_file *s;
-	int64_t max_size;
-	struct sparse_file **out_s;
-	int files;
-	char filename[4096];
-
-	if (argc != 4) {
-		usage();
-		exit(-1);
-	}
-
-	max_size = atoll(argv[3]);
-
-	in = open(argv[1], O_RDONLY | O_BINARY);
-	if (in < 0) {
-		fprintf(stderr, "Cannot open input file %s\n", argv[1]);
-		exit(-1);
-	}
-
-	s = sparse_file_import(in, true, false);
-	if (!s) {
-		fprintf(stderr, "Failed to import sparse file\n");
-		exit(-1);
-	}
-
-	files = sparse_file_resparse(s, max_size, NULL, 0);
-	if (files < 0) {
-		fprintf(stderr, "Failed to resparse\n");
-		exit(-1);
-	}
-
-	out_s = calloc(sizeof(struct sparse_file *), files);
-	if (!out_s) {
-		fprintf(stderr, "Failed to allocate sparse file array\n");
-		exit(-1);
-	}
-
-	files = sparse_file_resparse(s, max_size, out_s, files);
-	if (files < 0) {
-		fprintf(stderr, "Failed to resparse\n");
-		exit(-1);
-	}
-
-	for (i = 0; i < files; i++) {
-		ret = snprintf(filename, sizeof(filename), "%s.%d", argv[2], i);
-		if (ret >= (int)sizeof(filename)) {
-			fprintf(stderr, "Filename too long\n");
-			exit(-1);
-		}
-
-		out = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0664);
-		if (out < 0) {
-			fprintf(stderr, "Cannot open output file %s\n", argv[2]);
-			exit(-1);
-		}
-
-		ret = sparse_file_write(out_s[i], out, false, true, false);
-		if (ret) {
-			fprintf(stderr, "Failed to write sparse file\n");
-			exit(-1);
-		}
-		close(out);
-	}
-
-	close(in);
-
-	exit(0);
-}
diff --git a/libsparse/simg2simg.cpp b/libsparse/simg2simg.cpp
new file mode 100644
index 0000000..a2c296e
--- /dev/null
+++ b/libsparse/simg2simg.cpp
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+#define _FILE_OFFSET_BITS 64
+#define _LARGEFILE64_SOURCE 1
+
+#include <fcntl.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <sparse/sparse.h>
+
+#ifndef O_BINARY
+#define O_BINARY 0
+#endif
+
+void usage() {
+  fprintf(stderr, "Usage: simg2simg <sparse image file> <sparse_image_file> <max_size>\n");
+}
+
+int main(int argc, char* argv[]) {
+  int in;
+  int out;
+  int i;
+  int ret;
+  struct sparse_file* s;
+  int64_t max_size;
+  struct sparse_file** out_s;
+  int files;
+  char filename[4096];
+
+  if (argc != 4) {
+    usage();
+    exit(-1);
+  }
+
+  max_size = atoll(argv[3]);
+
+  in = open(argv[1], O_RDONLY | O_BINARY);
+  if (in < 0) {
+    fprintf(stderr, "Cannot open input file %s\n", argv[1]);
+    exit(-1);
+  }
+
+  s = sparse_file_import(in, true, false);
+  if (!s) {
+    fprintf(stderr, "Failed to import sparse file\n");
+    exit(-1);
+  }
+
+  files = sparse_file_resparse(s, max_size, nullptr, 0);
+  if (files < 0) {
+    fprintf(stderr, "Failed to resparse\n");
+    exit(-1);
+  }
+
+  out_s = calloc(sizeof(struct sparse_file*), files);
+  if (!out_s) {
+    fprintf(stderr, "Failed to allocate sparse file array\n");
+    exit(-1);
+  }
+
+  files = sparse_file_resparse(s, max_size, out_s, files);
+  if (files < 0) {
+    fprintf(stderr, "Failed to resparse\n");
+    exit(-1);
+  }
+
+  for (i = 0; i < files; i++) {
+    ret = snprintf(filename, sizeof(filename), "%s.%d", argv[2], i);
+    if (ret >= (int)sizeof(filename)) {
+      fprintf(stderr, "Filename too long\n");
+      exit(-1);
+    }
+
+    out = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0664);
+    if (out < 0) {
+      fprintf(stderr, "Cannot open output file %s\n", argv[2]);
+      exit(-1);
+    }
+
+    ret = sparse_file_write(out_s[i], out, false, true, false);
+    if (ret) {
+      fprintf(stderr, "Failed to write sparse file\n");
+      exit(-1);
+    }
+    close(out);
+  }
+
+  close(in);
+
+  exit(0);
+}
diff --git a/libsparse/simg_dump.py b/libsparse/simg_dump.py
index c70d45f..82a03ad 100755
--- a/libsparse/simg_dump.py
+++ b/libsparse/simg_dump.py
@@ -15,43 +15,64 @@
 # limitations under the License.
 
 from __future__ import print_function
-import getopt, posixpath, signal, struct, sys
+import csv
+import getopt
+import hashlib
+import posixpath
+import signal
+import struct
+import sys
+
 
 def usage(argv0):
   print("""
-Usage: %s [-v] sparse_image_file ...
+Usage: %s [-v] [-s] [-c <filename>] sparse_image_file ...
  -v             verbose output
-""" % ( argv0 ))
+ -s             show sha1sum of data blocks
+ -c <filename>  save .csv file of blocks
+""" % (argv0))
   sys.exit(2)
 
-def main():
 
+def main():
   signal.signal(signal.SIGPIPE, signal.SIG_DFL)
 
   me = posixpath.basename(sys.argv[0])
 
   # Parse the command line
-  verbose = 0			# -v
+  verbose = 0                   # -v
+  showhash = 0                  # -s
+  csvfilename = None            # -c
   try:
     opts, args = getopt.getopt(sys.argv[1:],
-                               "v",
-                               ["verbose"])
+                               "vsc:",
+                               ["verbose", "showhash", "csvfile"])
   except getopt.GetoptError, e:
     print(e)
     usage(me)
   for o, a in opts:
     if o in ("-v", "--verbose"):
       verbose += 1
+    elif o in ("-s", "--showhash"):
+      showhash = True
+    elif o in ("-c", "--csvfile"):
+      csvfilename = a
     else:
       print("Unrecognized option \"%s\"" % (o))
       usage(me)
 
-  if len(args) == 0:
+  if not args:
     print("No sparse_image_file specified")
     usage(me)
 
+  if csvfilename:
+    csvfile = open(csvfilename, "wb")
+    csvwriter = csv.writer(csvfile)
+
+  output = verbose or csvfilename or showhash
+
   for path in args:
-    FH = open(path, 'rb')
+    FH = open(path, "rb")
     header_bin = FH.read(28)
     header = struct.unpack("<I4H4I", header_bin)
 
@@ -88,71 +109,99 @@
     if image_checksum != 0:
       print("checksum=0x%08X" % (image_checksum))
 
-    if not verbose:
+    if not output:
       continue
-    print("            input_bytes      output_blocks")
-    print("chunk    offset     number  offset  number")
+
+    if verbose > 0:
+      print("            input_bytes      output_blocks")
+      print("chunk    offset     number  offset  number")
+
+    if csvfilename:
+      csvwriter.writerow(["chunk", "input offset", "input bytes",
+                          "output offset", "output blocks", "type", "hash"])
+
     offset = 0
-    for i in xrange(1,total_chunks+1):
+    for i in xrange(1, total_chunks + 1):
       header_bin = FH.read(12)
       header = struct.unpack("<2H2I", header_bin)
       chunk_type = header[0]
-      reserved1 = header[1]
       chunk_sz = header[2]
       total_sz = header[3]
       data_sz = total_sz - 12
+      curhash = ""
+      curtype = ""
+      curpos = FH.tell()
 
-      print("%4u %10u %10u %7u %7u" % (i, FH.tell(), data_sz, offset, chunk_sz),
-            end=" ")
+      if verbose > 0:
+        print("%4u %10u %10u %7u %7u" % (i, curpos, data_sz, offset, chunk_sz),
+              end=" ")
 
       if chunk_type == 0xCAC1:
         if data_sz != (chunk_sz * blk_sz):
           print("Raw chunk input size (%u) does not match output size (%u)"
                 % (data_sz, chunk_sz * blk_sz))
-          break;
+          break
         else:
-          print("Raw data", end="")
-          FH.read(data_sz)
+          curtype = "Raw data"
+          data = FH.read(data_sz)
+          if showhash:
+            h = hashlib.sha1()
+            h.update(data)
+            curhash = h.hexdigest()
       elif chunk_type == 0xCAC2:
         if data_sz != 4:
           print("Fill chunk should have 4 bytes of fill, but this has %u"
-                % (data_sz), end="")
-          break;
+                % (data_sz))
+          break
         else:
           fill_bin = FH.read(4)
           fill = struct.unpack("<I", fill_bin)
-          print("Fill with 0x%08X" % (fill))
+          curtype = format("Fill with 0x%08X" % (fill))
+          if showhash:
+            h = hashlib.sha1()
+            data = fill_bin * (blk_sz / 4);
+            for block in xrange(chunk_sz):
+              h.update(data)
+            curhash = h.hexdigest()
       elif chunk_type == 0xCAC3:
         if data_sz != 0:
           print("Don't care chunk input size is non-zero (%u)" % (data_sz))
-          break;
+          break
         else:
-          print("Don't care", end="")
+          curtype = "Don't care"
       elif chunk_type == 0xCAC4:
         if data_sz != 4:
           print("CRC32 chunk should have 4 bytes of CRC, but this has %u"
-                % (data_sz), end="")
-          break;
+                % (data_sz))
+          break
         else:
           crc_bin = FH.read(4)
           crc = struct.unpack("<I", crc_bin)
-          print("Unverified CRC32 0x%08X" % (crc))
+          curtype = format("Unverified CRC32 0x%08X" % (crc))
       else:
-          print("Unknown chunk type 0x%04X" % (chunk_type), end="")
-          break;
+        print("Unknown chunk type 0x%04X" % (chunk_type))
+        break
 
-      if verbose > 1:
-        header = struct.unpack("<12B", header_bin)
-        print(" (%02X%02X %02X%02X %02X%02X%02X%02X %02X%02X%02X%02X)"
-              % (header[0], header[1], header[2], header[3],
-                 header[4], header[5], header[6], header[7],
-                 header[8], header[9], header[10], header[11]))
-      else:
-        print()
+      if verbose > 0:
+        print("%-18s" % (curtype), end=" ")
+
+        if verbose > 1:
+          header = struct.unpack("<12B", header_bin)
+          print(" (%02X%02X %02X%02X %02X%02X%02X%02X %02X%02X%02X%02X)"
+                % (header[0], header[1], header[2], header[3],
+                   header[4], header[5], header[6], header[7],
+                   header[8], header[9], header[10], header[11]), end=" ")
+
+        print(curhash)
+
+      if csvfilename:
+        csvwriter.writerow([i, curpos, data_sz, offset, chunk_sz, curtype,
+                            curhash])
 
       offset += chunk_sz
 
-    print("     %10u            %7u         End" % (FH.tell(), offset))
+    if verbose > 0:
+      print("     %10u            %7u         End" % (FH.tell(), offset))
 
     if total_blks != offset:
       print("The header said we should have %u output blocks, but we saw %u"
@@ -163,6 +212,9 @@
       print("There were %u bytes of extra data at the end of the file."
             % (junk_len))
 
+  if csvfilename:
+    csvfile.close()
+
   sys.exit(0)
 
 if __name__ == "__main__":
diff --git a/libsparse/sparse.c b/libsparse/sparse.c
deleted file mode 100644
index b175860..0000000
--- a/libsparse/sparse.c
+++ /dev/null
@@ -1,395 +0,0 @@
-/*
- * Copyright (C) 2012 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 <assert.h>
-#include <stdlib.h>
-
-#include <sparse/sparse.h>
-
-#include "defs.h"
-#include "sparse_file.h"
-
-#include "output_file.h"
-#include "backed_block.h"
-#include "sparse_defs.h"
-#include "sparse_format.h"
-
-struct sparse_file *sparse_file_new(unsigned int block_size, int64_t len)
-{
-	struct sparse_file *s = calloc(sizeof(struct sparse_file), 1);
-	if (!s) {
-		return NULL;
-	}
-
-	s->backed_block_list = backed_block_list_new(block_size);
-	if (!s->backed_block_list) {
-		free(s);
-		return NULL;
-	}
-
-	s->block_size = block_size;
-	s->len = len;
-
-	return s;
-}
-
-void sparse_file_destroy(struct sparse_file *s)
-{
-	backed_block_list_destroy(s->backed_block_list);
-	free(s);
-}
-
-int sparse_file_add_data(struct sparse_file *s,
-		void *data, unsigned int len, unsigned int block)
-{
-	return backed_block_add_data(s->backed_block_list, data, len, block);
-}
-
-int sparse_file_add_fill(struct sparse_file *s,
-		uint32_t fill_val, unsigned int len, unsigned int block)
-{
-	return backed_block_add_fill(s->backed_block_list, fill_val, len, block);
-}
-
-int sparse_file_add_file(struct sparse_file *s,
-		const char *filename, int64_t file_offset, unsigned int len,
-		unsigned int block)
-{
-	return backed_block_add_file(s->backed_block_list, filename, file_offset,
-			len, block);
-}
-
-int sparse_file_add_fd(struct sparse_file *s,
-		int fd, int64_t file_offset, unsigned int len, unsigned int block)
-{
-	return backed_block_add_fd(s->backed_block_list, fd, file_offset,
-			len, block);
-}
-unsigned int sparse_count_chunks(struct sparse_file *s)
-{
-	struct backed_block *bb;
-	unsigned int last_block = 0;
-	unsigned int chunks = 0;
-
-	for (bb = backed_block_iter_new(s->backed_block_list); bb;
-			bb = backed_block_iter_next(bb)) {
-		if (backed_block_block(bb) > last_block) {
-			/* If there is a gap between chunks, add a skip chunk */
-			chunks++;
-		}
-		chunks++;
-		last_block = backed_block_block(bb) +
-				DIV_ROUND_UP(backed_block_len(bb), s->block_size);
-	}
-	if (last_block < DIV_ROUND_UP(s->len, s->block_size)) {
-		chunks++;
-	}
-
-	return chunks;
-}
-
-static int sparse_file_write_block(struct output_file *out,
-		struct backed_block *bb)
-{
-	int ret = -EINVAL;
-
-	switch (backed_block_type(bb)) {
-	case BACKED_BLOCK_DATA:
-		ret = write_data_chunk(out, backed_block_len(bb), backed_block_data(bb));
-		break;
-	case BACKED_BLOCK_FILE:
-		ret = write_file_chunk(out, backed_block_len(bb),
-				       backed_block_filename(bb),
-				       backed_block_file_offset(bb));
-		break;
-	case BACKED_BLOCK_FD:
-		ret = write_fd_chunk(out, backed_block_len(bb),
-				     backed_block_fd(bb),
-				     backed_block_file_offset(bb));
-		break;
-	case BACKED_BLOCK_FILL:
-		ret = write_fill_chunk(out, backed_block_len(bb),
-				       backed_block_fill_val(bb));
-		break;
-	}
-
-	return ret;
-}
-
-static int write_all_blocks(struct sparse_file *s, struct output_file *out)
-{
-	struct backed_block *bb;
-	unsigned int last_block = 0;
-	int64_t pad;
-	int ret = 0;
-
-	for (bb = backed_block_iter_new(s->backed_block_list); bb;
-			bb = backed_block_iter_next(bb)) {
-		if (backed_block_block(bb) > last_block) {
-			unsigned int blocks = backed_block_block(bb) - last_block;
-			write_skip_chunk(out, (int64_t)blocks * s->block_size);
-		}
-		ret = sparse_file_write_block(out, bb);
-		if (ret)
-			return ret;
-		last_block = backed_block_block(bb) +
-				DIV_ROUND_UP(backed_block_len(bb), s->block_size);
-	}
-
-	pad = s->len - (int64_t)last_block * s->block_size;
-	assert(pad >= 0);
-	if (pad > 0) {
-		write_skip_chunk(out, pad);
-	}
-
-	return 0;
-}
-
-int sparse_file_write(struct sparse_file *s, int fd, bool gz, bool sparse,
-		bool crc)
-{
-	int ret;
-	int chunks;
-	struct output_file *out;
-
-	chunks = sparse_count_chunks(s);
-	out = output_file_open_fd(fd, s->block_size, s->len, gz, sparse, chunks, crc);
-
-	if (!out)
-		return -ENOMEM;
-
-	ret = write_all_blocks(s, out);
-
-	output_file_close(out);
-
-	return ret;
-}
-
-int sparse_file_callback(struct sparse_file *s, bool sparse, bool crc,
-		int (*write)(void *priv, const void *data, int len), void *priv)
-{
-	int ret;
-	int chunks;
-	struct output_file *out;
-
-	chunks = sparse_count_chunks(s);
-	out = output_file_open_callback(write, priv, s->block_size, s->len, false,
-			sparse, chunks, crc);
-
-	if (!out)
-		return -ENOMEM;
-
-	ret = write_all_blocks(s, out);
-
-	output_file_close(out);
-
-	return ret;
-}
-
-struct chunk_data {
-	void		*priv;
-	unsigned int	block;
-	unsigned int	nr_blocks;
-	int (*write)(void *priv, const void *data, int len, unsigned int block,
-		     unsigned int nr_blocks);
-};
-
-static int foreach_chunk_write(void *priv, const void *data, int len)
-{
-	struct chunk_data *chk = priv;
-
-	return chk->write(chk->priv, data, len, chk->block, chk->nr_blocks);
-}
-
-int sparse_file_foreach_chunk(struct sparse_file *s, bool sparse, bool crc,
-	int (*write)(void *priv, const void *data, int len, unsigned int block,
-		     unsigned int nr_blocks),
-	void *priv)
-{
-	int ret;
-	int chunks;
-	struct chunk_data chk;
-	struct output_file *out;
-	struct backed_block *bb;
-
-	chk.priv = priv;
-	chk.write = write;
-	chk.block = chk.nr_blocks = 0;
-	chunks = sparse_count_chunks(s);
-	out = output_file_open_callback(foreach_chunk_write, &chk,
-					s->block_size, s->len, false, sparse,
-					chunks, crc);
-
-	if (!out)
-		return -ENOMEM;
-
-	for (bb = backed_block_iter_new(s->backed_block_list); bb;
-			bb = backed_block_iter_next(bb)) {
-		chk.block = backed_block_block(bb);
-		chk.nr_blocks = (backed_block_len(bb) - 1) / s->block_size + 1;
-		ret = sparse_file_write_block(out, bb);
-		if (ret)
-			return ret;
-	}
-
-	output_file_close(out);
-
-	return ret;
-}
-
-static int out_counter_write(void *priv, const void *data __unused, int len)
-{
-	int64_t *count = priv;
-	*count += len;
-	return 0;
-}
-
-int64_t sparse_file_len(struct sparse_file *s, bool sparse, bool crc)
-{
-	int ret;
-	int chunks = sparse_count_chunks(s);
-	int64_t count = 0;
-	struct output_file *out;
-
-	out = output_file_open_callback(out_counter_write, &count,
-			s->block_size, s->len, false, sparse, chunks, crc);
-	if (!out) {
-		return -1;
-	}
-
-	ret = write_all_blocks(s, out);
-
-	output_file_close(out);
-
-	if (ret < 0) {
-		return -1;
-	}
-
-	return count;
-}
-
-unsigned int sparse_file_block_size(struct sparse_file *s)
-{
-	return s->block_size;
-}
-
-static struct backed_block *move_chunks_up_to_len(struct sparse_file *from,
-		struct sparse_file *to, unsigned int len)
-{
-	int64_t count = 0;
-	struct output_file *out_counter;
-	struct backed_block *last_bb = NULL;
-	struct backed_block *bb;
-	struct backed_block *start;
-	unsigned int last_block = 0;
-	int64_t file_len = 0;
-	int ret;
-
-	/*
-	 * overhead is sparse file header, the potential end skip
-	 * chunk and crc chunk.
-	 */
-	int overhead = sizeof(sparse_header_t) + 2 * sizeof(chunk_header_t) +
-			sizeof(uint32_t);
-	len -= overhead;
-
-	start = backed_block_iter_new(from->backed_block_list);
-	out_counter = output_file_open_callback(out_counter_write, &count,
-			to->block_size, to->len, false, true, 0, false);
-	if (!out_counter) {
-		return NULL;
-	}
-
-	for (bb = start; bb; bb = backed_block_iter_next(bb)) {
-		count = 0;
-		if (backed_block_block(bb) > last_block)
-			count += sizeof(chunk_header_t);
-		last_block = backed_block_block(bb) +
-				DIV_ROUND_UP(backed_block_len(bb), to->block_size);
-
-		/* will call out_counter_write to update count */
-		ret = sparse_file_write_block(out_counter, bb);
-		if (ret) {
-			bb = NULL;
-			goto out;
-		}
-		if (file_len + count > len) {
-			/*
-			 * If the remaining available size is more than 1/8th of the
-			 * requested size, split the chunk.  Results in sparse files that
-			 * are at least 7/8ths of the requested size
-			 */
-			file_len += sizeof(chunk_header_t);
-			if (!last_bb || (len - file_len > (len / 8))) {
-				backed_block_split(from->backed_block_list, bb, len - file_len);
-				last_bb = bb;
-			}
-			goto move;
-		}
-		file_len += count;
-		last_bb = bb;
-	}
-
-move:
-	backed_block_list_move(from->backed_block_list,
-		to->backed_block_list, start, last_bb);
-
-out:
-	output_file_close(out_counter);
-
-	return bb;
-}
-
-int sparse_file_resparse(struct sparse_file *in_s, unsigned int max_len,
-		struct sparse_file **out_s, int out_s_count)
-{
-	struct backed_block *bb;
-	struct sparse_file *s;
-	struct sparse_file *tmp;
-	int c = 0;
-
-	tmp = sparse_file_new(in_s->block_size, in_s->len);
-	if (!tmp) {
-		return -ENOMEM;
-	}
-
-	do {
-		s = sparse_file_new(in_s->block_size, in_s->len);
-
-		bb = move_chunks_up_to_len(in_s, s, max_len);
-
-		if (c < out_s_count) {
-			out_s[c] = s;
-		} else {
-			backed_block_list_move(s->backed_block_list, tmp->backed_block_list,
-					NULL, NULL);
-			sparse_file_destroy(s);
-		}
-		c++;
-	} while (bb);
-
-	backed_block_list_move(tmp->backed_block_list, in_s->backed_block_list,
-			NULL, NULL);
-
-	sparse_file_destroy(tmp);
-
-	return c;
-}
-
-void sparse_file_verbose(struct sparse_file *s)
-{
-	s->verbose = true;
-}
diff --git a/libsparse/sparse.cpp b/libsparse/sparse.cpp
new file mode 100644
index 0000000..cb288c5
--- /dev/null
+++ b/libsparse/sparse.cpp
@@ -0,0 +1,348 @@
+/*
+ * Copyright (C) 2012 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 <assert.h>
+#include <stdlib.h>
+
+#include <sparse/sparse.h>
+
+#include "defs.h"
+#include "sparse_file.h"
+
+#include "backed_block.h"
+#include "output_file.h"
+#include "sparse_defs.h"
+#include "sparse_format.h"
+
+struct sparse_file* sparse_file_new(unsigned int block_size, int64_t len) {
+  struct sparse_file* s = reinterpret_cast<sparse_file*>(calloc(sizeof(struct sparse_file), 1));
+  if (!s) {
+    return nullptr;
+  }
+
+  s->backed_block_list = backed_block_list_new(block_size);
+  if (!s->backed_block_list) {
+    free(s);
+    return nullptr;
+  }
+
+  s->block_size = block_size;
+  s->len = len;
+
+  return s;
+}
+
+void sparse_file_destroy(struct sparse_file* s) {
+  backed_block_list_destroy(s->backed_block_list);
+  free(s);
+}
+
+int sparse_file_add_data(struct sparse_file* s, void* data, unsigned int len, unsigned int block) {
+  return backed_block_add_data(s->backed_block_list, data, len, block);
+}
+
+int sparse_file_add_fill(struct sparse_file* s, uint32_t fill_val, unsigned int len,
+                         unsigned int block) {
+  return backed_block_add_fill(s->backed_block_list, fill_val, len, block);
+}
+
+int sparse_file_add_file(struct sparse_file* s, const char* filename, int64_t file_offset,
+                         unsigned int len, unsigned int block) {
+  return backed_block_add_file(s->backed_block_list, filename, file_offset, len, block);
+}
+
+int sparse_file_add_fd(struct sparse_file* s, int fd, int64_t file_offset, unsigned int len,
+                       unsigned int block) {
+  return backed_block_add_fd(s->backed_block_list, fd, file_offset, len, block);
+}
+unsigned int sparse_count_chunks(struct sparse_file* s) {
+  struct backed_block* bb;
+  unsigned int last_block = 0;
+  unsigned int chunks = 0;
+
+  for (bb = backed_block_iter_new(s->backed_block_list); bb; bb = backed_block_iter_next(bb)) {
+    if (backed_block_block(bb) > last_block) {
+      /* If there is a gap between chunks, add a skip chunk */
+      chunks++;
+    }
+    chunks++;
+    last_block = backed_block_block(bb) + DIV_ROUND_UP(backed_block_len(bb), s->block_size);
+  }
+  if (last_block < DIV_ROUND_UP(s->len, s->block_size)) {
+    chunks++;
+  }
+
+  return chunks;
+}
+
+static int sparse_file_write_block(struct output_file* out, struct backed_block* bb) {
+  int ret = -EINVAL;
+
+  switch (backed_block_type(bb)) {
+    case BACKED_BLOCK_DATA:
+      ret = write_data_chunk(out, backed_block_len(bb), backed_block_data(bb));
+      break;
+    case BACKED_BLOCK_FILE:
+      ret = write_file_chunk(out, backed_block_len(bb), backed_block_filename(bb),
+                             backed_block_file_offset(bb));
+      break;
+    case BACKED_BLOCK_FD:
+      ret = write_fd_chunk(out, backed_block_len(bb), backed_block_fd(bb),
+                           backed_block_file_offset(bb));
+      break;
+    case BACKED_BLOCK_FILL:
+      ret = write_fill_chunk(out, backed_block_len(bb), backed_block_fill_val(bb));
+      break;
+  }
+
+  return ret;
+}
+
+static int write_all_blocks(struct sparse_file* s, struct output_file* out) {
+  struct backed_block* bb;
+  unsigned int last_block = 0;
+  int64_t pad;
+  int ret = 0;
+
+  for (bb = backed_block_iter_new(s->backed_block_list); bb; bb = backed_block_iter_next(bb)) {
+    if (backed_block_block(bb) > last_block) {
+      unsigned int blocks = backed_block_block(bb) - last_block;
+      write_skip_chunk(out, (int64_t)blocks * s->block_size);
+    }
+    ret = sparse_file_write_block(out, bb);
+    if (ret) return ret;
+    last_block = backed_block_block(bb) + DIV_ROUND_UP(backed_block_len(bb), s->block_size);
+  }
+
+  pad = s->len - (int64_t)last_block * s->block_size;
+  assert(pad >= 0);
+  if (pad > 0) {
+    write_skip_chunk(out, pad);
+  }
+
+  return 0;
+}
+
+int sparse_file_write(struct sparse_file* s, int fd, bool gz, bool sparse, bool crc) {
+  int ret;
+  int chunks;
+  struct output_file* out;
+
+  chunks = sparse_count_chunks(s);
+  out = output_file_open_fd(fd, s->block_size, s->len, gz, sparse, chunks, crc);
+
+  if (!out) return -ENOMEM;
+
+  ret = write_all_blocks(s, out);
+
+  output_file_close(out);
+
+  return ret;
+}
+
+int sparse_file_callback(struct sparse_file* s, bool sparse, bool crc,
+                         int (*write)(void* priv, const void* data, size_t len), void* priv) {
+  int ret;
+  int chunks;
+  struct output_file* out;
+
+  chunks = sparse_count_chunks(s);
+  out = output_file_open_callback(write, priv, s->block_size, s->len, false, sparse, chunks, crc);
+
+  if (!out) return -ENOMEM;
+
+  ret = write_all_blocks(s, out);
+
+  output_file_close(out);
+
+  return ret;
+}
+
+struct chunk_data {
+  void* priv;
+  unsigned int block;
+  unsigned int nr_blocks;
+  int (*write)(void* priv, const void* data, size_t len, unsigned int block, unsigned int nr_blocks);
+};
+
+static int foreach_chunk_write(void* priv, const void* data, size_t len) {
+  struct chunk_data* chk = reinterpret_cast<chunk_data*>(priv);
+
+  return chk->write(chk->priv, data, len, chk->block, chk->nr_blocks);
+}
+
+int sparse_file_foreach_chunk(struct sparse_file* s, bool sparse, bool crc,
+                              int (*write)(void* priv, const void* data, size_t len,
+                                           unsigned int block, unsigned int nr_blocks),
+                              void* priv) {
+  int ret;
+  int chunks;
+  struct chunk_data chk;
+  struct output_file* out;
+  struct backed_block* bb;
+
+  chk.priv = priv;
+  chk.write = write;
+  chk.block = chk.nr_blocks = 0;
+  chunks = sparse_count_chunks(s);
+  out = output_file_open_callback(foreach_chunk_write, &chk, s->block_size, s->len, false, sparse,
+                                  chunks, crc);
+
+  if (!out) return -ENOMEM;
+
+  for (bb = backed_block_iter_new(s->backed_block_list); bb; bb = backed_block_iter_next(bb)) {
+    chk.block = backed_block_block(bb);
+    chk.nr_blocks = (backed_block_len(bb) - 1) / s->block_size + 1;
+    ret = sparse_file_write_block(out, bb);
+    if (ret) return ret;
+  }
+
+  output_file_close(out);
+
+  return ret;
+}
+
+static int out_counter_write(void* priv, const void* data __unused, size_t len) {
+  int64_t* count = reinterpret_cast<int64_t*>(priv);
+  *count += len;
+  return 0;
+}
+
+int64_t sparse_file_len(struct sparse_file* s, bool sparse, bool crc) {
+  int ret;
+  int chunks = sparse_count_chunks(s);
+  int64_t count = 0;
+  struct output_file* out;
+
+  out = output_file_open_callback(out_counter_write, &count, s->block_size, s->len, false, sparse,
+                                  chunks, crc);
+  if (!out) {
+    return -1;
+  }
+
+  ret = write_all_blocks(s, out);
+
+  output_file_close(out);
+
+  if (ret < 0) {
+    return -1;
+  }
+
+  return count;
+}
+
+unsigned int sparse_file_block_size(struct sparse_file* s) {
+  return s->block_size;
+}
+
+static struct backed_block* move_chunks_up_to_len(struct sparse_file* from, struct sparse_file* to,
+                                                  unsigned int len) {
+  int64_t count = 0;
+  struct output_file* out_counter;
+  struct backed_block* last_bb = nullptr;
+  struct backed_block* bb;
+  struct backed_block* start;
+  unsigned int last_block = 0;
+  int64_t file_len = 0;
+  int ret;
+
+  /*
+   * overhead is sparse file header, the potential end skip
+   * chunk and crc chunk.
+   */
+  int overhead = sizeof(sparse_header_t) + 2 * sizeof(chunk_header_t) + sizeof(uint32_t);
+  len -= overhead;
+
+  start = backed_block_iter_new(from->backed_block_list);
+  out_counter = output_file_open_callback(out_counter_write, &count, to->block_size, to->len, false,
+                                          true, 0, false);
+  if (!out_counter) {
+    return nullptr;
+  }
+
+  for (bb = start; bb; bb = backed_block_iter_next(bb)) {
+    count = 0;
+    if (backed_block_block(bb) > last_block) count += sizeof(chunk_header_t);
+    last_block = backed_block_block(bb) + DIV_ROUND_UP(backed_block_len(bb), to->block_size);
+
+    /* will call out_counter_write to update count */
+    ret = sparse_file_write_block(out_counter, bb);
+    if (ret) {
+      bb = nullptr;
+      goto out;
+    }
+    if (file_len + count > len) {
+      /*
+       * If the remaining available size is more than 1/8th of the
+       * requested size, split the chunk.  Results in sparse files that
+       * are at least 7/8ths of the requested size
+       */
+      file_len += sizeof(chunk_header_t);
+      if (!last_bb || (len - file_len > (len / 8))) {
+        backed_block_split(from->backed_block_list, bb, len - file_len);
+        last_bb = bb;
+      }
+      goto move;
+    }
+    file_len += count;
+    last_bb = bb;
+  }
+
+move:
+  backed_block_list_move(from->backed_block_list, to->backed_block_list, start, last_bb);
+
+out:
+  output_file_close(out_counter);
+
+  return bb;
+}
+
+int sparse_file_resparse(struct sparse_file* in_s, unsigned int max_len, struct sparse_file** out_s,
+                         int out_s_count) {
+  struct backed_block* bb;
+  struct sparse_file* s;
+  struct sparse_file* tmp;
+  int c = 0;
+
+  tmp = sparse_file_new(in_s->block_size, in_s->len);
+  if (!tmp) {
+    return -ENOMEM;
+  }
+
+  do {
+    s = sparse_file_new(in_s->block_size, in_s->len);
+
+    bb = move_chunks_up_to_len(in_s, s, max_len);
+
+    if (c < out_s_count) {
+      out_s[c] = s;
+    } else {
+      backed_block_list_move(s->backed_block_list, tmp->backed_block_list, nullptr, nullptr);
+      sparse_file_destroy(s);
+    }
+    c++;
+  } while (bb);
+
+  backed_block_list_move(tmp->backed_block_list, in_s->backed_block_list, nullptr, nullptr);
+
+  sparse_file_destroy(tmp);
+
+  return c;
+}
+
+void sparse_file_verbose(struct sparse_file* s) {
+  s->verbose = true;
+}
diff --git a/libsparse/sparse_crc32.c b/libsparse/sparse_crc32.c
deleted file mode 100644
index 38bfe4a..0000000
--- a/libsparse/sparse_crc32.c
+++ /dev/null
@@ -1,111 +0,0 @@
-/*-
- *  COPYRIGHT (C) 1986 Gary S. Brown.  You may use this program, or
- *  code or tables extracted from it, as desired without restriction.
- */
-
-/*
- *  First, the polynomial itself and its table of feedback terms.  The
- *  polynomial is
- *  X^32+X^26+X^23+X^22+X^16+X^12+X^11+X^10+X^8+X^7+X^5+X^4+X^2+X^1+X^0
- *
- *  Note that we take it "backwards" and put the highest-order term in
- *  the lowest-order bit.  The X^32 term is "implied"; the LSB is the
- *  X^31 term, etc.  The X^0 term (usually shown as "+1") results in
- *  the MSB being 1
- *
- *  Note that the usual hardware shift register implementation, which
- *  is what we're using (we're merely optimizing it by doing eight-bit
- *  chunks at a time) shifts bits into the lowest-order term.  In our
- *  implementation, that means shifting towards the right.  Why do we
- *  do it this way?  Because the calculated CRC must be transmitted in
- *  order from highest-order term to lowest-order term.  UARTs transmit
- *  characters in order from LSB to MSB.  By storing the CRC this way
- *  we hand it to the UART in the order low-byte to high-byte; the UART
- *  sends each low-bit to hight-bit; and the result is transmission bit
- *  by bit from highest- to lowest-order term without requiring any bit
- *  shuffling on our part.  Reception works similarly
- *
- *  The feedback terms table consists of 256, 32-bit entries.  Notes
- *
- *      The table can be generated at runtime if desired; code to do so
- *      is shown later.  It might not be obvious, but the feedback
- *      terms simply represent the results of eight shift/xor opera
- *      tions for all combinations of data and CRC register values
- *
- *      The values must be right-shifted by eight bits by the "updcrc
- *      logic; the shift must be unsigned (bring in zeroes).  On some
- *      hardware you could probably optimize the shift in assembler by
- *      using byte-swap instructions
- *      polynomial $edb88320
- *
- *
- * CRC32 code derived from work by Gary S. Brown.
- */
-
-/* Code taken from FreeBSD 8 */
-#include <stdint.h>
-
-static uint32_t crc32_tab[] = {
-        0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f,
-        0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
-        0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2,
-        0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
-        0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
-        0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
-        0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c,
-        0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
-        0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423,
-        0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
-        0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106,
-        0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
-        0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d,
-        0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
-        0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
-        0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
-        0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7,
-        0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
-        0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa,
-        0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
-        0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81,
-        0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
-        0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84,
-        0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
-        0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
-        0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
-        0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e,
-        0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
-        0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55,
-        0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
-        0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28,
-        0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
-        0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f,
-        0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
-        0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
-        0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
-        0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69,
-        0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
-        0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc,
-        0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
-        0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693,
-        0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
-        0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
-};
-
-/*
- * A function that calculates the CRC-32 based on the table above is
- * given below for documentation purposes. An equivalent implementation
- * of this function that's actually used in the kernel can be found
- * in sys/libkern.h, where it can be inlined.
- */
-
-uint32_t sparse_crc32(uint32_t crc_in, const void *buf, int size)
-{
-        const uint8_t *p = buf;
-        uint32_t crc;
-
-        crc = crc_in ^ ~0U;
-        while (size--)
-                crc = crc32_tab[(crc ^ *p++) & 0xFF] ^ (crc >> 8);
-        return crc ^ ~0U;
-}
-
diff --git a/libsparse/sparse_crc32.cpp b/libsparse/sparse_crc32.cpp
new file mode 100644
index 0000000..267322c
--- /dev/null
+++ b/libsparse/sparse_crc32.cpp
@@ -0,0 +1,97 @@
+/*-
+ *  COPYRIGHT (C) 1986 Gary S. Brown.  You may use this program, or
+ *  code or tables extracted from it, as desired without restriction.
+ */
+
+/*
+ *  First, the polynomial itself and its table of feedback terms.  The
+ *  polynomial is
+ *  X^32+X^26+X^23+X^22+X^16+X^12+X^11+X^10+X^8+X^7+X^5+X^4+X^2+X^1+X^0
+ *
+ *  Note that we take it "backwards" and put the highest-order term in
+ *  the lowest-order bit.  The X^32 term is "implied"; the LSB is the
+ *  X^31 term, etc.  The X^0 term (usually shown as "+1") results in
+ *  the MSB being 1
+ *
+ *  Note that the usual hardware shift register implementation, which
+ *  is what we're using (we're merely optimizing it by doing eight-bit
+ *  chunks at a time) shifts bits into the lowest-order term.  In our
+ *  implementation, that means shifting towards the right.  Why do we
+ *  do it this way?  Because the calculated CRC must be transmitted in
+ *  order from highest-order term to lowest-order term.  UARTs transmit
+ *  characters in order from LSB to MSB.  By storing the CRC this way
+ *  we hand it to the UART in the order low-byte to high-byte; the UART
+ *  sends each low-bit to hight-bit; and the result is transmission bit
+ *  by bit from highest- to lowest-order term without requiring any bit
+ *  shuffling on our part.  Reception works similarly
+ *
+ *  The feedback terms table consists of 256, 32-bit entries.  Notes
+ *
+ *      The table can be generated at runtime if desired; code to do so
+ *      is shown later.  It might not be obvious, but the feedback
+ *      terms simply represent the results of eight shift/xor opera
+ *      tions for all combinations of data and CRC register values
+ *
+ *      The values must be right-shifted by eight bits by the "updcrc
+ *      logic; the shift must be unsigned (bring in zeroes).  On some
+ *      hardware you could probably optimize the shift in assembler by
+ *      using byte-swap instructions
+ *      polynomial $edb88320
+ *
+ *
+ * CRC32 code derived from work by Gary S. Brown.
+ */
+
+/* Code taken from FreeBSD 8 */
+#include <stdint.h>
+#include <stdio.h>
+
+static uint32_t crc32_tab[] = {
+    0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3,
+    0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91,
+    0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
+    0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5,
+    0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,
+    0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
+    0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f,
+    0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d,
+    0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
+    0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,
+    0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457,
+    0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
+    0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb,
+    0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9,
+    0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
+    0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad,
+    0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683,
+    0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
+    0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7,
+    0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,
+    0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
+    0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79,
+    0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f,
+    0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
+    0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,
+    0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21,
+    0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
+    0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45,
+    0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db,
+    0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
+    0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf,
+    0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d};
+
+/*
+ * A function that calculates the CRC-32 based on the table above is
+ * given below for documentation purposes. An equivalent implementation
+ * of this function that's actually used in the kernel can be found
+ * in sys/libkern.h, where it can be inlined.
+ */
+
+uint32_t sparse_crc32(uint32_t crc_in, const void* buf, size_t size) {
+  const uint8_t* p = reinterpret_cast<const uint8_t*>(buf);
+  uint32_t crc;
+
+  crc = crc_in ^ ~0U;
+  while (size--) crc = crc32_tab[(crc ^ *p++) & 0xFF] ^ (crc >> 8);
+  return crc ^ ~0U;
+}
diff --git a/libsparse/sparse_crc32.h b/libsparse/sparse_crc32.h
index 50cd9e9..2702c4f 100644
--- a/libsparse/sparse_crc32.h
+++ b/libsparse/sparse_crc32.h
@@ -19,14 +19,6 @@
 
 #include <stdint.h>
 
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-uint32_t sparse_crc32(uint32_t crc, const void *buf, size_t size);
-
-#ifdef __cplusplus
-}
-#endif
+uint32_t sparse_crc32(uint32_t crc, const void* buf, size_t size);
 
 #endif
diff --git a/libsparse/sparse_defs.h b/libsparse/sparse_defs.h
index b99cfd5..9137805 100644
--- a/libsparse/sparse_defs.h
+++ b/libsparse/sparse_defs.h
@@ -39,11 +39,14 @@
 typedef unsigned short int u16;
 typedef unsigned char u8;
 
-#define DIV_ROUND_UP(x, y) (((x) + (y) - 1)/(y))
-#define ALIGN(x, y) ((y) * DIV_ROUND_UP((x), (y)))
+#define DIV_ROUND_UP(x, y) (((x) + (y)-1) / (y))
+#define ALIGN(x, y) ((y)*DIV_ROUND_UP((x), (y)))
 #define ALIGN_DOWN(x, y) ((y) * ((x) / (y)))
 
-#define error(fmt, args...) do { fprintf(stderr, "error: %s: " fmt "\n", __func__, ## args); } while (0)
+#define error(fmt, args...)                                    \
+  do {                                                         \
+    fprintf(stderr, "error: %s: " fmt "\n", __func__, ##args); \
+  } while (0)
 #define error_errno(s, args...) error(s ": %s", ##args, strerror(errno))
 
 #endif
diff --git a/libsparse/sparse_err.c b/libsparse/sparse_err.c
deleted file mode 100644
index 0f392ad..0000000
--- a/libsparse/sparse_err.c
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright (C) 2012 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 <sparse/sparse.h>
-
-#include <stdarg.h>
-#include <stdio.h>
-#include <unistd.h>
-
-void sparse_default_print(const char *fmt, ...)
-{
-	va_list argp;
-
-	va_start(argp, fmt);
-	vfprintf(stderr, fmt, argp);
-	va_end(argp);
-}
-
-void (*sparse_print_error)(const char *fmt, ...) = sparse_default_print;
-void (*sparse_print_verbose)(const char *fmt, ...) = sparse_default_print;
diff --git a/libsparse/sparse_err.cpp b/libsparse/sparse_err.cpp
new file mode 100644
index 0000000..6886d31
--- /dev/null
+++ b/libsparse/sparse_err.cpp
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2012 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 <sparse/sparse.h>
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <unistd.h>
+
+void sparse_default_print(const char* fmt, ...) {
+  va_list argp;
+
+  va_start(argp, fmt);
+  vfprintf(stderr, fmt, argp);
+  va_end(argp);
+}
+
+void (*sparse_print_error)(const char* fmt, ...) = sparse_default_print;
+void (*sparse_print_verbose)(const char* fmt, ...) = sparse_default_print;
diff --git a/libsparse/sparse_file.h b/libsparse/sparse_file.h
index 763f43f..e565f63 100644
--- a/libsparse/sparse_file.h
+++ b/libsparse/sparse_file.h
@@ -24,12 +24,12 @@
 #include <sparse/sparse.h>
 
 struct sparse_file {
-	unsigned int block_size;
-	int64_t len;
-	bool verbose;
+  unsigned int block_size;
+  int64_t len;
+  bool verbose;
 
-	struct backed_block_list *backed_block_list;
-	struct output_file *out;
+  struct backed_block_list* backed_block_list;
+  struct output_file* out;
 };
 
 #ifdef __cplusplus
diff --git a/libsparse/sparse_format.h b/libsparse/sparse_format.h
index 779e038..a8a721e 100644
--- a/libsparse/sparse_format.h
+++ b/libsparse/sparse_format.h
@@ -23,31 +23,31 @@
 #endif
 
 typedef struct sparse_header {
-  __le32	magic;		/* 0xed26ff3a */
-  __le16	major_version;	/* (0x1) - reject images with higher major versions */
-  __le16	minor_version;	/* (0x0) - allow images with higer minor versions */
-  __le16	file_hdr_sz;	/* 28 bytes for first revision of the file format */
-  __le16	chunk_hdr_sz;	/* 12 bytes for first revision of the file format */
-  __le32	blk_sz;		/* block size in bytes, must be a multiple of 4 (4096) */
-  __le32	total_blks;	/* total blocks in the non-sparse output image */
-  __le32	total_chunks;	/* total chunks in the sparse input image */
-  __le32	image_checksum; /* CRC32 checksum of the original data, counting "don't care" */
-				/* as 0. Standard 802.3 polynomial, use a Public Domain */
-				/* table implementation */
+  __le32 magic;          /* 0xed26ff3a */
+  __le16 major_version;  /* (0x1) - reject images with higher major versions */
+  __le16 minor_version;  /* (0x0) - allow images with higer minor versions */
+  __le16 file_hdr_sz;    /* 28 bytes for first revision of the file format */
+  __le16 chunk_hdr_sz;   /* 12 bytes for first revision of the file format */
+  __le32 blk_sz;         /* block size in bytes, must be a multiple of 4 (4096) */
+  __le32 total_blks;     /* total blocks in the non-sparse output image */
+  __le32 total_chunks;   /* total chunks in the sparse input image */
+  __le32 image_checksum; /* CRC32 checksum of the original data, counting "don't care" */
+                         /* as 0. Standard 802.3 polynomial, use a Public Domain */
+                         /* table implementation */
 } sparse_header_t;
 
-#define SPARSE_HEADER_MAGIC	0xed26ff3a
+#define SPARSE_HEADER_MAGIC 0xed26ff3a
 
-#define CHUNK_TYPE_RAW		0xCAC1
-#define CHUNK_TYPE_FILL		0xCAC2
-#define CHUNK_TYPE_DONT_CARE	0xCAC3
-#define CHUNK_TYPE_CRC32    0xCAC4
+#define CHUNK_TYPE_RAW 0xCAC1
+#define CHUNK_TYPE_FILL 0xCAC2
+#define CHUNK_TYPE_DONT_CARE 0xCAC3
+#define CHUNK_TYPE_CRC32 0xCAC4
 
 typedef struct chunk_header {
-  __le16	chunk_type;	/* 0xCAC1 -> raw; 0xCAC2 -> fill; 0xCAC3 -> don't care */
-  __le16	reserved1;
-  __le32	chunk_sz;	/* in blocks in output image */
-  __le32	total_sz;	/* in bytes of chunk input file including chunk header and data */
+  __le16 chunk_type; /* 0xCAC1 -> raw; 0xCAC2 -> fill; 0xCAC3 -> don't care */
+  __le16 reserved1;
+  __le32 chunk_sz; /* in blocks in output image */
+  __le32 total_sz; /* in bytes of chunk input file including chunk header and data */
 } chunk_header_t;
 
 /* Following a Raw or Fill or CRC32 chunk is data.
diff --git a/libsparse/sparse_read.cpp b/libsparse/sparse_read.cpp
index bd66873..c4c1823 100644
--- a/libsparse/sparse_read.cpp
+++ b/libsparse/sparse_read.cpp
@@ -17,16 +17,16 @@
 #define _FILE_OFFSET_BITS 64
 #define _LARGEFILE64_SOURCE 1
 
-#include <algorithm>
-#include <inttypes.h>
 #include <fcntl.h>
+#include <inttypes.h>
 #include <stdarg.h>
-#include <stdbool.h>
 #include <stdint.h>
 #include <stdio.h>
 #include <stdlib.h>
-#include <string>
+#include <string.h>
 #include <unistd.h>
+#include <algorithm>
+#include <string>
 
 #include <sparse/sparse.h>
 
@@ -37,447 +37,541 @@
 #include "sparse_file.h"
 #include "sparse_format.h"
 
-
 #if defined(__APPLE__) && defined(__MACH__)
 #define lseek64 lseek
 #define off64_t off_t
 #endif
 
 #define SPARSE_HEADER_MAJOR_VER 1
-#define SPARSE_HEADER_LEN       (sizeof(sparse_header_t))
+#define SPARSE_HEADER_LEN (sizeof(sparse_header_t))
 #define CHUNK_HEADER_LEN (sizeof(chunk_header_t))
 
 static constexpr int64_t COPY_BUF_SIZE = 1024 * 1024;
-static char *copybuf;
+static char* copybuf;
 
-static std::string ErrorString(int err)
-{
-	if (err == -EOVERFLOW) return "EOF while reading file";
-	if (err == -EINVAL) return "Invalid sparse file format";
-	if (err == -ENOMEM) return "Failed allocation while reading file";
-	return android::base::StringPrintf("Unknown error %d", err);
+static std::string ErrorString(int err) {
+  if (err == -EOVERFLOW) return "EOF while reading file";
+  if (err == -EINVAL) return "Invalid sparse file format";
+  if (err == -ENOMEM) return "Failed allocation while reading file";
+  return android::base::StringPrintf("Unknown error %d", err);
 }
 
-static void verbose_error(bool verbose, int err, const char *fmt, ...)
-{
-	if (!verbose) return;
+class SparseFileSource {
+ public:
+  /* Seeks the source ahead by the given offset. */
+  virtual void Seek(int64_t offset) = 0;
 
-	std::string msg = ErrorString(err);
-	if (fmt) {
-		msg += " at ";
-		va_list argp;
-		va_start(argp, fmt);
-		android::base::StringAppendV(&msg, fmt, argp);
-		va_end(argp);
-	}
-	sparse_print_verbose("%s\n", msg.c_str());
+  /* Return the current offset. */
+  virtual int64_t GetOffset() = 0;
+
+  /* Set the current offset. Return 0 if successful. */
+  virtual int SetOffset(int64_t offset) = 0;
+
+  /* Adds the given length from the current offset of the source to the file at the given block.
+   * Return 0 if successful. */
+  virtual int AddToSparseFile(struct sparse_file* s, int64_t len, unsigned int block) = 0;
+
+  /* Get data of fixed size from the current offset and seek len bytes. Return 0 if successful. */
+  virtual int ReadValue(void* ptr, int len) = 0;
+
+  /* Find the crc32 of the next len bytes and seek ahead len bytes. Return 0 if successful. */
+  virtual int GetCrc32(uint32_t* crc32, int64_t len) = 0;
+
+  virtual ~SparseFileSource(){};
+};
+
+class SparseFileFdSource : public SparseFileSource {
+ private:
+  int fd;
+
+ public:
+  SparseFileFdSource(int fd) : fd(fd) {}
+  ~SparseFileFdSource() override {}
+
+  void Seek(int64_t off) override { lseek64(fd, off, SEEK_CUR); }
+
+  int64_t GetOffset() override { return lseek64(fd, 0, SEEK_CUR); }
+
+  int SetOffset(int64_t offset) override {
+    return lseek64(fd, offset, SEEK_SET) == offset ? 0 : -errno;
+  }
+
+  int AddToSparseFile(struct sparse_file* s, int64_t len, unsigned int block) override {
+    return sparse_file_add_fd(s, fd, GetOffset(), len, block);
+  }
+
+  int ReadValue(void* ptr, int len) override { return read_all(fd, ptr, len); }
+
+  int GetCrc32(uint32_t* crc32, int64_t len) override {
+    int chunk;
+    int ret;
+    while (len) {
+      chunk = std::min(len, COPY_BUF_SIZE);
+      ret = read_all(fd, copybuf, chunk);
+      if (ret < 0) {
+        return ret;
+      }
+      *crc32 = sparse_crc32(*crc32, copybuf, chunk);
+      len -= chunk;
+    }
+    return 0;
+  }
+};
+
+class SparseFileBufSource : public SparseFileSource {
+ private:
+  char* buf;
+  int64_t offset;
+
+ public:
+  SparseFileBufSource(char* buf) : buf(buf), offset(0) {}
+  ~SparseFileBufSource() override {}
+
+  void Seek(int64_t off) override {
+    buf += off;
+    offset += off;
+  }
+
+  int64_t GetOffset() override { return offset; }
+
+  int SetOffset(int64_t off) override {
+    buf += off - offset;
+    offset = off;
+    return 0;
+  }
+
+  int AddToSparseFile(struct sparse_file* s, int64_t len, unsigned int block) override {
+    return sparse_file_add_data(s, buf, len, block);
+  }
+
+  int ReadValue(void* ptr, int len) override {
+    memcpy(ptr, buf, len);
+    Seek(len);
+    return 0;
+  }
+
+  int GetCrc32(uint32_t* crc32, int64_t len) override {
+    *crc32 = sparse_crc32(*crc32, buf, len);
+    Seek(len);
+    return 0;
+  }
+};
+
+static void verbose_error(bool verbose, int err, const char* fmt, ...) {
+  if (!verbose) return;
+
+  std::string msg = ErrorString(err);
+  if (fmt) {
+    msg += " at ";
+    va_list argp;
+    va_start(argp, fmt);
+    android::base::StringAppendV(&msg, fmt, argp);
+    va_end(argp);
+  }
+  sparse_print_verbose("%s\n", msg.c_str());
 }
 
-static int process_raw_chunk(struct sparse_file *s, unsigned int chunk_size,
-		int fd, int64_t offset, unsigned int blocks, unsigned int block,
-		uint32_t *crc32)
-{
-	int ret;
-	int chunk;
-	int64_t len = blocks * s->block_size;
+static int process_raw_chunk(struct sparse_file* s, unsigned int chunk_size,
+                             SparseFileSource* source, unsigned int blocks, unsigned int block,
+                             uint32_t* crc32) {
+  int ret;
+  int64_t len = blocks * s->block_size;
 
-	if (chunk_size % s->block_size != 0) {
-		return -EINVAL;
-	}
+  if (chunk_size % s->block_size != 0) {
+    return -EINVAL;
+  }
 
-	if (chunk_size / s->block_size != blocks) {
-		return -EINVAL;
-	}
+  if (chunk_size / s->block_size != blocks) {
+    return -EINVAL;
+  }
 
-	ret = sparse_file_add_fd(s, fd, offset, len, block);
-	if (ret < 0) {
-		return ret;
-	}
+  ret = source->AddToSparseFile(s, len, block);
+  if (ret < 0) {
+    return ret;
+  }
 
-	if (crc32) {
-		while (len) {
-			chunk = std::min(len, COPY_BUF_SIZE);
-			ret = read_all(fd, copybuf, chunk);
-			if (ret < 0) {
-				return ret;
-			}
-			*crc32 = sparse_crc32(*crc32, copybuf, chunk);
-			len -= chunk;
-		}
-	} else {
-		lseek64(fd, len, SEEK_CUR);
-	}
+  if (crc32) {
+    ret = source->GetCrc32(crc32, len);
+    if (ret < 0) {
+      return ret;
+    }
+  } else {
+    source->Seek(len);
+  }
 
-	return 0;
+  return 0;
 }
 
-static int process_fill_chunk(struct sparse_file *s, unsigned int chunk_size,
-		int fd, unsigned int blocks, unsigned int block, uint32_t *crc32)
-{
-	int ret;
-	int chunk;
-	int64_t len = (int64_t)blocks * s->block_size;
-	uint32_t fill_val;
-	uint32_t *fillbuf;
-	unsigned int i;
+static int process_fill_chunk(struct sparse_file* s, unsigned int chunk_size,
+                              SparseFileSource* source, unsigned int blocks, unsigned int block,
+                              uint32_t* crc32) {
+  int ret;
+  int chunk;
+  int64_t len = (int64_t)blocks * s->block_size;
+  uint32_t fill_val;
+  uint32_t* fillbuf;
+  unsigned int i;
 
-	if (chunk_size != sizeof(fill_val)) {
-		return -EINVAL;
-	}
+  if (chunk_size != sizeof(fill_val)) {
+    return -EINVAL;
+  }
 
-	ret = read_all(fd, &fill_val, sizeof(fill_val));
-	if (ret < 0) {
-		return ret;
-	}
+  ret = source->ReadValue(&fill_val, sizeof(fill_val));
+  if (ret < 0) {
+    return ret;
+  }
 
-	ret = sparse_file_add_fill(s, fill_val, len, block);
-	if (ret < 0) {
-		return ret;
-	}
+  ret = sparse_file_add_fill(s, fill_val, len, block);
+  if (ret < 0) {
+    return ret;
+  }
 
-	if (crc32) {
-		/* Fill copy_buf with the fill value */
-		fillbuf = (uint32_t *)copybuf;
-		for (i = 0; i < (COPY_BUF_SIZE / sizeof(fill_val)); i++) {
-			fillbuf[i] = fill_val;
-		}
+  if (crc32) {
+    /* Fill copy_buf with the fill value */
+    fillbuf = (uint32_t*)copybuf;
+    for (i = 0; i < (COPY_BUF_SIZE / sizeof(fill_val)); i++) {
+      fillbuf[i] = fill_val;
+    }
 
-		while (len) {
-			chunk = std::min(len, COPY_BUF_SIZE);
-			*crc32 = sparse_crc32(*crc32, copybuf, chunk);
-			len -= chunk;
-		}
-	}
+    while (len) {
+      chunk = std::min(len, COPY_BUF_SIZE);
+      *crc32 = sparse_crc32(*crc32, copybuf, chunk);
+      len -= chunk;
+    }
+  }
 
-	return 0;
+  return 0;
 }
 
-static int process_skip_chunk(struct sparse_file *s, unsigned int chunk_size,
-		int fd __unused, unsigned int blocks,
-		unsigned int block __unused, uint32_t *crc32)
-{
-	if (chunk_size != 0) {
-		return -EINVAL;
-	}
+static int process_skip_chunk(struct sparse_file* s, unsigned int chunk_size,
+                              SparseFileSource* source __unused, unsigned int blocks,
+                              unsigned int block __unused, uint32_t* crc32) {
+  if (chunk_size != 0) {
+    return -EINVAL;
+  }
 
-	if (crc32) {
-	        int64_t len = (int64_t)blocks * s->block_size;
-		memset(copybuf, 0, COPY_BUF_SIZE);
+  if (crc32) {
+    int64_t len = (int64_t)blocks * s->block_size;
+    memset(copybuf, 0, COPY_BUF_SIZE);
 
-		while (len) {
-			int chunk = std::min(len, COPY_BUF_SIZE);
-			*crc32 = sparse_crc32(*crc32, copybuf, chunk);
-			len -= chunk;
-		}
-	}
+    while (len) {
+      int chunk = std::min(len, COPY_BUF_SIZE);
+      *crc32 = sparse_crc32(*crc32, copybuf, chunk);
+      len -= chunk;
+    }
+  }
 
-	return 0;
+  return 0;
 }
 
-static int process_crc32_chunk(int fd, unsigned int chunk_size, uint32_t *crc32)
-{
-	uint32_t file_crc32;
-	int ret;
+static int process_crc32_chunk(SparseFileSource* source, unsigned int chunk_size, uint32_t* crc32) {
+  uint32_t file_crc32;
 
-	if (chunk_size != sizeof(file_crc32)) {
-		return -EINVAL;
-	}
+  if (chunk_size != sizeof(file_crc32)) {
+    return -EINVAL;
+  }
 
-	ret = read_all(fd, &file_crc32, sizeof(file_crc32));
-	if (ret < 0) {
-		return ret;
-	}
+  int ret = source->ReadValue(&file_crc32, sizeof(file_crc32));
+  if (ret < 0) {
+    return ret;
+  }
 
-	if (crc32 != NULL && file_crc32 != *crc32) {
-		return -EINVAL;
-	}
+  if (crc32 != nullptr && file_crc32 != *crc32) {
+    return -EINVAL;
+  }
 
-	return 0;
+  return 0;
 }
 
-static int process_chunk(struct sparse_file *s, int fd, off64_t offset,
-		unsigned int chunk_hdr_sz, chunk_header_t *chunk_header,
-		unsigned int cur_block, uint32_t *crc_ptr)
-{
-	int ret;
-	unsigned int chunk_data_size;
+static int process_chunk(struct sparse_file* s, SparseFileSource* source, unsigned int chunk_hdr_sz,
+                         chunk_header_t* chunk_header, unsigned int cur_block, uint32_t* crc_ptr) {
+  int ret;
+  unsigned int chunk_data_size;
+  int64_t offset = source->GetOffset();
 
-	chunk_data_size = chunk_header->total_sz - chunk_hdr_sz;
+  chunk_data_size = chunk_header->total_sz - chunk_hdr_sz;
 
-	switch (chunk_header->chunk_type) {
-		case CHUNK_TYPE_RAW:
-			ret = process_raw_chunk(s, chunk_data_size, fd, offset,
-					chunk_header->chunk_sz, cur_block, crc_ptr);
-			if (ret < 0) {
-				verbose_error(s->verbose, ret, "data block at %" PRId64, offset);
-				return ret;
-			}
-			return chunk_header->chunk_sz;
-		case CHUNK_TYPE_FILL:
-			ret = process_fill_chunk(s, chunk_data_size, fd,
-					chunk_header->chunk_sz, cur_block, crc_ptr);
-			if (ret < 0) {
-				verbose_error(s->verbose, ret, "fill block at %" PRId64, offset);
-				return ret;
-			}
-			return chunk_header->chunk_sz;
-		case CHUNK_TYPE_DONT_CARE:
-			ret = process_skip_chunk(s, chunk_data_size, fd,
-					chunk_header->chunk_sz, cur_block, crc_ptr);
-			if (chunk_data_size != 0) {
-				if (ret < 0) {
-					verbose_error(s->verbose, ret, "skip block at %" PRId64, offset);
-					return ret;
-				}
-			}
-			return chunk_header->chunk_sz;
-		case CHUNK_TYPE_CRC32:
-			ret = process_crc32_chunk(fd, chunk_data_size, crc_ptr);
-			if (ret < 0) {
-				verbose_error(s->verbose, -EINVAL, "crc block at %" PRId64,
-						offset);
-				return ret;
-			}
-			return 0;
-		default:
-			verbose_error(s->verbose, -EINVAL, "unknown block %04X at %" PRId64,
-					chunk_header->chunk_type, offset);
-	}
+  switch (chunk_header->chunk_type) {
+    case CHUNK_TYPE_RAW:
+      ret =
+          process_raw_chunk(s, chunk_data_size, source, chunk_header->chunk_sz, cur_block, crc_ptr);
+      if (ret < 0) {
+        verbose_error(s->verbose, ret, "data block at %" PRId64, offset);
+        return ret;
+      }
+      return chunk_header->chunk_sz;
+    case CHUNK_TYPE_FILL:
+      ret = process_fill_chunk(s, chunk_data_size, source, chunk_header->chunk_sz, cur_block,
+                               crc_ptr);
+      if (ret < 0) {
+        verbose_error(s->verbose, ret, "fill block at %" PRId64, offset);
+        return ret;
+      }
+      return chunk_header->chunk_sz;
+    case CHUNK_TYPE_DONT_CARE:
+      ret = process_skip_chunk(s, chunk_data_size, source, chunk_header->chunk_sz, cur_block,
+                               crc_ptr);
+      if (chunk_data_size != 0) {
+        if (ret < 0) {
+          verbose_error(s->verbose, ret, "skip block at %" PRId64, offset);
+          return ret;
+        }
+      }
+      return chunk_header->chunk_sz;
+    case CHUNK_TYPE_CRC32:
+      ret = process_crc32_chunk(source, chunk_data_size, crc_ptr);
+      if (ret < 0) {
+        verbose_error(s->verbose, -EINVAL, "crc block at %" PRId64, offset);
+        return ret;
+      }
+      return 0;
+    default:
+      verbose_error(s->verbose, -EINVAL, "unknown block %04X at %" PRId64, chunk_header->chunk_type,
+                    offset);
+  }
 
-	return 0;
+  return 0;
 }
 
-static int sparse_file_read_sparse(struct sparse_file *s, int fd, bool crc)
-{
-	int ret;
-	unsigned int i;
-	sparse_header_t sparse_header;
-	chunk_header_t chunk_header;
-	uint32_t crc32 = 0;
-	uint32_t *crc_ptr = 0;
-	unsigned int cur_block = 0;
-	off64_t offset;
+static int sparse_file_read_sparse(struct sparse_file* s, SparseFileSource* source, bool crc) {
+  int ret;
+  unsigned int i;
+  sparse_header_t sparse_header;
+  chunk_header_t chunk_header;
+  uint32_t crc32 = 0;
+  uint32_t* crc_ptr = nullptr;
+  unsigned int cur_block = 0;
 
-	if (!copybuf) {
-		copybuf = (char *)malloc(COPY_BUF_SIZE);
-	}
+  if (!copybuf) {
+    copybuf = (char*)malloc(COPY_BUF_SIZE);
+  }
 
-	if (!copybuf) {
-		return -ENOMEM;
-	}
+  if (!copybuf) {
+    return -ENOMEM;
+  }
 
-	if (crc) {
-		crc_ptr = &crc32;
-	}
+  if (crc) {
+    crc_ptr = &crc32;
+  }
 
-	ret = read_all(fd, &sparse_header, sizeof(sparse_header));
-	if (ret < 0) {
-		return ret;
-	}
+  ret = source->ReadValue(&sparse_header, sizeof(sparse_header));
+  if (ret < 0) {
+    return ret;
+  }
 
-	if (sparse_header.magic != SPARSE_HEADER_MAGIC) {
-		return -EINVAL;
-	}
+  if (sparse_header.magic != SPARSE_HEADER_MAGIC) {
+    return -EINVAL;
+  }
 
-	if (sparse_header.major_version != SPARSE_HEADER_MAJOR_VER) {
-		return -EINVAL;
-	}
+  if (sparse_header.major_version != SPARSE_HEADER_MAJOR_VER) {
+    return -EINVAL;
+  }
 
-	if (sparse_header.file_hdr_sz < SPARSE_HEADER_LEN) {
-		return -EINVAL;
-	}
+  if (sparse_header.file_hdr_sz < SPARSE_HEADER_LEN) {
+    return -EINVAL;
+  }
 
-	if (sparse_header.chunk_hdr_sz < sizeof(chunk_header)) {
-		return -EINVAL;
-	}
+  if (sparse_header.chunk_hdr_sz < sizeof(chunk_header)) {
+    return -EINVAL;
+  }
 
-	if (sparse_header.file_hdr_sz > SPARSE_HEADER_LEN) {
-		/* Skip the remaining bytes in a header that is longer than
-		 * we expected.
-		 */
-		lseek64(fd, sparse_header.file_hdr_sz - SPARSE_HEADER_LEN, SEEK_CUR);
-	}
+  if (sparse_header.file_hdr_sz > SPARSE_HEADER_LEN) {
+    /* Skip the remaining bytes in a header that is longer than
+     * we expected.
+     */
+    source->Seek(sparse_header.file_hdr_sz - SPARSE_HEADER_LEN);
+  }
 
-	for (i = 0; i < sparse_header.total_chunks; i++) {
-		ret = read_all(fd, &chunk_header, sizeof(chunk_header));
-		if (ret < 0) {
-			return ret;
-		}
+  for (i = 0; i < sparse_header.total_chunks; i++) {
+    ret = source->ReadValue(&chunk_header, sizeof(chunk_header));
+    if (ret < 0) {
+      return ret;
+    }
 
-		if (sparse_header.chunk_hdr_sz > CHUNK_HEADER_LEN) {
-			/* Skip the remaining bytes in a header that is longer than
-			 * we expected.
-			 */
-			lseek64(fd, sparse_header.chunk_hdr_sz - CHUNK_HEADER_LEN, SEEK_CUR);
-		}
+    if (sparse_header.chunk_hdr_sz > CHUNK_HEADER_LEN) {
+      /* Skip the remaining bytes in a header that is longer than
+       * we expected.
+       */
+      source->Seek(sparse_header.chunk_hdr_sz - CHUNK_HEADER_LEN);
+    }
 
-		offset = lseek64(fd, 0, SEEK_CUR);
+    ret = process_chunk(s, source, sparse_header.chunk_hdr_sz, &chunk_header, cur_block, crc_ptr);
+    if (ret < 0) {
+      return ret;
+    }
 
-		ret = process_chunk(s, fd, offset, sparse_header.chunk_hdr_sz, &chunk_header,
-				cur_block, crc_ptr);
-		if (ret < 0) {
-			return ret;
-		}
+    cur_block += ret;
+  }
 
-		cur_block += ret;
-	}
+  if (sparse_header.total_blks != cur_block) {
+    return -EINVAL;
+  }
 
-	if (sparse_header.total_blks != cur_block) {
-		return -EINVAL;
-	}
-
-	return 0;
+  return 0;
 }
 
-static int sparse_file_read_normal(struct sparse_file *s, int fd)
-{
-	int ret;
-	uint32_t *buf = (uint32_t *)malloc(s->block_size);
-	unsigned int block = 0;
-	int64_t remain = s->len;
-	int64_t offset = 0;
-	unsigned int to_read;
-	unsigned int i;
-	bool sparse_block;
+static int sparse_file_read_normal(struct sparse_file* s, int fd) {
+  int ret;
+  uint32_t* buf = (uint32_t*)malloc(s->block_size);
+  unsigned int block = 0;
+  int64_t remain = s->len;
+  int64_t offset = 0;
+  unsigned int to_read;
+  unsigned int i;
+  bool sparse_block;
 
-	if (!buf) {
-		return -ENOMEM;
-	}
+  if (!buf) {
+    return -ENOMEM;
+  }
 
-	while (remain > 0) {
-		to_read = std::min(remain, (int64_t)(s->block_size));
-		ret = read_all(fd, buf, to_read);
-		if (ret < 0) {
-			error("failed to read sparse file");
-			free(buf);
-			return ret;
-		}
+  while (remain > 0) {
+    to_read = std::min(remain, (int64_t)(s->block_size));
+    ret = read_all(fd, buf, to_read);
+    if (ret < 0) {
+      error("failed to read sparse file");
+      free(buf);
+      return ret;
+    }
 
-		if (to_read == s->block_size) {
-			sparse_block = true;
-			for (i = 1; i < s->block_size / sizeof(uint32_t); i++) {
-				if (buf[0] != buf[i]) {
-					sparse_block = false;
-					break;
-				}
-			}
-		} else {
-			sparse_block = false;
-		}
+    if (to_read == s->block_size) {
+      sparse_block = true;
+      for (i = 1; i < s->block_size / sizeof(uint32_t); i++) {
+        if (buf[0] != buf[i]) {
+          sparse_block = false;
+          break;
+        }
+      }
+    } else {
+      sparse_block = false;
+    }
 
-		if (sparse_block) {
-			/* TODO: add flag to use skip instead of fill for buf[0] == 0 */
-			sparse_file_add_fill(s, buf[0], to_read, block);
-		} else {
-			sparse_file_add_fd(s, fd, offset, to_read, block);
-		}
+    if (sparse_block) {
+      /* TODO: add flag to use skip instead of fill for buf[0] == 0 */
+      sparse_file_add_fill(s, buf[0], to_read, block);
+    } else {
+      sparse_file_add_fd(s, fd, offset, to_read, block);
+    }
 
-		remain -= to_read;
-		offset += to_read;
-		block++;
-	}
+    remain -= to_read;
+    offset += to_read;
+    block++;
+  }
 
-	free(buf);
-	return 0;
+  free(buf);
+  return 0;
 }
 
-int sparse_file_read(struct sparse_file *s, int fd, bool sparse, bool crc)
-{
-	if (crc && !sparse) {
-		return -EINVAL;
-	}
+int sparse_file_read(struct sparse_file* s, int fd, bool sparse, bool crc) {
+  if (crc && !sparse) {
+    return -EINVAL;
+  }
 
-	if (sparse) {
-		return sparse_file_read_sparse(s, fd, crc);
-	} else {
-		return sparse_file_read_normal(s, fd);
-	}
+  if (sparse) {
+    SparseFileFdSource source(fd);
+    return sparse_file_read_sparse(s, &source, crc);
+  } else {
+    return sparse_file_read_normal(s, fd);
+  }
 }
 
-struct sparse_file *sparse_file_import(int fd, bool verbose, bool crc)
-{
-	int ret;
-	sparse_header_t sparse_header;
-	int64_t len;
-	struct sparse_file *s;
-
-	ret = read_all(fd, &sparse_header, sizeof(sparse_header));
-	if (ret < 0) {
-		verbose_error(verbose, ret, "header");
-		return NULL;
-	}
-
-	if (sparse_header.magic != SPARSE_HEADER_MAGIC) {
-		verbose_error(verbose, -EINVAL, "header magic");
-		return NULL;
-	}
-
-	if (sparse_header.major_version != SPARSE_HEADER_MAJOR_VER) {
-		verbose_error(verbose, -EINVAL, "header major version");
-		return NULL;
-	}
-
-	if (sparse_header.file_hdr_sz < SPARSE_HEADER_LEN) {
-		return NULL;
-	}
-
-	if (sparse_header.chunk_hdr_sz < sizeof(chunk_header_t)) {
-		return NULL;
-	}
-
-	len = (int64_t)sparse_header.total_blks * sparse_header.blk_sz;
-	s = sparse_file_new(sparse_header.blk_sz, len);
-	if (!s) {
-		verbose_error(verbose, -EINVAL, NULL);
-		return NULL;
-	}
-
-	ret = lseek64(fd, 0, SEEK_SET);
-	if (ret < 0) {
-		verbose_error(verbose, ret, "seeking");
-		sparse_file_destroy(s);
-		return NULL;
-	}
-
-	s->verbose = verbose;
-
-	ret = sparse_file_read(s, fd, true, crc);
-	if (ret < 0) {
-		sparse_file_destroy(s);
-		return NULL;
-	}
-
-	return s;
+int sparse_file_read_buf(struct sparse_file* s, char* buf, bool crc) {
+  SparseFileBufSource source(buf);
+  return sparse_file_read_sparse(s, &source, crc);
 }
 
-struct sparse_file *sparse_file_import_auto(int fd, bool crc, bool verbose)
-{
-	struct sparse_file *s;
-	int64_t len;
-	int ret;
+static struct sparse_file* sparse_file_import_source(SparseFileSource* source, bool verbose,
+                                                     bool crc) {
+  int ret;
+  sparse_header_t sparse_header;
+  int64_t len;
+  struct sparse_file* s;
 
-	s = sparse_file_import(fd, verbose, crc);
-	if (s) {
-		return s;
-	}
+  ret = source->ReadValue(&sparse_header, sizeof(sparse_header));
+  if (ret < 0) {
+    verbose_error(verbose, ret, "header");
+    return nullptr;
+  }
 
-	len = lseek64(fd, 0, SEEK_END);
-	if (len < 0) {
-		return NULL;
-	}
+  if (sparse_header.magic != SPARSE_HEADER_MAGIC) {
+    verbose_error(verbose, -EINVAL, "header magic");
+    return nullptr;
+  }
 
-	lseek64(fd, 0, SEEK_SET);
+  if (sparse_header.major_version != SPARSE_HEADER_MAJOR_VER) {
+    verbose_error(verbose, -EINVAL, "header major version");
+    return nullptr;
+  }
 
-	s = sparse_file_new(4096, len);
-	if (!s) {
-		return NULL;
-	}
+  if (sparse_header.file_hdr_sz < SPARSE_HEADER_LEN) {
+    return nullptr;
+  }
 
-	ret = sparse_file_read_normal(s, fd);
-	if (ret < 0) {
-		sparse_file_destroy(s);
-		return NULL;
-	}
+  if (sparse_header.chunk_hdr_sz < sizeof(chunk_header_t)) {
+    return nullptr;
+  }
 
-	return s;
+  len = (int64_t)sparse_header.total_blks * sparse_header.blk_sz;
+  s = sparse_file_new(sparse_header.blk_sz, len);
+  if (!s) {
+    verbose_error(verbose, -EINVAL, nullptr);
+    return nullptr;
+  }
+
+  ret = source->SetOffset(0);
+  if (ret < 0) {
+    verbose_error(verbose, ret, "seeking");
+    sparse_file_destroy(s);
+    return nullptr;
+  }
+
+  s->verbose = verbose;
+
+  ret = sparse_file_read_sparse(s, source, crc);
+  if (ret < 0) {
+    sparse_file_destroy(s);
+    return nullptr;
+  }
+
+  return s;
+}
+
+struct sparse_file* sparse_file_import(int fd, bool verbose, bool crc) {
+  SparseFileFdSource source(fd);
+  return sparse_file_import_source(&source, verbose, crc);
+}
+
+struct sparse_file* sparse_file_import_buf(char* buf, bool verbose, bool crc) {
+  SparseFileBufSource source(buf);
+  return sparse_file_import_source(&source, verbose, crc);
+}
+
+struct sparse_file* sparse_file_import_auto(int fd, bool crc, bool verbose) {
+  struct sparse_file* s;
+  int64_t len;
+  int ret;
+
+  s = sparse_file_import(fd, verbose, crc);
+  if (s) {
+    return s;
+  }
+
+  len = lseek64(fd, 0, SEEK_END);
+  if (len < 0) {
+    return nullptr;
+  }
+
+  lseek64(fd, 0, SEEK_SET);
+
+  s = sparse_file_new(4096, len);
+  if (!s) {
+    return nullptr;
+  }
+
+  ret = sparse_file_read_normal(s, fd);
+  if (ret < 0) {
+    sparse_file_destroy(s);
+    return nullptr;
+  }
+
+  return s;
 }
diff --git a/libstats/Android.bp b/libstats/Android.bp
new file mode 100644
index 0000000..d58f294
--- /dev/null
+++ b/libstats/Android.bp
@@ -0,0 +1,37 @@
+//
+// 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.
+//
+
+// ==========================================================
+// Native library to write stats log to statsd socket
+// ==========================================================
+cc_library_static {
+    name: "libstatssocket",
+    srcs: [
+        "stats_event_list.c",
+        "statsd_writer.c",
+    ],
+    cflags: [
+        "-Wall",
+        "-Werror",
+        "-DLIBLOG_LOG_TAG=1006",
+        "-DWRITE_TO_STATSD=1",
+        "-DWRITE_TO_LOGD=0",
+    ],
+    export_include_dirs: ["include"],
+    shared_libs: [
+        "liblog",
+    ],
+}
diff --git a/libstats/OWNERS b/libstats/OWNERS
new file mode 100644
index 0000000..ed06fbc
--- /dev/null
+++ b/libstats/OWNERS
@@ -0,0 +1,4 @@
+bookatz@google.com
+joeo@google.com
+yaochen@google.com
+yanglu@google.com
diff --git a/libstats/include/stats_event_list.h b/libstats/include/stats_event_list.h
new file mode 100644
index 0000000..5d174ae
--- /dev/null
+++ b/libstats/include/stats_event_list.h
@@ -0,0 +1,250 @@
+/*
+ * 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 ANDROID_STATS_LOG_STATS_EVENT_LIST_H
+#define ANDROID_STATS_LOG_STATS_EVENT_LIST_H
+
+#include <log/log_event_list.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+void reset_log_context(android_log_context ctx);
+int write_to_logger(android_log_context context, log_id_t id);
+
+#ifdef __cplusplus
+}
+#endif
+
+#ifdef __cplusplus
+/**
+ * A copy of android_log_event_list class.
+ *
+ * android_log_event_list is going to be deprecated soon, so copy it here to
+ * avoid creating dependency on upstream code. TODO(b/78304629): Rewrite this
+ * code.
+ */
+class stats_event_list {
+  private:
+    android_log_context ctx;
+    int ret;
+
+    stats_event_list(const stats_event_list&) = delete;
+    void operator=(const stats_event_list&) = delete;
+
+  public:
+    explicit stats_event_list(int tag) : ret(0) {
+        ctx = create_android_logger(static_cast<uint32_t>(tag));
+    }
+    explicit stats_event_list(log_msg& log_msg) : ret(0) {
+        ctx = create_android_log_parser(log_msg.msg() + sizeof(uint32_t),
+                                        log_msg.entry.len - sizeof(uint32_t));
+    }
+    ~stats_event_list() { android_log_destroy(&ctx); }
+
+    int close() {
+        int retval = android_log_destroy(&ctx);
+        if (retval < 0) {
+            ret = retval;
+        }
+        return retval;
+    }
+
+    /* To allow above C calls to use this class as parameter */
+    operator android_log_context() const { return ctx; }
+
+    /* return errors or transmit status */
+    int status() const { return ret; }
+
+    int begin() {
+        int retval = android_log_write_list_begin(ctx);
+        if (retval < 0) {
+            ret = retval;
+        }
+        return ret;
+    }
+    int end() {
+        int retval = android_log_write_list_end(ctx);
+        if (retval < 0) {
+            ret = retval;
+        }
+        return ret;
+    }
+
+    stats_event_list& operator<<(int32_t value) {
+        int retval = android_log_write_int32(ctx, value);
+        if (retval < 0) {
+            ret = retval;
+        }
+        return *this;
+    }
+
+    stats_event_list& operator<<(uint32_t value) {
+        int retval = android_log_write_int32(ctx, static_cast<int32_t>(value));
+        if (retval < 0) {
+            ret = retval;
+        }
+        return *this;
+    }
+
+    stats_event_list& operator<<(bool value) {
+        int retval = android_log_write_int32(ctx, value ? 1 : 0);
+        if (retval < 0) {
+            ret = retval;
+        }
+        return *this;
+    }
+
+    stats_event_list& operator<<(int64_t value) {
+        int retval = android_log_write_int64(ctx, value);
+        if (retval < 0) {
+            ret = retval;
+        }
+        return *this;
+    }
+
+    stats_event_list& operator<<(uint64_t value) {
+        int retval = android_log_write_int64(ctx, static_cast<int64_t>(value));
+        if (retval < 0) {
+            ret = retval;
+        }
+        return *this;
+    }
+
+    stats_event_list& operator<<(const char* value) {
+        int retval = android_log_write_string8(ctx, value);
+        if (retval < 0) {
+            ret = retval;
+        }
+        return *this;
+    }
+
+#if defined(_USING_LIBCXX)
+    stats_event_list& operator<<(const std::string& value) {
+        int retval = android_log_write_string8_len(ctx, value.data(), value.length());
+        if (retval < 0) {
+            ret = retval;
+        }
+        return *this;
+    }
+#endif
+
+    stats_event_list& operator<<(float value) {
+        int retval = android_log_write_float32(ctx, value);
+        if (retval < 0) {
+            ret = retval;
+        }
+        return *this;
+    }
+
+    int write(log_id_t id = LOG_ID_EVENTS) {
+        /* facilitate -EBUSY retry */
+        if ((ret == -EBUSY) || (ret > 0)) {
+            ret = 0;
+        }
+        int retval = write_to_logger(ctx, id);
+        /* existing errors trump transmission errors */
+        if (!ret) {
+            ret = retval;
+        }
+        return ret;
+    }
+
+    /*
+     * Append<Type> methods removes any integer promotion
+     * confusion, and adds access to string with length.
+     * Append methods are also added for all types for
+     * convenience.
+     */
+
+    bool AppendInt(int32_t value) {
+        int retval = android_log_write_int32(ctx, value);
+        if (retval < 0) {
+            ret = retval;
+        }
+        return ret >= 0;
+    }
+
+    bool AppendLong(int64_t value) {
+        int retval = android_log_write_int64(ctx, value);
+        if (retval < 0) {
+            ret = retval;
+        }
+        return ret >= 0;
+    }
+
+    bool AppendString(const char* value) {
+        int retval = android_log_write_string8(ctx, value);
+        if (retval < 0) {
+            ret = retval;
+        }
+        return ret >= 0;
+    }
+
+    bool AppendString(const char* value, size_t len) {
+        int retval = android_log_write_string8_len(ctx, value, len);
+        if (retval < 0) {
+            ret = retval;
+        }
+        return ret >= 0;
+    }
+
+#if defined(_USING_LIBCXX)
+    bool AppendString(const std::string& value) {
+        int retval = android_log_write_string8_len(ctx, value.data(), value.length());
+        if (retval < 0) {
+            ret = retval;
+        }
+        return ret;
+    }
+
+    bool Append(const std::string& value) {
+        int retval = android_log_write_string8_len(ctx, value.data(), value.length());
+        if (retval < 0) {
+            ret = retval;
+        }
+        return ret;
+    }
+#endif
+
+    bool AppendFloat(float value) {
+        int retval = android_log_write_float32(ctx, value);
+        if (retval < 0) {
+            ret = retval;
+        }
+        return ret >= 0;
+    }
+
+    template <typename Tvalue>
+    bool Append(Tvalue value) {
+        *this << value;
+        return ret >= 0;
+    }
+
+    bool Append(const char* value, size_t len) {
+        int retval = android_log_write_string8_len(ctx, value, len);
+        if (retval < 0) {
+            ret = retval;
+        }
+        return ret >= 0;
+    }
+
+    android_log_list_element read() { return android_log_read_next(ctx); }
+    android_log_list_element peek() { return android_log_peek_next(ctx); }
+};
+
+#endif
+#endif  // ANDROID_STATS_LOG_STATS_EVENT_LIST_H
diff --git a/libstats/stats_event_list.c b/libstats/stats_event_list.c
new file mode 100644
index 0000000..3d746db
--- /dev/null
+++ b/libstats/stats_event_list.c
@@ -0,0 +1,174 @@
+/*
+ * 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 "include/stats_event_list.h"
+
+#include <string.h>
+#include "statsd_writer.h"
+
+#define MAX_EVENT_PAYLOAD (LOGGER_ENTRY_MAX_PAYLOAD - sizeof(int32_t))
+
+typedef struct {
+    uint32_t tag;
+    unsigned pos;                                    /* Read/write position into buffer */
+    unsigned count[ANDROID_MAX_LIST_NEST_DEPTH + 1]; /* Number of elements   */
+    unsigned list[ANDROID_MAX_LIST_NEST_DEPTH + 1];  /* pos for list counter */
+    unsigned list_nest_depth;
+    unsigned len; /* Length or raw buffer. */
+    bool overflow;
+    bool list_stop; /* next call decrement list_nest_depth and issue a stop */
+    enum {
+        kAndroidLoggerRead = 1,
+        kAndroidLoggerWrite = 2,
+    } read_write_flag;
+    uint8_t storage[LOGGER_ENTRY_MAX_PAYLOAD];
+} android_log_context_internal;
+
+extern struct android_log_transport_write statsdLoggerWrite;
+
+static int __write_to_statsd_init(struct iovec* vec, size_t nr);
+static int (*write_to_statsd)(struct iovec* vec, size_t nr) = __write_to_statsd_init;
+
+// Similar to create_android_logger(), but instead of allocation a new buffer,
+// this function resets the buffer for resuse.
+void reset_log_context(android_log_context ctx) {
+    if (!ctx) {
+        return;
+    }
+    android_log_context_internal* context = (android_log_context_internal*)(ctx);
+    uint32_t tag = context->tag;
+    memset(context, 0, sizeof(android_log_context_internal));
+
+    context->tag = tag;
+    context->read_write_flag = kAndroidLoggerWrite;
+    size_t needed = sizeof(uint8_t) + sizeof(uint8_t);
+    if ((context->pos + needed) > MAX_EVENT_PAYLOAD) {
+        context->overflow = true;
+    }
+    /* Everything is a list */
+    context->storage[context->pos + 0] = EVENT_TYPE_LIST;
+    context->list[0] = context->pos + 1;
+    context->pos += needed;
+}
+
+int stats_write_list(android_log_context ctx) {
+    android_log_context_internal* context;
+    const char* msg;
+    ssize_t len;
+
+    context = (android_log_context_internal*)(ctx);
+    if (!context || (kAndroidLoggerWrite != context->read_write_flag)) {
+        return -EBADF;
+    }
+
+    if (context->list_nest_depth) {
+        return -EIO;
+    }
+
+    /* NB: if there was overflow, then log is truncated. Nothing reported */
+    context->storage[1] = context->count[0];
+    len = context->len = context->pos;
+    msg = (const char*)context->storage;
+    /* it's not a list */
+    if (context->count[0] <= 1) {
+        len -= sizeof(uint8_t) + sizeof(uint8_t);
+        if (len < 0) {
+            len = 0;
+        }
+        msg += sizeof(uint8_t) + sizeof(uint8_t);
+    }
+
+    struct iovec vec[2];
+    vec[0].iov_base = &context->tag;
+    vec[0].iov_len = sizeof(context->tag);
+    vec[1].iov_base = (void*)msg;
+    vec[1].iov_len = len;
+    return write_to_statsd(vec, 2);
+}
+
+int write_to_logger(android_log_context ctx, log_id_t id) {
+    int retValue = 0;
+
+    if (WRITE_TO_LOGD) {
+        retValue = android_log_write_list(ctx, id);
+    }
+
+    if (WRITE_TO_STATSD) {
+        // log_event_list's cast operator is overloaded.
+        int ret = stats_write_list(ctx);
+        // In debugging phase, we may write to both logd and statsd. Prefer to
+        // return statsd socket write error code here.
+        if (ret < 0) {
+            retValue = ret;
+        }
+    }
+
+    return retValue;
+}
+
+/* log_init_lock assumed */
+static int __write_to_statsd_initialize_locked() {
+    if (!statsdLoggerWrite.open || ((*statsdLoggerWrite.open)() < 0)) {
+        if (statsdLoggerWrite.close) {
+            (*statsdLoggerWrite.close)();
+            return -ENODEV;
+        }
+    }
+    return 1;
+}
+
+static int __write_to_stats_daemon(struct iovec* vec, size_t nr) {
+    int save_errno;
+    struct timespec ts;
+    size_t len, i;
+
+    for (len = i = 0; i < nr; ++i) {
+        len += vec[i].iov_len;
+    }
+    if (!len) {
+        return -EINVAL;
+    }
+
+    save_errno = errno;
+    clock_gettime(CLOCK_REALTIME, &ts);
+
+    int ret = (int)(*statsdLoggerWrite.write)(&ts, vec, nr);
+    errno = save_errno;
+    return ret;
+}
+
+static int __write_to_statsd_init(struct iovec* vec, size_t nr) {
+    int ret, save_errno = errno;
+
+    statsd_writer_init_lock();
+
+    if (write_to_statsd == __write_to_statsd_init) {
+        ret = __write_to_statsd_initialize_locked();
+        if (ret < 0) {
+            statsd_writer_init_unlock();
+            errno = save_errno;
+            return ret;
+        }
+
+        write_to_statsd = __write_to_stats_daemon;
+    }
+
+    statsd_writer_init_unlock();
+
+    ret = write_to_statsd(vec, nr);
+    errno = save_errno;
+    return ret;
+}
diff --git a/libstats/statsd_writer.c b/libstats/statsd_writer.c
new file mode 100644
index 0000000..9953bba
--- /dev/null
+++ b/libstats/statsd_writer.c
@@ -0,0 +1,260 @@
+/*
+ * 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 "statsd_writer.h"
+
+#include <cutils/sockets.h>
+#include <endian.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <poll.h>
+#include <private/android_filesystem_config.h>
+#include <private/android_logger.h>
+#include <stdarg.h>
+#include <stdatomic.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/un.h>
+#include <time.h>
+#include <unistd.h>
+
+/* branchless on many architectures. */
+#define min(x, y) ((y) ^ (((x) ^ (y)) & -((x) < (y))))
+
+static pthread_mutex_t log_init_lock = PTHREAD_MUTEX_INITIALIZER;
+
+void statsd_writer_init_lock() {
+    /*
+     * If we trigger a signal handler in the middle of locked activity and the
+     * signal handler logs a message, we could get into a deadlock state.
+     */
+    pthread_mutex_lock(&log_init_lock);
+}
+
+int statd_writer_trylock() {
+    return pthread_mutex_trylock(&log_init_lock);
+}
+
+void statsd_writer_init_unlock() {
+    pthread_mutex_unlock(&log_init_lock);
+}
+
+static int statsdAvailable();
+static int statsdOpen();
+static void statsdClose();
+static int statsdWrite(struct timespec* ts, struct iovec* vec, size_t nr);
+
+struct android_log_transport_write statsdLoggerWrite = {
+    .name = "statsd",
+    .sock = -EBADF,
+    .available = statsdAvailable,
+    .open = statsdOpen,
+    .close = statsdClose,
+    .write = statsdWrite,
+};
+
+/* log_init_lock assumed */
+static int statsdOpen() {
+    int i, ret = 0;
+
+    i = atomic_load(&statsdLoggerWrite.sock);
+    if (i < 0) {
+        int sock = TEMP_FAILURE_RETRY(socket(PF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0));
+        if (sock < 0) {
+            ret = -errno;
+        } else {
+            struct sockaddr_un un;
+            memset(&un, 0, sizeof(struct sockaddr_un));
+            un.sun_family = AF_UNIX;
+            strcpy(un.sun_path, "/dev/socket/statsdw");
+
+            if (TEMP_FAILURE_RETRY(
+                    connect(sock, (struct sockaddr*)&un, sizeof(struct sockaddr_un))) < 0) {
+                ret = -errno;
+                switch (ret) {
+                    case -ENOTCONN:
+                    case -ECONNREFUSED:
+                    case -ENOENT:
+                        i = atomic_exchange(&statsdLoggerWrite.sock, ret);
+                    /* FALLTHRU */
+                    default:
+                        break;
+                }
+                close(sock);
+            } else {
+                ret = atomic_exchange(&statsdLoggerWrite.sock, sock);
+                if ((ret >= 0) && (ret != sock)) {
+                    close(ret);
+                }
+                ret = 0;
+            }
+        }
+    }
+
+    return ret;
+}
+
+static void __statsdClose(int negative_errno) {
+    int sock = atomic_exchange(&statsdLoggerWrite.sock, negative_errno);
+    if (sock >= 0) {
+        close(sock);
+    }
+}
+
+static void statsdClose() {
+    __statsdClose(-EBADF);
+}
+
+static int statsdAvailable() {
+    if (atomic_load(&statsdLoggerWrite.sock) < 0) {
+        if (access("/dev/socket/statsdw", W_OK) == 0) {
+            return 0;
+        }
+        return -EBADF;
+    }
+    return 1;
+}
+
+static int statsdWrite(struct timespec* ts, struct iovec* vec, size_t nr) {
+    ssize_t ret;
+    int sock;
+    static const unsigned headerLength = 1;
+    struct iovec newVec[nr + headerLength];
+    android_log_header_t header;
+    size_t i, payloadSize;
+    static atomic_int dropped;
+
+    sock = atomic_load(&statsdLoggerWrite.sock);
+    if (sock < 0) switch (sock) {
+            case -ENOTCONN:
+            case -ECONNREFUSED:
+            case -ENOENT:
+                break;
+            default:
+                return -EBADF;
+        }
+    /*
+     *  struct {
+     *      // what we provide to socket
+     *      android_log_header_t header;
+     *      // caller provides
+     *      union {
+     *          struct {
+     *              char     prio;
+     *              char     payload[];
+     *          } string;
+     *          struct {
+     *              uint32_t tag
+     *              char     payload[];
+     *          } binary;
+     *      };
+     *  };
+     */
+
+    header.tid = gettid();
+    header.realtime.tv_sec = ts->tv_sec;
+    header.realtime.tv_nsec = ts->tv_nsec;
+
+    newVec[0].iov_base = (unsigned char*)&header;
+    newVec[0].iov_len = sizeof(header);
+
+    // If we dropped events before, try to tell statsd.
+    if (sock >= 0) {
+        int32_t snapshot = atomic_exchange_explicit(&dropped, 0, memory_order_relaxed);
+        if (snapshot) {
+            android_log_event_int_t buffer;
+            header.id = LOG_ID_STATS;
+            buffer.header.tag = htole32(LIBLOG_LOG_TAG);
+            buffer.payload.type = EVENT_TYPE_INT;
+            buffer.payload.data = htole32(snapshot);
+
+            newVec[headerLength].iov_base = &buffer;
+            newVec[headerLength].iov_len = sizeof(buffer);
+
+            ret = TEMP_FAILURE_RETRY(writev(sock, newVec, 2));
+            if (ret != (ssize_t)(sizeof(header) + sizeof(buffer))) {
+                atomic_fetch_add_explicit(&dropped, snapshot, memory_order_relaxed);
+            }
+        }
+    }
+
+    header.id = LOG_ID_STATS;
+
+    for (payloadSize = 0, i = headerLength; i < nr + headerLength; i++) {
+        newVec[i].iov_base = vec[i - headerLength].iov_base;
+        payloadSize += newVec[i].iov_len = vec[i - headerLength].iov_len;
+
+        if (payloadSize > LOGGER_ENTRY_MAX_PAYLOAD) {
+            newVec[i].iov_len -= payloadSize - LOGGER_ENTRY_MAX_PAYLOAD;
+            if (newVec[i].iov_len) {
+                ++i;
+            }
+            break;
+        }
+    }
+
+    /*
+     * The write below could be lost, but will never block.
+     *
+     * ENOTCONN occurs if statsd has died.
+     * ENOENT occurs if statsd is not running and socket is missing.
+     * ECONNREFUSED occurs if we can not reconnect to statsd.
+     * EAGAIN occurs if statsd is overloaded.
+     */
+    if (sock < 0) {
+        ret = sock;
+    } else {
+        ret = TEMP_FAILURE_RETRY(writev(sock, newVec, i));
+        if (ret < 0) {
+            ret = -errno;
+        }
+    }
+    switch (ret) {
+        case -ENOTCONN:
+        case -ECONNREFUSED:
+        case -ENOENT:
+            if (statd_writer_trylock()) {
+                return ret; /* in a signal handler? try again when less stressed
+                             */
+            }
+            __statsdClose(ret);
+            ret = statsdOpen();
+            statsd_writer_init_unlock();
+
+            if (ret < 0) {
+                return ret;
+            }
+
+            ret = TEMP_FAILURE_RETRY(writev(atomic_load(&statsdLoggerWrite.sock), newVec, i));
+            if (ret < 0) {
+                ret = -errno;
+            }
+        /* FALLTHRU */
+        default:
+            break;
+    }
+
+    if (ret > (ssize_t)sizeof(header)) {
+        ret -= sizeof(header);
+    } else if (ret == -EAGAIN) {
+        atomic_fetch_add_explicit(&dropped, 1, memory_order_relaxed);
+    }
+
+    return ret;
+}
diff --git a/libstats/statsd_writer.h b/libstats/statsd_writer.h
new file mode 100644
index 0000000..82e14e0
--- /dev/null
+++ b/libstats/statsd_writer.h
@@ -0,0 +1,43 @@
+/*
+ * 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 ANDROID_STATS_LOG_STATS_WRITER_H
+#define ANDROID_STATS_LOG_STATS_WRITER_H
+
+#include <pthread.h>
+#include <stdatomic.h>
+#include <sys/socket.h>
+
+/**
+ * Internal lock should not be exposed. This is bad design.
+ * TODO: rewrite it in c++ code and encapsulate the functionality in a
+ * StatsdWriter class.
+ */
+void statsd_writer_init_lock();
+int statsd_writer_init_trylock();
+void statsd_writer_init_unlock();
+
+struct android_log_transport_write {
+    const char* name; /* human name to describe the transport */
+    atomic_int sock;
+    int (*available)(); /* Does not cause resources to be taken */
+    int (*open)();      /* can be called multiple times, reusing current resources */
+    void (*close)();    /* free up resources */
+    /* write log to transport, returns number of bytes propagated, or -errno */
+    int (*write)(struct timespec* ts, struct iovec* vec, size_t nr);
+};
+
+#endif  // ANDROID_STATS_LOG_STATS_WRITER_H
diff --git a/libsuspend/Android.bp b/libsuspend/Android.bp
index 7883151..b3e36c2 100644
--- a/libsuspend/Android.bp
+++ b/libsuspend/Android.bp
@@ -3,16 +3,18 @@
 cc_library {
     name: "libsuspend",
     vendor_available: true,
+    vndk: {
+        enabled: true,
+    },
 
     srcs: [
         "autosuspend.c",
-        "autosuspend_autosleep.c",
-        "autosuspend_earlysuspend.c",
-        "autosuspend_wakeup_count.c",
+        "autosuspend_wakeup_count.cpp",
     ],
     export_include_dirs: ["include"],
     local_include_dirs: ["include"],
     shared_libs: [
+        "libbase",
         "liblog",
         "libcutils",
     ],
diff --git a/libsuspend/autosuspend.c b/libsuspend/autosuspend.c
index 54730c2..b87f59c 100644
--- a/libsuspend/autosuspend.c
+++ b/libsuspend/autosuspend.c
@@ -24,48 +24,25 @@
 
 #include "autosuspend_ops.h"
 
-static struct autosuspend_ops *autosuspend_ops;
+static struct autosuspend_ops* autosuspend_ops = NULL;
 static bool autosuspend_enabled;
-static bool autosuspend_inited;
 
-static int autosuspend_init(void)
-{
-    if (autosuspend_inited) {
+static int autosuspend_init(void) {
+    if (autosuspend_ops != NULL) {
         return 0;
     }
 
-    autosuspend_ops = autosuspend_earlysuspend_init();
-    if (autosuspend_ops) {
-        goto out;
-    }
-
-/* Remove autosleep so userspace can manager suspend/resume and keep stats */
-#if 0
-    autosuspend_ops = autosuspend_autosleep_init();
-    if (autosuspend_ops) {
-        goto out;
-    }
-#endif
-
     autosuspend_ops = autosuspend_wakeup_count_init();
-    if (autosuspend_ops) {
-        goto out;
-    }
-
-    if (!autosuspend_ops) {
-        ALOGE("failed to initialize autosuspend\n");
+    if (autosuspend_ops == NULL) {
+        ALOGE("failed to initialize autosuspend");
         return -1;
     }
 
-out:
-    autosuspend_inited = true;
-
-    ALOGV("autosuspend initialized\n");
+    ALOGV("autosuspend initialized");
     return 0;
 }
 
-int autosuspend_enable(void)
-{
+int autosuspend_enable(void) {
     int ret;
 
     ret = autosuspend_init();
@@ -73,7 +50,7 @@
         return ret;
     }
 
-    ALOGV("autosuspend_enable\n");
+    ALOGV("autosuspend_enable");
 
     if (autosuspend_enabled) {
         return 0;
@@ -88,8 +65,7 @@
     return 0;
 }
 
-int autosuspend_disable(void)
-{
+int autosuspend_disable(void) {
     int ret;
 
     ret = autosuspend_init();
@@ -97,7 +73,7 @@
         return ret;
     }
 
-    ALOGV("autosuspend_disable\n");
+    ALOGV("autosuspend_disable");
 
     if (!autosuspend_enabled) {
         return 0;
@@ -111,3 +87,29 @@
     autosuspend_enabled = false;
     return 0;
 }
+
+int autosuspend_force_suspend(int timeout_ms) {
+    int ret;
+
+    ret = autosuspend_init();
+    if (ret) {
+        return ret;
+    }
+
+    ALOGV("autosuspend_force_suspend");
+
+    return autosuspend_ops->force_suspend(timeout_ms);
+}
+
+void autosuspend_set_wakeup_callback(void (*func)(bool success)) {
+    int ret;
+
+    ret = autosuspend_init();
+    if (ret) {
+        return;
+    }
+
+    ALOGV("set_wakeup_callback");
+
+    autosuspend_ops->set_wakeup_callback(func);
+}
diff --git a/libsuspend/autosuspend_autosleep.c b/libsuspend/autosuspend_autosleep.c
deleted file mode 100644
index 77d8db0..0000000
--- a/libsuspend/autosuspend_autosleep.c
+++ /dev/null
@@ -1,102 +0,0 @@
-/*
- * Copyright (C) 2012 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.
- */
-
-#define LOG_TAG "libsuspend"
-
-#include <errno.h>
-#include <fcntl.h>
-#include <stddef.h>
-#include <string.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include <log/log.h>
-
-#include "autosuspend_ops.h"
-
-#define SYS_POWER_AUTOSLEEP "/sys/power/autosleep"
-
-static int autosleep_fd;
-static const char *sleep_state = "mem";
-static const char *on_state = "off";
-
-static int autosuspend_autosleep_enable(void)
-{
-    char buf[80];
-    int ret;
-
-    ALOGV("autosuspend_autosleep_enable\n");
-
-    ret = TEMP_FAILURE_RETRY(write(autosleep_fd, sleep_state, strlen(sleep_state)));
-    if (ret < 0) {
-        strerror_r(errno, buf, sizeof(buf));
-        ALOGE("Error writing to %s: %s\n", SYS_POWER_AUTOSLEEP, buf);
-        goto err;
-    }
-
-    ALOGV("autosuspend_autosleep_enable done\n");
-
-    return 0;
-
-err:
-    return ret;
-}
-
-static int autosuspend_autosleep_disable(void)
-{
-    char buf[80];
-    int ret;
-
-    ALOGV("autosuspend_autosleep_disable\n");
-
-    ret = TEMP_FAILURE_RETRY(write(autosleep_fd, on_state, strlen(on_state)));
-    if (ret < 0) {
-        strerror_r(errno, buf, sizeof(buf));
-        ALOGE("Error writing to %s: %s\n", SYS_POWER_AUTOSLEEP, buf);
-        goto err;
-    }
-
-    ALOGV("autosuspend_autosleep_disable done\n");
-
-    return 0;
-
-err:
-    return ret;
-}
-
-struct autosuspend_ops autosuspend_autosleep_ops = {
-        .enable = autosuspend_autosleep_enable,
-        .disable = autosuspend_autosleep_disable,
-};
-
-struct autosuspend_ops *autosuspend_autosleep_init(void)
-{
-    char buf[80];
-
-    autosleep_fd = TEMP_FAILURE_RETRY(open(SYS_POWER_AUTOSLEEP, O_WRONLY));
-    if (autosleep_fd < 0) {
-        strerror_r(errno, buf, sizeof(buf));
-        ALOGE("Error opening %s: %s\n", SYS_POWER_AUTOSLEEP, buf);
-        return NULL;
-    }
-
-    ALOGI("Selected autosleep\n");
-
-    autosuspend_autosleep_disable();
-
-    return &autosuspend_autosleep_ops;
-}
diff --git a/libsuspend/autosuspend_earlysuspend.c b/libsuspend/autosuspend_earlysuspend.c
deleted file mode 100644
index 809ee82..0000000
--- a/libsuspend/autosuspend_earlysuspend.c
+++ /dev/null
@@ -1,219 +0,0 @@
-/*
- * Copyright (C) 2012 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.
- */
-
-#define LOG_TAG "libsuspend"
-
-#include <errno.h>
-#include <fcntl.h>
-#include <pthread.h>
-#include <stdbool.h>
-#include <stddef.h>
-#include <string.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include <log/log.h>
-
-#include "autosuspend_ops.h"
-
-#define EARLYSUSPEND_SYS_POWER_STATE "/sys/power/state"
-#define EARLYSUSPEND_WAIT_FOR_FB_SLEEP "/sys/power/wait_for_fb_sleep"
-#define EARLYSUSPEND_WAIT_FOR_FB_WAKE "/sys/power/wait_for_fb_wake"
-
-static int sPowerStatefd;
-static const char *pwr_state_mem = "mem";
-static const char *pwr_state_on = "on";
-static pthread_t earlysuspend_thread;
-static pthread_mutex_t earlysuspend_mutex = PTHREAD_MUTEX_INITIALIZER;
-static pthread_cond_t earlysuspend_cond = PTHREAD_COND_INITIALIZER;
-static bool wait_for_earlysuspend;
-static enum {
-    EARLYSUSPEND_ON,
-    EARLYSUSPEND_MEM,
-} earlysuspend_state = EARLYSUSPEND_ON;
-
-int wait_for_fb_wake(void)
-{
-    int err = 0;
-    char buf;
-    int fd = TEMP_FAILURE_RETRY(open(EARLYSUSPEND_WAIT_FOR_FB_WAKE, O_RDONLY, 0));
-    // if the file doesn't exist, the error will be caught in read() below
-    err = TEMP_FAILURE_RETRY(read(fd, &buf, 1));
-    ALOGE_IF(err < 0,
-            "*** ANDROID_WAIT_FOR_FB_WAKE failed (%s)", strerror(errno));
-    close(fd);
-    return err < 0 ? err : 0;
-}
-
-static int wait_for_fb_sleep(void)
-{
-    int err = 0;
-    char buf;
-    int fd = TEMP_FAILURE_RETRY(open(EARLYSUSPEND_WAIT_FOR_FB_SLEEP, O_RDONLY, 0));
-    // if the file doesn't exist, the error will be caught in read() below
-    err = TEMP_FAILURE_RETRY(read(fd, &buf, 1));
-    ALOGE_IF(err < 0,
-            "*** ANDROID_WAIT_FOR_FB_SLEEP failed (%s)", strerror(errno));
-    close(fd);
-    return err < 0 ? err : 0;
-}
-
-static void *earlysuspend_thread_func(void __unused *arg)
-{
-    while (1) {
-        if (wait_for_fb_sleep()) {
-            ALOGE("Failed reading wait_for_fb_sleep, exiting earlysuspend thread\n");
-            return NULL;
-        }
-        pthread_mutex_lock(&earlysuspend_mutex);
-        earlysuspend_state = EARLYSUSPEND_MEM;
-        pthread_cond_signal(&earlysuspend_cond);
-        pthread_mutex_unlock(&earlysuspend_mutex);
-
-        if (wait_for_fb_wake()) {
-            ALOGE("Failed reading wait_for_fb_wake, exiting earlysuspend thread\n");
-            return NULL;
-        }
-        pthread_mutex_lock(&earlysuspend_mutex);
-        earlysuspend_state = EARLYSUSPEND_ON;
-        pthread_cond_signal(&earlysuspend_cond);
-        pthread_mutex_unlock(&earlysuspend_mutex);
-    }
-}
-static int autosuspend_earlysuspend_enable(void)
-{
-    char buf[80];
-    int ret;
-
-    ALOGV("autosuspend_earlysuspend_enable\n");
-
-    ret = write(sPowerStatefd, pwr_state_mem, strlen(pwr_state_mem));
-    if (ret < 0) {
-        strerror_r(errno, buf, sizeof(buf));
-        ALOGE("Error writing to %s: %s\n", EARLYSUSPEND_SYS_POWER_STATE, buf);
-        goto err;
-    }
-
-    if (wait_for_earlysuspend) {
-        pthread_mutex_lock(&earlysuspend_mutex);
-        while (earlysuspend_state != EARLYSUSPEND_MEM) {
-            pthread_cond_wait(&earlysuspend_cond, &earlysuspend_mutex);
-        }
-        pthread_mutex_unlock(&earlysuspend_mutex);
-    }
-
-    ALOGV("autosuspend_earlysuspend_enable done\n");
-
-    return 0;
-
-err:
-    return ret;
-}
-
-static int autosuspend_earlysuspend_disable(void)
-{
-    char buf[80];
-    int ret;
-
-    ALOGV("autosuspend_earlysuspend_disable\n");
-
-    ret = TEMP_FAILURE_RETRY(write(sPowerStatefd, pwr_state_on, strlen(pwr_state_on)));
-    if (ret < 0) {
-        strerror_r(errno, buf, sizeof(buf));
-        ALOGE("Error writing to %s: %s\n", EARLYSUSPEND_SYS_POWER_STATE, buf);
-        goto err;
-    }
-
-    if (wait_for_earlysuspend) {
-        pthread_mutex_lock(&earlysuspend_mutex);
-        while (earlysuspend_state != EARLYSUSPEND_ON) {
-            pthread_cond_wait(&earlysuspend_cond, &earlysuspend_mutex);
-        }
-        pthread_mutex_unlock(&earlysuspend_mutex);
-    }
-
-    ALOGV("autosuspend_earlysuspend_disable done\n");
-
-    return 0;
-
-err:
-    return ret;
-}
-
-struct autosuspend_ops autosuspend_earlysuspend_ops = {
-        .enable = autosuspend_earlysuspend_enable,
-        .disable = autosuspend_earlysuspend_disable,
-};
-
-void start_earlysuspend_thread(void)
-{
-    char buf[80];
-    int ret;
-
-    ret = access(EARLYSUSPEND_WAIT_FOR_FB_SLEEP, F_OK);
-    if (ret < 0) {
-        return;
-    }
-
-    ret = access(EARLYSUSPEND_WAIT_FOR_FB_WAKE, F_OK);
-    if (ret < 0) {
-        return;
-    }
-
-    wait_for_fb_wake();
-
-    ALOGI("Starting early suspend unblocker thread\n");
-    ret = pthread_create(&earlysuspend_thread, NULL, earlysuspend_thread_func, NULL);
-    if (ret) {
-        strerror_r(errno, buf, sizeof(buf));
-        ALOGE("Error creating thread: %s\n", buf);
-        return;
-    }
-
-    wait_for_earlysuspend = true;
-}
-
-struct autosuspend_ops *autosuspend_earlysuspend_init(void)
-{
-    char buf[80];
-    int ret;
-
-    sPowerStatefd = TEMP_FAILURE_RETRY(open(EARLYSUSPEND_SYS_POWER_STATE, O_RDWR));
-
-    if (sPowerStatefd < 0) {
-        strerror_r(errno, buf, sizeof(buf));
-        ALOGW("Error opening %s: %s\n", EARLYSUSPEND_SYS_POWER_STATE, buf);
-        return NULL;
-    }
-
-    ret = TEMP_FAILURE_RETRY(write(sPowerStatefd, "on", 2));
-    if (ret < 0) {
-        strerror_r(errno, buf, sizeof(buf));
-        ALOGW("Error writing 'on' to %s: %s\n", EARLYSUSPEND_SYS_POWER_STATE, buf);
-        goto err_write;
-    }
-
-    ALOGI("Selected early suspend\n");
-
-    start_earlysuspend_thread();
-
-    return &autosuspend_earlysuspend_ops;
-
-err_write:
-    close(sPowerStatefd);
-    return NULL;
-}
diff --git a/libsuspend/autosuspend_ops.h b/libsuspend/autosuspend_ops.h
index 698e25b..b0024c8 100644
--- a/libsuspend/autosuspend_ops.h
+++ b/libsuspend/autosuspend_ops.h
@@ -20,10 +20,12 @@
 struct autosuspend_ops {
     int (*enable)(void);
     int (*disable)(void);
+    int (*force_suspend)(int timeout_ms);
+    void (*set_wakeup_callback)(void (*func)(bool success));
 };
 
-struct autosuspend_ops *autosuspend_autosleep_init(void);
-struct autosuspend_ops *autosuspend_earlysuspend_init(void);
+__BEGIN_DECLS
 struct autosuspend_ops *autosuspend_wakeup_count_init(void);
+__END_DECLS
 
 #endif
diff --git a/libsuspend/autosuspend_wakeup_count.c b/libsuspend/autosuspend_wakeup_count.c
deleted file mode 100644
index 4dedf7f..0000000
--- a/libsuspend/autosuspend_wakeup_count.c
+++ /dev/null
@@ -1,201 +0,0 @@
-/*
- * Copyright (C) 2012 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.
- */
-
-#define LOG_TAG "libsuspend"
-//#define LOG_NDEBUG 0
-
-#include <errno.h>
-#include <fcntl.h>
-#include <pthread.h>
-#include <semaphore.h>
-#include <stddef.h>
-#include <stdbool.h>
-#include <string.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include <log/log.h>
-
-#include "autosuspend_ops.h"
-
-#define SYS_POWER_STATE "/sys/power/state"
-#define SYS_POWER_WAKEUP_COUNT "/sys/power/wakeup_count"
-
-static int state_fd;
-static int wakeup_count_fd;
-static pthread_t suspend_thread;
-static sem_t suspend_lockout;
-static const char *sleep_state = "mem";
-static void (*wakeup_func)(bool success) = NULL;
-
-static void *suspend_thread_func(void *arg __attribute__((unused)))
-{
-    char buf[80];
-    char wakeup_count[20];
-    int wakeup_count_len;
-    int ret;
-    bool success;
-
-    while (1) {
-        usleep(100000);
-        ALOGV("%s: read wakeup_count\n", __func__);
-        lseek(wakeup_count_fd, 0, SEEK_SET);
-        wakeup_count_len = TEMP_FAILURE_RETRY(read(wakeup_count_fd, wakeup_count,
-                sizeof(wakeup_count)));
-        if (wakeup_count_len < 0) {
-            strerror_r(errno, buf, sizeof(buf));
-            ALOGE("Error reading from %s: %s\n", SYS_POWER_WAKEUP_COUNT, buf);
-            wakeup_count_len = 0;
-            continue;
-        }
-        if (!wakeup_count_len) {
-            ALOGE("Empty wakeup count\n");
-            continue;
-        }
-
-        ALOGV("%s: wait\n", __func__);
-        ret = sem_wait(&suspend_lockout);
-        if (ret < 0) {
-            strerror_r(errno, buf, sizeof(buf));
-            ALOGE("Error waiting on semaphore: %s\n", buf);
-            continue;
-        }
-
-        success = true;
-        ALOGV("%s: write %*s to wakeup_count\n", __func__, wakeup_count_len, wakeup_count);
-        ret = TEMP_FAILURE_RETRY(write(wakeup_count_fd, wakeup_count, wakeup_count_len));
-        if (ret < 0) {
-            strerror_r(errno, buf, sizeof(buf));
-            ALOGE("Error writing to %s: %s\n", SYS_POWER_WAKEUP_COUNT, buf);
-        } else {
-            ALOGV("%s: write %s to %s\n", __func__, sleep_state, SYS_POWER_STATE);
-            ret = TEMP_FAILURE_RETRY(write(state_fd, sleep_state, strlen(sleep_state)));
-            if (ret < 0) {
-                success = false;
-            }
-            void (*func)(bool success) = wakeup_func;
-            if (func != NULL) {
-                (*func)(success);
-            }
-        }
-
-        ALOGV("%s: release sem\n", __func__);
-        ret = sem_post(&suspend_lockout);
-        if (ret < 0) {
-            strerror_r(errno, buf, sizeof(buf));
-            ALOGE("Error releasing semaphore: %s\n", buf);
-        }
-    }
-    return NULL;
-}
-
-static int autosuspend_wakeup_count_enable(void)
-{
-    char buf[80];
-    int ret;
-
-    ALOGV("autosuspend_wakeup_count_enable\n");
-
-    ret = sem_post(&suspend_lockout);
-
-    if (ret < 0) {
-        strerror_r(errno, buf, sizeof(buf));
-        ALOGE("Error changing semaphore: %s\n", buf);
-    }
-
-    ALOGV("autosuspend_wakeup_count_enable done\n");
-
-    return ret;
-}
-
-static int autosuspend_wakeup_count_disable(void)
-{
-    char buf[80];
-    int ret;
-
-    ALOGV("autosuspend_wakeup_count_disable\n");
-
-    ret = sem_wait(&suspend_lockout);
-
-    if (ret < 0) {
-        strerror_r(errno, buf, sizeof(buf));
-        ALOGE("Error changing semaphore: %s\n", buf);
-    }
-
-    ALOGV("autosuspend_wakeup_count_disable done\n");
-
-    return ret;
-}
-
-void set_wakeup_callback(void (*func)(bool success))
-{
-    if (wakeup_func != NULL) {
-        ALOGE("Duplicate wakeup callback applied, keeping original");
-        return;
-    }
-    wakeup_func = func;
-}
-
-struct autosuspend_ops autosuspend_wakeup_count_ops = {
-        .enable = autosuspend_wakeup_count_enable,
-        .disable = autosuspend_wakeup_count_disable,
-};
-
-struct autosuspend_ops *autosuspend_wakeup_count_init(void)
-{
-    int ret;
-    char buf[80];
-
-    state_fd = TEMP_FAILURE_RETRY(open(SYS_POWER_STATE, O_RDWR));
-    if (state_fd < 0) {
-        strerror_r(errno, buf, sizeof(buf));
-        ALOGE("Error opening %s: %s\n", SYS_POWER_STATE, buf);
-        goto err_open_state;
-    }
-
-    wakeup_count_fd = TEMP_FAILURE_RETRY(open(SYS_POWER_WAKEUP_COUNT, O_RDWR));
-    if (wakeup_count_fd < 0) {
-        strerror_r(errno, buf, sizeof(buf));
-        ALOGE("Error opening %s: %s\n", SYS_POWER_WAKEUP_COUNT, buf);
-        goto err_open_wakeup_count;
-    }
-
-    ret = sem_init(&suspend_lockout, 0, 0);
-    if (ret < 0) {
-        strerror_r(errno, buf, sizeof(buf));
-        ALOGE("Error creating semaphore: %s\n", buf);
-        goto err_sem_init;
-    }
-    ret = pthread_create(&suspend_thread, NULL, suspend_thread_func, NULL);
-    if (ret) {
-        strerror_r(ret, buf, sizeof(buf));
-        ALOGE("Error creating thread: %s\n", buf);
-        goto err_pthread_create;
-    }
-
-    ALOGI("Selected wakeup count\n");
-    return &autosuspend_wakeup_count_ops;
-
-err_pthread_create:
-    sem_destroy(&suspend_lockout);
-err_sem_init:
-    close(wakeup_count_fd);
-err_open_wakeup_count:
-    close(state_fd);
-err_open_state:
-    return NULL;
-}
diff --git a/libsuspend/autosuspend_wakeup_count.cpp b/libsuspend/autosuspend_wakeup_count.cpp
new file mode 100644
index 0000000..27c8629
--- /dev/null
+++ b/libsuspend/autosuspend_wakeup_count.cpp
@@ -0,0 +1,236 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+#define LOG_TAG "libsuspend"
+//#define LOG_NDEBUG 0
+
+#include <fcntl.h>
+#include <pthread.h>
+#include <semaphore.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <string.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/strings.h>
+
+#include "autosuspend_ops.h"
+
+#define BASE_SLEEP_TIME 100000
+#define MAX_SLEEP_TIME 60000000
+
+static int state_fd = -1;
+static int wakeup_count_fd;
+
+using android::base::ReadFdToString;
+using android::base::Trim;
+using android::base::WriteStringToFd;
+
+static pthread_t suspend_thread;
+static sem_t suspend_lockout;
+static constexpr char sleep_state[] = "mem";
+static void (*wakeup_func)(bool success) = NULL;
+static int sleep_time = BASE_SLEEP_TIME;
+static constexpr char sys_power_state[] = "/sys/power/state";
+static constexpr char sys_power_wakeup_count[] = "/sys/power/wakeup_count";
+static bool autosuspend_is_init = false;
+
+static void update_sleep_time(bool success) {
+    if (success) {
+        sleep_time = BASE_SLEEP_TIME;
+        return;
+    }
+    // double sleep time after each failure up to one minute
+    sleep_time = MIN(sleep_time * 2, MAX_SLEEP_TIME);
+}
+
+static void* suspend_thread_func(void* arg __attribute__((unused))) {
+    bool success = true;
+
+    while (true) {
+        update_sleep_time(success);
+        usleep(sleep_time);
+        success = false;
+        LOG(VERBOSE) << "read wakeup_count";
+        lseek(wakeup_count_fd, 0, SEEK_SET);
+        std::string wakeup_count;
+        if (!ReadFdToString(wakeup_count_fd, &wakeup_count)) {
+            PLOG(ERROR) << "error reading from " << sys_power_wakeup_count;
+            continue;
+        }
+
+        wakeup_count = Trim(wakeup_count);
+        if (wakeup_count.empty()) {
+            LOG(ERROR) << "empty wakeup count";
+            continue;
+        }
+
+        LOG(VERBOSE) << "wait";
+        int ret = sem_wait(&suspend_lockout);
+        if (ret < 0) {
+            PLOG(ERROR) << "error waiting on semaphore";
+            continue;
+        }
+
+        LOG(VERBOSE) << "write " << wakeup_count << " to wakeup_count";
+        if (WriteStringToFd(wakeup_count, wakeup_count_fd)) {
+            LOG(VERBOSE) << "write " << sleep_state << " to " << sys_power_state;
+            success = WriteStringToFd(sleep_state, state_fd);
+
+            void (*func)(bool success) = wakeup_func;
+            if (func != NULL) {
+                (*func)(success);
+            }
+        } else {
+            PLOG(ERROR) << "error writing to " << sys_power_wakeup_count;
+        }
+
+        LOG(VERBOSE) << "release sem";
+        ret = sem_post(&suspend_lockout);
+        if (ret < 0) {
+            PLOG(ERROR) << "error releasing semaphore";
+        }
+    }
+    return NULL;
+}
+
+static int init_state_fd(void) {
+    if (state_fd >= 0) {
+        return 0;
+    }
+
+    int fd = TEMP_FAILURE_RETRY(open(sys_power_state, O_CLOEXEC | O_RDWR));
+    if (fd < 0) {
+        PLOG(ERROR) << "error opening " << sys_power_state;
+        return -1;
+    }
+
+    state_fd = fd;
+    LOG(INFO) << "init_state_fd success";
+    return 0;
+}
+
+static int autosuspend_init(void) {
+    if (autosuspend_is_init) {
+        return 0;
+    }
+
+    int ret = init_state_fd();
+    if (ret < 0) {
+        return -1;
+    }
+
+    wakeup_count_fd = TEMP_FAILURE_RETRY(open(sys_power_wakeup_count, O_CLOEXEC | O_RDWR));
+    if (wakeup_count_fd < 0) {
+        PLOG(ERROR) << "error opening " << sys_power_wakeup_count;
+        goto err_open_wakeup_count;
+    }
+
+    ret = sem_init(&suspend_lockout, 0, 0);
+    if (ret < 0) {
+        PLOG(ERROR) << "error creating suspend_lockout semaphore";
+        goto err_sem_init;
+    }
+
+    ret = pthread_create(&suspend_thread, NULL, suspend_thread_func, NULL);
+    if (ret) {
+        LOG(ERROR) << "error creating thread: " << strerror(ret);
+        goto err_pthread_create;
+    }
+
+    LOG(VERBOSE) << "autosuspend_init success";
+    autosuspend_is_init = true;
+    return 0;
+
+err_pthread_create:
+    sem_destroy(&suspend_lockout);
+err_sem_init:
+    close(wakeup_count_fd);
+err_open_wakeup_count:
+    return -1;
+}
+
+static int autosuspend_wakeup_count_enable(void) {
+    LOG(VERBOSE) << "autosuspend_wakeup_count_enable";
+
+    int ret = autosuspend_init();
+    if (ret < 0) {
+        LOG(ERROR) << "autosuspend_init failed";
+        return ret;
+    }
+
+    ret = sem_post(&suspend_lockout);
+    if (ret < 0) {
+        PLOG(ERROR) << "error changing semaphore";
+    }
+
+    LOG(VERBOSE) << "autosuspend_wakeup_count_enable done";
+
+    return ret;
+}
+
+static int autosuspend_wakeup_count_disable(void) {
+    LOG(VERBOSE) << "autosuspend_wakeup_count_disable";
+
+    if (!autosuspend_is_init) {
+        return 0;  // always successful if no thread is running yet
+    }
+
+    int ret = sem_wait(&suspend_lockout);
+
+    if (ret < 0) {
+        PLOG(ERROR) << "error changing semaphore";
+    }
+
+    LOG(VERBOSE) << "autosuspend_wakeup_count_disable done";
+
+    return ret;
+}
+
+static int force_suspend(int timeout_ms) {
+    LOG(VERBOSE) << "force_suspend called with timeout: " << timeout_ms;
+
+    int ret = init_state_fd();
+    if (ret < 0) {
+        return ret;
+    }
+
+    return WriteStringToFd(sleep_state, state_fd) ? 0 : -1;
+}
+
+static void autosuspend_set_wakeup_callback(void (*func)(bool success)) {
+    if (wakeup_func != NULL) {
+        LOG(ERROR) << "duplicate wakeup callback applied, keeping original";
+        return;
+    }
+    wakeup_func = func;
+}
+
+struct autosuspend_ops autosuspend_wakeup_count_ops = {
+    .enable = autosuspend_wakeup_count_enable,
+    .disable = autosuspend_wakeup_count_disable,
+    .force_suspend = force_suspend,
+    .set_wakeup_callback = autosuspend_set_wakeup_callback,
+};
+
+struct autosuspend_ops* autosuspend_wakeup_count_init(void) {
+    return &autosuspend_wakeup_count_ops;
+}
diff --git a/libsuspend/include/suspend/autosuspend.h b/libsuspend/include/suspend/autosuspend.h
index 59188a8..21f4d61 100644
--- a/libsuspend/include/suspend/autosuspend.h
+++ b/libsuspend/include/suspend/autosuspend.h
@@ -45,13 +45,24 @@
 int autosuspend_disable(void);
 
 /*
+ * force_suspend
+ *
+ * Forces suspend to happen.  timeout_ms is used to give system a chance to suspend gracefully.
+ * When timeout expires, suspend will be forced via mem --> /sys/power/state.  timeout_ms of 0
+ * will force suspend immediately.
+ *
+ * Returns 0 if system suspended, -1 if suspend did not occur.
+ */
+int autosuspend_force_suspend(int timeout_ms);
+
+/*
  * set_wakeup_callback
  *
  * Set a function to be called each time the device returns from suspend.
  * success is true if the suspend was sucessful and false if the suspend
  * aborted due to some reason.
  */
-void set_wakeup_callback(void (*func)(bool success));
+void autosuspend_set_wakeup_callback(void (*func)(bool success));
 
 __END_DECLS
 
diff --git a/libsync/Android.bp b/libsync/Android.bp
index 4bafb08..c95563d 100644
--- a/libsync/Android.bp
+++ b/libsync/Android.bp
@@ -1,3 +1,17 @@
+ndk_headers {
+    name: "libsync_headers",
+    from: "include/ndk",
+    to: "android",
+    srcs: ["include/ndk/sync.h"],
+    license: "NOTICE",
+}
+
+ndk_library {
+    name: "libsync",
+    symbol_file: "libsync.map.txt",
+    first_version: "26",
+}
+
 cc_defaults {
     name: "libsync_defaults",
     srcs: ["sync.c"],
@@ -8,10 +22,15 @@
 
 cc_library_shared {
     name: "libsync",
-    vendor_available: true,
     defaults: ["libsync_defaults"],
 }
 
+llndk_library {
+    name: "libsync",
+    symbol_file: "libsync.map.txt",
+    export_include_dirs: ["include"],
+}
+
 // libsync_recovery is only intended for the recovery binary.
 // Future versions of the kernel WILL require an updated libsync, and will break
 // anything statically linked against the current libsync.
@@ -21,13 +40,6 @@
 }
 
 cc_test {
-    name: "sync_test",
-    defaults: ["libsync_defaults"],
-    gtest: false,
-    srcs: ["sync_test.c"],
-}
-
-cc_test {
     name: "sync-unit-tests",
     shared_libs: ["libsync"],
     srcs: ["tests/sync_test.cpp"],
@@ -38,5 +50,4 @@
         "-Wno-missing-field-initializers",
         "-Wno-sign-compare",
     ],
-    clang: true,
 }
diff --git a/libsync/NOTICE b/libsync/NOTICE
new file mode 100644
index 0000000..2c8db73
--- /dev/null
+++ b/libsync/NOTICE
@@ -0,0 +1,190 @@
+
+   Copyright (c) 2012-2017, 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.
+
+   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.
+
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
diff --git a/libsync/OWNERS b/libsync/OWNERS
new file mode 100644
index 0000000..dc61733
--- /dev/null
+++ b/libsync/OWNERS
@@ -0,0 +1,3 @@
+ghackmann@google.com
+jessehall@google.com
+marissaw@google.com
diff --git a/libsync/include/android/sync.h b/libsync/include/android/sync.h
new file mode 100644
index 0000000..32bb878
--- /dev/null
+++ b/libsync/include/android/sync.h
@@ -0,0 +1,49 @@
+/*
+ *  sync.h
+ *
+ *   Copyright 2012 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 __SYS_CORE_SYNC_H
+#define __SYS_CORE_SYNC_H
+
+/* This file contains the legacy sync interface used by Android platform and
+ * device code. The direct contents will be removed over time as code
+ * transitions to using the updated interface in ndk/sync.h. When this file is
+ * empty other than the ndk/sync.h include, that file will be renamed to
+ * replace this one.
+ *
+ * New code should continue to include this file (#include <android/sync.h>)
+ * instead of ndk/sync.h so the eventual rename is seamless, but should only
+ * use the things declared in ndk/sync.h.
+ *
+ * This file used to be called sync/sync.h, but we renamed to that both the
+ * platform and NDK call it android/sync.h. A symlink from the old name to this
+ * one exists temporarily to avoid having to change all sync clients
+ * simultaneously. It will be removed when they've been updated, and probably
+ * after this change has been delivered to AOSP so that integrations don't
+ * break builds.
+ */
+
+#include "../ndk/sync.h"
+
+__BEGIN_DECLS
+
+/* timeout in msecs */
+int sync_wait(int fd, int timeout);
+
+__END_DECLS
+
+#endif /* __SYS_CORE_SYNC_H */
diff --git a/libsync/include/ndk/sync.h b/libsync/include/ndk/sync.h
new file mode 100644
index 0000000..2a59e35
--- /dev/null
+++ b/libsync/include/ndk/sync.h
@@ -0,0 +1,110 @@
+/*
+ *  Copyright 2017 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.
+ */
+
+/**
+ * @addtogroup Sync
+ * @{
+ */
+
+/**
+ * @file sync.h
+ */
+
+#ifndef ANDROID_SYNC_H
+#define ANDROID_SYNC_H
+
+#include <stdint.h>
+#include <sys/cdefs.h>
+
+#include <linux/sync_file.h>
+
+__BEGIN_DECLS
+
+#if __ANDROID_API__ >= 26
+
+/* Fences indicate the status of an asynchronous task. They are initially
+ * in unsignaled state (0), and make a one-time transition to either signaled
+ * (1) or error (< 0) state. A sync file is a collection of one or more fences;
+ * the sync file's status is error if any of its fences are in error state,
+ * signaled if all of the child fences are signaled, or unsignaled otherwise.
+ *
+ * Sync files are created by various device APIs in response to submitting
+ * tasks to the device. Standard file descriptor lifetime syscalls like dup()
+ * and close() are used to manage sync file lifetime.
+ *
+ * The poll(), ppoll(), or select() syscalls can be used to wait for the sync
+ * file to change status, or (with a timeout of zero) to check its status.
+ *
+ * The functions below provide a few additional sync-specific operations.
+ */
+
+/**
+ * Merge two sync files.
+ *
+ * This produces a new sync file with the given name which has the union of the
+ * two original sync file's fences; redundant fences may be removed.
+ *
+ * If one of the input sync files is signaled or invalid, then this function
+ * may behave like dup(): the new file descriptor refers to the valid/unsignaled
+ * sync file with its original name, rather than a new sync file.
+ *
+ * The original fences remain valid, and the caller is responsible for closing
+ * them.
+ *
+ * Available since API level 26.
+ */
+int32_t sync_merge(const char* name, int32_t fd1, int32_t fd2) __INTRODUCED_IN(26);
+
+/**
+ * Retrieve detailed information about a sync file and its fences.
+ *
+ * The returned sync_file_info must be freed by calling sync_file_info_free().
+ *
+ * Available since API level 26.
+ */
+struct sync_file_info* sync_file_info(int32_t fd) __INTRODUCED_IN(26);
+
+/**
+ * Get the array of fence infos from the sync file's info.
+ *
+ * The returned array is owned by the parent sync file info, and has
+ * info->num_fences entries.
+ *
+ * Available since API level 26.
+ */
+static inline struct sync_fence_info* sync_get_fence_info(const struct sync_file_info* info) {
+// This header should compile in C, but some C++ projects enable
+// warnings-as-error for C-style casts.
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wold-style-cast"
+    return (struct sync_fence_info *)(uintptr_t)(info->sync_fence_info);
+#pragma GCC diagnostic pop
+}
+
+/**
+ * Free a struct sync_file_info structure
+ *
+ * Available since API level 26.
+ */
+void sync_file_info_free(struct sync_file_info* info) __INTRODUCED_IN(26);
+
+#endif /* __ANDROID_API__ >= 26 */
+
+__END_DECLS
+
+#endif /* ANDROID_SYNC_H */
+
+/** @} */
diff --git a/libsync/include/sync/sync.h b/libsync/include/sync/sync.h
deleted file mode 100644
index 50ed0ac..0000000
--- a/libsync/include/sync/sync.h
+++ /dev/null
@@ -1,163 +0,0 @@
-/*
- *  sync.h
- *
- *   Copyright 2012 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 __SYS_CORE_SYNC_H
-#define __SYS_CORE_SYNC_H
-
-#include <sys/cdefs.h>
-#include <stdint.h>
-
-#include <linux/types.h>
-
-__BEGIN_DECLS
-
-struct sync_legacy_merge_data {
- int32_t fd2;
- char name[32];
- int32_t fence;
-};
-
-struct sync_fence_info_data {
- uint32_t len;
- char name[32];
- int32_t status;
- uint8_t pt_info[0];
-};
-
-struct sync_pt_info {
- uint32_t len;
- char obj_name[32];
- char driver_name[32];
- int32_t status;
- uint64_t timestamp_ns;
- uint8_t driver_data[0];
-};
-
-#define SYNC_IOC_MAGIC		'>'
-
-/**
- * DOC: SYNC_IOC_LEGACY_WAIT - wait for a fence to signal
- *
- * pass timeout in milliseconds.  Waits indefinitely timeout < 0.
- *
- * This is the legacy version of the Sync API before the de-stage that happened
- * on Linux kernel 4.7.
- */
-#define SYNC_IOC_LEGACY_WAIT	_IOW(SYNC_IOC_MAGIC, 0, __s32)
-
-/**
- * DOC: SYNC_IOC_MERGE - merge two fences
- *
- * Takes a struct sync_merge_data.  Creates a new fence containing copies of
- * the sync_pts in both the calling fd and sync_merge_data.fd2.  Returns the
- * new fence's fd in sync_merge_data.fence
- *
- * This is the legacy version of the Sync API before the de-stage that happened
- * on Linux kernel 4.7.
- */
-#define SYNC_IOC_LEGACY_MERGE	_IOWR(SYNC_IOC_MAGIC, 1, \
-	struct sync_legacy_merge_data)
-
-/**
- * DOC: SYNC_IOC_LEGACY_FENCE_INFO - get detailed information on a fence
- *
- * Takes a struct sync_fence_info_data with extra space allocated for pt_info.
- * Caller should write the size of the buffer into len.  On return, len is
- * updated to reflect the total size of the sync_fence_info_data including
- * pt_info.
- *
- * pt_info is a buffer containing sync_pt_infos for every sync_pt in the fence.
- * To iterate over the sync_pt_infos, use the sync_pt_info.len field.
- *
- * This is the legacy version of the Sync API before the de-stage that happened
- * on Linux kernel 4.7.
- */
-#define SYNC_IOC_LEGACY_FENCE_INFO	_IOWR(SYNC_IOC_MAGIC, 2,\
-	struct sync_fence_info_data)
-
-struct sync_merge_data {
- char name[32];
- int32_t fd2;
- int32_t fence;
- uint32_t flags;
- uint32_t pad;
-};
-
-struct sync_file_info {
- char name[32];
- int32_t status;
- uint32_t flags;
- uint32_t num_fences;
- uint32_t pad;
-
- uint64_t sync_fence_info;
-};
-
-struct sync_fence_info {
- char obj_name[32];
- char driver_name[32];
- int32_t status;
- uint32_t flags;
- uint64_t timestamp_ns;
-};
-
-/**
- * Mainline API:
- *
- * Opcodes  0, 1 and 2 were burned during a API change to avoid users of the
- * old API to get weird errors when trying to handling sync_files. The API
- * change happened during the de-stage of the Sync Framework when there was
- * no upstream users available.
- */
-
-/**
- * DOC: SYNC_IOC_MERGE - merge two fences
- *
- * Takes a struct sync_merge_data.  Creates a new fence containing copies of
- * the sync_pts in both the calling fd and sync_merge_data.fd2.  Returns the
- * new fence's fd in sync_merge_data.fence
- *
- * This is the new version of the Sync API after the de-stage that happened
- * on Linux kernel 4.7.
- */
-#define SYNC_IOC_MERGE		_IOWR(SYNC_IOC_MAGIC, 3, struct sync_merge_data)
-
-/**
- * DOC: SYNC_IOC_FILE_INFO - get detailed information on a sync_file
- *
- * Takes a struct sync_file_info. If num_fences is 0, the field is updated
- * with the actual number of fences. If num_fences is > 0, the system will
- * use the pointer provided on sync_fence_info to return up to num_fences of
- * struct sync_fence_info, with detailed fence information.
- *
- * This is the new version of the Sync API after the de-stage that happened
- * on Linux kernel 4.7.
- */
-#define SYNC_IOC_FILE_INFO	_IOWR(SYNC_IOC_MAGIC, 4, struct sync_file_info)
-
-/* timeout in msecs */
-int sync_wait(int fd, int timeout);
-int sync_merge(const char *name, int fd1, int fd2);
-struct sync_fence_info_data *sync_fence_info(int fd);
-struct sync_pt_info *sync_pt_info(struct sync_fence_info_data *info,
-                                  struct sync_pt_info *itr);
-void sync_fence_info_free(struct sync_fence_info_data *info);
-
-__END_DECLS
-
-#endif /* __SYS_CORE_SYNC_H */
diff --git a/libsync/include/sync/sync.h b/libsync/include/sync/sync.h
new file mode 120000
index 0000000..3b17e48
--- /dev/null
+++ b/libsync/include/sync/sync.h
@@ -0,0 +1 @@
+../android/sync.h
\ No newline at end of file
diff --git a/libsync/libsync.map.txt b/libsync/libsync.map.txt
new file mode 100644
index 0000000..53bb07a
--- /dev/null
+++ b/libsync/libsync.map.txt
@@ -0,0 +1,28 @@
+#
+# Copyright 2017 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.
+#
+
+LIBSYNC {
+  global:
+    sync_merge; # introduced=26
+    sync_file_info; # introduced=26
+    sync_file_info_free; # introduced=26
+    sync_wait; # vndk
+    sync_fence_info; # vndk
+    sync_pt_info; # vndk
+    sync_fence_info_free; # vndk
+  local:
+    *;
+};
diff --git a/libsync/sync.c b/libsync/sync.c
index 9ed03db..b8c48c7 100644
--- a/libsync/sync.c
+++ b/libsync/sync.c
@@ -16,19 +16,82 @@
  *  limitations under the License.
  */
 
+#include <errno.h>
 #include <fcntl.h>
 #include <malloc.h>
+#include <poll.h>
+#include <stdatomic.h>
 #include <stdint.h>
 #include <string.h>
-#include <errno.h>
-#include <poll.h>
 
 #include <sys/ioctl.h>
 #include <sys/stat.h>
 #include <sys/types.h>
 
-#include <sync/sync.h>
+#include <android/sync.h>
 
+/* Prototypes for deprecated functions that used to be declared in the legacy
+ * android/sync.h. They've been moved here to make sure new code does not use
+ * them, but the functions are still defined to avoid breaking existing
+ * binaries. Eventually they can be removed altogether.
+ */
+struct sync_fence_info_data {
+    uint32_t len;
+    char name[32];
+    int32_t status;
+    uint8_t pt_info[0];
+};
+struct sync_pt_info {
+    uint32_t len;
+    char obj_name[32];
+    char driver_name[32];
+    int32_t status;
+    uint64_t timestamp_ns;
+    uint8_t driver_data[0];
+};
+struct sync_fence_info_data* sync_fence_info(int fd);
+struct sync_pt_info* sync_pt_info(struct sync_fence_info_data* info, struct sync_pt_info* itr);
+void sync_fence_info_free(struct sync_fence_info_data* info);
+
+/* Legacy Sync API */
+
+struct sync_legacy_merge_data {
+ int32_t fd2;
+ char name[32];
+ int32_t fence;
+};
+
+/**
+ * DOC: SYNC_IOC_MERGE - merge two fences
+ *
+ * Takes a struct sync_merge_data.  Creates a new fence containing copies of
+ * the sync_pts in both the calling fd and sync_merge_data.fd2.  Returns the
+ * new fence's fd in sync_merge_data.fence
+ *
+ * This is the legacy version of the Sync API before the de-stage that happened
+ * on Linux kernel 4.7.
+ */
+#define SYNC_IOC_LEGACY_MERGE   _IOWR(SYNC_IOC_MAGIC, 1, \
+    struct sync_legacy_merge_data)
+
+/**
+ * DOC: SYNC_IOC_LEGACY_FENCE_INFO - get detailed information on a fence
+ *
+ * Takes a struct sync_fence_info_data with extra space allocated for pt_info.
+ * Caller should write the size of the buffer into len.  On return, len is
+ * updated to reflect the total size of the sync_fence_info_data including
+ * pt_info.
+ *
+ * pt_info is a buffer containing sync_pt_infos for every sync_pt in the fence.
+ * To iterate over the sync_pt_infos, use the sync_pt_info.len field.
+ *
+ * This is the legacy version of the Sync API before the de-stage that happened
+ * on Linux kernel 4.7.
+ */
+#define SYNC_IOC_LEGACY_FENCE_INFO  _IOWR(SYNC_IOC_MAGIC, 2,\
+    struct sync_fence_info_data)
+
+/* SW Sync API */
 
 struct sw_sync_create_fence_data {
   __u32 value;
@@ -40,6 +103,24 @@
 #define SW_SYNC_IOC_CREATE_FENCE _IOWR(SW_SYNC_IOC_MAGIC, 0, struct sw_sync_create_fence_data)
 #define SW_SYNC_IOC_INC _IOW(SW_SYNC_IOC_MAGIC, 1, __u32)
 
+// ---------------------------------------------------------------------------
+// Support for caching the sync uapi version.
+//
+// This library supports both legacy (android/staging) uapi and modern
+// (mainline) sync uapi. Library calls first try one uapi, and if that fails,
+// try the other. Since any given kernel only supports one uapi version, after
+// the first successful syscall we know what the kernel supports and can skip
+// trying the other.
+
+enum uapi_version {
+    UAPI_UNKNOWN,
+    UAPI_MODERN,
+    UAPI_LEGACY
+};
+static atomic_int g_uapi_version = ATOMIC_VAR_INIT(UAPI_UNKNOWN);
+
+// ---------------------------------------------------------------------------
+
 int sync_wait(int fd, int timeout)
 {
     struct pollfd fds;
@@ -70,9 +151,21 @@
     return ret;
 }
 
-int sync_merge(const char *name, int fd1, int fd2)
+static int legacy_sync_merge(const char *name, int fd1, int fd2)
 {
-    struct sync_legacy_merge_data legacy_data;
+    struct sync_legacy_merge_data data;
+    int ret;
+
+    data.fd2 = fd2;
+    strlcpy(data.name, name, sizeof(data.name));
+    ret = ioctl(fd1, SYNC_IOC_LEGACY_MERGE, &data);
+    if (ret < 0)
+        return ret;
+    return data.fence;
+}
+
+static int modern_sync_merge(const char *name, int fd1, int fd2)
+{
     struct sync_merge_data data;
     int ret;
 
@@ -82,29 +175,42 @@
     data.pad = 0;
 
     ret = ioctl(fd1, SYNC_IOC_MERGE, &data);
-    if (ret < 0 && errno == ENOTTY) {
-        legacy_data.fd2 = fd2;
-        strlcpy(legacy_data.name, name, sizeof(legacy_data.name));
-
-        ret = ioctl(fd1, SYNC_IOC_LEGACY_MERGE, &legacy_data);
-        if (ret < 0)
-            return ret;
-
-        return legacy_data.fence;
-    } else if (ret < 0) {
+    if (ret < 0)
         return ret;
-    }
-
     return data.fence;
 }
 
-struct sync_fence_info_data *sync_fence_info(int fd)
+int sync_merge(const char *name, int fd1, int fd2)
+{
+    int uapi;
+    int ret;
+
+    uapi = atomic_load_explicit(&g_uapi_version, memory_order_acquire);
+
+    if (uapi == UAPI_MODERN || uapi == UAPI_UNKNOWN) {
+        ret = modern_sync_merge(name, fd1, fd2);
+        if (ret >= 0 || errno != ENOTTY) {
+            if (ret >= 0 && uapi == UAPI_UNKNOWN) {
+                atomic_store_explicit(&g_uapi_version, UAPI_MODERN,
+                                      memory_order_release);
+            }
+            return ret;
+        }
+    }
+
+    ret = legacy_sync_merge(name, fd1, fd2);
+    if (ret >= 0 && uapi == UAPI_UNKNOWN) {
+        atomic_store_explicit(&g_uapi_version, UAPI_LEGACY,
+                              memory_order_release);
+    }
+    return ret;
+}
+
+static struct sync_fence_info_data *legacy_sync_fence_info(int fd)
 {
     struct sync_fence_info_data *legacy_info;
     struct sync_pt_info *legacy_pt_info;
-    struct sync_file_info *info;
-    struct sync_fence_info *fence_info;
-    int err, num_fences, i;
+    int err;
 
     legacy_info = malloc(4096);
     if (legacy_info == NULL)
@@ -112,46 +218,59 @@
 
     legacy_info->len = 4096;
     err = ioctl(fd, SYNC_IOC_LEGACY_FENCE_INFO, legacy_info);
-    if (err < 0 && errno != ENOTTY) {
+    if (err < 0) {
         free(legacy_info);
         return NULL;
-    } else if (err == 0) {
-        return legacy_info;
     }
+    return legacy_info;
+}
 
-    info = calloc(1, sizeof(*info));
-    if (info == NULL)
-        goto free;
+static struct sync_file_info *modern_sync_file_info(int fd)
+{
+    struct sync_file_info local_info;
+    struct sync_file_info *info;
+    int err;
+
+    memset(&local_info, 0, sizeof(local_info));
+    err = ioctl(fd, SYNC_IOC_FILE_INFO, &local_info);
+    if (err < 0)
+        return NULL;
+
+    info = calloc(1, sizeof(struct sync_file_info) +
+                  local_info.num_fences * sizeof(struct sync_fence_info));
+    if (!info)
+        return NULL;
+
+    info->num_fences = local_info.num_fences;
+    info->sync_fence_info = (__u64)(uintptr_t)(info + 1);
 
     err = ioctl(fd, SYNC_IOC_FILE_INFO, info);
-    if (err < 0)
-        goto free;
-
-    num_fences = info->num_fences;
-
-    if (num_fences) {
-        info->flags = 0;
-        info->num_fences = num_fences;
-        info->sync_fence_info = (uint64_t) calloc(num_fences,
-                                        sizeof(struct sync_fence_info));
-        if ((void *)info->sync_fence_info == NULL)
-            goto free;
-
-        err = ioctl(fd, SYNC_IOC_FILE_INFO, info);
-        if (err < 0) {
-            free((void *)info->sync_fence_info);
-            goto free;
-        }
+    if (err < 0) {
+        free(info);
+        return NULL;
     }
 
+    return info;
+}
+
+static struct sync_fence_info_data *sync_file_info_to_legacy_fence_info(
+    const struct sync_file_info *info)
+{
+    struct sync_fence_info_data *legacy_info;
+    struct sync_pt_info *legacy_pt_info;
+    const struct sync_fence_info *fence_info = sync_get_fence_info(info);
+    const uint32_t num_fences = info->num_fences;
+
+    legacy_info = malloc(4096);
+    if (legacy_info == NULL)
+        return NULL;
     legacy_info->len = sizeof(*legacy_info) +
-                        num_fences * sizeof(struct sync_fence_info);
+                        num_fences * sizeof(struct sync_pt_info);
     strlcpy(legacy_info->name, info->name, sizeof(legacy_info->name));
     legacy_info->status = info->status;
 
     legacy_pt_info = (struct sync_pt_info *)legacy_info->pt_info;
-    fence_info = (struct sync_fence_info *)info->sync_fence_info;
-    for (i = 0 ; i < num_fences ; i++) {
+    for (uint32_t i = 0; i < num_fences; i++) {
         legacy_pt_info[i].len = sizeof(*legacy_pt_info);
         strlcpy(legacy_pt_info[i].obj_name, fence_info[i].obj_name,
                 sizeof(legacy_pt_info->obj_name));
@@ -161,14 +280,108 @@
         legacy_pt_info[i].timestamp_ns = fence_info[i].timestamp_ns;
     }
 
-    free((void *)info->sync_fence_info);
-    free(info);
     return legacy_info;
+}
 
-free:
-    free(legacy_info);
-    free(info);
-    return NULL;
+static struct sync_file_info* legacy_fence_info_to_sync_file_info(
+                                    struct sync_fence_info_data *legacy_info)
+{
+    struct sync_file_info *info;
+    struct sync_pt_info *pt;
+    struct sync_fence_info *fence;
+    size_t num_fences;
+    int err;
+
+    pt = NULL;
+    num_fences = 0;
+    while ((pt = sync_pt_info(legacy_info, pt)) != NULL)
+        num_fences++;
+
+    info = calloc(1, sizeof(struct sync_file_info) +
+                     num_fences * sizeof(struct sync_fence_info));
+    if (!info) {
+        return NULL;
+    }
+    info->sync_fence_info = (__u64)(uintptr_t)(info + 1);
+
+    strlcpy(info->name, legacy_info->name, sizeof(info->name));
+    info->status = legacy_info->status;
+    info->num_fences = num_fences;
+
+    pt = NULL;
+    fence = sync_get_fence_info(info);
+    while ((pt = sync_pt_info(legacy_info, pt)) != NULL) {
+        strlcpy(fence->obj_name, pt->obj_name, sizeof(fence->obj_name));
+        strlcpy(fence->driver_name, pt->driver_name,
+                sizeof(fence->driver_name));
+        fence->status = pt->status;
+        fence->timestamp_ns = pt->timestamp_ns;
+        fence++;
+    }
+
+    return info;
+}
+
+struct sync_fence_info_data *sync_fence_info(int fd)
+{
+    struct sync_fence_info_data *legacy_info;
+    int uapi;
+
+    uapi = atomic_load_explicit(&g_uapi_version, memory_order_acquire);
+
+    if (uapi == UAPI_LEGACY || uapi == UAPI_UNKNOWN) {
+        legacy_info = legacy_sync_fence_info(fd);
+        if (legacy_info || errno != ENOTTY) {
+            if (legacy_info && uapi == UAPI_UNKNOWN) {
+                atomic_store_explicit(&g_uapi_version, UAPI_LEGACY,
+                                      memory_order_release);
+            }
+            return legacy_info;
+        }
+    }
+
+    struct sync_file_info* file_info;
+    file_info = modern_sync_file_info(fd);
+    if (!file_info)
+        return NULL;
+    if (uapi == UAPI_UNKNOWN) {
+        atomic_store_explicit(&g_uapi_version, UAPI_MODERN,
+                              memory_order_release);
+    }
+    legacy_info = sync_file_info_to_legacy_fence_info(file_info);
+    sync_file_info_free(file_info);
+    return legacy_info;
+}
+
+struct sync_file_info* sync_file_info(int32_t fd)
+{
+    struct sync_file_info *info;
+    int uapi;
+
+    uapi = atomic_load_explicit(&g_uapi_version, memory_order_acquire);
+
+    if (uapi == UAPI_MODERN || uapi == UAPI_UNKNOWN) {
+        info = modern_sync_file_info(fd);
+        if (info || errno != ENOTTY) {
+            if (info && uapi == UAPI_UNKNOWN) {
+                atomic_store_explicit(&g_uapi_version, UAPI_MODERN,
+                                      memory_order_release);
+            }
+            return info;
+        }
+    }
+
+    struct sync_fence_info_data *legacy_info;
+    legacy_info = legacy_sync_fence_info(fd);
+    if (!legacy_info)
+        return NULL;
+    if (uapi == UAPI_UNKNOWN) {
+        atomic_store_explicit(&g_uapi_version, UAPI_LEGACY,
+                              memory_order_release);
+    }
+    info = legacy_fence_info_to_sync_file_info(legacy_info);
+    sync_fence_info_free(legacy_info);
+    return info;
 }
 
 struct sync_pt_info *sync_pt_info(struct sync_fence_info_data *info,
@@ -190,6 +403,11 @@
     free(info);
 }
 
+void sync_file_info_free(struct sync_file_info *info)
+{
+    free(info);
+}
+
 
 int sw_sync_timeline_create(void)
 {
diff --git a/libsync/sync_test.c b/libsync/sync_test.c
deleted file mode 100644
index 9a5f7d8..0000000
--- a/libsync/sync_test.c
+++ /dev/null
@@ -1,147 +0,0 @@
-/*
- *  sync_test.c
- *
- *   Copyright 2012 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 <pthread.h>
-#include <stdio.h>
-#include <string.h>
-#include <unistd.h>
-
-#include <sync/sync.h>
-#include "sw_sync.h"
-
-pthread_mutex_t printf_mutex = PTHREAD_MUTEX_INITIALIZER;
-
-struct sync_thread_data {
-    int thread_no;
-    int fd[2];
-};
-
-void *sync_thread(void *data)
-{
-    struct sync_thread_data *sync_data = data;
-    struct sync_fence_info_data *info;
-    int err;
-    int i;
-
-    for (i = 0; i < 2; i++) {
-        err = sync_wait(sync_data->fd[i], 10000);
-
-        pthread_mutex_lock(&printf_mutex);
-        if (err < 0) {
-            printf("thread %d wait %d failed: %s\n", sync_data->thread_no,
-                   i, strerror(errno));
-        } else {
-            printf("thread %d wait %d done\n", sync_data->thread_no, i);
-        }
-        info = sync_fence_info(sync_data->fd[i]);
-        if (info) {
-            struct sync_pt_info *pt_info = NULL;
-            printf("  fence %s %d\n", info->name, info->status);
-
-            while ((pt_info = sync_pt_info(info, pt_info))) {
-                int ts_sec = pt_info->timestamp_ns / 1000000000LL;
-                int ts_usec = (pt_info->timestamp_ns % 1000000000LL) / 1000LL;
-                printf("    pt %s %s %d %d.%06d", pt_info->obj_name,
-                       pt_info->driver_name, pt_info->status,
-                       ts_sec, ts_usec);
-                if (!strcmp(pt_info->driver_name, "sw_sync"))
-                    printf(" val=%d\n", *(uint32_t *)pt_info->driver_data);
-                else
-                    printf("\n");
-            }
-            sync_fence_info_free(info);
-        }
-        pthread_mutex_unlock(&printf_mutex);
-    }
-
-    return NULL;
-}
-
-int main(int argc __attribute__((unused)), char *argv[] __attribute__((unused)))
-{
-    struct sync_thread_data sync_data[4];
-    pthread_t threads[4];
-    int sync_timeline_fd;
-    int i, j;
-    char str[256];
-
-    sync_timeline_fd = sw_sync_timeline_create();
-    if (sync_timeline_fd < 0) {
-        perror("can't create sw_sync_timeline:");
-        return 1;
-    }
-
-    for (i = 0; i < 3; i++) {
-        sync_data[i].thread_no = i;
-
-        for (j = 0; j < 2; j++) {
-            unsigned val = i + j * 3 + 1;
-            snprintf(str, sizeof(str), "test_fence%d-%d", i, j);
-            int fd = sw_sync_fence_create(sync_timeline_fd, str, val);
-            if (fd < 0) {
-                printf("can't create sync pt %d: %s", val, strerror(errno));
-                return 1;
-            }
-            sync_data[i].fd[j] = fd;
-            printf("sync_data[%d].fd[%d] = %d;\n", i, j, fd);
-
-        }
-    }
-
-    sync_data[3].thread_no = 3;
-    for (j = 0; j < 2; j++) {
-        snprintf(str, sizeof(str), "merged_fence%d", j);
-        sync_data[3].fd[j] = sync_merge(str, sync_data[0].fd[j], sync_data[1].fd[j]);
-        if (sync_data[3].fd[j] < 0) {
-            printf("can't merge sync pts %d and %d: %s\n",
-                   sync_data[0].fd[j], sync_data[1].fd[j], strerror(errno));
-            return 1;
-        }
-    }
-
-    for (i = 0; i < 4; i++)
-        pthread_create(&threads[i], NULL, sync_thread, &sync_data[i]);
-
-
-    for (i = 0; i < 3; i++) {
-        int err;
-        printf("press enter to inc to %d\n", i+1);
-        fgets(str, sizeof(str), stdin);
-        err = sw_sync_timeline_inc(sync_timeline_fd, 1);
-        if (err < 0) {
-            perror("can't increment sync obj:");
-            return 1;
-        }
-    }
-
-    printf("press enter to close sync_timeline\n");
-    fgets(str, sizeof(str), stdin);
-
-    close(sync_timeline_fd);
-
-    printf("press enter to end test\n");
-    fgets(str, sizeof(str), stdin);
-
-    for (i = 0; i < 3; i++) {
-        void *val;
-        pthread_join(threads[i], &val);
-    }
-
-    return 0;
-}
diff --git a/libsync/tests/sync_test.cpp b/libsync/tests/sync_test.cpp
index 401aaee..011b09d 100644
--- a/libsync/tests/sync_test.cpp
+++ b/libsync/tests/sync_test.cpp
@@ -1,5 +1,5 @@
 #include <gtest/gtest.h>
-#include <sync/sync.h>
+#include <android/sync.h>
 #include <sw_sync.h>
 #include <fcntl.h>
 #include <vector>
@@ -15,6 +15,35 @@
 #include <random>
 #include <unordered_map>
 
+/* These deprecated declarations were in the legacy android/sync.h. They've been removed to
+ * encourage code to move to the modern equivalents. But they are still implemented in libsync.so
+ * to avoid breaking existing binaries; as long as that's true we should keep testing them here.
+ * That means making local copies of the declarations.
+ */
+extern "C" {
+
+struct sync_fence_info_data {
+    uint32_t len;
+    char name[32];
+    int32_t status;
+    uint8_t pt_info[0];
+};
+
+struct sync_pt_info {
+    uint32_t len;
+    char obj_name[32];
+    char driver_name[32];
+    int32_t status;
+    uint64_t timestamp_ns;
+    uint8_t driver_data[0];
+};
+
+struct sync_fence_info_data* sync_fence_info(int fd);
+struct sync_pt_info* sync_pt_info(struct sync_fence_info_data* info, struct sync_pt_info* itr);
+void sync_fence_info_free(struct sync_fence_info_data* info);
+
+}  // extern "C"
+
 // TODO: better stress tests?
 // Handle more than 64 fd's simultaneously, i.e. fix sync_fence_info's 4k limit.
 // Handle wraparound in timelines like nvidia.
@@ -172,20 +201,20 @@
         return sync_wait(m_fd, timeout);
     }
     vector<SyncPointInfo> getInfo() const {
-        struct sync_pt_info *pointInfo = nullptr;
         vector<SyncPointInfo> fenceInfo;
-        sync_fence_info_data *info = sync_fence_info(getFd());
+        struct sync_file_info *info = sync_file_info(getFd());
         if (!info) {
             return fenceInfo;
         }
-        while ((pointInfo = sync_pt_info(info, pointInfo))) {
+        const auto fences = sync_get_fence_info(info);
+        for (uint32_t i = 0; i < info->num_fences; i++) {
             fenceInfo.push_back(SyncPointInfo{
-                pointInfo->driver_name,
-                pointInfo->obj_name,
-                pointInfo->timestamp_ns,
-                pointInfo->status});
+                fences[i].driver_name,
+                fences[i].obj_name,
+                fences[i].timestamp_ns,
+                fences[i].status});
         }
-        sync_fence_info_free(info);
+        sync_file_info_free(info);
         return fenceInfo;
     }
     int getSize() const {
@@ -212,6 +241,32 @@
     }
 };
 
+static void CheckModernLegacyInfoMatch(const SyncFence& f) {
+    struct sync_file_info* modern = sync_file_info(f.getFd());
+    struct sync_fence_info_data* legacy = sync_fence_info(f.getFd());
+
+    ASSERT_TRUE(modern != NULL);
+    ASSERT_TRUE(legacy != NULL);
+
+    EXPECT_STREQ(modern->name, legacy->name);
+    EXPECT_EQ(modern->status, legacy->status);
+
+    uint32_t fenceIdx = 0;
+    struct sync_pt_info* pt = sync_pt_info(legacy, NULL);
+    const struct sync_fence_info* fences = sync_get_fence_info(modern);
+    while (fenceIdx < modern->num_fences && pt != NULL) {
+        EXPECT_STREQ(fences[fenceIdx].obj_name, pt->obj_name);
+        EXPECT_STREQ(fences[fenceIdx].driver_name, pt->driver_name);
+        EXPECT_EQ(fences[fenceIdx].status, pt->status);
+        EXPECT_EQ(fences[fenceIdx].timestamp_ns, pt->timestamp_ns);
+
+        fenceIdx++;
+        pt = sync_pt_info(legacy, pt);
+    }
+    EXPECT_EQ(fenceIdx, modern->num_fences);
+    EXPECT_EQ(NULL, pt);
+}
+
 int SyncFence::s_fenceCount = 0;
 
 TEST(AllocTest, Timeline) {
@@ -225,6 +280,7 @@
 
     SyncFence fence(timeline, 1);
     ASSERT_TRUE(fence.isValid());
+    CheckModernLegacyInfoMatch(fence);
 }
 
 TEST(AllocTest, FenceNegative) {
@@ -321,15 +377,21 @@
     timeline.inc(1);
     ASSERT_EQ(a.getSignaledCount(), 1);
     ASSERT_EQ(d.getActiveCount(), 1);
+    CheckModernLegacyInfoMatch(a);
+    CheckModernLegacyInfoMatch(d);
 
     timeline.inc(1);
     ASSERT_EQ(b.getSignaledCount(), 1);
     ASSERT_EQ(d.getActiveCount(), 1);
+    CheckModernLegacyInfoMatch(b);
+    CheckModernLegacyInfoMatch(d);
 
     timeline.inc(1);
     ASSERT_EQ(c.getSignaledCount(), 1);
     ASSERT_EQ(d.getActiveCount(), 0);
     ASSERT_EQ(d.getSignaledCount(), 1);
+    CheckModernLegacyInfoMatch(c);
+    CheckModernLegacyInfoMatch(d);
 }
 
 TEST(FenceTest, MergeSameFence) {
@@ -343,9 +405,11 @@
     ASSERT_TRUE(selfMergeFence.isValid());
 
     ASSERT_EQ(selfMergeFence.getSignaledCount(), 0);
+    CheckModernLegacyInfoMatch(selfMergeFence);
 
     timeline.inc(5);
     ASSERT_EQ(selfMergeFence.getSignaledCount(), 1);
+    CheckModernLegacyInfoMatch(selfMergeFence);
 }
 
 TEST(FenceTest, PollOnDestroyedTimeline) {
@@ -397,19 +461,57 @@
     timelineA.inc(5);
     ASSERT_EQ(mergedFence.getActiveCount(), 2);
     ASSERT_EQ(mergedFence.getSignaledCount(), 1);
+    CheckModernLegacyInfoMatch(mergedFence);
 
     timelineB.inc(5);
     ASSERT_EQ(mergedFence.getActiveCount(), 1);
     ASSERT_EQ(mergedFence.getSignaledCount(), 2);
+    CheckModernLegacyInfoMatch(mergedFence);
 
     timelineC.inc(5);
     ASSERT_EQ(mergedFence.getActiveCount(), 0);
     ASSERT_EQ(mergedFence.getSignaledCount(), 3);
+    CheckModernLegacyInfoMatch(mergedFence);
 
     // confirm you can successfully wait.
     ASSERT_EQ(mergedFence.wait(100), 0);
 }
 
+TEST(FenceTest, GetInfoActive) {
+    SyncTimeline timeline;
+    ASSERT_TRUE(timeline.isValid());
+
+    SyncFence fence(timeline, 1);
+    ASSERT_TRUE(fence.isValid());
+
+    vector<SyncPointInfo> info = fence.getInfo();
+    ASSERT_EQ(info.size(), 1);
+
+    ASSERT_FALSE(info[0].driverName.empty());
+    ASSERT_FALSE(info[0].objectName.empty());
+    ASSERT_EQ(info[0].timeStampNs, 0);
+    ASSERT_EQ(info[0].status, 0);
+}
+
+TEST(FenceTest, GetInfoSignaled) {
+    SyncTimeline timeline;
+    ASSERT_TRUE(timeline.isValid());
+
+    SyncFence fence(timeline, 1);
+    ASSERT_TRUE(fence.isValid());
+
+    ASSERT_EQ(timeline.inc(1), 0);
+    ASSERT_EQ(fence.wait(), 0);
+
+    vector<SyncPointInfo> info = fence.getInfo();
+    ASSERT_EQ(info.size(), 1);
+
+    ASSERT_FALSE(info[0].driverName.empty());
+    ASSERT_FALSE(info[0].objectName.empty());
+    ASSERT_GT(info[0].timeStampNs, 0);
+    ASSERT_EQ(info[0].status, 1);
+}
+
 TEST(StressTest, TwoThreadsSharedTimeline) {
     const int iterations = 1 << 16;
     int counter = 0;
@@ -560,6 +662,7 @@
         // Merge.
         fence = SyncFence(fence, SyncFence(timeline, syncPoint));
         ASSERT_TRUE(fence.isValid());
+        CheckModernLegacyInfoMatch(fence);
     }
 
     // Confirm our map matches the fence.
diff --git a/libsystem/Android.bp b/libsystem/Android.bp
index 846a585..2e22b43 100644
--- a/libsystem/Android.bp
+++ b/libsystem/Android.bp
@@ -1,6 +1,7 @@
 cc_library_headers {
     name: "libsystem_headers",
     vendor_available: true,
+    recovery_available: true,
     host_supported: true,
     export_include_dirs: ["include"],
 
@@ -11,5 +12,5 @@
         windows: {
             enabled: true,
         },
-    }
+    },
 }
diff --git a/libsystem/OWNERS b/libsystem/OWNERS
new file mode 100644
index 0000000..aeb160c
--- /dev/null
+++ b/libsystem/OWNERS
@@ -0,0 +1,2 @@
+jessehall@google.com
+olv@google.com
diff --git a/libsystem/include/system/graphics-base.h b/libsystem/include/system/graphics-base.h
new file mode 100644
index 0000000..2a44faf
--- /dev/null
+++ b/libsystem/include/system/graphics-base.h
@@ -0,0 +1,141 @@
+// This file is autogenerated by hidl-gen. Do not edit manually.
+// Source: android.hardware.graphics.common@1.0
+// Root: android.hardware:hardware/interfaces
+
+#ifndef HIDL_GENERATED_ANDROID_HARDWARE_GRAPHICS_COMMON_V1_0_EXPORTED_CONSTANTS_H_
+#define HIDL_GENERATED_ANDROID_HARDWARE_GRAPHICS_COMMON_V1_0_EXPORTED_CONSTANTS_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef enum {
+    HAL_PIXEL_FORMAT_RGBA_8888 = 1,
+    HAL_PIXEL_FORMAT_RGBX_8888 = 2,
+    HAL_PIXEL_FORMAT_RGB_888 = 3,
+    HAL_PIXEL_FORMAT_RGB_565 = 4,
+    HAL_PIXEL_FORMAT_BGRA_8888 = 5,
+    HAL_PIXEL_FORMAT_RGBA_1010102 = 43, // 0x2B
+    HAL_PIXEL_FORMAT_RGBA_FP16 = 22, // 0x16
+    HAL_PIXEL_FORMAT_YV12 = 842094169, // 0x32315659
+    HAL_PIXEL_FORMAT_Y8 = 538982489, // 0x20203859
+    HAL_PIXEL_FORMAT_Y16 = 540422489, // 0x20363159
+    HAL_PIXEL_FORMAT_RAW16 = 32, // 0x20
+    HAL_PIXEL_FORMAT_RAW10 = 37, // 0x25
+    HAL_PIXEL_FORMAT_RAW12 = 38, // 0x26
+    HAL_PIXEL_FORMAT_RAW_OPAQUE = 36, // 0x24
+    HAL_PIXEL_FORMAT_BLOB = 33, // 0x21
+    HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED = 34, // 0x22
+    HAL_PIXEL_FORMAT_YCBCR_420_888 = 35, // 0x23
+    HAL_PIXEL_FORMAT_YCBCR_422_888 = 39, // 0x27
+    HAL_PIXEL_FORMAT_YCBCR_444_888 = 40, // 0x28
+    HAL_PIXEL_FORMAT_FLEX_RGB_888 = 41, // 0x29
+    HAL_PIXEL_FORMAT_FLEX_RGBA_8888 = 42, // 0x2A
+    HAL_PIXEL_FORMAT_YCBCR_422_SP = 16, // 0x10
+    HAL_PIXEL_FORMAT_YCRCB_420_SP = 17, // 0x11
+    HAL_PIXEL_FORMAT_YCBCR_422_I = 20, // 0x14
+    HAL_PIXEL_FORMAT_JPEG = 256, // 0x100
+} android_pixel_format_t;
+
+typedef enum {
+    HAL_TRANSFORM_FLIP_H = 1, // 0x01
+    HAL_TRANSFORM_FLIP_V = 2, // 0x02
+    HAL_TRANSFORM_ROT_90 = 4, // 0x04
+    HAL_TRANSFORM_ROT_180 = 3, // 0x03
+    HAL_TRANSFORM_ROT_270 = 7, // 0x07
+} android_transform_t;
+
+typedef enum {
+    HAL_DATASPACE_UNKNOWN = 0, // 0x0
+    HAL_DATASPACE_ARBITRARY = 1, // 0x1
+    HAL_DATASPACE_STANDARD_SHIFT = 16,
+    HAL_DATASPACE_STANDARD_MASK = 4128768, // (63 << STANDARD_SHIFT)
+    HAL_DATASPACE_STANDARD_UNSPECIFIED = 0, // (0 << STANDARD_SHIFT)
+    HAL_DATASPACE_STANDARD_BT709 = 65536, // (1 << STANDARD_SHIFT)
+    HAL_DATASPACE_STANDARD_BT601_625 = 131072, // (2 << STANDARD_SHIFT)
+    HAL_DATASPACE_STANDARD_BT601_625_UNADJUSTED = 196608, // (3 << STANDARD_SHIFT)
+    HAL_DATASPACE_STANDARD_BT601_525 = 262144, // (4 << STANDARD_SHIFT)
+    HAL_DATASPACE_STANDARD_BT601_525_UNADJUSTED = 327680, // (5 << STANDARD_SHIFT)
+    HAL_DATASPACE_STANDARD_BT2020 = 393216, // (6 << STANDARD_SHIFT)
+    HAL_DATASPACE_STANDARD_BT2020_CONSTANT_LUMINANCE = 458752, // (7 << STANDARD_SHIFT)
+    HAL_DATASPACE_STANDARD_BT470M = 524288, // (8 << STANDARD_SHIFT)
+    HAL_DATASPACE_STANDARD_FILM = 589824, // (9 << STANDARD_SHIFT)
+    HAL_DATASPACE_STANDARD_DCI_P3 = 655360, // (10 << STANDARD_SHIFT)
+    HAL_DATASPACE_STANDARD_ADOBE_RGB = 720896, // (11 << STANDARD_SHIFT)
+    HAL_DATASPACE_TRANSFER_SHIFT = 22,
+    HAL_DATASPACE_TRANSFER_MASK = 130023424, // (31 << TRANSFER_SHIFT)
+    HAL_DATASPACE_TRANSFER_UNSPECIFIED = 0, // (0 << TRANSFER_SHIFT)
+    HAL_DATASPACE_TRANSFER_LINEAR = 4194304, // (1 << TRANSFER_SHIFT)
+    HAL_DATASPACE_TRANSFER_SRGB = 8388608, // (2 << TRANSFER_SHIFT)
+    HAL_DATASPACE_TRANSFER_SMPTE_170M = 12582912, // (3 << TRANSFER_SHIFT)
+    HAL_DATASPACE_TRANSFER_GAMMA2_2 = 16777216, // (4 << TRANSFER_SHIFT)
+    HAL_DATASPACE_TRANSFER_GAMMA2_6 = 20971520, // (5 << TRANSFER_SHIFT)
+    HAL_DATASPACE_TRANSFER_GAMMA2_8 = 25165824, // (6 << TRANSFER_SHIFT)
+    HAL_DATASPACE_TRANSFER_ST2084 = 29360128, // (7 << TRANSFER_SHIFT)
+    HAL_DATASPACE_TRANSFER_HLG = 33554432, // (8 << TRANSFER_SHIFT)
+    HAL_DATASPACE_RANGE_SHIFT = 27,
+    HAL_DATASPACE_RANGE_MASK = 939524096, // (7 << RANGE_SHIFT)
+    HAL_DATASPACE_RANGE_UNSPECIFIED = 0, // (0 << RANGE_SHIFT)
+    HAL_DATASPACE_RANGE_FULL = 134217728, // (1 << RANGE_SHIFT)
+    HAL_DATASPACE_RANGE_LIMITED = 268435456, // (2 << RANGE_SHIFT)
+    HAL_DATASPACE_RANGE_EXTENDED = 402653184, // (3 << RANGE_SHIFT)
+    HAL_DATASPACE_SRGB_LINEAR = 512, // 0x200
+    HAL_DATASPACE_V0_SRGB_LINEAR = 138477568, // ((STANDARD_BT709 | TRANSFER_LINEAR) | RANGE_FULL)
+    HAL_DATASPACE_V0_SCRGB_LINEAR = 406913024, // ((STANDARD_BT709 | TRANSFER_LINEAR) | RANGE_EXTENDED)
+    HAL_DATASPACE_SRGB = 513, // 0x201
+    HAL_DATASPACE_V0_SRGB = 142671872, // ((STANDARD_BT709 | TRANSFER_SRGB) | RANGE_FULL)
+    HAL_DATASPACE_V0_SCRGB = 411107328, // ((STANDARD_BT709 | TRANSFER_SRGB) | RANGE_EXTENDED)
+    HAL_DATASPACE_JFIF = 257, // 0x101
+    HAL_DATASPACE_V0_JFIF = 146931712, // ((STANDARD_BT601_625 | TRANSFER_SMPTE_170M) | RANGE_FULL)
+    HAL_DATASPACE_BT601_625 = 258, // 0x102
+    HAL_DATASPACE_V0_BT601_625 = 281149440, // ((STANDARD_BT601_625 | TRANSFER_SMPTE_170M) | RANGE_LIMITED)
+    HAL_DATASPACE_BT601_525 = 259, // 0x103
+    HAL_DATASPACE_V0_BT601_525 = 281280512, // ((STANDARD_BT601_525 | TRANSFER_SMPTE_170M) | RANGE_LIMITED)
+    HAL_DATASPACE_BT709 = 260, // 0x104
+    HAL_DATASPACE_V0_BT709 = 281083904, // ((STANDARD_BT709 | TRANSFER_SMPTE_170M) | RANGE_LIMITED)
+    HAL_DATASPACE_DCI_P3_LINEAR = 139067392, // ((STANDARD_DCI_P3 | TRANSFER_LINEAR) | RANGE_FULL)
+    HAL_DATASPACE_DCI_P3 = 155844608, // ((STANDARD_DCI_P3 | TRANSFER_GAMMA2_6) | RANGE_FULL)
+    HAL_DATASPACE_DISPLAY_P3_LINEAR = 139067392, // ((STANDARD_DCI_P3 | TRANSFER_LINEAR) | RANGE_FULL)
+    HAL_DATASPACE_DISPLAY_P3 = 143261696, // ((STANDARD_DCI_P3 | TRANSFER_SRGB) | RANGE_FULL)
+    HAL_DATASPACE_ADOBE_RGB = 151715840, // ((STANDARD_ADOBE_RGB | TRANSFER_GAMMA2_2) | RANGE_FULL)
+    HAL_DATASPACE_BT2020_LINEAR = 138805248, // ((STANDARD_BT2020 | TRANSFER_LINEAR) | RANGE_FULL)
+    HAL_DATASPACE_BT2020 = 147193856, // ((STANDARD_BT2020 | TRANSFER_SMPTE_170M) | RANGE_FULL)
+    HAL_DATASPACE_BT2020_PQ = 163971072, // ((STANDARD_BT2020 | TRANSFER_ST2084) | RANGE_FULL)
+    HAL_DATASPACE_DEPTH = 4096, // 0x1000
+    HAL_DATASPACE_SENSOR = 4097, // 0x1001
+} android_dataspace_t;
+
+typedef enum {
+    HAL_COLOR_MODE_NATIVE = 0,
+    HAL_COLOR_MODE_STANDARD_BT601_625 = 1,
+    HAL_COLOR_MODE_STANDARD_BT601_625_UNADJUSTED = 2,
+    HAL_COLOR_MODE_STANDARD_BT601_525 = 3,
+    HAL_COLOR_MODE_STANDARD_BT601_525_UNADJUSTED = 4,
+    HAL_COLOR_MODE_STANDARD_BT709 = 5,
+    HAL_COLOR_MODE_DCI_P3 = 6,
+    HAL_COLOR_MODE_SRGB = 7,
+    HAL_COLOR_MODE_ADOBE_RGB = 8,
+    HAL_COLOR_MODE_DISPLAY_P3 = 9,
+} android_color_mode_t;
+
+typedef enum {
+    HAL_COLOR_TRANSFORM_IDENTITY = 0,
+    HAL_COLOR_TRANSFORM_ARBITRARY_MATRIX = 1,
+    HAL_COLOR_TRANSFORM_VALUE_INVERSE = 2,
+    HAL_COLOR_TRANSFORM_GRAYSCALE = 3,
+    HAL_COLOR_TRANSFORM_CORRECT_PROTANOPIA = 4,
+    HAL_COLOR_TRANSFORM_CORRECT_DEUTERANOPIA = 5,
+    HAL_COLOR_TRANSFORM_CORRECT_TRITANOPIA = 6,
+} android_color_transform_t;
+
+typedef enum {
+    HAL_HDR_DOLBY_VISION = 1,
+    HAL_HDR_HDR10 = 2,
+    HAL_HDR_HLG = 3,
+} android_hdr_t;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // HIDL_GENERATED_ANDROID_HARDWARE_GRAPHICS_COMMON_V1_0_EXPORTED_CONSTANTS_H_
diff --git a/libsystem/include/system/graphics.h b/libsystem/include/system/graphics.h
index ae10fa0..1a99187 100644
--- a/libsystem/include/system/graphics.h
+++ b/libsystem/include/system/graphics.h
@@ -20,10 +20,30 @@
 #include <stddef.h>
 #include <stdint.h>
 
+/*
+ * Some of the enums are now defined in HIDL in hardware/interfaces and are
+ * generated.
+ */
+#include "graphics-base.h"
+
 #ifdef __cplusplus
 extern "C" {
 #endif
 
+/* for compatibility */
+#define HAL_PIXEL_FORMAT_YCbCr_420_888 HAL_PIXEL_FORMAT_YCBCR_420_888
+#define HAL_PIXEL_FORMAT_YCbCr_422_888 HAL_PIXEL_FORMAT_YCBCR_422_888
+#define HAL_PIXEL_FORMAT_YCbCr_444_888 HAL_PIXEL_FORMAT_YCBCR_444_888
+#define HAL_PIXEL_FORMAT_YCbCr_422_SP HAL_PIXEL_FORMAT_YCBCR_422_SP
+#define HAL_PIXEL_FORMAT_YCrCb_420_SP HAL_PIXEL_FORMAT_YCRCB_420_SP
+#define HAL_PIXEL_FORMAT_YCbCr_422_I HAL_PIXEL_FORMAT_YCBCR_422_I
+typedef android_pixel_format_t android_pixel_format;
+typedef android_transform_t android_transform;
+typedef android_dataspace_t android_dataspace;
+typedef android_color_mode_t android_color_mode;
+typedef android_color_transform_t android_color_transform;
+typedef android_hdr_t android_hdr;
+
 /*
  * If the HAL needs to create service threads to handle graphics related
  * tasks, these threads need to run at HAL_PRIORITY_URGENT_DISPLAY priority
@@ -38,411 +58,6 @@
 
 #define HAL_PRIORITY_URGENT_DISPLAY     (-8)
 
-/**
- * pixel format definitions
- */
-
-typedef enum android_pixel_format {
-    /*
-     * "linear" color pixel formats:
-     *
-     * When used with ANativeWindow, the dataSpace field describes the color
-     * space of the buffer.
-     *
-     * The color space determines, for example, if the formats are linear or
-     * gamma-corrected; or whether any special operations are performed when
-     * reading or writing into a buffer in one of these formats.
-     */
-    HAL_PIXEL_FORMAT_RGBA_8888          = 1,
-    HAL_PIXEL_FORMAT_RGBX_8888          = 2,
-    HAL_PIXEL_FORMAT_RGB_888            = 3,
-    HAL_PIXEL_FORMAT_RGB_565            = 4,
-    HAL_PIXEL_FORMAT_BGRA_8888          = 5,
-
-    /*
-     * 0x100 - 0x1FF
-     *
-     * This range is reserved for pixel formats that are specific to the HAL
-     * implementation.  Implementations can use any value in this range to
-     * communicate video pixel formats between their HAL modules.  These formats
-     * must not have an alpha channel.  Additionally, an EGLimage created from a
-     * gralloc buffer of one of these formats must be supported for use with the
-     * GL_OES_EGL_image_external OpenGL ES extension.
-     */
-
-    /*
-     * Android YUV format:
-     *
-     * This format is exposed outside of the HAL to software decoders and
-     * applications.  EGLImageKHR must support it in conjunction with the
-     * OES_EGL_image_external extension.
-     *
-     * YV12 is a 4:2:0 YCrCb planar format comprised of a WxH Y plane followed
-     * by (W/2) x (H/2) Cr and Cb planes.
-     *
-     * This format assumes
-     * - an even width
-     * - an even height
-     * - a horizontal stride multiple of 16 pixels
-     * - a vertical stride equal to the height
-     *
-     *   y_size = stride * height
-     *   c_stride = ALIGN(stride/2, 16)
-     *   c_size = c_stride * height/2
-     *   size = y_size + c_size * 2
-     *   cr_offset = y_size
-     *   cb_offset = y_size + c_size
-     *
-     * When used with ANativeWindow, the dataSpace field describes the color
-     * space of the buffer.
-     */
-    HAL_PIXEL_FORMAT_YV12   = 0x32315659, // YCrCb 4:2:0 Planar
-
-
-    /*
-     * Android Y8 format:
-     *
-     * This format is exposed outside of the HAL to the framework.
-     * The expected gralloc usage flags are SW_* and HW_CAMERA_*,
-     * and no other HW_ flags will be used.
-     *
-     * Y8 is a YUV planar format comprised of a WxH Y plane,
-     * with each pixel being represented by 8 bits.
-     *
-     * It is equivalent to just the Y plane from YV12.
-     *
-     * This format assumes
-     * - an even width
-     * - an even height
-     * - a horizontal stride multiple of 16 pixels
-     * - a vertical stride equal to the height
-     *
-     *   size = stride * height
-     *
-     * When used with ANativeWindow, the dataSpace field describes the color
-     * space of the buffer.
-     */
-    HAL_PIXEL_FORMAT_Y8     = 0x20203859,
-
-    /*
-     * Android Y16 format:
-     *
-     * This format is exposed outside of the HAL to the framework.
-     * The expected gralloc usage flags are SW_* and HW_CAMERA_*,
-     * and no other HW_ flags will be used.
-     *
-     * Y16 is a YUV planar format comprised of a WxH Y plane,
-     * with each pixel being represented by 16 bits.
-     *
-     * It is just like Y8, but has double the bits per pixel (little endian).
-     *
-     * This format assumes
-     * - an even width
-     * - an even height
-     * - a horizontal stride multiple of 16 pixels
-     * - a vertical stride equal to the height
-     * - strides are specified in pixels, not in bytes
-     *
-     *   size = stride * height * 2
-     *
-     * When used with ANativeWindow, the dataSpace field describes the color
-     * space of the buffer, except that dataSpace field
-     * HAL_DATASPACE_DEPTH indicates that this buffer contains a depth
-     * image where each sample is a distance value measured by a depth camera,
-     * plus an associated confidence value.
-     */
-    HAL_PIXEL_FORMAT_Y16    = 0x20363159,
-
-    /*
-     * Android RAW sensor format:
-     *
-     * This format is exposed outside of the camera HAL to applications.
-     *
-     * RAW16 is a single-channel, 16-bit, little endian format, typically
-     * representing raw Bayer-pattern images from an image sensor, with minimal
-     * processing.
-     *
-     * The exact pixel layout of the data in the buffer is sensor-dependent, and
-     * needs to be queried from the camera device.
-     *
-     * Generally, not all 16 bits are used; more common values are 10 or 12
-     * bits. If not all bits are used, the lower-order bits are filled first.
-     * All parameters to interpret the raw data (black and white points,
-     * color space, etc) must be queried from the camera device.
-     *
-     * This format assumes
-     * - an even width
-     * - an even height
-     * - a horizontal stride multiple of 16 pixels
-     * - a vertical stride equal to the height
-     * - strides are specified in pixels, not in bytes
-     *
-     *   size = stride * height * 2
-     *
-     * This format must be accepted by the gralloc module when used with the
-     * following usage flags:
-     *    - GRALLOC_USAGE_HW_CAMERA_*
-     *    - GRALLOC_USAGE_SW_*
-     *    - GRALLOC_USAGE_RENDERSCRIPT
-     *
-     * When used with ANativeWindow, the dataSpace should be
-     * HAL_DATASPACE_ARBITRARY, as raw image sensor buffers require substantial
-     * extra metadata to define.
-     */
-    HAL_PIXEL_FORMAT_RAW16 = 0x20,
-
-    /*
-     * Android RAW10 format:
-     *
-     * This format is exposed outside of the camera HAL to applications.
-     *
-     * RAW10 is a single-channel, 10-bit per pixel, densely packed in each row,
-     * unprocessed format, usually representing raw Bayer-pattern images coming from
-     * an image sensor.
-     *
-     * In an image buffer with this format, starting from the first pixel of each
-     * row, each 4 consecutive pixels are packed into 5 bytes (40 bits). Each one
-     * of the first 4 bytes contains the top 8 bits of each pixel, The fifth byte
-     * contains the 2 least significant bits of the 4 pixels, the exact layout data
-     * for each 4 consecutive pixels is illustrated below (Pi[j] stands for the jth
-     * bit of the ith pixel):
-     *
-     *          bit 7                                     bit 0
-     *          =====|=====|=====|=====|=====|=====|=====|=====|
-     * Byte 0: |P0[9]|P0[8]|P0[7]|P0[6]|P0[5]|P0[4]|P0[3]|P0[2]|
-     *         |-----|-----|-----|-----|-----|-----|-----|-----|
-     * Byte 1: |P1[9]|P1[8]|P1[7]|P1[6]|P1[5]|P1[4]|P1[3]|P1[2]|
-     *         |-----|-----|-----|-----|-----|-----|-----|-----|
-     * Byte 2: |P2[9]|P2[8]|P2[7]|P2[6]|P2[5]|P2[4]|P2[3]|P2[2]|
-     *         |-----|-----|-----|-----|-----|-----|-----|-----|
-     * Byte 3: |P3[9]|P3[8]|P3[7]|P3[6]|P3[5]|P3[4]|P3[3]|P3[2]|
-     *         |-----|-----|-----|-----|-----|-----|-----|-----|
-     * Byte 4: |P3[1]|P3[0]|P2[1]|P2[0]|P1[1]|P1[0]|P0[1]|P0[0]|
-     *          ===============================================
-     *
-     * This format assumes
-     * - a width multiple of 4 pixels
-     * - an even height
-     * - a vertical stride equal to the height
-     * - strides are specified in bytes, not in pixels
-     *
-     *   size = stride * height
-     *
-     * When stride is equal to width * (10 / 8), there will be no padding bytes at
-     * the end of each row, the entire image data is densely packed. When stride is
-     * larger than width * (10 / 8), padding bytes will be present at the end of each
-     * row (including the last row).
-     *
-     * This format must be accepted by the gralloc module when used with the
-     * following usage flags:
-     *    - GRALLOC_USAGE_HW_CAMERA_*
-     *    - GRALLOC_USAGE_SW_*
-     *    - GRALLOC_USAGE_RENDERSCRIPT
-     *
-     * When used with ANativeWindow, the dataSpace field should be
-     * HAL_DATASPACE_ARBITRARY, as raw image sensor buffers require substantial
-     * extra metadata to define.
-     */
-    HAL_PIXEL_FORMAT_RAW10 = 0x25,
-
-    /*
-     * Android RAW12 format:
-     *
-     * This format is exposed outside of camera HAL to applications.
-     *
-     * RAW12 is a single-channel, 12-bit per pixel, densely packed in each row,
-     * unprocessed format, usually representing raw Bayer-pattern images coming from
-     * an image sensor.
-     *
-     * In an image buffer with this format, starting from the first pixel of each
-     * row, each two consecutive pixels are packed into 3 bytes (24 bits). The first
-     * and second byte contains the top 8 bits of first and second pixel. The third
-     * byte contains the 4 least significant bits of the two pixels, the exact layout
-     * data for each two consecutive pixels is illustrated below (Pi[j] stands for
-     * the jth bit of the ith pixel):
-     *
-     *           bit 7                                            bit 0
-     *          ======|======|======|======|======|======|======|======|
-     * Byte 0: |P0[11]|P0[10]|P0[ 9]|P0[ 8]|P0[ 7]|P0[ 6]|P0[ 5]|P0[ 4]|
-     *         |------|------|------|------|------|------|------|------|
-     * Byte 1: |P1[11]|P1[10]|P1[ 9]|P1[ 8]|P1[ 7]|P1[ 6]|P1[ 5]|P1[ 4]|
-     *         |------|------|------|------|------|------|------|------|
-     * Byte 2: |P1[ 3]|P1[ 2]|P1[ 1]|P1[ 0]|P0[ 3]|P0[ 2]|P0[ 1]|P0[ 0]|
-     *          =======================================================
-     *
-     * This format assumes:
-     * - a width multiple of 4 pixels
-     * - an even height
-     * - a vertical stride equal to the height
-     * - strides are specified in bytes, not in pixels
-     *
-     *   size = stride * height
-     *
-     * When stride is equal to width * (12 / 8), there will be no padding bytes at
-     * the end of each row, the entire image data is densely packed. When stride is
-     * larger than width * (12 / 8), padding bytes will be present at the end of
-     * each row (including the last row).
-     *
-     * This format must be accepted by the gralloc module when used with the
-     * following usage flags:
-     *    - GRALLOC_USAGE_HW_CAMERA_*
-     *    - GRALLOC_USAGE_SW_*
-     *    - GRALLOC_USAGE_RENDERSCRIPT
-     *
-     * When used with ANativeWindow, the dataSpace field should be
-     * HAL_DATASPACE_ARBITRARY, as raw image sensor buffers require substantial
-     * extra metadata to define.
-     */
-    HAL_PIXEL_FORMAT_RAW12 = 0x26,
-
-    /*
-     * Android opaque RAW format:
-     *
-     * This format is exposed outside of the camera HAL to applications.
-     *
-     * RAW_OPAQUE is a format for unprocessed raw image buffers coming from an
-     * image sensor. The actual structure of buffers of this format is
-     * implementation-dependent.
-     *
-     * This format must be accepted by the gralloc module when used with the
-     * following usage flags:
-     *    - GRALLOC_USAGE_HW_CAMERA_*
-     *    - GRALLOC_USAGE_SW_*
-     *    - GRALLOC_USAGE_RENDERSCRIPT
-     *
-     * When used with ANativeWindow, the dataSpace field should be
-     * HAL_DATASPACE_ARBITRARY, as raw image sensor buffers require substantial
-     * extra metadata to define.
-     */
-    HAL_PIXEL_FORMAT_RAW_OPAQUE = 0x24,
-
-    /*
-     * Android binary blob graphics buffer format:
-     *
-     * This format is used to carry task-specific data which does not have a
-     * standard image structure. The details of the format are left to the two
-     * endpoints.
-     *
-     * A typical use case is for transporting JPEG-compressed images from the
-     * Camera HAL to the framework or to applications.
-     *
-     * Buffers of this format must have a height of 1, and width equal to their
-     * size in bytes.
-     *
-     * When used with ANativeWindow, the mapping of the dataSpace field to
-     * buffer contents for BLOB is as follows:
-     *
-     *  dataSpace value               | Buffer contents
-     * -------------------------------+-----------------------------------------
-     *  HAL_DATASPACE_JFIF            | An encoded JPEG image
-     *  HAL_DATASPACE_DEPTH           | An android_depth_points buffer
-     *  Other                         | Unsupported
-     *
-     */
-    HAL_PIXEL_FORMAT_BLOB = 0x21,
-
-    /*
-     * Android format indicating that the choice of format is entirely up to the
-     * device-specific Gralloc implementation.
-     *
-     * The Gralloc implementation should examine the usage bits passed in when
-     * allocating a buffer with this format, and it should derive the pixel
-     * format from those usage flags.  This format will never be used with any
-     * of the GRALLOC_USAGE_SW_* usage flags.
-     *
-     * If a buffer of this format is to be used as an OpenGL ES texture, the
-     * framework will assume that sampling the texture will always return an
-     * alpha value of 1.0 (i.e. the buffer contains only opaque pixel values).
-     *
-     * When used with ANativeWindow, the dataSpace field describes the color
-     * space of the buffer.
-     */
-    HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED = 0x22,
-
-    /*
-     * Android flexible YCbCr 4:2:0 formats
-     *
-     * This format allows platforms to use an efficient YCbCr/YCrCb 4:2:0
-     * buffer layout, while still describing the general format in a
-     * layout-independent manner.  While called YCbCr, it can be
-     * used to describe formats with either chromatic ordering, as well as
-     * whole planar or semiplanar layouts.
-     *
-     * struct android_ycbcr (below) is the the struct used to describe it.
-     *
-     * This format must be accepted by the gralloc module when
-     * USAGE_SW_WRITE_* or USAGE_SW_READ_* are set.
-     *
-     * This format is locked for use by gralloc's (*lock_ycbcr) method, and
-     * locking with the (*lock) method will return an error.
-     *
-     * When used with ANativeWindow, the dataSpace field describes the color
-     * space of the buffer.
-     */
-    HAL_PIXEL_FORMAT_YCbCr_420_888 = 0x23,
-
-    /*
-     * Android flexible YCbCr 4:2:2 formats
-     *
-     * This format allows platforms to use an efficient YCbCr/YCrCb 4:2:2
-     * buffer layout, while still describing the general format in a
-     * layout-independent manner.  While called YCbCr, it can be
-     * used to describe formats with either chromatic ordering, as well as
-     * whole planar or semiplanar layouts.
-     *
-     * This format is currently only used by SW readable buffers
-     * produced by MediaCodecs, so the gralloc module can ignore this format.
-     */
-    HAL_PIXEL_FORMAT_YCbCr_422_888 = 0x27,
-
-    /*
-     * Android flexible YCbCr 4:4:4 formats
-     *
-     * This format allows platforms to use an efficient YCbCr/YCrCb 4:4:4
-     * buffer layout, while still describing the general format in a
-     * layout-independent manner.  While called YCbCr, it can be
-     * used to describe formats with either chromatic ordering, as well as
-     * whole planar or semiplanar layouts.
-     *
-     * This format is currently only used by SW readable buffers
-     * produced by MediaCodecs, so the gralloc module can ignore this format.
-     */
-    HAL_PIXEL_FORMAT_YCbCr_444_888 = 0x28,
-
-    /*
-     * Android flexible RGB 888 formats
-     *
-     * This format allows platforms to use an efficient RGB/BGR/RGBX/BGRX
-     * buffer layout, while still describing the general format in a
-     * layout-independent manner.  While called RGB, it can be
-     * used to describe formats with either color ordering and optional
-     * padding, as well as whole planar layout.
-     *
-     * This format is currently only used by SW readable buffers
-     * produced by MediaCodecs, so the gralloc module can ignore this format.
-     */
-    HAL_PIXEL_FORMAT_FLEX_RGB_888 = 0x29,
-
-    /*
-     * Android flexible RGBA 8888 formats
-     *
-     * This format allows platforms to use an efficient RGBA/BGRA/ARGB/ABGR
-     * buffer layout, while still describing the general format in a
-     * layout-independent manner.  While called RGBA, it can be
-     * used to describe formats with any of the component orderings, as
-     * well as whole planar layout.
-     *
-     * This format is currently only used by SW readable buffers
-     * produced by MediaCodecs, so the gralloc module can ignore this format.
-     */
-    HAL_PIXEL_FORMAT_FLEX_RGBA_8888 = 0x2A,
-
-    /* Legacy formats (deprecated), used by ImageFormat.java */
-    HAL_PIXEL_FORMAT_YCbCr_422_SP       = 0x10, // NV16
-    HAL_PIXEL_FORMAT_YCrCb_420_SP       = 0x11, // NV21
-    HAL_PIXEL_FORMAT_YCbCr_422_I        = 0x14, // YUY2
-} android_pixel_format_t;
-
 /*
  * Structure for describing YCbCr formats for consumption by applications.
  * This is used with HAL_PIXEL_FORMAT_YCbCr_*_888.
@@ -623,795 +238,24 @@
 };
 
 /**
- * Transformation definitions
- *
- * IMPORTANT NOTE:
- * HAL_TRANSFORM_ROT_90 is applied CLOCKWISE and AFTER HAL_TRANSFORM_FLIP_{H|V}.
- *
- */
+  * These structures are used to define the reference display's
+  * capabilities for HDR content. Display engine can use this
+  * to better tone map content to user's display.
+  * Color is defined in CIE XYZ coordinates
+  */
+struct android_xy_color {
+    float x;
+    float y;
+};
 
-typedef enum android_transform {
-    /* flip source image horizontally (around the vertical axis) */
-    HAL_TRANSFORM_FLIP_H    = 0x01,
-    /* flip source image vertically (around the horizontal axis)*/
-    HAL_TRANSFORM_FLIP_V    = 0x02,
-    /* rotate source image 90 degrees clockwise */
-    HAL_TRANSFORM_ROT_90    = 0x04,
-    /* rotate source image 180 degrees */
-    HAL_TRANSFORM_ROT_180   = 0x03,
-    /* rotate source image 270 degrees clockwise */
-    HAL_TRANSFORM_ROT_270   = 0x07,
-    /* don't use. see system/window.h */
-    HAL_TRANSFORM_RESERVED  = 0x08,
-} android_transform_t;
-
-/**
- * Dataspace Definitions
- * ======================
- *
- * Dataspace is the definition of how pixel values should be interpreted.
- *
- * For many formats, this is the colorspace of the image data, which includes
- * primaries (including white point) and the transfer characteristic function,
- * which describes both gamma curve and numeric range (within the bit depth).
- *
- * Other dataspaces include depth measurement data from a depth camera.
- *
- * A dataspace is comprised of a number of fields.
- *
- * Version
- * --------
- * The top 2 bits represent the revision of the field specification. This is
- * currently always 0.
- *
- *
- * bits    31-30 29                      -                          0
- *        +-----+----------------------------------------------------+
- * fields | Rev |            Revision specific fields                |
- *        +-----+----------------------------------------------------+
- *
- * Field layout for version = 0:
- * ----------------------------
- *
- * A dataspace is comprised of the following fields:
- *      Standard
- *      Transfer function
- *      Range
- *
- * bits    31-30 29-27 26 -  22 21 -  16 15             -           0
- *        +-----+-----+--------+--------+----------------------------+
- * fields |  0  |Range|Transfer|Standard|    Legacy and custom       |
- *        +-----+-----+--------+--------+----------------------------+
- *          VV    RRR   TTTTT    SSSSSS    LLLLLLLL       LLLLLLLL
- *
- * If range, transfer and standard fields are all 0 (e.g. top 16 bits are
- * all zeroes), the bottom 16 bits contain either a legacy dataspace value,
- * or a custom value.
- */
-
-typedef enum android_dataspace {
-    /*
-     * Default-assumption data space, when not explicitly specified.
-     *
-     * It is safest to assume the buffer is an image with sRGB primaries and
-     * encoding ranges, but the consumer and/or the producer of the data may
-     * simply be using defaults. No automatic gamma transform should be
-     * expected, except for a possible display gamma transform when drawn to a
-     * screen.
-     */
-    HAL_DATASPACE_UNKNOWN = 0x0,
-
-    /*
-     * Arbitrary dataspace with manually defined characteristics.  Definition
-     * for colorspaces or other meaning must be communicated separately.
-     *
-     * This is used when specifying primaries, transfer characteristics,
-     * etc. separately.
-     *
-     * A typical use case is in video encoding parameters (e.g. for H.264),
-     * where a colorspace can have separately defined primaries, transfer
-     * characteristics, etc.
-     */
-    HAL_DATASPACE_ARBITRARY = 0x1,
-
-    /*
-     * Color-description aspects
-     *
-     * The following aspects define various characteristics of the color
-     * specification. These represent bitfields, so that a data space value
-     * can specify each of them independently.
-     */
-
-    HAL_DATASPACE_STANDARD_SHIFT = 16,
-
-    /*
-     * Standard aspect
-     *
-     * Defines the chromaticity coordinates of the source primaries in terms of
-     * the CIE 1931 definition of x and y specified in ISO 11664-1.
-     */
-    HAL_DATASPACE_STANDARD_MASK = 63 << HAL_DATASPACE_STANDARD_SHIFT,  // 0x3F
-
-    /*
-     * Chromacity coordinates are unknown or are determined by the application.
-     * Implementations shall use the following suggested standards:
-     *
-     * All YCbCr formats: BT709 if size is 720p or larger (since most video
-     *                    content is letterboxed this corresponds to width is
-     *                    1280 or greater, or height is 720 or greater).
-     *                    BT601_625 if size is smaller than 720p or is JPEG.
-     * All RGB formats:   BT709.
-     *
-     * For all other formats standard is undefined, and implementations should use
-     * an appropriate standard for the data represented.
-     */
-    HAL_DATASPACE_STANDARD_UNSPECIFIED = 0 << HAL_DATASPACE_STANDARD_SHIFT,
-
-    /*
-     * Primaries:       x       y
-     *  green           0.300   0.600
-     *  blue            0.150   0.060
-     *  red             0.640   0.330
-     *  white (D65)     0.3127  0.3290
-     *
-     * Use the unadjusted KR = 0.2126, KB = 0.0722 luminance interpretation
-     * for RGB conversion.
-     */
-    HAL_DATASPACE_STANDARD_BT709 = 1 << HAL_DATASPACE_STANDARD_SHIFT,
-
-    /*
-     * Primaries:       x       y
-     *  green           0.290   0.600
-     *  blue            0.150   0.060
-     *  red             0.640   0.330
-     *  white (D65)     0.3127  0.3290
-     *
-     *  KR = 0.299, KB = 0.114. This adjusts the luminance interpretation
-     *  for RGB conversion from the one purely determined by the primaries
-     *  to minimize the color shift into RGB space that uses BT.709
-     *  primaries.
-     */
-    HAL_DATASPACE_STANDARD_BT601_625 = 2 << HAL_DATASPACE_STANDARD_SHIFT,
-
-    /*
-     * Primaries:       x       y
-     *  green           0.290   0.600
-     *  blue            0.150   0.060
-     *  red             0.640   0.330
-     *  white (D65)     0.3127  0.3290
-     *
-     * Use the unadjusted KR = 0.222, KB = 0.071 luminance interpretation
-     * for RGB conversion.
-     */
-    HAL_DATASPACE_STANDARD_BT601_625_UNADJUSTED = 3 << HAL_DATASPACE_STANDARD_SHIFT,
-
-    /*
-     * Primaries:       x       y
-     *  green           0.310   0.595
-     *  blue            0.155   0.070
-     *  red             0.630   0.340
-     *  white (D65)     0.3127  0.3290
-     *
-     *  KR = 0.299, KB = 0.114. This adjusts the luminance interpretation
-     *  for RGB conversion from the one purely determined by the primaries
-     *  to minimize the color shift into RGB space that uses BT.709
-     *  primaries.
-     */
-    HAL_DATASPACE_STANDARD_BT601_525 = 4 << HAL_DATASPACE_STANDARD_SHIFT,
-
-    /*
-     * Primaries:       x       y
-     *  green           0.310   0.595
-     *  blue            0.155   0.070
-     *  red             0.630   0.340
-     *  white (D65)     0.3127  0.3290
-     *
-     * Use the unadjusted KR = 0.212, KB = 0.087 luminance interpretation
-     * for RGB conversion (as in SMPTE 240M).
-     */
-    HAL_DATASPACE_STANDARD_BT601_525_UNADJUSTED = 5 << HAL_DATASPACE_STANDARD_SHIFT,
-
-    /*
-     * Primaries:       x       y
-     *  green           0.170   0.797
-     *  blue            0.131   0.046
-     *  red             0.708   0.292
-     *  white (D65)     0.3127  0.3290
-     *
-     * Use the unadjusted KR = 0.2627, KB = 0.0593 luminance interpretation
-     * for RGB conversion.
-     */
-    HAL_DATASPACE_STANDARD_BT2020 = 6 << HAL_DATASPACE_STANDARD_SHIFT,
-
-    /*
-     * Primaries:       x       y
-     *  green           0.170   0.797
-     *  blue            0.131   0.046
-     *  red             0.708   0.292
-     *  white (D65)     0.3127  0.3290
-     *
-     * Use the unadjusted KR = 0.2627, KB = 0.0593 luminance interpretation
-     * for RGB conversion using the linear domain.
-     */
-    HAL_DATASPACE_STANDARD_BT2020_CONSTANT_LUMINANCE = 7 << HAL_DATASPACE_STANDARD_SHIFT,
-
-    /*
-     * Primaries:       x      y
-     *  green           0.21   0.71
-     *  blue            0.14   0.08
-     *  red             0.67   0.33
-     *  white (C)       0.310  0.316
-     *
-     * Use the unadjusted KR = 0.30, KB = 0.11 luminance interpretation
-     * for RGB conversion.
-     */
-    HAL_DATASPACE_STANDARD_BT470M = 8 << HAL_DATASPACE_STANDARD_SHIFT,
-
-    /*
-     * Primaries:       x       y
-     *  green           0.243   0.692
-     *  blue            0.145   0.049
-     *  red             0.681   0.319
-     *  white (C)       0.310   0.316
-     *
-     * Use the unadjusted KR = 0.254, KB = 0.068 luminance interpretation
-     * for RGB conversion.
-     */
-    HAL_DATASPACE_STANDARD_FILM = 9 << HAL_DATASPACE_STANDARD_SHIFT,
-
-    HAL_DATASPACE_TRANSFER_SHIFT = 22,
-
-    /*
-     * Transfer aspect
-     *
-     * Transfer characteristics are the opto-electronic transfer characteristic
-     * at the source as a function of linear optical intensity (luminance).
-     *
-     * For digital signals, E corresponds to the recorded value. Normally, the
-     * transfer function is applied in RGB space to each of the R, G and B
-     * components independently. This may result in color shift that can be
-     * minized by applying the transfer function in Lab space only for the L
-     * component. Implementation may apply the transfer function in RGB space
-     * for all pixel formats if desired.
-     */
-
-    HAL_DATASPACE_TRANSFER_MASK = 31 << HAL_DATASPACE_TRANSFER_SHIFT,  // 0x1F
-
-    /*
-     * Transfer characteristics are unknown or are determined by the
-     * application.
-     *
-     * Implementations should use the following transfer functions:
-     *
-     * For YCbCr formats: use HAL_DATASPACE_TRANSFER_SMPTE_170M
-     * For RGB formats: use HAL_DATASPACE_TRANSFER_SRGB
-     *
-     * For all other formats transfer function is undefined, and implementations
-     * should use an appropriate standard for the data represented.
-     */
-    HAL_DATASPACE_TRANSFER_UNSPECIFIED = 0 << HAL_DATASPACE_TRANSFER_SHIFT,
-
-    /*
-     * Transfer characteristic curve:
-     *  E = L
-     *      L - luminance of image 0 <= L <= 1 for conventional colorimetry
-     *      E - corresponding electrical signal
-     */
-    HAL_DATASPACE_TRANSFER_LINEAR = 1 << HAL_DATASPACE_TRANSFER_SHIFT,
-
-    /*
-     * Transfer characteristic curve:
-     *
-     * E = 1.055 * L^(1/2.4) - 0.055  for 0.0031308 <= L <= 1
-     *   = 12.92 * L                  for 0 <= L < 0.0031308
-     *     L - luminance of image 0 <= L <= 1 for conventional colorimetry
-     *     E - corresponding electrical signal
-     */
-    HAL_DATASPACE_TRANSFER_SRGB = 2 << HAL_DATASPACE_TRANSFER_SHIFT,
-
-    /*
-     * BT.601 525, BT.601 625, BT.709, BT.2020
-     *
-     * Transfer characteristic curve:
-     *  E = 1.099 * L ^ 0.45 - 0.099  for 0.018 <= L <= 1
-     *    = 4.500 * L                 for 0 <= L < 0.018
-     *      L - luminance of image 0 <= L <= 1 for conventional colorimetry
-     *      E - corresponding electrical signal
-     */
-    HAL_DATASPACE_TRANSFER_SMPTE_170M = 3 << HAL_DATASPACE_TRANSFER_SHIFT,
-
-    /*
-     * Assumed display gamma 2.2.
-     *
-     * Transfer characteristic curve:
-     *  E = L ^ (1/2.2)
-     *      L - luminance of image 0 <= L <= 1 for conventional colorimetry
-     *      E - corresponding electrical signal
-     */
-    HAL_DATASPACE_TRANSFER_GAMMA2_2 = 4 << HAL_DATASPACE_TRANSFER_SHIFT,
-
-    /*
-     *  display gamma 2.8.
-     *
-     * Transfer characteristic curve:
-     *  E = L ^ (1/2.8)
-     *      L - luminance of image 0 <= L <= 1 for conventional colorimetry
-     *      E - corresponding electrical signal
-     */
-    HAL_DATASPACE_TRANSFER_GAMMA2_8 = 5 << HAL_DATASPACE_TRANSFER_SHIFT,
-
-    /*
-     * SMPTE ST 2084
-     *
-     * Transfer characteristic curve:
-     *  E = ((c1 + c2 * L^n) / (1 + c3 * L^n)) ^ m
-     *  c1 = c3 - c2 + 1 = 3424 / 4096 = 0.8359375
-     *  c2 = 32 * 2413 / 4096 = 18.8515625
-     *  c3 = 32 * 2392 / 4096 = 18.6875
-     *  m = 128 * 2523 / 4096 = 78.84375
-     *  n = 0.25 * 2610 / 4096 = 0.1593017578125
-     *      L - luminance of image 0 <= L <= 1 for HDR colorimetry.
-     *          L = 1 corresponds to 10000 cd/m2
-     *      E - corresponding electrical signal
-     */
-    HAL_DATASPACE_TRANSFER_ST2084 = 6 << HAL_DATASPACE_TRANSFER_SHIFT,
-
-    /*
-     * ARIB STD-B67 Hybrid Log Gamma
-     *
-     * Transfer characteristic curve:
-     *  E = r * L^0.5                 for 0 <= L <= 1
-     *    = a * ln(L - b) + c         for 1 < L
-     *  a = 0.17883277
-     *  b = 0.28466892
-     *  c = 0.55991073
-     *  r = 0.5
-     *      L - luminance of image 0 <= L for HDR colorimetry. L = 1 corresponds
-     *          to reference white level of 100 cd/m2
-     *      E - corresponding electrical signal
-     */
-    HAL_DATASPACE_TRANSFER_HLG = 7 << HAL_DATASPACE_TRANSFER_SHIFT,
-
-    HAL_DATASPACE_RANGE_SHIFT = 27,
-
-    /*
-     * Range aspect
-     *
-     * Defines the range of values corresponding to the unit range of 0-1.
-     * This is defined for YCbCr only, but can be expanded to RGB space.
-     */
-    HAL_DATASPACE_RANGE_MASK = 7 << HAL_DATASPACE_RANGE_SHIFT,  // 0x7
-
-    /*
-     * Range is unknown or are determined by the application.  Implementations
-     * shall use the following suggested ranges:
-     *
-     * All YCbCr formats: limited range.
-     * All RGB or RGBA formats (including RAW and Bayer): full range.
-     * All Y formats: full range
-     *
-     * For all other formats range is undefined, and implementations should use
-     * an appropriate range for the data represented.
-     */
-    HAL_DATASPACE_RANGE_UNSPECIFIED = 0 << HAL_DATASPACE_RANGE_SHIFT,
-
-    /*
-     * Full range uses all values for Y, Cb and Cr from
-     * 0 to 2^b-1, where b is the bit depth of the color format.
-     */
-    HAL_DATASPACE_RANGE_FULL = 1 << HAL_DATASPACE_RANGE_SHIFT,
-
-    /*
-     * Limited range uses values 16/256*2^b to 235/256*2^b for Y, and
-     * 1/16*2^b to 15/16*2^b for Cb, Cr, R, G and B, where b is the bit depth of
-     * the color format.
-     *
-     * E.g. For 8-bit-depth formats:
-     * Luma (Y) samples should range from 16 to 235, inclusive
-     * Chroma (Cb, Cr) samples should range from 16 to 240, inclusive
-     *
-     * For 10-bit-depth formats:
-     * Luma (Y) samples should range from 64 to 940, inclusive
-     * Chroma (Cb, Cr) samples should range from 64 to 960, inclusive
-     */
-    HAL_DATASPACE_RANGE_LIMITED = 2 << HAL_DATASPACE_RANGE_SHIFT,
-
-    /*
-     * Legacy dataspaces
-     */
-
-    /*
-     * sRGB linear encoding:
-     *
-     * The red, green, and blue components are stored in sRGB space, but
-     * are linear, not gamma-encoded.
-     * The RGB primaries and the white point are the same as BT.709.
-     *
-     * The values are encoded using the full range ([0,255] for 8-bit) for all
-     * components.
-     */
-    HAL_DATASPACE_SRGB_LINEAR = 0x200, // deprecated, use HAL_DATASPACE_V0_SRGB_LINEAR
-
-    HAL_DATASPACE_V0_SRGB_LINEAR = HAL_DATASPACE_STANDARD_BT709 |
-            HAL_DATASPACE_TRANSFER_LINEAR | HAL_DATASPACE_RANGE_FULL,
-
-
-    /*
-     * sRGB gamma encoding:
-     *
-     * The red, green and blue components are stored in sRGB space, and
-     * converted to linear space when read, using the SRGB transfer function
-     * for each of the R, G and B components. When written, the inverse
-     * transformation is performed.
-     *
-     * The alpha component, if present, is always stored in linear space and
-     * is left unmodified when read or written.
-     *
-     * Use full range and BT.709 standard.
-     */
-    HAL_DATASPACE_SRGB = 0x201, // deprecated, use HAL_DATASPACE_V0_SRGB
-
-    HAL_DATASPACE_V0_SRGB = HAL_DATASPACE_STANDARD_BT709 |
-            HAL_DATASPACE_TRANSFER_SRGB | HAL_DATASPACE_RANGE_FULL,
-
-
-    /*
-     * YCbCr Colorspaces
-     * -----------------
-     *
-     * Primaries are given using (x,y) coordinates in the CIE 1931 definition
-     * of x and y specified by ISO 11664-1.
-     *
-     * Transfer characteristics are the opto-electronic transfer characteristic
-     * at the source as a function of linear optical intensity (luminance).
-     */
-
-    /*
-     * JPEG File Interchange Format (JFIF)
-     *
-     * Same model as BT.601-625, but all values (Y, Cb, Cr) range from 0 to 255
-     *
-     * Use full range, BT.601 transfer and BT.601_625 standard.
-     */
-    HAL_DATASPACE_JFIF = 0x101, // deprecated, use HAL_DATASPACE_V0_JFIF
-
-    HAL_DATASPACE_V0_JFIF = HAL_DATASPACE_STANDARD_BT601_625 |
-            HAL_DATASPACE_TRANSFER_SMPTE_170M | HAL_DATASPACE_RANGE_FULL,
-
-    /*
-     * ITU-R Recommendation 601 (BT.601) - 625-line
-     *
-     * Standard-definition television, 625 Lines (PAL)
-     *
-     * Use limited range, BT.601 transfer and BT.601_625 standard.
-     */
-    HAL_DATASPACE_BT601_625 = 0x102, // deprecated, use HAL_DATASPACE_V0_BT601_625
-
-    HAL_DATASPACE_V0_BT601_625 = HAL_DATASPACE_STANDARD_BT601_625 |
-            HAL_DATASPACE_TRANSFER_SMPTE_170M | HAL_DATASPACE_RANGE_LIMITED,
-
-
-    /*
-     * ITU-R Recommendation 601 (BT.601) - 525-line
-     *
-     * Standard-definition television, 525 Lines (NTSC)
-     *
-     * Use limited range, BT.601 transfer and BT.601_525 standard.
-     */
-    HAL_DATASPACE_BT601_525 = 0x103, // deprecated, use HAL_DATASPACE_V0_BT601_525
-
-    HAL_DATASPACE_V0_BT601_525 = HAL_DATASPACE_STANDARD_BT601_525 |
-            HAL_DATASPACE_TRANSFER_SMPTE_170M | HAL_DATASPACE_RANGE_LIMITED,
-
-    /*
-     * ITU-R Recommendation 709 (BT.709)
-     *
-     * High-definition television
-     *
-     * Use limited range, BT.709 transfer and BT.709 standard.
-     */
-    HAL_DATASPACE_BT709 = 0x104, // deprecated, use HAL_DATASPACE_V0_BT709
-
-    HAL_DATASPACE_V0_BT709 = HAL_DATASPACE_STANDARD_BT709 |
-            HAL_DATASPACE_TRANSFER_SMPTE_170M | HAL_DATASPACE_RANGE_LIMITED,
-
-    /*
-     * Data spaces for non-color formats
-     */
-
-    /*
-     * The buffer contains depth ranging measurements from a depth camera.
-     * This value is valid with formats:
-     *    HAL_PIXEL_FORMAT_Y16: 16-bit samples, consisting of a depth measurement
-     *       and an associated confidence value. The 3 MSBs of the sample make
-     *       up the confidence value, and the low 13 LSBs of the sample make up
-     *       the depth measurement.
-     *       For the confidence section, 0 means 100% confidence, 1 means 0%
-     *       confidence. The mapping to a linear float confidence value between
-     *       0.f and 1.f can be obtained with
-     *         float confidence = (((depthSample >> 13) - 1) & 0x7) / 7.0f;
-     *       The depth measurement can be extracted simply with
-     *         uint16_t range = (depthSample & 0x1FFF);
-     *    HAL_PIXEL_FORMAT_BLOB: A depth point cloud, as
-     *       a variable-length float (x,y,z, confidence) coordinate point list.
-     *       The point cloud will be represented with the android_depth_points
-     *       structure.
-     */
-    HAL_DATASPACE_DEPTH = 0x1000
-
-} android_dataspace_t;
-
-/*
- * Color modes that may be supported by a display.
- *
- * Definitions:
- * Rendering intent generally defines the goal in mapping a source (input)
- * color to a destination device color for a given color mode.
- *
- *  It is important to keep in mind three cases where mapping may be applied:
- *  1. The source gamut is much smaller than the destination (display) gamut
- *  2. The source gamut is much larger than the destination gamut (this will
- *  ordinarily be handled using colorimetric rendering, below)
- *  3. The source and destination gamuts are roughly equal, although not
- *  completely overlapping
- *  Also, a common requirement for mappings is that skin tones should be
- *  preserved, or at least remain natural in appearance.
- *
- *  Colorimetric Rendering Intent (All cases):
- *  Colorimetric indicates that colors should be preserved. In the case
- *  that the source gamut lies wholly within the destination gamut or is
- *  about the same (#1, #3), this will simply mean that no manipulations
- *  (no saturation boost, for example) are applied. In the case where some
- *  source colors lie outside the destination gamut (#2, #3), those will
- *  need to be mapped to colors that are within the destination gamut,
- *  while the already in-gamut colors remain unchanged.
- *
- *  Non-colorimetric transforms can take many forms. There are no hard
- *  rules and it's left to the implementation to define.
- *  Two common intents are described below.
- *
- *  Stretched-Gamut Enhancement Intent (Source < Destination):
- *  When the destination gamut is much larger than the source gamut (#1), the
- *  source primaries may be redefined to reflect the full extent of the
- *  destination space, or to reflect an intermediate gamut.
- *  Skin-tone preservation would likely be applied. An example might be sRGB
- *  input displayed on a DCI-P3 capable device, with skin-tone preservation.
- *
- *  Within-Gamut Enhancement Intent (Source >= Destination):
- *  When the device (destination) gamut is not larger than the source gamut
- *  (#2 or #3), but the appearance of a larger gamut is desired, techniques
- *  such as saturation boost may be applied to the source colors. Skin-tone
- *  preservation may be applied. There is no unique method for within-gamut
- *  enhancement; it would be defined within a flexible color mode.
- *
- */
-typedef enum android_color_mode {
-
-  /*
-   * HAL_COLOR_MODE_DEFAULT is the "native" gamut of the display.
-   * White Point: Vendor/OEM defined
-   * Panel Gamma: Vendor/OEM defined (typically 2.2)
-   * Rendering Intent: Vendor/OEM defined (typically 'enhanced')
-   */
-  HAL_COLOR_MODE_NATIVE = 0,
-
-  /*
-   * HAL_COLOR_MODE_STANDARD_BT601_625 corresponds with display
-   * settings that implement the ITU-R Recommendation BT.601
-   * or Rec 601. Using 625 line version
-   * Rendering Intent: Colorimetric
-   * Primaries:
-   *                  x       y
-   *  green           0.290   0.600
-   *  blue            0.150   0.060
-   *  red             0.640   0.330
-   *  white (D65)     0.3127  0.3290
-   *
-   *  KR = 0.299, KB = 0.114. This adjusts the luminance interpretation
-   *  for RGB conversion from the one purely determined by the primaries
-   *  to minimize the color shift into RGB space that uses BT.709
-   *  primaries.
-   *
-   * Gamma Correction (GC):
-   *
-   *  if Vlinear < 0.018
-   *    Vnonlinear = 4.500 * Vlinear
-   *  else
-   *    Vnonlinear = 1.099 * (Vlinear)^(0.45) – 0.099
-   */
-  HAL_COLOR_MODE_STANDARD_BT601_625 = 1,
-
-  /*
-   * Primaries:
-   *                  x       y
-   *  green           0.290   0.600
-   *  blue            0.150   0.060
-   *  red             0.640   0.330
-   *  white (D65)     0.3127  0.3290
-   *
-   *  Use the unadjusted KR = 0.222, KB = 0.071 luminance interpretation
-   *  for RGB conversion.
-   *
-   * Gamma Correction (GC):
-   *
-   *  if Vlinear < 0.018
-   *    Vnonlinear = 4.500 * Vlinear
-   *  else
-   *    Vnonlinear = 1.099 * (Vlinear)^(0.45) – 0.099
-   */
-  HAL_COLOR_MODE_STANDARD_BT601_625_UNADJUSTED = 2,
-
-  /*
-   * Primaries:
-   *                  x       y
-   *  green           0.310   0.595
-   *  blue            0.155   0.070
-   *  red             0.630   0.340
-   *  white (D65)     0.3127  0.3290
-   *
-   *  KR = 0.299, KB = 0.114. This adjusts the luminance interpretation
-   *  for RGB conversion from the one purely determined by the primaries
-   *  to minimize the color shift into RGB space that uses BT.709
-   *  primaries.
-   *
-   * Gamma Correction (GC):
-   *
-   *  if Vlinear < 0.018
-   *    Vnonlinear = 4.500 * Vlinear
-   *  else
-   *    Vnonlinear = 1.099 * (Vlinear)^(0.45) – 0.099
-   */
-  HAL_COLOR_MODE_STANDARD_BT601_525 = 3,
-
-  /*
-   * Primaries:
-   *                  x       y
-   *  green           0.310   0.595
-   *  blue            0.155   0.070
-   *  red             0.630   0.340
-   *  white (D65)     0.3127  0.3290
-   *
-   *  Use the unadjusted KR = 0.212, KB = 0.087 luminance interpretation
-   *  for RGB conversion (as in SMPTE 240M).
-   *
-   * Gamma Correction (GC):
-   *
-   *  if Vlinear < 0.018
-   *    Vnonlinear = 4.500 * Vlinear
-   *  else
-   *    Vnonlinear = 1.099 * (Vlinear)^(0.45) – 0.099
-   */
-  HAL_COLOR_MODE_STANDARD_BT601_525_UNADJUSTED = 4,
-
-  /*
-   * HAL_COLOR_MODE_REC709 corresponds with display settings that implement
-   * the ITU-R Recommendation BT.709 / Rec. 709 for high-definition television.
-   * Rendering Intent: Colorimetric
-   * Primaries:
-   *                  x       y
-   *  green           0.300   0.600
-   *  blue            0.150   0.060
-   *  red             0.640   0.330
-   *  white (D65)     0.3127  0.3290
-   *
-   * HDTV REC709 Inverse Gamma Correction (IGC): V represents normalized
-   * (with [0 to 1] range) value of R, G, or B.
-   *
-   *  if Vnonlinear < 0.081
-   *    Vlinear = Vnonlinear / 4.5
-   *  else
-   *    Vlinear = ((Vnonlinear + 0.099) / 1.099) ^ (1/0.45)
-   *
-   * HDTV REC709 Gamma Correction (GC):
-   *
-   *  if Vlinear < 0.018
-   *    Vnonlinear = 4.5 * Vlinear
-   *  else
-   *    Vnonlinear = 1.099 * (Vlinear) ^ 0.45 – 0.099
-   */
-  HAL_COLOR_MODE_STANDARD_BT709 = 5,
-
-  /*
-   * HAL_COLOR_MODE_DCI_P3 corresponds with display settings that implement
-   * SMPTE EG 432-1 and SMPTE RP 431-2
-   * Rendering Intent: Colorimetric
-   * Primaries:
-   *                  x       y
-   *  green           0.265   0.690
-   *  blue            0.150   0.060
-   *  red             0.680   0.320
-   *  white (D65)     0.3127  0.3290
-   *
-   * Gamma: 2.2
-   */
-  HAL_COLOR_MODE_DCI_P3 = 6,
-
-  /*
-   * HAL_COLOR_MODE_SRGB corresponds with display settings that implement
-   * the sRGB color space. Uses the same primaries as ITU-R Recommendation
-   * BT.709
-   * Rendering Intent: Colorimetric
-   * Primaries:
-   *                  x       y
-   *  green           0.300   0.600
-   *  blue            0.150   0.060
-   *  red             0.640   0.330
-   *  white (D65)     0.3127  0.3290
-   *
-   * PC/Internet (sRGB) Inverse Gamma Correction (IGC):
-   *
-   *  if Vnonlinear ≤ 0.03928
-   *    Vlinear = Vnonlinear / 12.92
-   *  else
-   *    Vlinear = ((Vnonlinear + 0.055)/1.055) ^ 2.4
-   *
-   * PC/Internet (sRGB) Gamma Correction (GC):
-   *
-   *  if Vlinear ≤ 0.0031308
-   *    Vnonlinear = 12.92 * Vlinear
-   *  else
-   *    Vnonlinear = 1.055 * (Vlinear)^(1/2.4) – 0.055
-   */
-  HAL_COLOR_MODE_SRGB = 7,
-
-  /*
-   * HAL_COLOR_MODE_ADOBE_RGB corresponds with the RGB color space developed
-   * by Adobe Systems, Inc. in 1998.
-   * Rendering Intent: Colorimetric
-   * Primaries:
-   *                  x       y
-   *  green           0.210   0.710
-   *  blue            0.150   0.060
-   *  red             0.640   0.330
-   *  white (D65)     0.3127  0.3290
-   *
-   * Gamma: 2.2
-   */
-  HAL_COLOR_MODE_ADOBE_RGB = 8
-
-} android_color_mode_t;
-
-/*
- * Color transforms that may be applied by hardware composer to the whole
- * display.
- */
-typedef enum android_color_transform {
-    /* Applies no transform to the output color */
-    HAL_COLOR_TRANSFORM_IDENTITY = 0,
-
-    /* Applies an arbitrary transform defined by a 4x4 affine matrix */
-    HAL_COLOR_TRANSFORM_ARBITRARY_MATRIX = 1,
-
-    /* Applies a transform that inverts the value or luminance of the color, but
-     * does not modify hue or saturation */
-    HAL_COLOR_TRANSFORM_VALUE_INVERSE = 2,
-
-    /* Applies a transform that maps all colors to shades of gray */
-    HAL_COLOR_TRANSFORM_GRAYSCALE = 3,
-
-    /* Applies a transform which corrects for protanopic color blindness */
-    HAL_COLOR_TRANSFORM_CORRECT_PROTANOPIA = 4,
-
-    /* Applies a transform which corrects for deuteranopic color blindness */
-    HAL_COLOR_TRANSFORM_CORRECT_DEUTERANOPIA = 5,
-
-    /* Applies a transform which corrects for tritanopic color blindness */
-    HAL_COLOR_TRANSFORM_CORRECT_TRITANOPIA = 6
-} android_color_transform_t;
-
-/*
- * Supported HDR formats. Must be kept in sync with equivalents in Display.java.
- */
-typedef enum android_hdr {
-    /* Device supports Dolby Vision HDR */
-    HAL_HDR_DOLBY_VISION = 1,
-
-    /* Device supports HDR10 */
-    HAL_HDR_HDR10 = 2,
-
-    /* Device supports hybrid log-gamma HDR */
-    HAL_HDR_HLG = 3
-} android_hdr_t;
+struct android_smpte2086_metadata {
+    struct android_xy_color displayPrimaryRed;
+    struct android_xy_color displayPrimaryGreen;
+    struct android_xy_color displayPrimaryBlue;
+    struct android_xy_color whitePoint;
+    float maxLuminance;
+    float minLuminance;
+};
 
 #ifdef __cplusplus
 }
diff --git a/libsystem/include/system/qemu_pipe.h b/libsystem/include/system/qemu_pipe.h
deleted file mode 100644
index af25079..0000000
--- a/libsystem/include/system/qemu_pipe.h
+++ /dev/null
@@ -1,134 +0,0 @@
-/*
- * Copyright (C) 2011 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 ANDROID_INCLUDE_SYSTEM_QEMU_PIPE_H
-#define ANDROID_INCLUDE_SYSTEM_QEMU_PIPE_H
-
-#include <unistd.h>
-#include <fcntl.h>
-#include <string.h>
-#include <errno.h>
-
-// Define QEMU_PIPE_DEBUG if you want to print error messages when an error
-// occurs during pipe operations. The macro should simply take a printf-style
-// formatting string followed by optional arguments.
-#ifndef QEMU_PIPE_DEBUG
-#  define  QEMU_PIPE_DEBUG(...)   (void)0
-#endif
-
-// Try to open a new Qemu fast-pipe. This function returns a file descriptor
-// that can be used to communicate with a named service managed by the
-// emulator.
-//
-// This file descriptor can be used as a standard pipe/socket descriptor.
-//
-// 'pipeName' is the name of the emulator service you want to connect to,
-// and must begin with 'pipe:' (e.g. 'pipe:camera' or 'pipe:opengles').
-//
-// On success, return a valid file descriptor, or -1/errno on failure. E.g.:
-//
-// EINVAL  -> unknown/unsupported pipeName
-// ENOSYS  -> fast pipes not available in this system.
-//
-// ENOSYS should never happen, except if you're trying to run within a
-// misconfigured emulator.
-//
-// You should be able to open several pipes to the same pipe service,
-// except for a few special cases (e.g. GSM modem), where EBUSY will be
-// returned if more than one client tries to connect to it.
-static __inline__ int qemu_pipe_open(const char* pipeName) {
-    // Sanity check.
-    if (!pipeName || memcmp(pipeName, "pipe:", 5) != 0) {
-        errno = EINVAL;
-        return -1;
-    }
-
-    int fd = TEMP_FAILURE_RETRY(open("/dev/qemu_pipe", O_RDWR));
-    if (fd < 0) {
-        QEMU_PIPE_DEBUG("%s: Could not open /dev/qemu_pipe: %s", __FUNCTION__,
-                        strerror(errno));
-        return -1;
-    }
-
-    // Write the pipe name, *including* the trailing zero which is necessary.
-    size_t pipeNameLen = strlen(pipeName);
-    ssize_t ret = TEMP_FAILURE_RETRY(write(fd, pipeName, pipeNameLen + 1U));
-    if (ret != (ssize_t)pipeNameLen + 1) {
-        QEMU_PIPE_DEBUG("%s: Could not connect to %s pipe service: %s",
-                        __FUNCTION__, pipeName, strerror(errno));
-        if (ret == 0) {
-            errno = ECONNRESET;
-        } else if (ret > 0) {
-            errno = EINVAL;
-        }
-        return -1;
-    }
-    return fd;
-}
-
-// Send a framed message |buff| of |len| bytes through the |fd| descriptor.
-// This really adds a 4-hexchar prefix describing the payload size.
-// Returns 0 on success, and -1 on error.
-static int __inline__ qemu_pipe_frame_send(int fd,
-                                           const void* buff,
-                                           size_t len) {
-    char header[5];
-    snprintf(header, sizeof(header), "%04zx", len);
-    ssize_t ret = TEMP_FAILURE_RETRY(write(fd, header, 4));
-    if (ret != 4) {
-        QEMU_PIPE_DEBUG("Can't write qemud frame header: %s", strerror(errno));
-        return -1;
-    }
-    ret = TEMP_FAILURE_RETRY(write(fd, buff, len));
-    if (ret != (ssize_t)len) {
-        QEMU_PIPE_DEBUG("Can't write qemud frame payload: %s", strerror(errno));
-        return -1;
-    }
-    return 0;
-}
-
-// Read a frame message from |fd|, and store it into |buff| of |len| bytes.
-// If the framed message is larger than |len|, then this returns -1 and the
-// content is lost. Otherwise, this returns the size of the message. NOTE:
-// empty messages are possible in a framed wire protocol and do not mean
-// end-of-stream.
-static int __inline__ qemu_pipe_frame_recv(int fd, void* buff, size_t len) {
-    char header[5];
-    ssize_t ret = TEMP_FAILURE_RETRY(read(fd, header, 4));
-    if (ret != 4) {
-        QEMU_PIPE_DEBUG("Can't read qemud frame header: %s", strerror(errno));
-        return -1;
-    }
-    header[4] = '\0';
-    size_t size;
-    if (sscanf(header, "%04zx", &size) != 1) {
-        QEMU_PIPE_DEBUG("Malformed qemud frame header: [%.*s]", 4, header);
-        return -1;
-    }
-    if (size > len) {
-        QEMU_PIPE_DEBUG("Oversized qemud frame (% bytes, expected <= %)", size,
-                        len);
-        return -1;
-    }
-    ret = TEMP_FAILURE_RETRY(read(fd, buff, size));
-    if (ret != (ssize_t)size) {
-        QEMU_PIPE_DEBUG("Could not read qemud frame payload: %s",
-                        strerror(errno));
-        return -1;
-    }
-    return size;
-}
-
-#endif /* ANDROID_INCLUDE_HARDWARE_QEMUD_PIPE_H */
diff --git a/libsystem/include/system/radio.h b/libsystem/include/system/radio.h
index 03b252e..acf3ea7 100644
--- a/libsystem/include/system/radio.h
+++ b/libsystem/include/system/radio.h
@@ -81,7 +81,7 @@
 } radio_direction_t;
 
 /* unique handle allocated to a radio module */
-typedef unsigned int radio_handle_t;
+typedef uint32_t radio_handle_t;
 
 /* Opaque meta data structure used by radio meta data API (see system/radio_metadata.h) */
 typedef struct radio_metadata radio_metadata_t;
@@ -109,10 +109,10 @@
 typedef struct radio_hal_band_config {
     radio_band_t type;
     bool         antenna_connected;
-    unsigned int lower_limit;
-    unsigned int upper_limit;
-    unsigned int num_spacings;
-    unsigned int spacings[RADIO_NUM_SPACINGS_MAX];
+    uint32_t     lower_limit;
+    uint32_t     upper_limit;
+    uint32_t     num_spacings;
+    uint32_t     spacings[RADIO_NUM_SPACINGS_MAX];
     union {
         radio_hal_fm_band_config_t fm;
         radio_hal_am_band_config_t am;
@@ -137,10 +137,10 @@
     char            product[RADIO_STRING_LEN_MAX];  /* product name */
     char            version[RADIO_STRING_LEN_MAX];  /* product version */
     char            serial[RADIO_STRING_LEN_MAX];  /* serial number (for subscription services) */
-    unsigned int    num_tuners;     /* number of tuners controllable independently */
-    unsigned int    num_audio_sources; /* number of audio sources driven simultaneously */
+    uint32_t        num_tuners;     /* number of tuners controllable independently */
+    uint32_t        num_audio_sources; /* number of audio sources driven simultaneously */
     bool            supports_capture; /* the hardware supports capture of audio source audio HAL */
-    unsigned int    num_bands;      /* number of band descriptors */
+    uint32_t        num_bands;      /* number of band descriptors */
     radio_hal_band_config_t bands[RADIO_NUM_BANDS_MAX]; /* band descriptors */
 } radio_hal_properties_t;
 
@@ -153,10 +153,10 @@
     char                product[RADIO_STRING_LEN_MAX];
     char                version[RADIO_STRING_LEN_MAX];
     char                serial[RADIO_STRING_LEN_MAX];
-    unsigned int        num_tuners;
-    unsigned int        num_audio_sources;
+    uint32_t            num_tuners;
+    uint32_t            num_audio_sources;
     bool                supports_capture;
-    unsigned int        num_bands;
+    uint32_t            num_bands;
     radio_band_config_t bands[RADIO_NUM_BANDS_MAX];
 } radio_properties_t;
 
@@ -164,12 +164,12 @@
  * Contains information on currently tuned channel.
  */
 typedef struct radio_program_info {
-    unsigned int     channel;   /* current channel. (e.g kHz for band type RADIO_BAND_FM) */
-    unsigned int     sub_channel; /* current sub channel. (used for RADIO_BAND_FM_HD) */
+    uint32_t         channel;   /* current channel. (e.g kHz for band type RADIO_BAND_FM) */
+    uint32_t         sub_channel; /* current sub channel. (used for RADIO_BAND_FM_HD) */
     bool             tuned;     /* tuned to a program or not */
     bool             stereo;    /* program is stereo or not */
     bool             digital;   /* digital program or not (e.g HD Radio program) */
-    unsigned int     signal_strength; /* signal strength from 0 to 100 */
+    uint32_t         signal_strength; /* signal strength from 0 to 100 */
                                 /* meta data (e.g PTY, song title ...), must not be NULL */
     __attribute__((aligned(8))) radio_metadata_t *metadata;
 } radio_program_info_t;
@@ -196,7 +196,7 @@
 /* Event passed to the framework by the HAL callback */
 typedef struct radio_hal_event {
     radio_event_type_t  type;       /* event type */
-    int                 status;     /* used by RADIO_EVENT_CONFIG, RADIO_EVENT_TUNED */
+    int32_t             status;     /* used by RADIO_EVENT_CONFIG, RADIO_EVENT_TUNED */
     union {
         /* RADIO_EVENT_ANTENNA, RADIO_EVENT_TA, RADIO_EVENT_EA */
         bool                    on;
@@ -209,12 +209,13 @@
 /* Used internally by the framework. Same information as in struct radio_hal_event */
 typedef struct radio_event {
     radio_event_type_t  type;
-    int                 status;
+    int32_t             status;
     union {
         bool                    on;
         radio_band_config_t     config;
         radio_program_info_t    info;
-        radio_metadata_t        *metadata; /* offset from start of struct when in shared memory */
+                                /* meta data (e.g PTY, song title ...), must not be NULL */
+        __attribute__((aligned(8))) radio_metadata_t *metadata;
     };
 } radio_event_t;
 
diff --git a/libsystem/include/system/thread_defs.h b/libsystem/include/system/thread_defs.h
index 377a48c..80d1160 100644
--- a/libsystem/include/system/thread_defs.h
+++ b/libsystem/include/system/thread_defs.h
@@ -55,6 +55,9 @@
     /* ui service treads might want to run at a urgent display (uncommon) */
     ANDROID_PRIORITY_URGENT_DISPLAY =  HAL_PRIORITY_URGENT_DISPLAY,
 
+    /* all normal video threads */
+    ANDROID_PRIORITY_VIDEO          = -10,
+
     /* all normal audio threads */
     ANDROID_PRIORITY_AUDIO          = -16,
 
diff --git a/libsystem/include/system/window.h b/libsystem/include/system/window.h
deleted file mode 100644
index f439705..0000000
--- a/libsystem/include/system/window.h
+++ /dev/null
@@ -1,1013 +0,0 @@
-/*
- * Copyright (C) 2011 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 SYSTEM_CORE_INCLUDE_ANDROID_WINDOW_H
-#define SYSTEM_CORE_INCLUDE_ANDROID_WINDOW_H
-
-#include <cutils/native_handle.h>
-#include <errno.h>
-#include <limits.h>
-#include <stdint.h>
-#include <string.h>
-#include <sys/cdefs.h>
-#include <system/graphics.h>
-#include <unistd.h>
-#include <stdbool.h>
-
-#ifndef __UNUSED
-#define __UNUSED __attribute__((__unused__))
-#endif
-#ifndef __deprecated
-#define __deprecated __attribute__((__deprecated__))
-#endif
-
-__BEGIN_DECLS
-
-/*****************************************************************************/
-
-#ifdef __cplusplus
-#define ANDROID_NATIVE_UNSIGNED_CAST(x) static_cast<unsigned int>(x)
-#else
-#define ANDROID_NATIVE_UNSIGNED_CAST(x) ((unsigned int)(x))
-#endif
-
-#define ANDROID_NATIVE_MAKE_CONSTANT(a,b,c,d) \
-    ((ANDROID_NATIVE_UNSIGNED_CAST(a) << 24) | \
-     (ANDROID_NATIVE_UNSIGNED_CAST(b) << 16) | \
-     (ANDROID_NATIVE_UNSIGNED_CAST(c) << 8) | \
-     (ANDROID_NATIVE_UNSIGNED_CAST(d)))
-
-#define ANDROID_NATIVE_WINDOW_MAGIC \
-    ANDROID_NATIVE_MAKE_CONSTANT('_','w','n','d')
-
-#define ANDROID_NATIVE_BUFFER_MAGIC \
-    ANDROID_NATIVE_MAKE_CONSTANT('_','b','f','r')
-
-// ---------------------------------------------------------------------------
-
-// This #define may be used to conditionally compile device-specific code to
-// support either the prior ANativeWindow interface, which did not pass libsync
-// fences around, or the new interface that does.  This #define is only present
-// when the ANativeWindow interface does include libsync support.
-#define ANDROID_NATIVE_WINDOW_HAS_SYNC 1
-
-// ---------------------------------------------------------------------------
-
-typedef const native_handle_t* buffer_handle_t;
-
-// ---------------------------------------------------------------------------
-
-typedef struct android_native_rect_t
-{
-    int32_t left;
-    int32_t top;
-    int32_t right;
-    int32_t bottom;
-} android_native_rect_t;
-
-// ---------------------------------------------------------------------------
-
-typedef struct android_native_base_t
-{
-    /* a magic value defined by the actual EGL native type */
-    int magic;
-
-    /* the sizeof() of the actual EGL native type */
-    int version;
-
-    void* reserved[4];
-
-    /* reference-counting interface */
-    void (*incRef)(struct android_native_base_t* base);
-    void (*decRef)(struct android_native_base_t* base);
-} android_native_base_t;
-
-typedef struct ANativeWindowBuffer
-{
-#ifdef __cplusplus
-    ANativeWindowBuffer() {
-        common.magic = ANDROID_NATIVE_BUFFER_MAGIC;
-        common.version = sizeof(ANativeWindowBuffer);
-        memset(common.reserved, 0, sizeof(common.reserved));
-    }
-
-    // Implement the methods that sp<ANativeWindowBuffer> expects so that it
-    // can be used to automatically refcount ANativeWindowBuffer's.
-    void incStrong(const void* /*id*/) const {
-        common.incRef(const_cast<android_native_base_t*>(&common));
-    }
-    void decStrong(const void* /*id*/) const {
-        common.decRef(const_cast<android_native_base_t*>(&common));
-    }
-#endif
-
-    struct android_native_base_t common;
-
-    int width;
-    int height;
-    int stride;
-    int format;
-    int usage;
-
-    void* reserved[2];
-
-    buffer_handle_t handle;
-
-    void* reserved_proc[8];
-} ANativeWindowBuffer_t;
-
-// Old typedef for backwards compatibility.
-typedef ANativeWindowBuffer_t android_native_buffer_t;
-
-// ---------------------------------------------------------------------------
-
-/* attributes queriable with query() */
-enum {
-    NATIVE_WINDOW_WIDTH     = 0,
-    NATIVE_WINDOW_HEIGHT    = 1,
-    NATIVE_WINDOW_FORMAT    = 2,
-
-    /* The minimum number of buffers that must remain un-dequeued after a buffer
-     * has been queued.  This value applies only if set_buffer_count was used to
-     * override the number of buffers and if a buffer has since been queued.
-     * Users of the set_buffer_count ANativeWindow method should query this
-     * value before calling set_buffer_count.  If it is necessary to have N
-     * buffers simultaneously dequeued as part of the steady-state operation,
-     * and this query returns M then N+M buffers should be requested via
-     * native_window_set_buffer_count.
-     *
-     * Note that this value does NOT apply until a single buffer has been
-     * queued.  In particular this means that it is possible to:
-     *
-     * 1. Query M = min undequeued buffers
-     * 2. Set the buffer count to N + M
-     * 3. Dequeue all N + M buffers
-     * 4. Cancel M buffers
-     * 5. Queue, dequeue, queue, dequeue, ad infinitum
-     */
-    NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS = 3,
-
-    /* Check whether queueBuffer operations on the ANativeWindow send the buffer
-     * to the window compositor.  The query sets the returned 'value' argument
-     * to 1 if the ANativeWindow DOES send queued buffers directly to the window
-     * compositor and 0 if the buffers do not go directly to the window
-     * compositor.
-     *
-     * This can be used to determine whether protected buffer content should be
-     * sent to the ANativeWindow.  Note, however, that a result of 1 does NOT
-     * indicate that queued buffers will be protected from applications or users
-     * capturing their contents.  If that behavior is desired then some other
-     * mechanism (e.g. the GRALLOC_USAGE_PROTECTED flag) should be used in
-     * conjunction with this query.
-     */
-    NATIVE_WINDOW_QUEUES_TO_WINDOW_COMPOSER = 4,
-
-    /* Get the concrete type of a ANativeWindow.  See below for the list of
-     * possible return values.
-     *
-     * This query should not be used outside the Android framework and will
-     * likely be removed in the near future.
-     */
-    NATIVE_WINDOW_CONCRETE_TYPE = 5,
-
-
-    /*
-     * Default width and height of ANativeWindow buffers, these are the
-     * dimensions of the window buffers irrespective of the
-     * NATIVE_WINDOW_SET_BUFFERS_DIMENSIONS call and match the native window
-     * size unless overridden by NATIVE_WINDOW_SET_BUFFERS_USER_DIMENSIONS.
-     */
-    NATIVE_WINDOW_DEFAULT_WIDTH = 6,
-    NATIVE_WINDOW_DEFAULT_HEIGHT = 7,
-
-    /*
-     * transformation that will most-likely be applied to buffers. This is only
-     * a hint, the actual transformation applied might be different.
-     *
-     * INTENDED USE:
-     *
-     * The transform hint can be used by a producer, for instance the GLES
-     * driver, to pre-rotate the rendering such that the final transformation
-     * in the composer is identity. This can be very useful when used in
-     * conjunction with the h/w composer HAL, in situations where it
-     * cannot handle arbitrary rotations.
-     *
-     * 1. Before dequeuing a buffer, the GL driver (or any other ANW client)
-     *    queries the ANW for NATIVE_WINDOW_TRANSFORM_HINT.
-     *
-     * 2. The GL driver overrides the width and height of the ANW to
-     *    account for NATIVE_WINDOW_TRANSFORM_HINT. This is done by querying
-     *    NATIVE_WINDOW_DEFAULT_{WIDTH | HEIGHT}, swapping the dimensions
-     *    according to NATIVE_WINDOW_TRANSFORM_HINT and calling
-     *    native_window_set_buffers_dimensions().
-     *
-     * 3. The GL driver dequeues a buffer of the new pre-rotated size.
-     *
-     * 4. The GL driver renders to the buffer such that the image is
-     *    already transformed, that is applying NATIVE_WINDOW_TRANSFORM_HINT
-     *    to the rendering.
-     *
-     * 5. The GL driver calls native_window_set_transform to apply
-     *    inverse transformation to the buffer it just rendered.
-     *    In order to do this, the GL driver needs
-     *    to calculate the inverse of NATIVE_WINDOW_TRANSFORM_HINT, this is
-     *    done easily:
-     *
-     *        int hintTransform, inverseTransform;
-     *        query(..., NATIVE_WINDOW_TRANSFORM_HINT, &hintTransform);
-     *        inverseTransform = hintTransform;
-     *        if (hintTransform & HAL_TRANSFORM_ROT_90)
-     *            inverseTransform ^= HAL_TRANSFORM_ROT_180;
-     *
-     *
-     * 6. The GL driver queues the pre-transformed buffer.
-     *
-     * 7. The composer combines the buffer transform with the display
-     *    transform.  If the buffer transform happens to cancel out the
-     *    display transform then no rotation is needed.
-     *
-     */
-    NATIVE_WINDOW_TRANSFORM_HINT = 8,
-
-    /*
-     * Boolean that indicates whether the consumer is running more than
-     * one buffer behind the producer.
-     */
-    NATIVE_WINDOW_CONSUMER_RUNNING_BEHIND = 9,
-
-    /*
-     * The consumer gralloc usage bits currently set by the consumer.
-     * The values are defined in hardware/libhardware/include/gralloc.h.
-     */
-    NATIVE_WINDOW_CONSUMER_USAGE_BITS = 10,
-
-    /**
-     * Transformation that will by applied to buffers by the hwcomposer.
-     * This must not be set or checked by producer endpoints, and will
-     * disable the transform hint set in SurfaceFlinger (see
-     * NATIVE_WINDOW_TRANSFORM_HINT).
-     *
-     * INTENDED USE:
-     * Temporary - Please do not use this.  This is intended only to be used
-     * by the camera's LEGACY mode.
-     *
-     * In situations where a SurfaceFlinger client wishes to set a transform
-     * that is not visible to the producer, and will always be applied in the
-     * hardware composer, the client can set this flag with
-     * native_window_set_buffers_sticky_transform.  This can be used to rotate
-     * and flip buffers consumed by hardware composer without actually changing
-     * the aspect ratio of the buffers produced.
-     */
-    NATIVE_WINDOW_STICKY_TRANSFORM = 11,
-
-    /**
-     * The default data space for the buffers as set by the consumer.
-     * The values are defined in graphics.h.
-     */
-    NATIVE_WINDOW_DEFAULT_DATASPACE = 12,
-
-    /*
-     * Returns the age of the contents of the most recently dequeued buffer as
-     * the number of frames that have elapsed since it was last queued. For
-     * example, if the window is double-buffered, the age of any given buffer in
-     * steady state will be 2. If the dequeued buffer has never been queued, its
-     * age will be 0.
-     */
-    NATIVE_WINDOW_BUFFER_AGE = 13,
-
-    /*
-     * Returns the duration of the last dequeueBuffer call in microseconds
-     */
-    NATIVE_WINDOW_LAST_DEQUEUE_DURATION = 14,
-
-    /*
-     * Returns the duration of the last queueBuffer call in microseconds
-     */
-    NATIVE_WINDOW_LAST_QUEUE_DURATION = 15,
-};
-
-/* Valid operations for the (*perform)() hook.
- *
- * Values marked as 'deprecated' are supported, but have been superceded by
- * other functionality.
- *
- * Values marked as 'private' should be considered private to the framework.
- * HAL implementation code with access to an ANativeWindow should not use these,
- * as it may not interact properly with the framework's use of the
- * ANativeWindow.
- */
-enum {
-    NATIVE_WINDOW_SET_USAGE                 =  0,
-    NATIVE_WINDOW_CONNECT                   =  1,   /* deprecated */
-    NATIVE_WINDOW_DISCONNECT                =  2,   /* deprecated */
-    NATIVE_WINDOW_SET_CROP                  =  3,   /* private */
-    NATIVE_WINDOW_SET_BUFFER_COUNT          =  4,
-    NATIVE_WINDOW_SET_BUFFERS_GEOMETRY      =  5,   /* deprecated */
-    NATIVE_WINDOW_SET_BUFFERS_TRANSFORM     =  6,
-    NATIVE_WINDOW_SET_BUFFERS_TIMESTAMP     =  7,
-    NATIVE_WINDOW_SET_BUFFERS_DIMENSIONS    =  8,
-    NATIVE_WINDOW_SET_BUFFERS_FORMAT        =  9,
-    NATIVE_WINDOW_SET_SCALING_MODE          = 10,   /* private */
-    NATIVE_WINDOW_LOCK                      = 11,   /* private */
-    NATIVE_WINDOW_UNLOCK_AND_POST           = 12,   /* private */
-    NATIVE_WINDOW_API_CONNECT               = 13,   /* private */
-    NATIVE_WINDOW_API_DISCONNECT            = 14,   /* private */
-    NATIVE_WINDOW_SET_BUFFERS_USER_DIMENSIONS = 15, /* private */
-    NATIVE_WINDOW_SET_POST_TRANSFORM_CROP   = 16,   /* private */
-    NATIVE_WINDOW_SET_BUFFERS_STICKY_TRANSFORM = 17,/* private */
-    NATIVE_WINDOW_SET_SIDEBAND_STREAM       = 18,
-    NATIVE_WINDOW_SET_BUFFERS_DATASPACE     = 19,
-    NATIVE_WINDOW_SET_SURFACE_DAMAGE        = 20,   /* private */
-    NATIVE_WINDOW_SET_SHARED_BUFFER_MODE    = 21,
-    NATIVE_WINDOW_SET_AUTO_REFRESH          = 22,
-    NATIVE_WINDOW_GET_FRAME_TIMESTAMPS      = 23,
-};
-
-/* parameter for NATIVE_WINDOW_[API_][DIS]CONNECT */
-enum {
-    /* Buffers will be queued by EGL via eglSwapBuffers after being filled using
-     * OpenGL ES.
-     */
-    NATIVE_WINDOW_API_EGL = 1,
-
-    /* Buffers will be queued after being filled using the CPU
-     */
-    NATIVE_WINDOW_API_CPU = 2,
-
-    /* Buffers will be queued by Stagefright after being filled by a video
-     * decoder.  The video decoder can either be a software or hardware decoder.
-     */
-    NATIVE_WINDOW_API_MEDIA = 3,
-
-    /* Buffers will be queued by the the camera HAL.
-     */
-    NATIVE_WINDOW_API_CAMERA = 4,
-};
-
-/* parameter for NATIVE_WINDOW_SET_BUFFERS_TRANSFORM */
-enum {
-    /* flip source image horizontally */
-    NATIVE_WINDOW_TRANSFORM_FLIP_H = HAL_TRANSFORM_FLIP_H ,
-    /* flip source image vertically */
-    NATIVE_WINDOW_TRANSFORM_FLIP_V = HAL_TRANSFORM_FLIP_V,
-    /* rotate source image 90 degrees clock-wise, and is applied after TRANSFORM_FLIP_{H|V} */
-    NATIVE_WINDOW_TRANSFORM_ROT_90 = HAL_TRANSFORM_ROT_90,
-    /* rotate source image 180 degrees */
-    NATIVE_WINDOW_TRANSFORM_ROT_180 = HAL_TRANSFORM_ROT_180,
-    /* rotate source image 270 degrees clock-wise */
-    NATIVE_WINDOW_TRANSFORM_ROT_270 = HAL_TRANSFORM_ROT_270,
-    /* transforms source by the inverse transform of the screen it is displayed onto. This
-     * transform is applied last */
-    NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY = 0x08
-};
-
-/* parameter for NATIVE_WINDOW_SET_SCALING_MODE
- * keep in sync with Surface.java in frameworks/base */
-enum {
-    /* the window content is not updated (frozen) until a buffer of
-     * the window size is received (enqueued)
-     */
-    NATIVE_WINDOW_SCALING_MODE_FREEZE           = 0,
-    /* the buffer is scaled in both dimensions to match the window size */
-    NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW  = 1,
-    /* the buffer is scaled uniformly such that the smaller dimension
-     * of the buffer matches the window size (cropping in the process)
-     */
-    NATIVE_WINDOW_SCALING_MODE_SCALE_CROP       = 2,
-    /* the window is clipped to the size of the buffer's crop rectangle; pixels
-     * outside the crop rectangle are treated as if they are completely
-     * transparent.
-     */
-    NATIVE_WINDOW_SCALING_MODE_NO_SCALE_CROP    = 3,
-};
-
-/* values returned by the NATIVE_WINDOW_CONCRETE_TYPE query */
-enum {
-    NATIVE_WINDOW_FRAMEBUFFER               = 0, /* FramebufferNativeWindow */
-    NATIVE_WINDOW_SURFACE                   = 1, /* Surface */
-};
-
-/* parameter for NATIVE_WINDOW_SET_BUFFERS_TIMESTAMP
- *
- * Special timestamp value to indicate that timestamps should be auto-generated
- * by the native window when queueBuffer is called.  This is equal to INT64_MIN,
- * defined directly to avoid problems with C99/C++ inclusion of stdint.h.
- */
-static const int64_t NATIVE_WINDOW_TIMESTAMP_AUTO = (-9223372036854775807LL-1);
-
-struct ANativeWindow
-{
-#ifdef __cplusplus
-    ANativeWindow()
-        : flags(0), minSwapInterval(0), maxSwapInterval(0), xdpi(0), ydpi(0)
-    {
-        common.magic = ANDROID_NATIVE_WINDOW_MAGIC;
-        common.version = sizeof(ANativeWindow);
-        memset(common.reserved, 0, sizeof(common.reserved));
-    }
-
-    /* Implement the methods that sp<ANativeWindow> expects so that it
-       can be used to automatically refcount ANativeWindow's. */
-    void incStrong(const void* /*id*/) const {
-        common.incRef(const_cast<android_native_base_t*>(&common));
-    }
-    void decStrong(const void* /*id*/) const {
-        common.decRef(const_cast<android_native_base_t*>(&common));
-    }
-#endif
-
-    struct android_native_base_t common;
-
-    /* flags describing some attributes of this surface or its updater */
-    const uint32_t flags;
-
-    /* min swap interval supported by this updated */
-    const int   minSwapInterval;
-
-    /* max swap interval supported by this updated */
-    const int   maxSwapInterval;
-
-    /* horizontal and vertical resolution in DPI */
-    const float xdpi;
-    const float ydpi;
-
-    /* Some storage reserved for the OEM's driver. */
-    intptr_t    oem[4];
-
-    /*
-     * Set the swap interval for this surface.
-     *
-     * Returns 0 on success or -errno on error.
-     */
-    int     (*setSwapInterval)(struct ANativeWindow* window,
-                int interval);
-
-    /*
-     * Hook called by EGL to acquire a buffer. After this call, the buffer
-     * is not locked, so its content cannot be modified. This call may block if
-     * no buffers are available.
-     *
-     * The window holds a reference to the buffer between dequeueBuffer and
-     * either queueBuffer or cancelBuffer, so clients only need their own
-     * reference if they might use the buffer after queueing or canceling it.
-     * Holding a reference to a buffer after queueing or canceling it is only
-     * allowed if a specific buffer count has been set.
-     *
-     * Returns 0 on success or -errno on error.
-     *
-     * XXX: This function is deprecated.  It will continue to work for some
-     * time for binary compatibility, but the new dequeueBuffer function that
-     * outputs a fence file descriptor should be used in its place.
-     */
-    int     (*dequeueBuffer_DEPRECATED)(struct ANativeWindow* window,
-                struct ANativeWindowBuffer** buffer);
-
-    /*
-     * hook called by EGL to lock a buffer. This MUST be called before modifying
-     * the content of a buffer. The buffer must have been acquired with
-     * dequeueBuffer first.
-     *
-     * Returns 0 on success or -errno on error.
-     *
-     * XXX: This function is deprecated.  It will continue to work for some
-     * time for binary compatibility, but it is essentially a no-op, and calls
-     * to it should be removed.
-     */
-    int     (*lockBuffer_DEPRECATED)(struct ANativeWindow* window,
-                struct ANativeWindowBuffer* buffer);
-
-    /*
-     * Hook called by EGL when modifications to the render buffer are done.
-     * This unlocks and post the buffer.
-     *
-     * The window holds a reference to the buffer between dequeueBuffer and
-     * either queueBuffer or cancelBuffer, so clients only need their own
-     * reference if they might use the buffer after queueing or canceling it.
-     * Holding a reference to a buffer after queueing or canceling it is only
-     * allowed if a specific buffer count has been set.
-     *
-     * Buffers MUST be queued in the same order than they were dequeued.
-     *
-     * Returns 0 on success or -errno on error.
-     *
-     * XXX: This function is deprecated.  It will continue to work for some
-     * time for binary compatibility, but the new queueBuffer function that
-     * takes a fence file descriptor should be used in its place (pass a value
-     * of -1 for the fence file descriptor if there is no valid one to pass).
-     */
-    int     (*queueBuffer_DEPRECATED)(struct ANativeWindow* window,
-                struct ANativeWindowBuffer* buffer);
-
-    /*
-     * hook used to retrieve information about the native window.
-     *
-     * Returns 0 on success or -errno on error.
-     */
-    int     (*query)(const struct ANativeWindow* window,
-                int what, int* value);
-
-    /*
-     * hook used to perform various operations on the surface.
-     * (*perform)() is a generic mechanism to add functionality to
-     * ANativeWindow while keeping backward binary compatibility.
-     *
-     * DO NOT CALL THIS HOOK DIRECTLY.  Instead, use the helper functions
-     * defined below.
-     *
-     * (*perform)() returns -ENOENT if the 'what' parameter is not supported
-     * by the surface's implementation.
-     *
-     * See above for a list of valid operations, such as
-     * NATIVE_WINDOW_SET_USAGE or NATIVE_WINDOW_CONNECT
-     */
-    int     (*perform)(struct ANativeWindow* window,
-                int operation, ... );
-
-    /*
-     * Hook used to cancel a buffer that has been dequeued.
-     * No synchronization is performed between dequeue() and cancel(), so
-     * either external synchronization is needed, or these functions must be
-     * called from the same thread.
-     *
-     * The window holds a reference to the buffer between dequeueBuffer and
-     * either queueBuffer or cancelBuffer, so clients only need their own
-     * reference if they might use the buffer after queueing or canceling it.
-     * Holding a reference to a buffer after queueing or canceling it is only
-     * allowed if a specific buffer count has been set.
-     *
-     * XXX: This function is deprecated.  It will continue to work for some
-     * time for binary compatibility, but the new cancelBuffer function that
-     * takes a fence file descriptor should be used in its place (pass a value
-     * of -1 for the fence file descriptor if there is no valid one to pass).
-     */
-    int     (*cancelBuffer_DEPRECATED)(struct ANativeWindow* window,
-                struct ANativeWindowBuffer* buffer);
-
-    /*
-     * Hook called by EGL to acquire a buffer. This call may block if no
-     * buffers are available.
-     *
-     * The window holds a reference to the buffer between dequeueBuffer and
-     * either queueBuffer or cancelBuffer, so clients only need their own
-     * reference if they might use the buffer after queueing or canceling it.
-     * Holding a reference to a buffer after queueing or canceling it is only
-     * allowed if a specific buffer count has been set.
-     *
-     * The libsync fence file descriptor returned in the int pointed to by the
-     * fenceFd argument will refer to the fence that must signal before the
-     * dequeued buffer may be written to.  A value of -1 indicates that the
-     * caller may access the buffer immediately without waiting on a fence.  If
-     * a valid file descriptor is returned (i.e. any value except -1) then the
-     * caller is responsible for closing the file descriptor.
-     *
-     * Returns 0 on success or -errno on error.
-     */
-    int     (*dequeueBuffer)(struct ANativeWindow* window,
-                struct ANativeWindowBuffer** buffer, int* fenceFd);
-
-    /*
-     * Hook called by EGL when modifications to the render buffer are done.
-     * This unlocks and post the buffer.
-     *
-     * The window holds a reference to the buffer between dequeueBuffer and
-     * either queueBuffer or cancelBuffer, so clients only need their own
-     * reference if they might use the buffer after queueing or canceling it.
-     * Holding a reference to a buffer after queueing or canceling it is only
-     * allowed if a specific buffer count has been set.
-     *
-     * The fenceFd argument specifies a libsync fence file descriptor for a
-     * fence that must signal before the buffer can be accessed.  If the buffer
-     * can be accessed immediately then a value of -1 should be used.  The
-     * caller must not use the file descriptor after it is passed to
-     * queueBuffer, and the ANativeWindow implementation is responsible for
-     * closing it.
-     *
-     * Returns 0 on success or -errno on error.
-     */
-    int     (*queueBuffer)(struct ANativeWindow* window,
-                struct ANativeWindowBuffer* buffer, int fenceFd);
-
-    /*
-     * Hook used to cancel a buffer that has been dequeued.
-     * No synchronization is performed between dequeue() and cancel(), so
-     * either external synchronization is needed, or these functions must be
-     * called from the same thread.
-     *
-     * The window holds a reference to the buffer between dequeueBuffer and
-     * either queueBuffer or cancelBuffer, so clients only need their own
-     * reference if they might use the buffer after queueing or canceling it.
-     * Holding a reference to a buffer after queueing or canceling it is only
-     * allowed if a specific buffer count has been set.
-     *
-     * The fenceFd argument specifies a libsync fence file decsriptor for a
-     * fence that must signal before the buffer can be accessed.  If the buffer
-     * can be accessed immediately then a value of -1 should be used.
-     *
-     * Note that if the client has not waited on the fence that was returned
-     * from dequeueBuffer, that same fence should be passed to cancelBuffer to
-     * ensure that future uses of the buffer are preceded by a wait on that
-     * fence.  The caller must not use the file descriptor after it is passed
-     * to cancelBuffer, and the ANativeWindow implementation is responsible for
-     * closing it.
-     *
-     * Returns 0 on success or -errno on error.
-     */
-    int     (*cancelBuffer)(struct ANativeWindow* window,
-                struct ANativeWindowBuffer* buffer, int fenceFd);
-};
-
- /* Backwards compatibility: use ANativeWindow (struct ANativeWindow in C).
-  * android_native_window_t is deprecated.
-  */
-typedef struct ANativeWindow ANativeWindow;
-typedef struct ANativeWindow android_native_window_t __deprecated;
-
-/*
- *  native_window_set_usage(..., usage)
- *  Sets the intended usage flags for the next buffers
- *  acquired with (*lockBuffer)() and on.
- *  By default (if this function is never called), a usage of
- *      GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE
- *  is assumed.
- *  Calling this function will usually cause following buffers to be
- *  reallocated.
- */
-
-static inline int native_window_set_usage(
-        struct ANativeWindow* window, int usage)
-{
-    return window->perform(window, NATIVE_WINDOW_SET_USAGE, usage);
-}
-
-/* deprecated. Always returns 0. Don't call. */
-static inline int native_window_connect(
-        struct ANativeWindow* window __UNUSED, int api __UNUSED) __deprecated;
-
-static inline int native_window_connect(
-        struct ANativeWindow* window __UNUSED, int api __UNUSED) {
-    return 0;
-}
-
-/* deprecated. Always returns 0. Don't call. */
-static inline int native_window_disconnect(
-        struct ANativeWindow* window __UNUSED, int api __UNUSED) __deprecated;
-
-static inline int native_window_disconnect(
-        struct ANativeWindow* window __UNUSED, int api __UNUSED) {
-    return 0;
-}
-
-/*
- * native_window_set_crop(..., crop)
- * Sets which region of the next queued buffers needs to be considered.
- * Depending on the scaling mode, a buffer's crop region is scaled and/or
- * cropped to match the surface's size.  This function sets the crop in
- * pre-transformed buffer pixel coordinates.
- *
- * The specified crop region applies to all buffers queued after it is called.
- *
- * If 'crop' is NULL, subsequently queued buffers won't be cropped.
- *
- * An error is returned if for instance the crop region is invalid, out of the
- * buffer's bound or if the window is invalid.
- */
-static inline int native_window_set_crop(
-        struct ANativeWindow* window,
-        android_native_rect_t const * crop)
-{
-    return window->perform(window, NATIVE_WINDOW_SET_CROP, crop);
-}
-
-/*
- * native_window_set_post_transform_crop(..., crop)
- * Sets which region of the next queued buffers needs to be considered.
- * Depending on the scaling mode, a buffer's crop region is scaled and/or
- * cropped to match the surface's size.  This function sets the crop in
- * post-transformed pixel coordinates.
- *
- * The specified crop region applies to all buffers queued after it is called.
- *
- * If 'crop' is NULL, subsequently queued buffers won't be cropped.
- *
- * An error is returned if for instance the crop region is invalid, out of the
- * buffer's bound or if the window is invalid.
- */
-static inline int native_window_set_post_transform_crop(
-        struct ANativeWindow* window,
-        android_native_rect_t const * crop)
-{
-    return window->perform(window, NATIVE_WINDOW_SET_POST_TRANSFORM_CROP, crop);
-}
-
-/*
- * native_window_set_active_rect(..., active_rect)
- *
- * This function is deprecated and will be removed soon.  For now it simply
- * sets the post-transform crop for compatibility while multi-project commits
- * get checked.
- */
-static inline int native_window_set_active_rect(
-        struct ANativeWindow* window,
-        android_native_rect_t const * active_rect) __deprecated;
-
-static inline int native_window_set_active_rect(
-        struct ANativeWindow* window,
-        android_native_rect_t const * active_rect)
-{
-    return native_window_set_post_transform_crop(window, active_rect);
-}
-
-/*
- * native_window_set_buffer_count(..., count)
- * Sets the number of buffers associated with this native window.
- */
-static inline int native_window_set_buffer_count(
-        struct ANativeWindow* window,
-        size_t bufferCount)
-{
-    return window->perform(window, NATIVE_WINDOW_SET_BUFFER_COUNT, bufferCount);
-}
-
-/*
- * native_window_set_buffers_geometry(..., int w, int h, int format)
- * All buffers dequeued after this call will have the dimensions and format
- * specified.  A successful call to this function has the same effect as calling
- * native_window_set_buffers_size and native_window_set_buffers_format.
- *
- * XXX: This function is deprecated.  The native_window_set_buffers_dimensions
- * and native_window_set_buffers_format functions should be used instead.
- */
-static inline int native_window_set_buffers_geometry(
-        struct ANativeWindow* window,
-        int w, int h, int format) __deprecated;
-
-static inline int native_window_set_buffers_geometry(
-        struct ANativeWindow* window,
-        int w, int h, int format)
-{
-    return window->perform(window, NATIVE_WINDOW_SET_BUFFERS_GEOMETRY,
-            w, h, format);
-}
-
-/*
- * native_window_set_buffers_dimensions(..., int w, int h)
- * All buffers dequeued after this call will have the dimensions specified.
- * In particular, all buffers will have a fixed-size, independent from the
- * native-window size. They will be scaled according to the scaling mode
- * (see native_window_set_scaling_mode) upon window composition.
- *
- * If w and h are 0, the normal behavior is restored. That is, dequeued buffers
- * following this call will be sized to match the window's size.
- *
- * Calling this function will reset the window crop to a NULL value, which
- * disables cropping of the buffers.
- */
-static inline int native_window_set_buffers_dimensions(
-        struct ANativeWindow* window,
-        int w, int h)
-{
-    return window->perform(window, NATIVE_WINDOW_SET_BUFFERS_DIMENSIONS,
-            w, h);
-}
-
-/*
- * native_window_set_buffers_user_dimensions(..., int w, int h)
- *
- * Sets the user buffer size for the window, which overrides the
- * window's size.  All buffers dequeued after this call will have the
- * dimensions specified unless overridden by
- * native_window_set_buffers_dimensions.  All buffers will have a
- * fixed-size, independent from the native-window size. They will be
- * scaled according to the scaling mode (see
- * native_window_set_scaling_mode) upon window composition.
- *
- * If w and h are 0, the normal behavior is restored. That is, the
- * default buffer size will match the windows's size.
- *
- * Calling this function will reset the window crop to a NULL value, which
- * disables cropping of the buffers.
- */
-static inline int native_window_set_buffers_user_dimensions(
-        struct ANativeWindow* window,
-        int w, int h)
-{
-    return window->perform(window, NATIVE_WINDOW_SET_BUFFERS_USER_DIMENSIONS,
-            w, h);
-}
-
-/*
- * native_window_set_buffers_format(..., int format)
- * All buffers dequeued after this call will have the format specified.
- *
- * If the specified format is 0, the default buffer format will be used.
- */
-static inline int native_window_set_buffers_format(
-        struct ANativeWindow* window,
-        int format)
-{
-    return window->perform(window, NATIVE_WINDOW_SET_BUFFERS_FORMAT, format);
-}
-
-/*
- * native_window_set_buffers_data_space(..., int dataSpace)
- * All buffers queued after this call will be associated with the dataSpace
- * parameter specified.
- *
- * dataSpace specifies additional information about the buffer that's dependent
- * on the buffer format and the endpoints. For example, it can be used to convey
- * the color space of the image data in the buffer, or it can be used to
- * indicate that the buffers contain depth measurement data instead of color
- * images.  The default dataSpace is 0, HAL_DATASPACE_UNKNOWN, unless it has been
- * overridden by the consumer.
- */
-static inline int native_window_set_buffers_data_space(
-        struct ANativeWindow* window,
-        android_dataspace_t dataSpace)
-{
-    return window->perform(window, NATIVE_WINDOW_SET_BUFFERS_DATASPACE,
-            dataSpace);
-}
-
-/*
- * native_window_set_buffers_transform(..., int transform)
- * All buffers queued after this call will be displayed transformed according
- * to the transform parameter specified.
- */
-static inline int native_window_set_buffers_transform(
-        struct ANativeWindow* window,
-        int transform)
-{
-    return window->perform(window, NATIVE_WINDOW_SET_BUFFERS_TRANSFORM,
-            transform);
-}
-
-/*
- * native_window_set_buffers_sticky_transform(..., int transform)
- * All buffers queued after this call will be displayed transformed according
- * to the transform parameter specified applied on top of the regular buffer
- * transform.  Setting this transform will disable the transform hint.
- *
- * Temporary - This is only intended to be used by the LEGACY camera mode, do
- *   not use this for anything else.
- */
-static inline int native_window_set_buffers_sticky_transform(
-        struct ANativeWindow* window,
-        int transform)
-{
-    return window->perform(window, NATIVE_WINDOW_SET_BUFFERS_STICKY_TRANSFORM,
-            transform);
-}
-
-/*
- * native_window_set_buffers_timestamp(..., int64_t timestamp)
- * All buffers queued after this call will be associated with the timestamp
- * parameter specified. If the timestamp is set to NATIVE_WINDOW_TIMESTAMP_AUTO
- * (the default), timestamps will be generated automatically when queueBuffer is
- * called. The timestamp is measured in nanoseconds, and is normally monotonically
- * increasing. The timestamp should be unaffected by time-of-day adjustments,
- * and for a camera should be strictly monotonic but for a media player may be
- * reset when the position is set.
- */
-static inline int native_window_set_buffers_timestamp(
-        struct ANativeWindow* window,
-        int64_t timestamp)
-{
-    return window->perform(window, NATIVE_WINDOW_SET_BUFFERS_TIMESTAMP,
-            timestamp);
-}
-
-/*
- * native_window_set_scaling_mode(..., int mode)
- * All buffers queued after this call will be associated with the scaling mode
- * specified.
- */
-static inline int native_window_set_scaling_mode(
-        struct ANativeWindow* window,
-        int mode)
-{
-    return window->perform(window, NATIVE_WINDOW_SET_SCALING_MODE,
-            mode);
-}
-
-/*
- * native_window_api_connect(..., int api)
- * connects an API to this window. only one API can be connected at a time.
- * Returns -EINVAL if for some reason the window cannot be connected, which
- * can happen if it's connected to some other API.
- */
-static inline int native_window_api_connect(
-        struct ANativeWindow* window, int api)
-{
-    return window->perform(window, NATIVE_WINDOW_API_CONNECT, api);
-}
-
-/*
- * native_window_api_disconnect(..., int api)
- * disconnect the API from this window.
- * An error is returned if for instance the window wasn't connected in the
- * first place.
- */
-static inline int native_window_api_disconnect(
-        struct ANativeWindow* window, int api)
-{
-    return window->perform(window, NATIVE_WINDOW_API_DISCONNECT, api);
-}
-
-/*
- * native_window_dequeue_buffer_and_wait(...)
- * Dequeue a buffer and wait on the fence associated with that buffer.  The
- * buffer may safely be accessed immediately upon this function returning.  An
- * error is returned if either of the dequeue or the wait operations fail.
- */
-static inline int native_window_dequeue_buffer_and_wait(ANativeWindow *anw,
-        struct ANativeWindowBuffer** anb) {
-    return anw->dequeueBuffer_DEPRECATED(anw, anb);
-}
-
-/*
- * native_window_set_sideband_stream(..., native_handle_t*)
- * Attach a sideband buffer stream to a native window.
- */
-static inline int native_window_set_sideband_stream(
-        struct ANativeWindow* window,
-        native_handle_t* sidebandHandle)
-{
-    return window->perform(window, NATIVE_WINDOW_SET_SIDEBAND_STREAM,
-            sidebandHandle);
-}
-
-/*
- * native_window_set_surface_damage(..., android_native_rect_t* rects, int numRects)
- * Set the surface damage (i.e., the region of the surface that has changed
- * since the previous frame). The damage set by this call will be reset (to the
- * default of full-surface damage) after calling queue, so this must be called
- * prior to every frame with damage that does not cover the whole surface if the
- * caller desires downstream consumers to use this optimization.
- *
- * The damage region is specified as an array of rectangles, with the important
- * caveat that the origin of the surface is considered to be the bottom-left
- * corner, as in OpenGL ES.
- *
- * If numRects is set to 0, rects may be NULL, and the surface damage will be
- * set to the full surface (the same as if this function had not been called for
- * this frame).
- */
-static inline int native_window_set_surface_damage(
-        struct ANativeWindow* window,
-        const android_native_rect_t* rects, size_t numRects)
-{
-    return window->perform(window, NATIVE_WINDOW_SET_SURFACE_DAMAGE,
-            rects, numRects);
-}
-
-/*
- * native_window_set_shared_buffer_mode(..., bool sharedBufferMode)
- * Enable/disable shared buffer mode
- */
-static inline int native_window_set_shared_buffer_mode(
-        struct ANativeWindow* window,
-        bool sharedBufferMode)
-{
-    return window->perform(window, NATIVE_WINDOW_SET_SHARED_BUFFER_MODE,
-            sharedBufferMode);
-}
-
-/*
- * native_window_set_auto_refresh(..., autoRefresh)
- * Enable/disable auto refresh when in shared buffer mode
- */
-static inline int native_window_set_auto_refresh(
-        struct ANativeWindow* window,
-        bool autoRefresh)
-{
-    return window->perform(window, NATIVE_WINDOW_SET_AUTO_REFRESH, autoRefresh);
-}
-
-static inline int native_window_get_frame_timestamps(
-        struct ANativeWindow* window, uint32_t framesAgo,
-        int64_t* outPostedTime, int64_t* outAcquireTime,
-        int64_t* outRefreshStartTime, int64_t* outGlCompositionDoneTime,
-        int64_t* outDisplayRetireTime, int64_t* outReleaseTime)
-{
-    return window->perform(window, NATIVE_WINDOW_GET_FRAME_TIMESTAMPS,
-            framesAgo, outPostedTime, outAcquireTime, outRefreshStartTime,
-            outGlCompositionDoneTime, outDisplayRetireTime, outReleaseTime);
-}
-
-
-__END_DECLS
-
-#endif /* SYSTEM_CORE_INCLUDE_ANDROID_WINDOW_H */
diff --git a/libsysutils/Android.bp b/libsysutils/Android.bp
index 550ef42..3a12292 100644
--- a/libsysutils/Android.bp
+++ b/libsysutils/Android.bp
@@ -1,6 +1,9 @@
 cc_library_shared {
     name: "libsysutils",
     vendor_available: true,
+    vndk: {
+        enabled: true,
+    },
 
     srcs: [
         "src/SocketListener.cpp",
@@ -20,7 +23,6 @@
         "libbase",
         "libcutils",
         "liblog",
-        "libnl",
     ],
 
     export_include_dirs: ["include"],
diff --git a/libsysutils/include/sysutils/FrameworkClient.h b/libsysutils/include/sysutils/FrameworkClient.h
deleted file mode 100644
index 4a3f0de..0000000
--- a/libsysutils/include/sysutils/FrameworkClient.h
+++ /dev/null
@@ -1,21 +0,0 @@
-#ifndef _FRAMEWORK_CLIENT_H
-#define _FRAMEWORK_CLIENT_H
-
-#include "List.h"
-
-#include <pthread.h>
-
-class FrameworkClient {
-    int             mSocket;
-    pthread_mutex_t mWriteMutex;
-
-public:
-    FrameworkClient(int sock);
-    virtual ~FrameworkClient() {}
-
-    int sendMsg(const char *msg);
-    int sendMsg(const char *msg, const char *data);
-};
-
-typedef android::sysutils::List<FrameworkClient *> FrameworkClientCollection;
-#endif
diff --git a/libsysutils/include/sysutils/NetlinkEvent.h b/libsysutils/include/sysutils/NetlinkEvent.h
index b80f3ea..f9fc11b 100644
--- a/libsysutils/include/sysutils/NetlinkEvent.h
+++ b/libsysutils/include/sysutils/NetlinkEvent.h
@@ -64,6 +64,7 @@
     bool parseNfPacketMessage(struct nlmsghdr *nh);
     bool parseRtMessage(const struct nlmsghdr *nh);
     bool parseNdUserOptMessage(const struct nlmsghdr *nh);
+    struct nlattr* findNlAttr(const nlmsghdr* nl, size_t hdrlen, uint16_t attr);
 };
 
 #endif
diff --git a/libsysutils/include/sysutils/OWNERS b/libsysutils/include/sysutils/OWNERS
new file mode 100644
index 0000000..b78918e
--- /dev/null
+++ b/libsysutils/include/sysutils/OWNERS
@@ -0,0 +1,2 @@
+per-file Netlink* = ek@google.com
+per-file Netlink* = lorenzo@google.com
diff --git a/libsysutils/src/FrameworkClient.cpp b/libsysutils/src/FrameworkClient.cpp
deleted file mode 100644
index 72b3d0a..0000000
--- a/libsysutils/src/FrameworkClient.cpp
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright (C) 2009-2016 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.
- */
-
-#define LOG_TAG "FrameworkClient"
-
-#include <alloca.h>
-#include <errno.h>
-#include <pthread.h>
-#include <sys/types.h>
-
-#include <android/log.h>
-#include <sysutils/FrameworkClient.h>
-
-FrameworkClient::FrameworkClient(int socket) {
-    mSocket = socket;
-    pthread_mutex_init(&mWriteMutex, NULL);
-}
-
-int FrameworkClient::sendMsg(const char *msg) {
-    int ret;
-    if (mSocket < 0) {
-        errno = EHOSTUNREACH;
-        return -1;
-    }
-
-    pthread_mutex_lock(&mWriteMutex);
-    ret = TEMP_FAILURE_RETRY(write(mSocket, msg, strlen(msg) +1));
-    if (ret < 0) {
-        SLOGW("Unable to send msg '%s' (%s)", msg, strerror(errno));
-    }
-    pthread_mutex_unlock(&mWriteMutex);
-    return 0;
-}
-
-int FrameworkClient::sendMsg(const char *msg, const char *data) {
-    size_t bufflen = strlen(msg) + strlen(data) + 1;
-    char *buffer = (char *) alloca(bufflen);
-    if (!buffer) {
-        errno = -ENOMEM;
-        return -1;
-    }
-    snprintf(buffer, bufflen, "%s%s", msg, data);
-    return sendMsg(buffer);
-}
-
diff --git a/libsysutils/src/FrameworkListener.cpp b/libsysutils/src/FrameworkListener.cpp
index 6f76e76..835e226 100644
--- a/libsysutils/src/FrameworkListener.cpp
+++ b/libsysutils/src/FrameworkListener.cpp
@@ -42,7 +42,7 @@
 
 FrameworkListener::FrameworkListener(int sock) :
                             SocketListener(sock, true) {
-    init(NULL, false);
+    init(nullptr, false);
 }
 
 void FrameworkListener::init(const char *socketName UNUSED, bool withSeq) {
@@ -154,7 +154,7 @@
             if (!haveCmdNum) {
                 char *endptr;
                 int cmdNum = (int)strtol(tmp, &endptr, 0);
-                if (endptr == NULL || *endptr != '\0') {
+                if (endptr == nullptr || *endptr != '\0') {
                     cli->sendMsg(500, "Invalid sequence number", false);
                     goto out;
                 }
@@ -211,7 +211,6 @@
     return;
 
 overflow:
-    LOG_EVENT_INT(78001, cli->getUid());
     cli->sendMsg(500, "Command too long", false);
     goto out;
 }
diff --git a/libsysutils/src/NetlinkEvent.cpp b/libsysutils/src/NetlinkEvent.cpp
index 79bc888..24ea7aa 100644
--- a/libsysutils/src/NetlinkEvent.cpp
+++ b/libsysutils/src/NetlinkEvent.cpp
@@ -17,6 +17,8 @@
 #define LOG_TAG "NetlinkEvent"
 
 #include <arpa/inet.h>
+#include <limits.h>
+#include <linux/genetlink.h>
 #include <linux/if.h>
 #include <linux/if_addr.h>
 #include <linux/if_link.h>
@@ -26,12 +28,8 @@
 #include <linux/netlink.h>
 #include <linux/rtnetlink.h>
 #include <net/if.h>
-#include <netinet/in.h>
 #include <netinet/icmp6.h>
-#include <netlink/attr.h>
-#include <netlink/genl/genl.h>
-#include <netlink/handlers.h>
-#include <netlink/msg.h>
+#include <netinet/in.h>
 #include <stdlib.h>
 #include <string.h>
 #include <sys/socket.h>
@@ -47,8 +45,8 @@
 NetlinkEvent::NetlinkEvent() {
     mAction = Action::kUnknown;
     memset(mParams, 0, sizeof(mParams));
-    mPath = NULL;
-    mSubsystem = NULL;
+    mPath = nullptr;
+    mSubsystem = nullptr;
 }
 
 NetlinkEvent::~NetlinkEvent() {
@@ -91,7 +89,7 @@
         NL_EVENT_RTM_NAME(LOCAL_QLOG_NL_EVENT);
         NL_EVENT_RTM_NAME(LOCAL_NFLOG_PACKET);
         default:
-            return NULL;
+            return nullptr;
     }
 #undef NL_EVENT_RTM_NAME
 }
@@ -139,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");
@@ -154,7 +158,7 @@
  */
 bool NetlinkEvent::parseIfAddrMessage(const struct nlmsghdr *nh) {
     struct ifaddrmsg *ifaddr = (struct ifaddrmsg *) NLMSG_DATA(nh);
-    struct ifa_cacheinfo *cacheinfo = NULL;
+    struct ifa_cacheinfo *cacheinfo = nullptr;
     char addrstr[INET6_ADDRSTRLEN] = "";
     char ifname[IFNAMSIZ] = "";
 
@@ -235,12 +239,13 @@
     asprintf(&mParams[1], "INTERFACE=%s", ifname);
     asprintf(&mParams[2], "FLAGS=%u", ifaddr->ifa_flags);
     asprintf(&mParams[3], "SCOPE=%u", ifaddr->ifa_scope);
+    asprintf(&mParams[4], "IFINDEX=%u", ifaddr->ifa_index);
 
     if (cacheinfo) {
-        asprintf(&mParams[4], "PREFERRED=%u", cacheinfo->ifa_prefered);
-        asprintf(&mParams[5], "VALID=%u", cacheinfo->ifa_valid);
-        asprintf(&mParams[6], "CSTAMP=%u", cacheinfo->cstamp);
-        asprintf(&mParams[7], "TSTAMP=%u", cacheinfo->tstamp);
+        asprintf(&mParams[5], "PREFERRED=%u", cacheinfo->ifa_prefered);
+        asprintf(&mParams[6], "VALID=%u", cacheinfo->ifa_valid);
+        asprintf(&mParams[7], "CSTAMP=%u", cacheinfo->cstamp);
+        asprintf(&mParams[8], "TSTAMP=%u", cacheinfo->tstamp);
     }
 
     return true;
@@ -263,25 +268,37 @@
     return true;
 }
 
+static size_t nlAttrLen(const nlattr* nla) {
+    return nla->nla_len - NLA_HDRLEN;
+}
+
+static const uint8_t* nlAttrData(const nlattr* nla) {
+    return reinterpret_cast<const uint8_t*>(nla) + NLA_HDRLEN;
+}
+
+static uint32_t nlAttrU32(const nlattr* nla) {
+    return *reinterpret_cast<const uint32_t*>(nlAttrData(nla));
+}
+
 /*
  * Parse a LOCAL_NFLOG_PACKET message.
  */
 bool NetlinkEvent::parseNfPacketMessage(struct nlmsghdr *nh) {
     int uid = -1;
     int len = 0;
-    char* raw = NULL;
+    char* raw = nullptr;
 
-    struct nlattr *uid_attr = nlmsg_find_attr(nh, sizeof(struct genlmsghdr), NFULA_UID);
+    struct nlattr* uid_attr = findNlAttr(nh, sizeof(struct genlmsghdr), NFULA_UID);
     if (uid_attr) {
-        uid = ntohl(nla_get_u32(uid_attr));
+        uid = ntohl(nlAttrU32(uid_attr));
     }
 
-    struct nlattr *payload = nlmsg_find_attr(nh, sizeof(struct genlmsghdr), NFULA_PAYLOAD);
+    struct nlattr* payload = findNlAttr(nh, sizeof(struct genlmsghdr), NFULA_PAYLOAD);
     if (payload) {
         /* First 256 bytes is plenty */
-        len = nla_len(payload);
+        len = nlAttrLen(payload);
         if (len > 256) len = 256;
-        raw = (char*) nla_data(payload);
+        raw = (char*)nlAttrData(payload);
     }
 
     char* hex = (char*) calloc(1, 5 + (len * 2));
@@ -491,6 +508,8 @@
         asprintf(&mParams[0], "INTERFACE=%s", ifname);
         asprintf(&mParams[1], "LIFETIME=%u", lifetime);
         mParams[2] = buf;
+    } else if (opthdr->nd_opt_type == ND_OPT_DNSSL) {
+        // TODO: support DNSSL.
     } else {
         SLOGD("Unknown ND option type %d\n", opthdr->nd_opt_type);
         return false;
@@ -565,7 +584,7 @@
         (prefixlen == 0 || !memcmp(str, prefix, prefixlen))) {
         return str + prefixlen;
     } else {
-        return NULL;
+        return nullptr;
     }
 }
 
@@ -606,16 +625,16 @@
             first = 0;
         } else {
             const char* a;
-            if ((a = HAS_CONST_PREFIX(s, end, "ACTION=")) != NULL) {
+            if ((a = HAS_CONST_PREFIX(s, end, "ACTION=")) != nullptr) {
                 if (!strcmp(a, "add"))
                     mAction = Action::kAdd;
                 else if (!strcmp(a, "remove"))
                     mAction = Action::kRemove;
                 else if (!strcmp(a, "change"))
                     mAction = Action::kChange;
-            } else if ((a = HAS_CONST_PREFIX(s, end, "SEQNUM=")) != NULL) {
+            } else if ((a = HAS_CONST_PREFIX(s, end, "SEQNUM=")) != nullptr) {
                 mSeq = atoi(a);
-            } else if ((a = HAS_CONST_PREFIX(s, end, "SUBSYSTEM=")) != NULL) {
+            } else if ((a = HAS_CONST_PREFIX(s, end, "SUBSYSTEM=")) != nullptr) {
                 mSubsystem = strdup(a);
             } else if (param_idx < NL_PARAMS_MAX) {
                 mParams[param_idx++] = strdup(s);
@@ -637,12 +656,35 @@
 
 const char *NetlinkEvent::findParam(const char *paramName) {
     size_t len = strlen(paramName);
-    for (int i = 0; i < NL_PARAMS_MAX && mParams[i] != NULL; ++i) {
+    for (int i = 0; i < NL_PARAMS_MAX && mParams[i] != nullptr; ++i) {
         const char *ptr = mParams[i] + len;
         if (!strncmp(mParams[i], paramName, len) && *ptr == '=')
             return ++ptr;
     }
 
     SLOGE("NetlinkEvent::FindParam(): Parameter '%s' not found", paramName);
-    return NULL;
+    return nullptr;
+}
+
+nlattr* NetlinkEvent::findNlAttr(const nlmsghdr* nh, size_t hdrlen, uint16_t attr) {
+    if (nh == nullptr || NLMSG_HDRLEN + NLMSG_ALIGN(hdrlen) > SSIZE_MAX) {
+        return nullptr;
+    }
+
+    // Skip header, padding, and family header.
+    const ssize_t NLA_START = NLMSG_HDRLEN + NLMSG_ALIGN(hdrlen);
+    ssize_t left = nh->nlmsg_len - NLA_START;
+    uint8_t* hdr = ((uint8_t*)nh) + NLA_START;
+
+    while (left >= NLA_HDRLEN) {
+        nlattr* nla = (nlattr*)hdr;
+        if (nla->nla_type == attr) {
+            return nla;
+        }
+
+        hdr += NLA_ALIGN(nla->nla_len);
+        left -= NLA_ALIGN(nla->nla_len);
+    }
+
+    return nullptr;
 }
diff --git a/libsysutils/src/NetlinkListener.cpp b/libsysutils/src/NetlinkListener.cpp
index 896dad3..aad0394 100644
--- a/libsysutils/src/NetlinkListener.cpp
+++ b/libsysutils/src/NetlinkListener.cpp
@@ -57,8 +57,6 @@
     count = TEMP_FAILURE_RETRY(uevent_kernel_recv(socket,
             mBuffer, sizeof(mBuffer), require_group, &uid));
     if (count < 0) {
-        if (uid > 0)
-            LOG_EVENT_INT(65537, uid);
         SLOGE("recvmsg failed (%s)", strerror(errno));
         return false;
     }
diff --git a/libsysutils/src/OWNERS b/libsysutils/src/OWNERS
new file mode 100644
index 0000000..b78918e
--- /dev/null
+++ b/libsysutils/src/OWNERS
@@ -0,0 +1,2 @@
+per-file Netlink* = ek@google.com
+per-file Netlink* = lorenzo@google.com
diff --git a/libsysutils/src/SocketClient.cpp b/libsysutils/src/SocketClient.cpp
index 971f908..0625db7 100644
--- a/libsysutils/src/SocketClient.cpp
+++ b/libsysutils/src/SocketClient.cpp
@@ -42,8 +42,8 @@
     mSocket = socket;
     mSocketOwned = owned;
     mUseCmdNum = useCmdNum;
-    pthread_mutex_init(&mWriteMutex, NULL);
-    pthread_mutex_init(&mRefCountMutex, NULL);
+    pthread_mutex_init(&mWriteMutex, nullptr);
+    pthread_mutex_init(&mRefCountMutex, nullptr);
     mPid = -1;
     mUid = -1;
     mGid = -1;
@@ -135,9 +135,9 @@
     const char *end = arg + len;
     char *oldresult;
 
-    if(result == NULL) {
+    if(result == nullptr) {
         SLOGW("malloc error (%s)", strerror(errno));
-        return NULL;
+        return nullptr;
     }
 
     *(current++) = '"';
diff --git a/libsysutils/src/SocketListener.cpp b/libsysutils/src/SocketListener.cpp
index 3f8f3db..0c8a688 100644
--- a/libsysutils/src/SocketListener.cpp
+++ b/libsysutils/src/SocketListener.cpp
@@ -39,7 +39,7 @@
 }
 
 SocketListener::SocketListener(int socketFd, bool listen) {
-    init(NULL, socketFd, listen, false);
+    init(nullptr, socketFd, listen, false);
 }
 
 SocketListener::SocketListener(const char *socketName, bool listen, bool useCmdNum) {
@@ -51,7 +51,7 @@
     mSocketName = socketName;
     mSock = socketFd;
     mUseCmdNum = useCmdNum;
-    pthread_mutex_init(&mClientsLock, NULL);
+    pthread_mutex_init(&mClientsLock, nullptr);
     mClients = new SocketClientCollection();
 }
 
@@ -102,7 +102,7 @@
         return -1;
     }
 
-    if (pthread_create(&mThread, NULL, SocketListener::threadStart, this)) {
+    if (pthread_create(&mThread, nullptr, SocketListener::threadStart, this)) {
         SLOGE("pthread_create (%s)", strerror(errno));
         return -1;
     }
@@ -147,8 +147,8 @@
     SocketListener *me = reinterpret_cast<SocketListener *>(obj);
 
     me->runListener();
-    pthread_exit(NULL);
-    return NULL;
+    pthread_exit(nullptr);
+    return nullptr;
 }
 
 void SocketListener::runListener() {
@@ -183,7 +183,7 @@
         }
         pthread_mutex_unlock(&mClientsLock);
         SLOGV("mListen=%d, max=%d, mSocketName=%s", mListen, max, mSocketName);
-        if ((rc = select(max + 1, &read_fds, NULL, NULL, NULL)) < 0) {
+        if ((rc = select(max + 1, &read_fds, nullptr, nullptr, nullptr)) < 0) {
             if (errno == EINTR)
                 continue;
             SLOGE("select failed (%s) mListen=%d, max=%d", strerror(errno), mListen, max);
diff --git a/libunwindstack/Android.bp b/libunwindstack/Android.bp
index 03a9a54..df5da65 100644
--- a/libunwindstack/Android.bp
+++ b/libunwindstack/Android.bp
@@ -29,89 +29,174 @@
         darwin: {
             enabled: false,
         },
-    },
-
-    multilib: {
-        lib32: {
-            suffix: "32",
-        },
-        lib64: {
-            suffix: "64",
+        linux_bionic: {
+            enabled: true,
         },
     },
 }
 
-cc_defaults {
-    name: "libunwindstack_common",
+cc_library {
+    name: "libunwindstack",
+    vendor_available: true,
+    recovery_available: true,
+    vndk: {
+        enabled: true,
+        support_system_process: true,
+    },
     defaults: ["libunwindstack_flags"],
+    export_include_dirs: ["include"],
 
     srcs: [
         "ArmExidx.cpp",
+        "DexFile.cpp",
+        "DexFiles.cpp",
         "DwarfCfa.cpp",
-        "DwarfDebugFrame.cpp",
-        "DwarfEhFrame.cpp",
+        "DwarfEhFrameWithHdr.cpp",
         "DwarfMemory.cpp",
         "DwarfOp.cpp",
         "DwarfSection.cpp",
         "Elf.cpp",
         "ElfInterface.cpp",
         "ElfInterfaceArm.cpp",
+        "JitDebug.cpp",
         "Log.cpp",
         "MapInfo.cpp",
         "Maps.cpp",
         "Memory.cpp",
+        "LocalUnwinder.cpp",
         "Regs.cpp",
+        "RegsArm.cpp",
+        "RegsArm64.cpp",
+        "RegsX86.cpp",
+        "RegsX86_64.cpp",
+        "RegsMips.cpp",
+        "RegsMips64.cpp",
+        "Unwinder.cpp",
         "Symbols.cpp",
     ],
 
+    cflags: [
+        "-Wexit-time-destructors",
+    ],
+
+    target: {
+        // Always disable optimizations for host to make it easier to debug.
+        host: {
+            cflags: [
+                "-O0",
+                "-g",
+            ],
+        },
+        vendor: {
+            cflags: ["-DNO_LIBDEXFILE_SUPPORT"],
+            exclude_srcs: [
+                "DexFile.cpp",
+                "DexFiles.cpp",
+            ],
+            exclude_shared_libs: ["libdexfile"],
+        },
+        recovery: {
+            cflags: ["-DNO_LIBDEXFILE_SUPPORT"],
+            exclude_srcs: [
+                "DexFile.cpp",
+                "DexFiles.cpp",
+            ],
+            exclude_shared_libs: ["libdexfile"],
+        },
+    },
+
+    arch: {
+        x86: {
+            srcs: ["AsmGetRegsX86.S"],
+        },
+        x86_64: {
+            srcs: ["AsmGetRegsX86_64.S"],
+        },
+        mips: {
+            srcs: ["AsmGetRegsMips.S"],
+        },
+        mips64: {
+            srcs: ["AsmGetRegsMips64.S"],
+        },
+    },
+
+    static_libs: [
+        "libprocinfo",
+    ],
+
     shared_libs: [
         "libbase",
+        "libdexfile",
         "liblog",
         "liblzma",
     ],
 }
 
-cc_library {
-    name: "libunwindstack",
-    defaults: ["libunwindstack_common"],
-}
-
 //-------------------------------------------------------------------------
 // Unit Tests
 //-------------------------------------------------------------------------
-cc_defaults {
-    name: "libunwindstack_test_common",
+cc_test_library {
+    name: "libunwindstack_local",
+    defaults: ["libunwindstack_flags"],
+    srcs: ["tests/TestLocal.cpp"],
+
+    cflags: [
+        "-O0",
+        "-g",
+    ],
+
+    shared_libs: [
+        "libunwindstack",
+    ],
+}
+
+cc_test {
+    name: "libunwindstack_test",
     defaults: ["libunwindstack_flags"],
 
     srcs: [
         "tests/ArmExidxDecodeTest.cpp",
         "tests/ArmExidxExtractTest.cpp",
+        "tests/DexFileTest.cpp",
+        "tests/DexFilesTest.cpp",
         "tests/DwarfCfaLogTest.cpp",
         "tests/DwarfCfaTest.cpp",
         "tests/DwarfDebugFrameTest.cpp",
         "tests/DwarfEhFrameTest.cpp",
+        "tests/DwarfEhFrameWithHdrTest.cpp",
         "tests/DwarfMemoryTest.cpp",
         "tests/DwarfOpLogTest.cpp",
         "tests/DwarfOpTest.cpp",
         "tests/DwarfSectionTest.cpp",
         "tests/DwarfSectionImplTest.cpp",
+        "tests/ElfCacheTest.cpp",
+        "tests/ElfFake.cpp",
         "tests/ElfInterfaceArmTest.cpp",
         "tests/ElfInterfaceTest.cpp",
         "tests/ElfTest.cpp",
         "tests/ElfTestUtils.cpp",
+        "tests/JitDebugTest.cpp",
+        "tests/LocalUnwinderTest.cpp",
         "tests/LogFake.cpp",
-        "tests/MapInfoCreateMemoryTest.cpp",
         "tests/MapInfoGetElfTest.cpp",
+        "tests/MapInfoGetLoadBiasTest.cpp",
         "tests/MapsTest.cpp",
-        "tests/MemoryBuffer.cpp",
+        "tests/MemoryBufferTest.cpp",
         "tests/MemoryFake.cpp",
         "tests/MemoryFileTest.cpp",
         "tests/MemoryLocalTest.cpp",
+        "tests/MemoryOfflineBufferTest.cpp",
+        "tests/MemoryOfflineTest.cpp",
         "tests/MemoryRangeTest.cpp",
         "tests/MemoryRemoteTest.cpp",
         "tests/MemoryTest.cpp",
+        "tests/RegsIterateTest.cpp",
+        "tests/RegsStepIfSignalHandlerTest.cpp",
         "tests/RegsTest.cpp",
         "tests/SymbolsTest.cpp",
+        "tests/UnwindOfflineTest.cpp",
+        "tests/UnwindTest.cpp",
+        "tests/UnwinderTest.cpp",
     ],
 
     cflags: [
@@ -123,41 +208,41 @@
         "libbase",
         "liblog",
         "liblzma",
+        "libunwindstack",
+        "libdexfile",
     ],
 
     static_libs: [
         "libgmock",
     ],
 
-    target: {
-        linux: {
-            host_ldlibs: [
-                "-lrt",
-            ],
-        },
-    },
-}
-
-// These unit tests run against the shared library.
-cc_test {
-    name: "libunwindstack_test",
-    defaults: ["libunwindstack_test_common"],
-
-    shared_libs: [
-        "libunwindstack",
-    ],
-
+    test_suites: ["device-tests"],
     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/debug_frame_load_bias_arm/*",
+        "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/offset_arm/*",
+        "tests/files/offline/straddle_arm/*",
+        "tests/files/offline/straddle_arm64/*",
+    ],
+    required: [
+        "libunwindstack_local",
     ],
 }
 
 //-------------------------------------------------------------------------
-// Utility Executables
+// Tools
 //-------------------------------------------------------------------------
 cc_defaults {
-    name: "libunwindstack_executables",
+    name: "libunwindstack_tools",
     defaults: ["libunwindstack_flags"],
 
     shared_libs: [
@@ -165,20 +250,59 @@
         "libbase",
         "liblzma",
     ],
+    target: {
+        // Always disable optimizations for host to make it easier to debug.
+        host: {
+            cflags: [
+                "-O0",
+                "-g",
+            ],
+        },
+    },
+}
 
-    static_libs: [
-        "liblog",
+cc_binary {
+    name: "unwind",
+    defaults: ["libunwindstack_tools"],
+
+    srcs: [
+        "tools/unwind.cpp",
     ],
-
-    compile_multilib: "both",
 }
 
 cc_binary {
     name: "unwind_info",
-    defaults: ["libunwindstack_executables"],
+    defaults: ["libunwindstack_tools"],
 
     srcs: [
-        "unwind_info.cpp",
+        "tools/unwind_info.cpp",
+    ],
+}
+
+cc_binary {
+    name: "unwind_symbols",
+    defaults: ["libunwindstack_tools"],
+
+    srcs: [
+        "tools/unwind_symbols.cpp",
+    ],
+}
+
+cc_binary {
+    name: "unwind_for_offline",
+    defaults: ["libunwindstack_tools"],
+
+    srcs: [
+        "tools/unwind_for_offline.cpp",
+    ],
+}
+
+cc_binary {
+    name: "unwind_reg_info",
+    defaults: ["libunwindstack_tools"],
+
+    srcs: [
+        "tools/unwind_reg_info.cpp",
     ],
 }
 
@@ -186,20 +310,9 @@
 // Once these files are generated, use the xz command to compress the data.
 cc_binary_host {
     name: "gen_gnudebugdata",
-
-    cflags: [
-        "-Wall",
-        "-Werror",
-        "-Wextra",
-    ],
+    defaults: ["libunwindstack_flags"],
 
     srcs: [
         "tests/GenGnuDebugdata.cpp",
     ],
-
-    target: {
-        darwin: {
-            enabled: false,
-        },
-    },
 }
diff --git a/libunwindstack/ArmExidx.cpp b/libunwindstack/ArmExidx.cpp
index 5c7caca..818f5d1 100644
--- a/libunwindstack/ArmExidx.cpp
+++ b/libunwindstack/ArmExidx.cpp
@@ -21,12 +21,17 @@
 
 #include <android-base/stringprintf.h>
 
+#include <unwindstack/Log.h>
+#include <unwindstack/MachineArm.h>
+#include <unwindstack/Memory.h>
+#include <unwindstack/RegsArm.h>
+
 #include "ArmExidx.h"
 #include "Check.h"
-#include "Log.h"
-#include "Machine.h"
-#include "Memory.h"
-#include "Regs.h"
+
+namespace unwindstack {
+
+static constexpr uint8_t LOG_CFA_REG = 64;
 
 void ArmExidx::LogRawData() {
   std::string log_str("Raw Data:");
@@ -54,13 +59,16 @@
   uint32_t data;
   if (!elf_memory_->Read32(entry_offset + 4, &data)) {
     status_ = ARM_STATUS_READ_FAILED;
+    status_address_ = entry_offset + 4;
     return false;
   }
   if (data == 1) {
     // This is a CANT UNWIND entry.
     status_ = ARM_STATUS_NO_UNWIND;
-    if (log_) {
-      log(log_indent_, "Raw Data: 0x00 0x00 0x00 0x01");
+    if (log_type_ != ARM_LOG_NONE) {
+      if (log_type_ == ARM_LOG_FULL) {
+        log(log_indent_, "Raw Data: 0x00 0x00 0x00 0x01");
+      }
       log(log_indent_, "[cantunwind]");
     }
     return false;
@@ -82,7 +90,7 @@
       // If this didn't end with a finish op, add one.
       data_.push_back(ARM_OP_FINISH);
     }
-    if (log_) {
+    if (log_type_ == ARM_LOG_FULL) {
       LogRawData();
     }
     return true;
@@ -94,6 +102,7 @@
   uint32_t addr = (entry_offset + 4) + signed_data;
   if (!elf_memory_->Read32(addr, &data)) {
     status_ = ARM_STATUS_READ_FAILED;
+    status_address_ = addr;
     return false;
   }
 
@@ -125,6 +134,7 @@
     addr += 4;
     if (!elf_memory_->Read32(addr, &data)) {
       status_ = ARM_STATUS_READ_FAILED;
+      status_address_ = addr;
       return false;
     }
     num_table_words = (data >> 24) & 0xff;
@@ -142,6 +152,7 @@
   for (size_t i = 0; i < num_table_words; i++) {
     if (!elf_memory_->Read32(addr, &data)) {
       status_ = ARM_STATUS_READ_FAILED;
+      status_address_ = addr;
       return false;
     }
     data_.push_back((data >> 24) & 0xff);
@@ -156,7 +167,7 @@
     data_.push_back(ARM_OP_FINISH);
   }
 
-  if (log_) {
+  if (log_type_ == ARM_LOG_FULL) {
     LogRawData();
   }
   return true;
@@ -183,36 +194,50 @@
   registers |= byte;
   if (registers == 0) {
     // 10000000 00000000: Refuse to unwind
-    if (log_) {
+    if (log_type_ != ARM_LOG_NONE) {
       log(log_indent_, "Refuse to unwind");
     }
     status_ = ARM_STATUS_NO_UNWIND;
     return false;
   }
   // 1000iiii iiiiiiii: Pop up to 12 integer registers under masks {r15-r12}, {r11-r4}
-  if (log_) {
-    bool add_comma = false;
-    std::string msg = "pop {";
-    for (size_t i = 0; i < 12; i++) {
-      if (registers & (1 << i)) {
-        if (add_comma) {
-          msg += ", ";
+  registers <<= 4;
+
+  if (log_type_ != ARM_LOG_NONE) {
+    if (log_type_ == ARM_LOG_FULL) {
+      bool add_comma = false;
+      std::string msg = "pop {";
+      for (size_t reg = 4; reg < 16; reg++) {
+        if (registers & (1 << reg)) {
+          if (add_comma) {
+            msg += ", ";
+          }
+          msg += android::base::StringPrintf("r%zu", reg);
+          add_comma = true;
         }
-        msg += android::base::StringPrintf("r%zu", i + 4);
-        add_comma = true;
+      }
+      log(log_indent_, "%s}", msg.c_str());
+    } else {
+      uint32_t cfa_offset = __builtin_popcount(registers) * 4;
+      log_cfa_offset_ += cfa_offset;
+      for (size_t reg = 4; reg < 16; reg++) {
+        if (registers & (1 << reg)) {
+          log_regs_[reg] = cfa_offset;
+          cfa_offset -= 4;
+        }
       }
     }
-    log(log_indent_, "%s}", msg.c_str());
+
     if (log_skip_execution_) {
       return true;
     }
   }
 
-  registers <<= 4;
   for (size_t reg = 4; reg < 16; reg++) {
     if (registers & (1 << reg)) {
       if (!process_memory_->Read32(cfa_, &(*regs_)[reg])) {
         status_ = ARM_STATUS_READ_FAILED;
+        status_address_ = cfa_;
         return false;
       }
       cfa_ += 4;
@@ -238,15 +263,20 @@
   if (bits == 13 || bits == 15) {
     // 10011101: Reserved as prefix for ARM register to register moves
     // 10011111: Reserved as prefix for Intel Wireless MMX register to register moves
-    if (log_) {
+    if (log_type_ != ARM_LOG_NONE) {
       log(log_indent_, "[Reserved]");
     }
     status_ = ARM_STATUS_RESERVED;
     return false;
   }
   // 1001nnnn: Set vsp = r[nnnn] (nnnn != 13, 15)
-  if (log_) {
-    log(log_indent_, "vsp = r%d", bits);
+  if (log_type_ != ARM_LOG_NONE) {
+    if (log_type_ == ARM_LOG_FULL) {
+      log(log_indent_, "vsp = r%d", bits);
+    } else {
+      log_regs_[LOG_CFA_REG] = bits;
+    }
+
     if (log_skip_execution_) {
       return true;
     }
@@ -262,17 +292,36 @@
 
   // 10100nnn: Pop r4-r[4+nnn]
   // 10101nnn: Pop r4-r[4+nnn], r14
-  if (log_) {
-    std::string msg = "pop {r4";
+  if (log_type_ != ARM_LOG_NONE) {
     uint8_t end_reg = byte & 0x7;
-    if (end_reg) {
-      msg += android::base::StringPrintf("-r%d", 4 + end_reg);
-    }
-    if (byte & 0x8) {
-      log(log_indent_, "%s, r14}", msg.c_str());
+    if (log_type_ == ARM_LOG_FULL) {
+      std::string msg = "pop {r4";
+      if (end_reg) {
+        msg += android::base::StringPrintf("-r%d", 4 + end_reg);
+      }
+      if (byte & 0x8) {
+        log(log_indent_, "%s, r14}", msg.c_str());
+      } else {
+        log(log_indent_, "%s}", msg.c_str());
+      }
     } else {
-      log(log_indent_, "%s}", msg.c_str());
+      end_reg += 4;
+      uint32_t cfa_offset = (end_reg - 3) * 4;
+      if (byte & 0x8) {
+        cfa_offset += 4;
+      }
+      log_cfa_offset_ += cfa_offset;
+
+      for (uint8_t reg = 4; reg <= end_reg; reg++) {
+        log_regs_[reg] = cfa_offset;
+        cfa_offset -= 4;
+      }
+
+      if (byte & 0x8) {
+        log_regs_[14] = cfa_offset;
+      }
     }
+
     if (log_skip_execution_) {
       return true;
     }
@@ -281,6 +330,7 @@
   for (size_t i = 4; i <= 4 + (byte & 0x7); i++) {
     if (!process_memory_->Read32(cfa_, &(*regs_)[i])) {
       status_ = ARM_STATUS_READ_FAILED;
+      status_address_ = cfa_;
       return false;
     }
     cfa_ += 4;
@@ -288,6 +338,7 @@
   if (byte & 0x8) {
     if (!process_memory_->Read32(cfa_, &(*regs_)[ARM_REG_R14])) {
       status_ = ARM_STATUS_READ_FAILED;
+      status_address_ = cfa_;
       return false;
     }
     cfa_ += 4;
@@ -297,8 +348,11 @@
 
 inline bool ArmExidx::DecodePrefix_10_11_0000() {
   // 10110000: Finish
-  if (log_) {
-    log(log_indent_, "finish");
+  if (log_type_ != ARM_LOG_NONE) {
+    if (log_type_ == ARM_LOG_FULL) {
+      log(log_indent_, "finish");
+    }
+
     if (log_skip_execution_) {
       status_ = ARM_STATUS_FINISH;
       return false;
@@ -316,7 +370,7 @@
 
   if (byte == 0) {
     // 10110001 00000000: Spare
-    if (log_) {
+    if (log_type_ != ARM_LOG_NONE) {
       log(log_indent_, "Spare");
     }
     status_ = ARM_STATUS_SPARE;
@@ -324,7 +378,7 @@
   }
   if (byte >> 4) {
     // 10110001 xxxxyyyy: Spare (xxxx != 0000)
-    if (log_) {
+    if (log_type_ != ARM_LOG_NONE) {
       log(log_indent_, "Spare");
     }
     status_ = ARM_STATUS_SPARE;
@@ -332,19 +386,32 @@
   }
 
   // 10110001 0000iiii: Pop integer registers under mask {r3, r2, r1, r0}
-  if (log_) {
-    bool add_comma = false;
-    std::string msg = "pop {";
-    for (size_t i = 0; i < 4; i++) {
-      if (byte & (1 << i)) {
-        if (add_comma) {
-          msg += ", ";
+  if (log_type_ != ARM_LOG_NONE) {
+    if (log_type_ == ARM_LOG_FULL) {
+      bool add_comma = false;
+      std::string msg = "pop {";
+      for (size_t i = 0; i < 4; i++) {
+        if (byte & (1 << i)) {
+          if (add_comma) {
+            msg += ", ";
+          }
+          msg += android::base::StringPrintf("r%zu", i);
+          add_comma = true;
         }
-        msg += android::base::StringPrintf("r%zu", i);
-        add_comma = true;
+      }
+      log(log_indent_, "%s}", msg.c_str());
+    } else {
+      byte &= 0xf;
+      uint32_t cfa_offset = __builtin_popcount(byte) * 4;
+      log_cfa_offset_ += cfa_offset;
+      for (size_t reg = 0; reg < 4; reg++) {
+        if (byte & (1 << reg)) {
+          log_regs_[reg] = cfa_offset;
+          cfa_offset -= 4;
+        }
       }
     }
-    log(log_indent_, "%s}", msg.c_str());
+
     if (log_skip_execution_) {
       return true;
     }
@@ -354,6 +421,7 @@
     if (byte & (1 << reg)) {
       if (!process_memory_->Read32(cfa_, &(*regs_)[reg])) {
         status_ = ARM_STATUS_READ_FAILED;
+        status_address_ = cfa_;
         return false;
       }
       cfa_ += 4;
@@ -362,6 +430,15 @@
   return true;
 }
 
+inline void ArmExidx::AdjustRegisters(int32_t offset) {
+  for (auto& entry : log_regs_) {
+    if (entry.first >= LOG_CFA_REG) {
+      break;
+    }
+    entry.second += offset;
+  }
+}
+
 inline bool ArmExidx::DecodePrefix_10_11_0010() {
   // 10110010 uleb128: vsp = vsp + 0x204 + (uleb128 << 2)
   uint32_t result = 0;
@@ -376,8 +453,15 @@
     shift += 7;
   } while (byte & 0x80);
   result <<= 2;
-  if (log_) {
-    log(log_indent_, "vsp = vsp + %d", 0x204 + result);
+  if (log_type_ != ARM_LOG_NONE) {
+    int32_t cfa_offset = 0x204 + result;
+    if (log_type_ == ARM_LOG_FULL) {
+      log(log_indent_, "vsp = vsp + %d", cfa_offset);
+    } else {
+      log_cfa_offset_ += cfa_offset;
+    }
+    AdjustRegisters(cfa_offset);
+
     if (log_skip_execution_) {
       return true;
     }
@@ -393,14 +477,20 @@
     return false;
   }
 
-  if (log_) {
+  if (log_type_ != ARM_LOG_NONE) {
     uint8_t start_reg = byte >> 4;
-    std::string msg = android::base::StringPrintf("pop {d%d", start_reg);
     uint8_t end_reg = start_reg + (byte & 0xf);
-    if (end_reg) {
-      msg += android::base::StringPrintf("-d%d", end_reg);
+
+    if (log_type_ == ARM_LOG_FULL) {
+      std::string msg = android::base::StringPrintf("pop {d%d", start_reg);
+      if (end_reg) {
+        msg += android::base::StringPrintf("-d%d", end_reg);
+      }
+      log(log_indent_, "%s}", msg.c_str());
+    } else {
+      log(log_indent_, "Unsupported DX register display");
     }
-    log(log_indent_, "%s}", msg.c_str());
+
     if (log_skip_execution_) {
       return true;
     }
@@ -411,7 +501,7 @@
 
 inline bool ArmExidx::DecodePrefix_10_11_01nn() {
   // 101101nn: Spare
-  if (log_) {
+  if (log_type_ != ARM_LOG_NONE) {
     log(log_indent_, "Spare");
   }
   status_ = ARM_STATUS_SPARE;
@@ -422,13 +512,18 @@
   CHECK((byte & ~0x07) == 0xb8);
 
   // 10111nnn: Pop VFP double-precision registers D[8]-D[8+nnn] by FSTMFDX
-  if (log_) {
-    std::string msg = "pop {d8";
-    uint8_t last_reg = (byte & 0x7);
-    if (last_reg) {
-      msg += android::base::StringPrintf("-d%d", last_reg + 8);
+  if (log_type_ != ARM_LOG_NONE) {
+    if (log_type_ == ARM_LOG_FULL) {
+      uint8_t last_reg = (byte & 0x7);
+      std::string msg = "pop {d8";
+      if (last_reg) {
+        msg += android::base::StringPrintf("-d%d", last_reg + 8);
+      }
+      log(log_indent_, "%s}", msg.c_str());
+    } else {
+      log(log_indent_, "Unsupported DX register display");
     }
-    log(log_indent_, "%s}", msg.c_str());
+
     if (log_skip_execution_) {
       return true;
     }
@@ -478,14 +573,19 @@
     }
 
     // 11000110 sssscccc: Intel Wireless MMX pop wR[ssss]-wR[ssss+cccc]
-    if (log_) {
-      uint8_t start_reg = byte >> 4;
-      std::string msg = android::base::StringPrintf("pop {wR%d", start_reg);
-      uint8_t end_reg = byte & 0xf;
-      if (end_reg) {
-        msg += android::base::StringPrintf("-wR%d", start_reg + end_reg);
+    if (log_type_ != ARM_LOG_NONE) {
+      if (log_type_ == ARM_LOG_FULL) {
+        uint8_t start_reg = byte >> 4;
+        std::string msg = android::base::StringPrintf("pop {wR%d", start_reg);
+        uint8_t end_reg = byte & 0xf;
+        if (end_reg) {
+          msg += android::base::StringPrintf("-wR%d", start_reg + end_reg);
+        }
+        log(log_indent_, "%s}", msg.c_str());
+      } else {
+        log(log_indent_, "Unsupported wRX register display");
       }
-      log(log_indent_, "%s}", msg.c_str());
+
       if (log_skip_execution_) {
         return true;
       }
@@ -499,32 +599,40 @@
 
     if (byte == 0) {
       // 11000111 00000000: Spare
-      if (log_) {
+      if (log_type_ != ARM_LOG_NONE) {
         log(log_indent_, "Spare");
       }
       status_ = ARM_STATUS_SPARE;
       return false;
     } else if ((byte >> 4) == 0) {
       // 11000111 0000iiii: Intel Wireless MMX pop wCGR registers {wCGR0,1,2,3}
-      if (log_) {
-        bool add_comma = false;
-        std::string msg = "pop {";
-        for (size_t i = 0; i < 4; i++) {
-          if (byte & (1 << i)) {
-            if (add_comma) {
-              msg += ", ";
+      if (log_type_ != ARM_LOG_NONE) {
+        if (log_type_ == ARM_LOG_FULL) {
+          bool add_comma = false;
+          std::string msg = "pop {";
+          for (size_t i = 0; i < 4; i++) {
+            if (byte & (1 << i)) {
+              if (add_comma) {
+                msg += ", ";
+              }
+              msg += android::base::StringPrintf("wCGR%zu", i);
+              add_comma = true;
             }
-            msg += android::base::StringPrintf("wCGR%zu", i);
-            add_comma = true;
           }
+          log(log_indent_, "%s}", msg.c_str());
+        } else {
+          log(log_indent_, "Unsupported wCGR register display");
         }
-        log(log_indent_, "%s}", msg.c_str());
+
+        if (log_skip_execution_) {
+          return true;
+        }
       }
       // Only update the cfa.
       cfa_ += __builtin_popcount(byte) * 4;
     } else {
       // 11000111 xxxxyyyy: Spare (xxxx != 0000)
-      if (log_) {
+      if (log_type_ != ARM_LOG_NONE) {
         log(log_indent_, "Spare");
       }
       status_ = ARM_STATUS_SPARE;
@@ -532,13 +640,18 @@
     }
   } else {
     // 11000nnn: Intel Wireless MMX pop wR[10]-wR[10+nnn] (nnn != 6, 7)
-    if (log_) {
-      std::string msg = "pop {wR10";
-      uint8_t nnn = byte & 0x7;
-      if (nnn) {
-        msg += android::base::StringPrintf("-wR%d", 10 + nnn);
+    if (log_type_ != ARM_LOG_NONE) {
+      if (log_type_ == ARM_LOG_FULL) {
+        std::string msg = "pop {wR10";
+        uint8_t nnn = byte & 0x7;
+        if (nnn) {
+          msg += android::base::StringPrintf("-wR%d", 10 + nnn);
+        }
+        log(log_indent_, "%s}", msg.c_str());
+      } else {
+        log(log_indent_, "Unsupported wRX register display");
       }
-      log(log_indent_, "%s}", msg.c_str());
+
       if (log_skip_execution_) {
         return true;
       }
@@ -559,14 +672,19 @@
       return false;
     }
 
-    if (log_) {
-      uint8_t start_reg = byte >> 4;
-      std::string msg = android::base::StringPrintf("pop {d%d", 16 + start_reg);
-      uint8_t end_reg = byte & 0xf;
-      if (end_reg) {
-        msg += android::base::StringPrintf("-d%d", 16 + start_reg + end_reg);
+    if (log_type_ != ARM_LOG_NONE) {
+      if (log_type_ == ARM_LOG_FULL) {
+        uint8_t start_reg = byte >> 4;
+        std::string msg = android::base::StringPrintf("pop {d%d", 16 + start_reg);
+        uint8_t end_reg = byte & 0xf;
+        if (end_reg) {
+          msg += android::base::StringPrintf("-d%d", 16 + start_reg + end_reg);
+        }
+        log(log_indent_, "%s}", msg.c_str());
+      } else {
+        log(log_indent_, "Unsupported DX register display");
       }
-      log(log_indent_, "%s}", msg.c_str());
+
       if (log_skip_execution_) {
         return true;
       }
@@ -579,14 +697,19 @@
       return false;
     }
 
-    if (log_) {
-      uint8_t start_reg = byte >> 4;
-      std::string msg = android::base::StringPrintf("pop {d%d", start_reg);
-      uint8_t end_reg = byte & 0xf;
-      if (end_reg) {
-        msg += android::base::StringPrintf("-d%d", start_reg + end_reg);
+    if (log_type_ != ARM_LOG_NONE) {
+      if (log_type_ == ARM_LOG_FULL) {
+        uint8_t start_reg = byte >> 4;
+        std::string msg = android::base::StringPrintf("pop {d%d", start_reg);
+        uint8_t end_reg = byte & 0xf;
+        if (end_reg) {
+          msg += android::base::StringPrintf("-d%d", start_reg + end_reg);
+        }
+        log(log_indent_, "%s}", msg.c_str());
+      } else {
+        log(log_indent_, "Unsupported DX register display");
       }
-      log(log_indent_, "%s}", msg.c_str());
+
       if (log_skip_execution_) {
         return true;
       }
@@ -595,7 +718,7 @@
     cfa_ += (byte & 0xf) * 8 + 8;
   } else {
     // 11001yyy: Spare (yyy != 000, 001)
-    if (log_) {
+    if (log_type_ != ARM_LOG_NONE) {
       log(log_indent_, "Spare");
     }
     status_ = ARM_STATUS_SPARE;
@@ -608,13 +731,18 @@
   CHECK((byte & ~0x07) == 0xd0);
 
   // 11010nnn: Pop VFP double precision registers D[8]-D[8+nnn] by VPUSH
-  if (log_) {
-    std::string msg = "pop {d8";
-    uint8_t end_reg = byte & 0x7;
-    if (end_reg) {
-      msg += android::base::StringPrintf("-d%d", 8 + end_reg);
+  if (log_type_ != ARM_LOG_NONE) {
+    if (log_type_ == ARM_LOG_FULL) {
+      std::string msg = "pop {d8";
+      uint8_t end_reg = byte & 0x7;
+      if (end_reg) {
+        msg += android::base::StringPrintf("-d%d", 8 + end_reg);
+      }
+      log(log_indent_, "%s}", msg.c_str());
+    } else {
+      log(log_indent_, "Unsupported DX register display");
     }
-    log(log_indent_, "%s}", msg.c_str());
+
     if (log_skip_execution_) {
       return true;
     }
@@ -635,7 +763,7 @@
     return DecodePrefix_11_010(byte);
   default:
     // 11xxxyyy: Spare (xxx != 000, 001, 010)
-    if (log_) {
+    if (log_type_ != ARM_LOG_NONE) {
       log(log_indent_, "Spare");
     }
     status_ = ARM_STATUS_SPARE;
@@ -653,8 +781,15 @@
   switch (byte >> 6) {
   case 0:
     // 00xxxxxx: vsp = vsp + (xxxxxxx << 2) + 4
-    if (log_) {
-      log(log_indent_, "vsp = vsp + %d", ((byte & 0x3f) << 2) + 4);
+    if (log_type_ != ARM_LOG_NONE) {
+      int32_t cfa_offset = ((byte & 0x3f) << 2) + 4;
+      if (log_type_ == ARM_LOG_FULL) {
+        log(log_indent_, "vsp = vsp + %d", cfa_offset);
+      } else {
+        log_cfa_offset_ += cfa_offset;
+      }
+      AdjustRegisters(cfa_offset);
+
       if (log_skip_execution_) {
         break;
       }
@@ -663,8 +798,15 @@
     break;
   case 1:
     // 01xxxxxx: vsp = vsp - (xxxxxxx << 2) + 4
-    if (log_) {
-      log(log_indent_, "vsp = vsp - %d", ((byte & 0x3f) << 2) + 4);
+    if (log_type_ != ARM_LOG_NONE) {
+      uint32_t cfa_offset = ((byte & 0x3f) << 2) + 4;
+      if (log_type_ == ARM_LOG_FULL) {
+        log(log_indent_, "vsp = vsp - %d", cfa_offset);
+      } else {
+        log_cfa_offset_ -= cfa_offset;
+      }
+      AdjustRegisters(-cfa_offset);
+
       if (log_skip_execution_) {
         break;
       }
@@ -684,3 +826,37 @@
   while (Decode());
   return status_ == ARM_STATUS_FINISH;
 }
+
+void ArmExidx::LogByReg() {
+  if (log_type_ != ARM_LOG_BY_REG) {
+    return;
+  }
+
+  uint8_t cfa_reg;
+  if (log_regs_.count(LOG_CFA_REG) == 0) {
+    cfa_reg = 13;
+  } else {
+    cfa_reg = log_regs_[LOG_CFA_REG];
+  }
+
+  if (log_cfa_offset_ != 0) {
+    char sign = (log_cfa_offset_ > 0) ? '+' : '-';
+    log(log_indent_, "cfa = r%zu %c %d", cfa_reg, sign, abs(log_cfa_offset_));
+  } else {
+    log(log_indent_, "cfa = r%zu", cfa_reg);
+  }
+
+  for (const auto& entry : log_regs_) {
+    if (entry.first >= LOG_CFA_REG) {
+      break;
+    }
+    if (entry.second == 0) {
+      log(log_indent_, "r%zu = [cfa]", entry.first);
+    } else {
+      char sign = (entry.second > 0) ? '-' : '+';
+      log(log_indent_, "r%zu = [cfa %c %d]", entry.first, sign, abs(entry.second));
+    }
+  }
+}
+
+}  // namespace unwindstack
diff --git a/libunwindstack/ArmExidx.h b/libunwindstack/ArmExidx.h
index 8c7f15a..d9fc371 100644
--- a/libunwindstack/ArmExidx.h
+++ b/libunwindstack/ArmExidx.h
@@ -20,6 +20,9 @@
 #include <stdint.h>
 
 #include <deque>
+#include <map>
+
+namespace unwindstack {
 
 // Forward declarations.
 class Memory;
@@ -42,6 +45,12 @@
   ARM_OP_FINISH = 0xb0,
 };
 
+enum ArmLogType : uint8_t {
+  ARM_LOG_NONE,
+  ARM_LOG_FULL,
+  ARM_LOG_BY_REG,
+};
+
 class ArmExidx {
  public:
   ArmExidx(RegsArm* regs, Memory* elf_memory, Memory* process_memory)
@@ -50,6 +59,8 @@
 
   void LogRawData();
 
+  void LogByReg();
+
   bool ExtractEntryData(uint32_t entry_offset);
 
   bool Eval();
@@ -59,6 +70,7 @@
   std::deque<uint8_t>* data() { return &data_; }
 
   ArmStatus status() { return status_; }
+  uint64_t status_address() { return status_address_; }
 
   RegsArm* regs() { return regs_; }
 
@@ -68,12 +80,13 @@
   bool pc_set() { return pc_set_; }
   void set_pc_set(bool pc_set) { pc_set_ = pc_set; }
 
-  void set_log(bool log) { log_ = log; }
+  void set_log(ArmLogType log_type) { log_type_ = log_type; }
   void set_log_skip_execution(bool skip_execution) { log_skip_execution_ = skip_execution; }
   void set_log_indent(uint8_t indent) { log_indent_ = indent; }
 
  private:
   bool GetByte(uint8_t* byte);
+  void AdjustRegisters(int32_t offset);
 
   bool DecodePrefix_10_00(uint8_t byte);
   bool DecodePrefix_10_01(uint8_t byte);
@@ -95,14 +108,19 @@
   uint32_t cfa_ = 0;
   std::deque<uint8_t> data_;
   ArmStatus status_ = ARM_STATUS_NONE;
+  uint64_t status_address_ = 0;
 
   Memory* elf_memory_;
   Memory* process_memory_;
 
-  bool log_ = false;
+  ArmLogType log_type_ = ARM_LOG_NONE;
   uint8_t log_indent_ = 0;
   bool log_skip_execution_ = false;
   bool pc_set_ = false;
+  int32_t log_cfa_offset_ = 0;
+  std::map<uint8_t, int32_t> log_regs_;
 };
 
+}  // namespace unwindstack
+
 #endif  // _LIBUNWINDSTACK_ARM_EXIDX_H
diff --git a/libunwindstack/AsmGetRegsMips.S b/libunwindstack/AsmGetRegsMips.S
new file mode 100644
index 0000000..183d0a9
--- /dev/null
+++ b/libunwindstack/AsmGetRegsMips.S
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+  .text
+  .type     AsmGetRegs, %function
+  .globl    AsmGetRegs
+  .ent      AsmGetRegs
+  .balign   16
+AsmGetRegs:
+  .cfi_startproc
+  .cfi_def_cfa $sp, 0
+  .set push
+  .set noreorder
+  .cpload $t9
+  sw   $zero, 0($a0)
+  .set noat
+  sw   $at, 4($a0)
+  .set at
+  sw   $v0, 8($a0)
+  sw   $v1, 12($a0)
+  sw   $a0, 16($a0)
+  sw   $a1, 20($a0)
+  sw   $a2, 24($a0)
+  sw   $a3, 28($a0)
+  sw   $t0, 32($a0)
+  sw   $t1, 36($a0)
+  sw   $t2, 40($a0)
+  sw   $t3, 44($a0)
+  sw   $t4, 48($a0)
+  sw   $t5, 52($a0)
+  sw   $t6, 56($a0)
+  sw   $t7, 60($a0)
+  sw   $s0, 64($a0)
+  sw   $s1, 68($a0)
+  sw   $s2, 72($a0)
+  sw   $s3, 76($a0)
+  sw   $s4, 80($a0)
+  sw   $s5, 84($a0)
+  sw   $s6, 88($a0)
+  sw   $s7, 92($a0)
+  sw   $t8, 96($a0)
+  sw   $t9, 100($a0)
+  sw   $k0, 104($a0)
+  sw   $k1, 108($a0)
+  sw   $gp, 112($a0)
+  sw   $sp, 116($a0)
+  sw   $s8, 120($a0)
+  sw   $ra, 124($a0)
+  jalr $zero, $ra
+  sw   $ra, 128($a0)   // set PC to the calling function
+
+  .set pop
+  .cfi_endproc
+  .size     AsmGetRegs, .-AsmGetRegs
+  .end      AsmGetRegs
diff --git a/libunwindstack/AsmGetRegsMips64.S b/libunwindstack/AsmGetRegsMips64.S
new file mode 100644
index 0000000..7a244f6
--- /dev/null
+++ b/libunwindstack/AsmGetRegsMips64.S
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+  .text
+  .type     AsmGetRegs, %function
+  .globl    AsmGetRegs
+  .ent      AsmGetRegs
+  .balign    16
+AsmGetRegs:
+  .cfi_startproc
+  .cfi_def_cfa $sp, 0
+  .set push
+  .set noreorder
+  .cpload $t9
+  sd   $zero, 0($a0)
+  .set noat
+  sd   $at, 8($a0)
+  .set at
+  sd   $v0, 16($a0)
+  sd   $v1, 24($a0)
+  sd   $a0, 32($a0)
+  sd   $a1, 40($a0)
+  sd   $a2, 48($a0)
+  sd   $a3, 56($a0)
+  sd   $a4, 64($a0)
+  sd   $a5, 72($a0)
+  sd   $a6, 80($a0)
+  sd   $a7, 88($a0)
+  sd   $t0, 96($a0)
+  sd   $t1, 104($a0)
+  sd   $t2, 112($a0)
+  sd   $t3, 120($a0)
+  sd   $s0, 128($a0)
+  sd   $s1, 136($a0)
+  sd   $s2, 144($a0)
+  sd   $s3, 152($a0)
+  sd   $s4, 160($a0)
+  sd   $s5, 168($a0)
+  sd   $s6, 176($a0)
+  sd   $s7, 184($a0)
+  sd   $t8, 192($a0)
+  sd   $t9, 200($a0)
+  sd   $k0, 208($a0)
+  sd   $k1, 216($a0)
+  sd   $gp, 224($a0)
+  sd   $sp, 232($a0)
+  sd   $s8, 240($a0)
+  sd   $ra, 248($a0)
+  jalr $zero, $ra
+  sd   $ra, 256($a0)   // set PC to the calling function
+
+  .set pop
+  .cfi_endproc
+  .size AsmGetRegs, .-AsmGetRegs
+  .end      AsmGetRegs
diff --git a/libunwindstack/AsmGetRegsX86.S b/libunwindstack/AsmGetRegsX86.S
new file mode 100644
index 0000000..021e628
--- /dev/null
+++ b/libunwindstack/AsmGetRegsX86.S
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+  .text
+  .global AsmGetRegs
+  .balign 16
+  .type AsmGetRegs, @function
+AsmGetRegs:
+  .cfi_startproc
+  mov 4(%esp), %eax
+  movl $0, (%eax)
+  movl %ecx, 4(%eax)
+  movl %edx, 8(%eax)
+  movl %ebx, 12(%eax)
+
+  /* ESP */
+  leal 4(%esp), %ecx
+  movl %ecx, 16(%eax)
+
+  movl %ebp, 20(%eax)
+  movl %esi, 24(%eax)
+  movl %edi, 28(%eax)
+
+  /* EIP */
+  movl (%esp), %ecx
+  movl %ecx, 32(%eax)
+
+  mov  %cs, 36(%eax)
+  mov  %ss, 40(%eax)
+  mov  %ds, 44(%eax)
+  mov  %es, 48(%eax)
+  mov  %fs, 52(%eax)
+  mov  %gs, 56(%eax)
+  ret
+
+  .cfi_endproc
+  .size AsmGetRegs, .-AsmGetRegs
diff --git a/libunwindstack/AsmGetRegsX86_64.S b/libunwindstack/AsmGetRegsX86_64.S
new file mode 100644
index 0000000..4cd3b6f
--- /dev/null
+++ b/libunwindstack/AsmGetRegsX86_64.S
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+  .text
+  .global AsmGetRegs
+  .balign 16
+  .type AsmGetRegs, @function
+AsmGetRegs:
+  .cfi_startproc
+  movq %rax, (%rdi)
+  movq %rdx, 8(%rdi)
+  movq %rcx, 16(%rdi)
+  movq %rbx, 24(%rdi)
+  movq %rsi, 32(%rdi)
+  movq %rdi, 40(%rdi)
+  movq %rbp, 48(%rdi)
+
+  /* RSP */
+  lea 8(%rsp), %rax
+  movq %rax, 56(%rdi)
+
+  movq %r8, 64(%rdi)
+  movq %r9, 72(%rdi)
+  movq %r10, 80(%rdi)
+  movq %r11, 88(%rdi)
+  movq %r12, 96(%rdi)
+  movq %r13, 104(%rdi)
+  movq %r14, 112(%rdi)
+  movq %r15, 120(%rdi)
+
+  /* RIP */
+  movq (%rsp), %rax
+  movq %rax, 128(%rdi)
+  ret
+
+  .cfi_endproc
+  .size AsmGetRegs, .-AsmGetRegs
diff --git a/libunwindstack/Check.h b/libunwindstack/Check.h
index e0c48c7..9643d76 100644
--- a/libunwindstack/Check.h
+++ b/libunwindstack/Check.h
@@ -14,12 +14,14 @@
  * limitations under the License.
  */
 
-#ifndef _LIBUNWINDSTACK_ERROR_H
-#define _LIBUNWINDSTACK_ERROR_H
+#ifndef _LIBUNWINDSTACK_CHECK_H
+#define _LIBUNWINDSTACK_CHECK_H
 
 #include <stdlib.h>
 
-#include "Log.h"
+#include <unwindstack/Log.h>
+
+namespace unwindstack {
 
 #define CHECK(assertion)                                   \
   if (__builtin_expect(!(assertion), false)) {             \
@@ -27,4 +29,6 @@
     abort();                                               \
   }
 
-#endif  // _LIBUNWINDSTACK_ERROR_H
+}  // namespace unwindstack
+
+#endif  // _LIBUNWINDSTACK_CHECK_H
diff --git a/libunwindstack/DexFile.cpp b/libunwindstack/DexFile.cpp
new file mode 100644
index 0000000..3d982f6
--- /dev/null
+++ b/libunwindstack/DexFile.cpp
@@ -0,0 +1,223 @@
+/*
+ * 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 <stdint.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <memory>
+
+#include <android-base/unique_fd.h>
+
+#include <dex/code_item_accessors-inl.h>
+#include <dex/compact_dex_file.h>
+#include <dex/dex_file-inl.h>
+#include <dex/dex_file_loader.h>
+#include <dex/standard_dex_file.h>
+
+#include <unwindstack/MapInfo.h>
+#include <unwindstack/Memory.h>
+
+#include "DexFile.h"
+
+namespace unwindstack {
+
+DexFile* DexFile::Create(uint64_t dex_file_offset_in_memory, Memory* memory, MapInfo* info) {
+  if (!info->name.empty()) {
+    std::unique_ptr<DexFileFromFile> dex_file(new DexFileFromFile);
+    if (dex_file->Open(dex_file_offset_in_memory - info->start + info->offset, info->name)) {
+      return dex_file.release();
+    }
+  }
+
+  std::unique_ptr<DexFileFromMemory> dex_file(new DexFileFromMemory);
+  if (dex_file->Open(dex_file_offset_in_memory, memory)) {
+    return dex_file.release();
+  }
+  return nullptr;
+}
+
+DexFileFromFile::~DexFileFromFile() {
+  if (size_ != 0) {
+    munmap(mapped_memory_, size_);
+  }
+}
+
+bool DexFile::GetMethodInformation(uint64_t dex_offset, std::string* method_name,
+                                   uint64_t* method_offset) {
+  if (dex_file_ == nullptr) {
+    return false;
+  }
+
+  if (!dex_file_->IsInDataSection(dex_file_->Begin() + dex_offset)) {
+    return false;  // The DEX offset is not within the bytecode of this dex file.
+  }
+
+  if (dex_file_->IsCompactDexFile()) {
+    // The data section of compact dex files might be shared.
+    // Check the subrange unique to this compact dex.
+    const auto& cdex_header = dex_file_->AsCompactDexFile()->GetHeader();
+    uint32_t begin = cdex_header.data_off_ + cdex_header.OwnedDataBegin();
+    uint32_t end = cdex_header.data_off_ + cdex_header.OwnedDataEnd();
+    if (dex_offset < begin || dex_offset >= end) {
+      return false;  // The DEX offset is not within the bytecode of this dex file.
+    }
+  }
+
+  // The method data is cached in a std::map indexed by method end offset and
+  // contains the start offset and the method member index.
+  // Only cache the method data as it is searched. Do not read the entire
+  // set of method data into the cache at once.
+  // This is done because many unwinds only find a single frame with dex file
+  // info, so reading the entire method data is wasteful. However, still cache
+  // the data so that anything doing multiple unwinds will have this data
+  // cached for future use.
+
+  // First look in the method cache.
+  auto entry = method_cache_.upper_bound(dex_offset);
+  if (entry != method_cache_.end() && dex_offset >= entry->second.first) {
+    *method_name = dex_file_->PrettyMethod(entry->second.second, false);
+    *method_offset = dex_offset - entry->second.first;
+    return true;
+  }
+
+  // Check the methods we haven't cached.
+  for (; class_def_index_ < dex_file_->NumClassDefs(); class_def_index_++) {
+    const art::DexFile::ClassDef& class_def = dex_file_->GetClassDef(class_def_index_);
+    const uint8_t* class_data = dex_file_->GetClassData(class_def);
+    if (class_data == nullptr) {
+      continue;
+    }
+
+    if (class_it_.get() == nullptr || !class_it_->HasNext()) {
+      class_it_.reset(new art::ClassDataItemIterator(*dex_file_.get(), class_data));
+    }
+
+    for (; class_it_->HasNext(); class_it_->Next()) {
+      if (!class_it_->IsAtMethod()) {
+        continue;
+      }
+      const art::DexFile::CodeItem* code_item = class_it_->GetMethodCodeItem();
+      if (code_item == nullptr) {
+        continue;
+      }
+      art::CodeItemInstructionAccessor code(*dex_file_.get(), code_item);
+      if (!code.HasCodeItem()) {
+        continue;
+      }
+
+      uint32_t offset = reinterpret_cast<const uint8_t*>(code.Insns()) - dex_file_->Begin();
+      uint32_t offset_end = offset + code.InsnsSizeInCodeUnits() * sizeof(uint16_t);
+      uint32_t member_index = class_it_->GetMemberIndex();
+      method_cache_[offset_end] = std::make_pair(offset, member_index);
+      if (offset <= dex_offset && dex_offset < offset_end) {
+        *method_name = dex_file_->PrettyMethod(member_index, false);
+        *method_offset = dex_offset - offset;
+        // Move past this element.
+        class_it_->Next();
+        return true;
+      }
+    }
+  }
+  return false;
+}
+
+bool DexFileFromFile::Open(uint64_t dex_file_offset_in_file, const std::string& file) {
+  android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(file.c_str(), O_RDONLY | O_CLOEXEC)));
+  if (fd == -1) {
+    return false;
+  }
+  struct stat buf;
+  if (fstat(fd, &buf) == -1) {
+    return false;
+  }
+  uint64_t length;
+  if (buf.st_size < 0 ||
+      __builtin_add_overflow(dex_file_offset_in_file, sizeof(art::DexFile::Header), &length) ||
+      static_cast<uint64_t>(buf.st_size) < length) {
+    return false;
+  }
+
+  mapped_memory_ = mmap(nullptr, buf.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
+  if (mapped_memory_ == MAP_FAILED) {
+    return false;
+  }
+  size_ = buf.st_size;
+
+  uint8_t* memory = reinterpret_cast<uint8_t*>(mapped_memory_);
+
+  art::DexFile::Header* header =
+      reinterpret_cast<art::DexFile::Header*>(&memory[dex_file_offset_in_file]);
+  if (!art::StandardDexFile::IsMagicValid(header->magic_) &&
+      !art::CompactDexFile::IsMagicValid(header->magic_)) {
+    return false;
+  }
+
+  if (__builtin_add_overflow(dex_file_offset_in_file, header->file_size_, &length) ||
+      static_cast<uint64_t>(buf.st_size) < length) {
+    return false;
+  }
+
+  art::DexFileLoader loader;
+  std::string error_msg;
+  auto dex = loader.Open(&memory[dex_file_offset_in_file], header->file_size_, "", 0, nullptr,
+                         false, false, &error_msg);
+  dex_file_.reset(dex.release());
+  return dex_file_ != nullptr;
+}
+
+bool DexFileFromMemory::Open(uint64_t dex_file_offset_in_memory, Memory* memory) {
+  memory_.resize(sizeof(art::DexFile::Header));
+  if (!memory->ReadFully(dex_file_offset_in_memory, memory_.data(), memory_.size())) {
+    return false;
+  }
+
+  art::DexFile::Header* header = reinterpret_cast<art::DexFile::Header*>(memory_.data());
+  uint32_t file_size = header->file_size_;
+  if (art::CompactDexFile::IsMagicValid(header->magic_)) {
+    // Compact dex file store data section separately so that it can be shared.
+    // Therefore we need to extend the read memory range to include it.
+    // TODO: This might be wasteful as we might read data in between as well.
+    //       In practice, this should be fine, as such sharing only happens on disk.
+    uint32_t computed_file_size;
+    if (__builtin_add_overflow(header->data_off_, header->data_size_, &computed_file_size)) {
+      return false;
+    }
+    if (computed_file_size > file_size) {
+      file_size = computed_file_size;
+    }
+  } else if (!art::StandardDexFile::IsMagicValid(header->magic_)) {
+    return false;
+  }
+
+  memory_.resize(file_size);
+  if (!memory->ReadFully(dex_file_offset_in_memory, memory_.data(), memory_.size())) {
+    return false;
+  }
+
+  header = reinterpret_cast<art::DexFile::Header*>(memory_.data());
+
+  art::DexFileLoader loader;
+  std::string error_msg;
+  auto dex =
+      loader.Open(memory_.data(), header->file_size_, "", 0, nullptr, false, false, &error_msg);
+  dex_file_.reset(dex.release());
+  return dex_file_ != nullptr;
+}
+
+}  // namespace unwindstack
diff --git a/libunwindstack/DexFile.h b/libunwindstack/DexFile.h
new file mode 100644
index 0000000..508692d
--- /dev/null
+++ b/libunwindstack/DexFile.h
@@ -0,0 +1,76 @@
+/*
+ * 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 _LIBUNWINDSTACK_DEX_FILE_H
+#define _LIBUNWINDSTACK_DEX_FILE_H
+
+#include <stdint.h>
+
+#include <map>
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include <dex/dex_file-inl.h>
+
+namespace unwindstack {
+
+class DexFile {
+ public:
+  DexFile() = default;
+  virtual ~DexFile() = default;
+
+  bool GetMethodInformation(uint64_t dex_offset, std::string* method_name, uint64_t* method_offset);
+
+  static DexFile* Create(uint64_t dex_file_offset_in_memory, Memory* memory, MapInfo* info);
+
+ protected:
+  void Init();
+
+  std::unique_ptr<const art::DexFile> dex_file_;
+  std::map<uint32_t, std::pair<uint64_t, uint32_t>> method_cache_;  // dex offset to method index.
+
+  uint32_t class_def_index_ = 0;
+  std::unique_ptr<art::ClassDataItemIterator> class_it_;
+};
+
+class DexFileFromFile : public DexFile {
+ public:
+  DexFileFromFile() = default;
+  virtual ~DexFileFromFile();
+
+  bool Open(uint64_t dex_file_offset_in_file, const std::string& name);
+
+ private:
+  void* mapped_memory_ = nullptr;
+  size_t size_ = 0;
+};
+
+class DexFileFromMemory : public DexFile {
+ public:
+  DexFileFromMemory() = default;
+  virtual ~DexFileFromMemory() = default;
+
+  bool Open(uint64_t dex_file_offset_in_memory, Memory* memory);
+
+ private:
+  std::vector<uint8_t> memory_;
+};
+
+}  // namespace unwindstack
+
+#endif  // _LIBUNWINDSTACK_DEX_FILE_H
diff --git a/libunwindstack/DexFiles.cpp b/libunwindstack/DexFiles.cpp
new file mode 100644
index 0000000..430f6c5
--- /dev/null
+++ b/libunwindstack/DexFiles.cpp
@@ -0,0 +1,206 @@
+/*
+ * 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 <stdint.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <memory>
+
+#include <unwindstack/DexFiles.h>
+#include <unwindstack/MapInfo.h>
+#include <unwindstack/Maps.h>
+#include <unwindstack/Memory.h>
+
+#include "DexFile.h"
+
+namespace unwindstack {
+
+struct DEXFileEntry32 {
+  uint32_t next;
+  uint32_t prev;
+  uint32_t dex_file;
+};
+
+struct DEXFileEntry64 {
+  uint64_t next;
+  uint64_t prev;
+  uint64_t dex_file;
+};
+
+DexFiles::DexFiles(std::shared_ptr<Memory>& memory) : memory_(memory) {}
+
+DexFiles::DexFiles(std::shared_ptr<Memory>& memory, std::vector<std::string>& search_libs)
+    : memory_(memory), search_libs_(search_libs) {}
+
+DexFiles::~DexFiles() {
+  for (auto& entry : files_) {
+    delete entry.second;
+  }
+}
+
+void DexFiles::SetArch(ArchEnum arch) {
+  switch (arch) {
+    case ARCH_ARM:
+    case ARCH_MIPS:
+    case ARCH_X86:
+      read_entry_ptr_func_ = &DexFiles::ReadEntryPtr32;
+      read_entry_func_ = &DexFiles::ReadEntry32;
+      break;
+
+    case ARCH_ARM64:
+    case ARCH_MIPS64:
+    case ARCH_X86_64:
+      read_entry_ptr_func_ = &DexFiles::ReadEntryPtr64;
+      read_entry_func_ = &DexFiles::ReadEntry64;
+      break;
+
+    case ARCH_UNKNOWN:
+      abort();
+  }
+}
+
+uint64_t DexFiles::ReadEntryPtr32(uint64_t addr) {
+  uint32_t 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;
+}
+
+uint64_t DexFiles::ReadEntryPtr64(uint64_t addr) {
+  uint64_t 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;
+}
+
+bool DexFiles::ReadEntry32() {
+  DEXFileEntry32 entry;
+  if (!memory_->ReadFully(entry_addr_, &entry, sizeof(entry)) || entry.dex_file == 0) {
+    entry_addr_ = 0;
+    return false;
+  }
+
+  addrs_.push_back(entry.dex_file);
+  entry_addr_ = entry.next;
+  return true;
+}
+
+bool DexFiles::ReadEntry64() {
+  DEXFileEntry64 entry;
+  if (!memory_->ReadFully(entry_addr_, &entry, sizeof(entry)) || entry.dex_file == 0) {
+    entry_addr_ = 0;
+    return false;
+  }
+
+  addrs_.push_back(entry.dex_file);
+  entry_addr_ = entry.next;
+  return true;
+}
+
+void DexFiles::Init(Maps* maps) {
+  if (initialized_) {
+    return;
+  }
+  initialized_ = true;
+  entry_addr_ = 0;
+
+  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;
+    }
+
+    if (!search_libs_.empty()) {
+      bool found = false;
+      const char* lib = basename(info->name.c_str());
+      for (const std::string& name : search_libs_) {
+        if (name == lib) {
+          found = true;
+          break;
+        }
+      }
+      if (!found) {
+        continue;
+      }
+    }
+
+    Elf* elf = info->GetElf(memory_, true);
+    uint64_t ptr;
+    // Find first non-empty list (libart might be loaded multiple times).
+    if (elf->GetGlobalVariable(dex_debug_name, &ptr) && ptr != 0) {
+      entry_addr_ = (this->*read_entry_ptr_func_)(ptr + info->start);
+      if (entry_addr_ != 0) {
+        break;
+      }
+    }
+  }
+}
+
+DexFile* DexFiles::GetDexFile(uint64_t dex_file_offset, MapInfo* info) {
+  // Lock while processing the data.
+  DexFile* dex_file;
+  auto entry = files_.find(dex_file_offset);
+  if (entry == files_.end()) {
+    dex_file = DexFile::Create(dex_file_offset, memory_.get(), info);
+    files_[dex_file_offset] = dex_file;
+  } else {
+    dex_file = entry->second;
+  }
+  return dex_file;
+}
+
+bool DexFiles::GetAddr(size_t index, uint64_t* addr) {
+  if (index < addrs_.size()) {
+    *addr = addrs_[index];
+    return true;
+  }
+  if (entry_addr_ != 0 && (this->*read_entry_func_)()) {
+    *addr = addrs_.back();
+    return true;
+  }
+  return false;
+}
+
+void DexFiles::GetMethodInformation(Maps* maps, MapInfo* info, uint64_t dex_pc,
+                                    std::string* method_name, uint64_t* method_offset) {
+  std::lock_guard<std::mutex> guard(lock_);
+  if (!initialized_) {
+    Init(maps);
+  }
+
+  size_t index = 0;
+  uint64_t addr;
+  while (GetAddr(index++, &addr)) {
+    if (addr < info->start || addr >= info->end) {
+      continue;
+    }
+
+    DexFile* dex_file = GetDexFile(addr, info);
+    if (dex_file != nullptr &&
+        dex_file->GetMethodInformation(dex_pc - addr, method_name, method_offset)) {
+      break;
+    }
+  }
+}
+
+}  // namespace unwindstack
diff --git a/libunwindstack/DwarfCfa.cpp b/libunwindstack/DwarfCfa.cpp
index 006f039..cd9ef61 100644
--- a/libunwindstack/DwarfCfa.cpp
+++ b/libunwindstack/DwarfCfa.cpp
@@ -23,12 +23,15 @@
 
 #include <android-base/stringprintf.h>
 
+#include <unwindstack/DwarfError.h>
+#include <unwindstack/DwarfLocation.h>
+#include <unwindstack/Log.h>
+
 #include "DwarfCfa.h"
 #include "DwarfEncoding.h"
-#include "DwarfMemory.h"
 #include "DwarfOp.h"
-#include "DwarfStructs.h"
-#include "Log.h"
+
+namespace unwindstack {
 
 template <typename AddressType>
 constexpr typename DwarfCfa<AddressType>::process_func DwarfCfa<AddressType>::kCallbackTable[64];
@@ -41,17 +44,29 @@
       (*loc_regs)[entry.first] = entry.second;
     }
   }
-  last_error_ = DWARF_ERROR_NONE;
+  last_error_.code = DWARF_ERROR_NONE;
+  last_error_.address = 0;
 
   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;
     if (!memory_->ReadBytes(&cfa_value, 1)) {
-      last_error_ = DWARF_ERROR_MEMORY_INVALID;
+      last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+      last_error_.address = memory_->cur_offset();
       return false;
     }
     uint8_t cfa_low = cfa_value & 0x3f;
@@ -63,7 +78,8 @@
       case 2: {
         uint64_t offset;
         if (!memory_->ReadULEB128(&offset)) {
-          last_error_ = DWARF_ERROR_MEMORY_INVALID;
+          last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+          last_error_.address = memory_->cur_offset();
           return false;
         }
         SignedType signed_offset =
@@ -75,7 +91,7 @@
       case 3: {
         if (cie_loc_regs_ == nullptr) {
           log(0, "restore while processing cie");
-          last_error_ = DWARF_ERROR_ILLEGAL_STATE;
+          last_error_.code = DWARF_ERROR_ILLEGAL_STATE;
           return false;
         }
 
@@ -90,7 +106,7 @@
       case 0: {
         const auto handle_func = DwarfCfa<AddressType>::kCallbackTable[cfa_low];
         if (handle_func == nullptr) {
-          last_error_ = DWARF_ERROR_ILLEGAL_VALUE;
+          last_error_.code = DWARF_ERROR_ILLEGAL_VALUE;
           return false;
         }
 
@@ -99,7 +115,8 @@
           if (cfa->operands[i] == DW_EH_PE_block) {
             uint64_t block_length;
             if (!memory_->ReadULEB128(&block_length)) {
-              last_error_ = DWARF_ERROR_MEMORY_INVALID;
+              last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+              last_error_.address = memory_->cur_offset();
               return false;
             }
             operands_.push_back(block_length);
@@ -108,7 +125,8 @@
           }
           uint64_t value;
           if (!memory_->ReadEncodedValue<AddressType>(cfa->operands[i], &value)) {
-            last_error_ = DWARF_ERROR_MEMORY_INVALID;
+            last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+            last_error_.address = memory_->cur_offset();
             return false;
           }
           operands_.push_back(value);
@@ -121,7 +139,6 @@
       }
     }
   }
-  return true;
 }
 
 template <typename AddressType>
@@ -247,8 +264,8 @@
 }
 
 template <typename AddressType>
-bool DwarfCfa<AddressType>::Log(uint32_t indent, uint64_t pc, uint64_t load_bias,
-                                uint64_t start_offset, uint64_t end_offset) {
+bool DwarfCfa<AddressType>::Log(uint32_t indent, uint64_t pc, uint64_t start_offset,
+                                uint64_t end_offset) {
   memory_->set_cur_offset(start_offset);
   uint64_t cfa_offset;
   uint64_t cur_pc = fde_->pc_start;
@@ -284,8 +301,8 @@
         break;
     }
     if (cur_pc != old_pc) {
-      log(indent, "");
-      log(indent, "PC 0x%" PRIx64, cur_pc + load_bias);
+      log(0, "");
+      log(indent, "PC 0x%" PRIx64, cur_pc);
     }
     old_pc = cur_pc;
   }
@@ -331,7 +348,7 @@
   AddressType reg = operands_[0];
   if (cie_loc_regs_ == nullptr) {
     log(0, "restore while processing cie");
-    last_error_ = DWARF_ERROR_ILLEGAL_STATE;
+    last_error_.code = DWARF_ERROR_ILLEGAL_STATE;
     return false;
   }
   auto reg_entry = cie_loc_regs_->find(reg);
@@ -393,7 +410,7 @@
   auto cfa_location = loc_regs->find(CFA_REG);
   if (cfa_location == loc_regs->end() || cfa_location->second.type != DWARF_LOCATION_REGISTER) {
     log(0, "Attempt to set new register, but cfa is not already set to a register.");
-    last_error_ = DWARF_ERROR_ILLEGAL_STATE;
+    last_error_.code = DWARF_ERROR_ILLEGAL_STATE;
     return false;
   }
 
@@ -407,7 +424,7 @@
   auto cfa_location = loc_regs->find(CFA_REG);
   if (cfa_location == loc_regs->end() || cfa_location->second.type != DWARF_LOCATION_REGISTER) {
     log(0, "Attempt to set offset, but cfa is not set to a register.");
-    last_error_ = DWARF_ERROR_ILLEGAL_STATE;
+    last_error_.code = DWARF_ERROR_ILLEGAL_STATE;
     return false;
   }
   cfa_location->second.values[1] = operands_[0];
@@ -416,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;
 }
@@ -451,7 +471,7 @@
   auto cfa_location = loc_regs->find(CFA_REG);
   if (cfa_location == loc_regs->end() || cfa_location->second.type != DWARF_LOCATION_REGISTER) {
     log(0, "Attempt to set offset, but cfa is not set to a register.");
-    last_error_ = DWARF_ERROR_ILLEGAL_STATE;
+    last_error_.code = DWARF_ERROR_ILLEGAL_STATE;
     return false;
   }
   SignedType offset = static_cast<SignedType>(operands_[0]) * fde_->cie->data_alignment_factor;
@@ -711,3 +731,5 @@
 // Explicitly instantiate DwarfCfa.
 template class DwarfCfa<uint32_t>;
 template class DwarfCfa<uint64_t>;
+
+}  // namespace unwindstack
diff --git a/libunwindstack/DwarfCfa.h b/libunwindstack/DwarfCfa.h
index 42ebae1..c5ffb8e 100644
--- a/libunwindstack/DwarfCfa.h
+++ b/libunwindstack/DwarfCfa.h
@@ -24,10 +24,12 @@
 #include <type_traits>
 #include <vector>
 
-#include "DwarfError.h"
-#include "DwarfLocation.h"
-#include "DwarfMemory.h"
-#include "DwarfStructs.h"
+#include <unwindstack/DwarfError.h>
+#include <unwindstack/DwarfLocation.h>
+#include <unwindstack/DwarfMemory.h>
+#include <unwindstack/DwarfStructs.h>
+
+namespace unwindstack {
 
 // DWARF Standard home: http://dwarfstd.org/
 // This code is based on DWARF 4: http://http://dwarfstd.org/doc/DWARF4.pdf
@@ -69,10 +71,11 @@
   bool GetLocationInfo(uint64_t pc, uint64_t start_offset, uint64_t end_offset,
                        dwarf_loc_regs_t* loc_regs);
 
-  bool Log(uint32_t indent, uint64_t pc, uint64_t load_bias, uint64_t start_offset,
-           uint64_t end_offset);
+  bool Log(uint32_t indent, uint64_t pc, uint64_t start_offset, uint64_t end_offset);
 
-  DwarfError last_error() { return last_error_; }
+  const DwarfErrorData& last_error() { return last_error_; }
+  DwarfErrorCode LastErrorCode() { return last_error_.code; }
+  uint64_t LastErrorAddress() { return last_error_.address; }
 
   AddressType cur_pc() { return cur_pc_; }
 
@@ -86,7 +89,7 @@
   bool LogInstruction(uint32_t indent, uint64_t cfa_offset, uint8_t op, uint64_t* cur_pc);
 
  private:
-  DwarfError last_error_;
+  DwarfErrorData last_error_;
   DwarfMemory* memory_;
   const DwarfFde* fde_;
 
@@ -252,4 +255,6 @@
   };
 };
 
+}  // namespace unwindstack
+
 #endif  // _LIBUNWINDSTACK_DWARF_CFA_H
diff --git a/libunwindstack/DwarfDebugFrame.cpp b/libunwindstack/DwarfDebugFrame.cpp
deleted file mode 100644
index 3ac02fc..0000000
--- a/libunwindstack/DwarfDebugFrame.cpp
+++ /dev/null
@@ -1,311 +0,0 @@
-/*
- * Copyright (C) 2017 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 <stdint.h>
-#include <stdlib.h>
-
-#include <algorithm>
-
-#include "DwarfDebugFrame.h"
-#include "DwarfMemory.h"
-#include "DwarfSection.h"
-#include "DwarfStructs.h"
-#include "Memory.h"
-
-template <typename AddressType>
-bool DwarfDebugFrame<AddressType>::Init(uint64_t offset, uint64_t size) {
-  offset_ = offset;
-  end_offset_ = offset + size;
-
-  memory_.clear_func_offset();
-  memory_.clear_text_offset();
-  memory_.set_data_offset(offset);
-  memory_.set_cur_offset(offset);
-
-  return CreateSortedFdeList();
-}
-
-template <typename AddressType>
-bool DwarfDebugFrame<AddressType>::GetCieInfo(uint8_t* segment_size, uint8_t* encoding) {
-  uint8_t version;
-  if (!memory_.ReadBytes(&version, 1)) {
-    last_error_ = DWARF_ERROR_MEMORY_INVALID;
-    return false;
-  }
-  // Read the augmentation string.
-  std::vector<char> aug_string;
-  char aug_value;
-  bool get_encoding = false;
-  do {
-    if (!memory_.ReadBytes(&aug_value, 1)) {
-      last_error_ = DWARF_ERROR_MEMORY_INVALID;
-      return false;
-    }
-    if (aug_value == 'R') {
-      get_encoding = true;
-    }
-    aug_string.push_back(aug_value);
-  } while (aug_value != '\0');
-
-  if (version == 4) {
-    // Skip the Address Size field.
-    memory_.set_cur_offset(memory_.cur_offset() + 1);
-
-    // Read the segment size.
-    if (!memory_.ReadBytes(segment_size, 1)) {
-      last_error_ = DWARF_ERROR_MEMORY_INVALID;
-      return false;
-    }
-  } else {
-    *segment_size = 0;
-  }
-
-  if (aug_string[0] != 'z' || !get_encoding) {
-    // No encoding
-    return true;
-  }
-
-  // Skip code alignment factor
-  uint8_t value;
-  do {
-    if (!memory_.ReadBytes(&value, 1)) {
-      last_error_ = DWARF_ERROR_MEMORY_INVALID;
-      return false;
-    }
-  } while (value & 0x80);
-
-  // Skip data alignment factor
-  do {
-    if (!memory_.ReadBytes(&value, 1)) {
-      last_error_ = DWARF_ERROR_MEMORY_INVALID;
-      return false;
-    }
-  } while (value & 0x80);
-
-  if (version == 1) {
-    // Skip return address register.
-    memory_.set_cur_offset(memory_.cur_offset() + 1);
-  } else {
-    // Skip return address register.
-    do {
-      if (!memory_.ReadBytes(&value, 1)) {
-        last_error_ = DWARF_ERROR_MEMORY_INVALID;
-        return false;
-      }
-    } while (value & 0x80);
-  }
-
-  // Skip the augmentation length.
-  do {
-    if (!memory_.ReadBytes(&value, 1)) {
-      last_error_ = DWARF_ERROR_MEMORY_INVALID;
-      return false;
-    }
-  } while (value & 0x80);
-
-  for (size_t i = 1; i < aug_string.size(); i++) {
-    if (aug_string[i] == 'R') {
-      if (!memory_.ReadBytes(encoding, 1)) {
-        last_error_ = DWARF_ERROR_MEMORY_INVALID;
-        return false;
-      }
-      // Got the encoding, that's all we are looking for.
-      return true;
-    } else if (aug_string[i] == 'L') {
-      memory_.set_cur_offset(memory_.cur_offset() + 1);
-    } else if (aug_string[i] == 'P') {
-      uint8_t encoding;
-      if (!memory_.ReadBytes(&encoding, 1)) {
-        last_error_ = DWARF_ERROR_MEMORY_INVALID;
-        return false;
-      }
-      uint64_t value;
-      if (!memory_.template ReadEncodedValue<AddressType>(encoding, &value)) {
-        last_error_ = DWARF_ERROR_MEMORY_INVALID;
-        return false;
-      }
-    }
-  }
-
-  // It should be impossible to get here.
-  abort();
-}
-
-template <typename AddressType>
-bool DwarfDebugFrame<AddressType>::AddFdeInfo(uint64_t entry_offset, uint8_t segment_size,
-                                              uint8_t encoding) {
-  if (segment_size != 0) {
-    memory_.set_cur_offset(memory_.cur_offset() + 1);
-  }
-
-  uint64_t start;
-  if (!memory_.template ReadEncodedValue<AddressType>(encoding & 0xf, &start)) {
-    last_error_ = DWARF_ERROR_MEMORY_INVALID;
-    return false;
-  }
-
-  uint64_t length;
-  if (!memory_.template ReadEncodedValue<AddressType>(encoding & 0xf, &length)) {
-    last_error_ = DWARF_ERROR_MEMORY_INVALID;
-    return false;
-  }
-  if (length != 0) {
-    fdes_.emplace_back(entry_offset, start, length);
-  }
-
-  return true;
-}
-
-template <typename AddressType>
-bool DwarfDebugFrame<AddressType>::CreateSortedFdeList() {
-  memory_.set_cur_offset(offset_);
-
-  // Loop through all of the entries and read just enough to create
-  // a sorted list of pcs.
-  // This code assumes that first comes the cie, then the fdes that
-  // it applies to.
-  uint64_t cie_offset = 0;
-  uint8_t address_encoding;
-  uint8_t segment_size;
-  while (memory_.cur_offset() < end_offset_) {
-    uint64_t cur_entry_offset = memory_.cur_offset();
-
-    // Figure out the entry length and type.
-    uint32_t value32;
-    if (!memory_.ReadBytes(&value32, sizeof(value32))) {
-      last_error_ = DWARF_ERROR_MEMORY_INVALID;
-      return false;
-    }
-
-    uint64_t next_entry_offset;
-    if (value32 == static_cast<uint32_t>(-1)) {
-      uint64_t value64;
-      if (!memory_.ReadBytes(&value64, sizeof(value64))) {
-        last_error_ = DWARF_ERROR_MEMORY_INVALID;
-        return false;
-      }
-      next_entry_offset = memory_.cur_offset() + value64;
-
-      // Read the Cie Id of a Cie or the pointer of the Fde.
-      if (!memory_.ReadBytes(&value64, sizeof(value64))) {
-        last_error_ = DWARF_ERROR_MEMORY_INVALID;
-        return false;
-      }
-
-      if (value64 == static_cast<uint64_t>(-1)) {
-        // Cie 64 bit
-        address_encoding = DW_EH_PE_sdata8;
-        if (!GetCieInfo(&segment_size, &address_encoding)) {
-          return false;
-        }
-        cie_offset = cur_entry_offset;
-      } else {
-        if (offset_ + value64 != cie_offset) {
-          // This means that this Fde is not following the Cie.
-          last_error_ = DWARF_ERROR_ILLEGAL_VALUE;
-          return false;
-        }
-
-        // Fde 64 bit
-        if (!AddFdeInfo(cur_entry_offset, segment_size, address_encoding)) {
-          return false;
-        }
-      }
-    } else {
-      next_entry_offset = memory_.cur_offset() + value32;
-
-      // Read the Cie Id of a Cie or the pointer of the Fde.
-      if (!memory_.ReadBytes(&value32, sizeof(value32))) {
-        last_error_ = DWARF_ERROR_MEMORY_INVALID;
-        return false;
-      }
-
-      if (value32 == static_cast<uint32_t>(-1)) {
-        // Cie 32 bit
-        address_encoding = DW_EH_PE_sdata4;
-        if (!GetCieInfo(&segment_size, &address_encoding)) {
-          return false;
-        }
-        cie_offset = cur_entry_offset;
-      } else {
-        if (offset_ + value32 != cie_offset) {
-          // This means that this Fde is not following the Cie.
-          last_error_ = DWARF_ERROR_ILLEGAL_VALUE;
-          return false;
-        }
-
-        // Fde 32 bit
-        if (!AddFdeInfo(cur_entry_offset, segment_size, address_encoding)) {
-          return false;
-        }
-      }
-    }
-
-    if (next_entry_offset < memory_.cur_offset()) {
-      // This indicates some kind of corruption, or malformed section data.
-      last_error_ = DWARF_ERROR_ILLEGAL_VALUE;
-      return false;
-    }
-    memory_.set_cur_offset(next_entry_offset);
-  }
-
-  // Sort the entries.
-  std::sort(fdes_.begin(), fdes_.end(), [](const FdeInfo& a, const FdeInfo& b) {
-    if (a.start == b.start) return a.end < b.end;
-    return a.start < b.start;
-  });
-
-  fde_count_ = fdes_.size();
-
-  return true;
-}
-
-template <typename AddressType>
-bool DwarfDebugFrame<AddressType>::GetFdeOffsetFromPc(uint64_t pc, uint64_t* fde_offset) {
-  if (fde_count_ == 0) {
-    return false;
-  }
-
-  size_t first = 0;
-  size_t last = fde_count_;
-  while (first < last) {
-    size_t current = (first + last) / 2;
-    const FdeInfo* info = &fdes_[current];
-    if (pc >= info->start && pc <= info->end) {
-      *fde_offset = info->offset;
-      return true;
-    }
-
-    if (pc < info->start) {
-      last = current;
-    } else {
-      first = current + 1;
-    }
-  }
-  return false;
-}
-
-template <typename AddressType>
-const DwarfFde* DwarfDebugFrame<AddressType>::GetFdeFromIndex(size_t index) {
-  if (index >= fdes_.size()) {
-    return nullptr;
-  }
-  return this->GetFdeFromOffset(fdes_[index].offset);
-}
-
-// Explicitly instantiate DwarfDebugFrame.
-template class DwarfDebugFrame<uint32_t>;
-template class DwarfDebugFrame<uint64_t>;
diff --git a/libunwindstack/DwarfDebugFrame.h b/libunwindstack/DwarfDebugFrame.h
index 320368c..388ab0a 100644
--- a/libunwindstack/DwarfDebugFrame.h
+++ b/libunwindstack/DwarfDebugFrame.h
@@ -21,56 +21,30 @@
 
 #include <vector>
 
-#include "DwarfSection.h"
+#include <unwindstack/DwarfSection.h>
+
+namespace unwindstack {
 
 template <typename AddressType>
-class DwarfDebugFrame : public DwarfSectionImpl<AddressType> {
+class DwarfDebugFrame : public DwarfSectionImplNoHdr<AddressType> {
  public:
-  // Add these so that the protected members of DwarfSectionImpl
-  // can be accessed without needing a this->.
-  using DwarfSectionImpl<AddressType>::memory_;
-  using DwarfSectionImpl<AddressType>::fde_count_;
-  using DwarfSectionImpl<AddressType>::last_error_;
-
-  struct FdeInfo {
-    FdeInfo(uint64_t offset, uint64_t start, uint64_t length)
-        : offset(offset), start(start), end(start + length) {}
-
-    uint64_t offset;
-    AddressType start;
-    AddressType end;
-  };
-
-  DwarfDebugFrame(Memory* memory) : DwarfSectionImpl<AddressType>(memory) {}
+  DwarfDebugFrame(Memory* memory) : DwarfSectionImplNoHdr<AddressType>(memory) {
+    this->cie32_value_ = static_cast<uint32_t>(-1);
+    this->cie64_value_ = static_cast<uint64_t>(-1);
+  }
   virtual ~DwarfDebugFrame() = default;
 
-  bool Init(uint64_t offset, uint64_t size) override;
+  uint64_t GetCieOffsetFromFde32(uint32_t pointer) override {
+    return this->entries_offset_ + pointer;
+  }
 
-  bool GetFdeOffsetFromPc(uint64_t pc, uint64_t* fde_offset) override;
-
-  const DwarfFde* GetFdeFromIndex(size_t index) override;
-
-  bool IsCie32(uint32_t value32) override { return value32 == static_cast<uint32_t>(-1); }
-
-  bool IsCie64(uint64_t value64) override { return value64 == static_cast<uint64_t>(-1); }
-
-  uint64_t GetCieOffsetFromFde32(uint32_t pointer) override { return offset_ + pointer; }
-
-  uint64_t GetCieOffsetFromFde64(uint64_t pointer) override { return offset_ + pointer; }
+  uint64_t GetCieOffsetFromFde64(uint64_t pointer) override {
+    return this->entries_offset_ + pointer;
+  }
 
   uint64_t AdjustPcFromFde(uint64_t pc) override { return pc; }
-
-  bool GetCieInfo(uint8_t* segment_size, uint8_t* encoding);
-
-  bool AddFdeInfo(uint64_t entry_offset, uint8_t segment_size, uint8_t encoding);
-
-  bool CreateSortedFdeList();
-
- protected:
-  uint64_t offset_;
-  uint64_t end_offset_;
-
-  std::vector<FdeInfo> fdes_;
 };
 
+}  // namespace unwindstack
+
 #endif  // _LIBUNWINDSTACK_DWARF_DEBUG_FRAME_H
diff --git a/libunwindstack/DwarfEhFrame.cpp b/libunwindstack/DwarfEhFrame.cpp
deleted file mode 100644
index 937b6bd..0000000
--- a/libunwindstack/DwarfEhFrame.cpp
+++ /dev/null
@@ -1,213 +0,0 @@
-/*
- * Copyright (C) 2017 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 <stdint.h>
-
-#include "Check.h"
-#include "DwarfEhFrame.h"
-#include "DwarfMemory.h"
-#include "DwarfSection.h"
-#include "DwarfStructs.h"
-#include "Memory.h"
-
-template <typename AddressType>
-bool DwarfEhFrame<AddressType>::Init(uint64_t offset, uint64_t size) {
-  uint8_t data[4];
-
-  memory_.clear_func_offset();
-  memory_.clear_text_offset();
-  memory_.set_data_offset(offset);
-  memory_.set_cur_offset(offset);
-
-  // Read the first four bytes all at once.
-  if (!memory_.ReadBytes(data, 4)) {
-    last_error_ = DWARF_ERROR_MEMORY_INVALID;
-    return false;
-  }
-
-  version_ = data[0];
-  if (version_ != 1) {
-    // Unknown version.
-    last_error_ = DWARF_ERROR_UNSUPPORTED_VERSION;
-    return false;
-  }
-
-  ptr_encoding_ = data[1];
-  uint8_t fde_count_encoding = data[2];
-  table_encoding_ = data[3];
-  table_entry_size_ = memory_.template GetEncodedSize<AddressType>(table_encoding_);
-
-  memory_.set_pc_offset(memory_.cur_offset());
-  if (!memory_.template ReadEncodedValue<AddressType>(ptr_encoding_, &ptr_offset_)) {
-    last_error_ = DWARF_ERROR_MEMORY_INVALID;
-    return false;
-  }
-
-  memory_.set_pc_offset(memory_.cur_offset());
-  if (!memory_.template ReadEncodedValue<AddressType>(fde_count_encoding, &fde_count_)) {
-    last_error_ = DWARF_ERROR_MEMORY_INVALID;
-    return false;
-  }
-
-  entries_offset_ = memory_.cur_offset();
-  entries_end_ = offset + size;
-  entries_data_offset_ = offset;
-  cur_entries_offset_ = entries_offset_;
-
-  return true;
-}
-
-template <typename AddressType>
-const DwarfFde* DwarfEhFrame<AddressType>::GetFdeFromIndex(size_t index) {
-  const FdeInfo* info = GetFdeInfoFromIndex(index);
-  if (info == nullptr) {
-    return nullptr;
-  }
-  return this->GetFdeFromOffset(info->offset);
-}
-
-template <typename AddressType>
-const typename DwarfEhFrame<AddressType>::FdeInfo* DwarfEhFrame<AddressType>::GetFdeInfoFromIndex(
-    size_t index) {
-  auto entry = fde_info_.find(index);
-  if (entry != fde_info_.end()) {
-    return &fde_info_[index];
-  }
-  FdeInfo* info = &fde_info_[index];
-
-  memory_.set_data_offset(entries_data_offset_);
-  memory_.set_cur_offset(entries_offset_ + 2 * index * table_entry_size_);
-  memory_.set_pc_offset(memory_.cur_offset());
-  uint64_t value;
-  if (!memory_.template ReadEncodedValue<AddressType>(table_encoding_, &value) ||
-      !memory_.template ReadEncodedValue<AddressType>(table_encoding_, &info->offset)) {
-    last_error_ = DWARF_ERROR_MEMORY_INVALID;
-    fde_info_.erase(index);
-    return nullptr;
-  }
-  info->pc = value;
-  return info;
-}
-
-template <typename AddressType>
-bool DwarfEhFrame<AddressType>::GetFdeOffsetBinary(uint64_t pc, uint64_t* fde_offset,
-                                                   uint64_t total_entries) {
-  CHECK(fde_count_ > 0);
-  CHECK(total_entries <= fde_count_);
-
-  size_t first = 0;
-  size_t last = total_entries;
-  while (first < last) {
-    size_t current = (first + last) / 2;
-    const FdeInfo* info = GetFdeInfoFromIndex(current);
-    if (pc == info->pc) {
-      *fde_offset = info->offset;
-      return true;
-    }
-    if (pc < info->pc) {
-      last = current;
-    } else {
-      first = current + 1;
-    }
-  }
-  if (last != 0) {
-    const FdeInfo* info = GetFdeInfoFromIndex(last - 1);
-    *fde_offset = info->offset;
-    return true;
-  }
-  return false;
-}
-
-template <typename AddressType>
-bool DwarfEhFrame<AddressType>::GetFdeOffsetSequential(uint64_t pc, uint64_t* fde_offset) {
-  CHECK(fde_count_ != 0);
-  last_error_ = DWARF_ERROR_NONE;
-  // We can do a binary search if the pc is in the range of the elements
-  // that have already been cached.
-  if (!fde_info_.empty()) {
-    const FdeInfo* info = &fde_info_[fde_info_.size() - 1];
-    if (pc >= info->pc) {
-      *fde_offset = info->offset;
-      return true;
-    }
-    if (pc < info->pc) {
-      return GetFdeOffsetBinary(pc, fde_offset, fde_info_.size());
-    }
-  }
-
-  if (cur_entries_offset_ == 0) {
-    // All entries read, or error encountered.
-    return false;
-  }
-
-  memory_.set_data_offset(entries_data_offset_);
-  memory_.set_cur_offset(cur_entries_offset_);
-  cur_entries_offset_ = 0;
-
-  FdeInfo* prev_info = nullptr;
-  for (size_t current = fde_info_.size();
-       current < fde_count_ && memory_.cur_offset() < entries_end_; current++) {
-    memory_.set_pc_offset(memory_.cur_offset());
-    uint64_t value;
-    if (!memory_.template ReadEncodedValue<AddressType>(table_encoding_, &value)) {
-      last_error_ = DWARF_ERROR_MEMORY_INVALID;
-      return false;
-    }
-
-    FdeInfo* info = &fde_info_[current];
-    if (!memory_.template ReadEncodedValue<AddressType>(table_encoding_, &info->offset)) {
-      fde_info_.erase(current);
-      last_error_ = DWARF_ERROR_MEMORY_INVALID;
-      return false;
-    }
-    info->pc = value;
-
-    if (pc < info->pc) {
-      if (prev_info == nullptr) {
-        return false;
-      }
-      cur_entries_offset_ = memory_.cur_offset();
-      *fde_offset = prev_info->offset;
-      return true;
-    }
-    prev_info = info;
-  }
-
-  if (fde_count_ == fde_info_.size() && pc >= prev_info->pc) {
-    *fde_offset = prev_info->offset;
-    return true;
-  }
-  return false;
-}
-
-template <typename AddressType>
-bool DwarfEhFrame<AddressType>::GetFdeOffsetFromPc(uint64_t pc, uint64_t* fde_offset) {
-  if (fde_count_ == 0) {
-    return false;
-  }
-
-  if (table_entry_size_ > 0) {
-    // Do a binary search since the size of each table entry is fixed.
-    return GetFdeOffsetBinary(pc, fde_offset, fde_count_);
-  } else {
-    // Do a sequential search since each table entry size is variable.
-    return GetFdeOffsetSequential(pc, fde_offset);
-  }
-}
-
-// Explicitly instantiate DwarfEhFrame.
-template class DwarfEhFrame<uint32_t>;
-template class DwarfEhFrame<uint64_t>;
diff --git a/libunwindstack/DwarfEhFrame.h b/libunwindstack/DwarfEhFrame.h
index e6909ed..df441fb 100644
--- a/libunwindstack/DwarfEhFrame.h
+++ b/libunwindstack/DwarfEhFrame.h
@@ -19,71 +19,31 @@
 
 #include <stdint.h>
 
-#include "DwarfSection.h"
+#include <unwindstack/DwarfSection.h>
+#include <unwindstack/Memory.h>
 
-// Forward declarations.
-class Memory;
+namespace unwindstack {
 
 template <typename AddressType>
-class DwarfEhFrame : public DwarfSectionImpl<AddressType> {
+class DwarfEhFrame : public DwarfSectionImplNoHdr<AddressType> {
  public:
-  // Add these so that the protected members of DwarfSectionImpl
-  // can be accessed without needing a this->.
-  using DwarfSectionImpl<AddressType>::memory_;
-  using DwarfSectionImpl<AddressType>::fde_count_;
-  using DwarfSectionImpl<AddressType>::last_error_;
-
-  struct FdeInfo {
-    AddressType pc;
-    uint64_t offset;
-  };
-
-  DwarfEhFrame(Memory* memory) : DwarfSectionImpl<AddressType>(memory) {}
+  DwarfEhFrame(Memory* memory) : DwarfSectionImplNoHdr<AddressType>(memory) {}
   virtual ~DwarfEhFrame() = default;
 
-  bool Init(uint64_t offset, uint64_t size) override;
-
-  bool GetFdeOffsetFromPc(uint64_t pc, uint64_t* fde_offset) override;
-
-  const DwarfFde* GetFdeFromIndex(size_t index) override;
-
-  bool IsCie32(uint32_t value32) override { return value32 == 0; }
-
-  bool IsCie64(uint64_t value64) override { return value64 == 0; }
-
   uint64_t GetCieOffsetFromFde32(uint32_t pointer) override {
-    return memory_.cur_offset() - pointer - 4;
+    return this->memory_.cur_offset() - pointer - 4;
   }
 
   uint64_t GetCieOffsetFromFde64(uint64_t pointer) override {
-    return memory_.cur_offset() - pointer - 8;
+    return this->memory_.cur_offset() - pointer - 8;
   }
 
   uint64_t AdjustPcFromFde(uint64_t pc) override {
     // The eh_frame uses relative pcs.
-    return pc + memory_.cur_offset();
+    return pc + this->memory_.cur_offset() - 4;
   }
-
-  const FdeInfo* GetFdeInfoFromIndex(size_t index);
-
-  bool GetFdeOffsetSequential(uint64_t pc, uint64_t* fde_offset);
-
-  bool GetFdeOffsetBinary(uint64_t pc, uint64_t* fde_offset, uint64_t total_entries);
-
- protected:
-  uint8_t version_;
-  uint8_t ptr_encoding_;
-  uint8_t table_encoding_;
-  size_t table_entry_size_;
-
-  uint64_t ptr_offset_;
-
-  uint64_t entries_offset_;
-  uint64_t entries_end_;
-  uint64_t entries_data_offset_;
-  uint64_t cur_entries_offset_ = 0;
-
-  std::unordered_map<uint64_t, FdeInfo> fde_info_;
 };
 
+}  // namespace unwindstack
+
 #endif  // _LIBUNWINDSTACK_DWARF_EH_FRAME_H
diff --git a/libunwindstack/DwarfEhFrameWithHdr.cpp b/libunwindstack/DwarfEhFrameWithHdr.cpp
new file mode 100644
index 0000000..668527a
--- /dev/null
+++ b/libunwindstack/DwarfEhFrameWithHdr.cpp
@@ -0,0 +1,274 @@
+/*
+ * Copyright (C) 2017 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 <stdint.h>
+
+#include <unwindstack/DwarfError.h>
+#include <unwindstack/DwarfStructs.h>
+#include <unwindstack/Memory.h>
+
+#include "Check.h"
+#include "DwarfEhFrameWithHdr.h"
+#include "DwarfEncoding.h"
+
+namespace unwindstack {
+
+static inline bool IsEncodingRelative(uint8_t encoding) {
+  encoding >>= 4;
+  return encoding > 0 && encoding <= DW_EH_PE_funcrel;
+}
+
+template <typename AddressType>
+bool DwarfEhFrameWithHdr<AddressType>::Init(uint64_t offset, uint64_t size, uint64_t load_bias) {
+  load_bias_ = load_bias;
+
+  memory_.clear_func_offset();
+  memory_.clear_text_offset();
+  memory_.set_data_offset(offset);
+  memory_.set_cur_offset(offset);
+  pc_offset_ = offset;
+
+  // Read the first four bytes all at once.
+  uint8_t data[4];
+  if (!memory_.ReadBytes(data, 4)) {
+    last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+    last_error_.address = memory_.cur_offset();
+    return false;
+  }
+
+  version_ = data[0];
+  if (version_ != 1) {
+    // Unknown version.
+    last_error_.code = DWARF_ERROR_UNSUPPORTED_VERSION;
+    return false;
+  }
+
+  ptr_encoding_ = data[1];
+  uint8_t fde_count_encoding = data[2];
+  table_encoding_ = data[3];
+  table_entry_size_ = memory_.template GetEncodedSize<AddressType>(table_encoding_);
+
+  memory_.set_pc_offset(memory_.cur_offset());
+  if (!memory_.template ReadEncodedValue<AddressType>(ptr_encoding_, &ptr_offset_)) {
+    last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+    last_error_.address = memory_.cur_offset();
+    return false;
+  }
+
+  memory_.set_pc_offset(memory_.cur_offset());
+  if (!memory_.template ReadEncodedValue<AddressType>(fde_count_encoding, &fde_count_)) {
+    last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+    last_error_.address = memory_.cur_offset();
+    return false;
+  }
+
+  if (fde_count_ == 0) {
+    last_error_.code = DWARF_ERROR_NO_FDES;
+    return false;
+  }
+
+  entries_offset_ = memory_.cur_offset();
+  entries_end_ = offset + size;
+  entries_data_offset_ = offset;
+  cur_entries_offset_ = entries_offset_;
+
+  return true;
+}
+
+template <typename AddressType>
+const DwarfFde* DwarfEhFrameWithHdr<AddressType>::GetFdeFromPc(uint64_t pc) {
+  uint64_t fde_offset;
+  if (!GetFdeOffsetFromPc(pc, &fde_offset)) {
+    return nullptr;
+  }
+  const DwarfFde* fde = this->GetFdeFromOffset(fde_offset);
+  if (fde == nullptr) {
+    return nullptr;
+  }
+
+  // Guaranteed pc >= pc_start, need to check pc in the fde range.
+  if (pc < fde->pc_end) {
+    return fde;
+  }
+  last_error_.code = DWARF_ERROR_ILLEGAL_STATE;
+  return nullptr;
+}
+
+template <typename AddressType>
+const typename DwarfEhFrameWithHdr<AddressType>::FdeInfo*
+DwarfEhFrameWithHdr<AddressType>::GetFdeInfoFromIndex(size_t index) {
+  auto entry = fde_info_.find(index);
+  if (entry != fde_info_.end()) {
+    return &fde_info_[index];
+  }
+  FdeInfo* info = &fde_info_[index];
+
+  memory_.set_data_offset(entries_data_offset_);
+  memory_.set_cur_offset(entries_offset_ + 2 * index * table_entry_size_);
+  memory_.set_pc_offset(0);
+  uint64_t value;
+  if (!memory_.template ReadEncodedValue<AddressType>(table_encoding_, &value) ||
+      !memory_.template ReadEncodedValue<AddressType>(table_encoding_, &info->offset)) {
+    last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+    last_error_.address = memory_.cur_offset();
+    fde_info_.erase(index);
+    return nullptr;
+  }
+
+  // Relative encodings require adding in the load bias.
+  if (IsEncodingRelative(table_encoding_)) {
+    value += load_bias_;
+  }
+  info->pc = value;
+  return info;
+}
+
+template <typename AddressType>
+bool DwarfEhFrameWithHdr<AddressType>::GetFdeOffsetBinary(uint64_t pc, uint64_t* fde_offset,
+                                                          uint64_t total_entries) {
+  CHECK(fde_count_ > 0);
+  CHECK(total_entries <= fde_count_);
+
+  size_t first = 0;
+  size_t last = total_entries;
+  while (first < last) {
+    size_t current = (first + last) / 2;
+    const FdeInfo* info = GetFdeInfoFromIndex(current);
+    if (info == nullptr) {
+      return false;
+    }
+    if (pc == info->pc) {
+      *fde_offset = info->offset;
+      return true;
+    }
+    if (pc < info->pc) {
+      last = current;
+    } else {
+      first = current + 1;
+    }
+  }
+  if (last != 0) {
+    const FdeInfo* info = GetFdeInfoFromIndex(last - 1);
+    if (info == nullptr) {
+      return false;
+    }
+    *fde_offset = info->offset;
+    return true;
+  }
+  return false;
+}
+
+template <typename AddressType>
+bool DwarfEhFrameWithHdr<AddressType>::GetFdeOffsetSequential(uint64_t pc, uint64_t* fde_offset) {
+  CHECK(fde_count_ != 0);
+  last_error_.code = DWARF_ERROR_NONE;
+  last_error_.address = 0;
+
+  // We can do a binary search if the pc is in the range of the elements
+  // that have already been cached.
+  if (!fde_info_.empty()) {
+    const FdeInfo* info = &fde_info_[fde_info_.size() - 1];
+    if (pc >= info->pc) {
+      *fde_offset = info->offset;
+      return true;
+    }
+    if (pc < info->pc) {
+      return GetFdeOffsetBinary(pc, fde_offset, fde_info_.size());
+    }
+  }
+
+  if (cur_entries_offset_ == 0) {
+    // All entries read, or error encountered.
+    return false;
+  }
+
+  memory_.set_data_offset(entries_data_offset_);
+  memory_.set_cur_offset(cur_entries_offset_);
+  memory_.set_pc_offset(0);
+  cur_entries_offset_ = 0;
+
+  FdeInfo* prev_info = nullptr;
+  for (size_t current = fde_info_.size();
+       current < fde_count_ && memory_.cur_offset() < entries_end_; current++) {
+    FdeInfo* info = &fde_info_[current];
+    uint64_t value;
+    if (!memory_.template ReadEncodedValue<AddressType>(table_encoding_, &value) ||
+        !memory_.template ReadEncodedValue<AddressType>(table_encoding_, &info->offset)) {
+      fde_info_.erase(current);
+      last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+      last_error_.address = memory_.cur_offset();
+      return false;
+    }
+
+    // Relative encodings require adding in the load bias.
+    if (IsEncodingRelative(table_encoding_)) {
+      value += load_bias_;
+    }
+    info->pc = value;
+
+    if (pc < info->pc) {
+      if (prev_info == nullptr) {
+        return false;
+      }
+      cur_entries_offset_ = memory_.cur_offset();
+      *fde_offset = prev_info->offset;
+      return true;
+    }
+    prev_info = info;
+  }
+
+  if (fde_count_ == fde_info_.size() && pc >= prev_info->pc) {
+    *fde_offset = prev_info->offset;
+    return true;
+  }
+  return false;
+}
+
+template <typename AddressType>
+bool DwarfEhFrameWithHdr<AddressType>::GetFdeOffsetFromPc(uint64_t pc, uint64_t* fde_offset) {
+  if (fde_count_ == 0) {
+    return false;
+  }
+
+  if (table_entry_size_ > 0) {
+    // Do a binary search since the size of each table entry is fixed.
+    return GetFdeOffsetBinary(pc, fde_offset, fde_count_);
+  } else {
+    // Do a sequential search since each table entry size is variable.
+    return GetFdeOffsetSequential(pc, fde_offset);
+  }
+}
+
+template <typename AddressType>
+void DwarfEhFrameWithHdr<AddressType>::GetFdes(std::vector<const DwarfFde*>* fdes) {
+  for (size_t i = 0; i < fde_count_; i++) {
+    const FdeInfo* info = GetFdeInfoFromIndex(i);
+    if (info == nullptr) {
+      break;
+    }
+    const DwarfFde* fde = this->GetFdeFromOffset(info->offset);
+    if (fde == nullptr) {
+      break;
+    }
+    fdes->push_back(fde);
+  }
+}
+
+// Explicitly instantiate DwarfEhFrameWithHdr
+template class DwarfEhFrameWithHdr<uint32_t>;
+template class DwarfEhFrameWithHdr<uint64_t>;
+
+}  // namespace unwindstack
diff --git a/libunwindstack/DwarfEhFrameWithHdr.h b/libunwindstack/DwarfEhFrameWithHdr.h
new file mode 100644
index 0000000..e3e9ca8
--- /dev/null
+++ b/libunwindstack/DwarfEhFrameWithHdr.h
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2016 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 _LIBUNWINDSTACK_DWARF_EH_FRAME_WITH_HDR_H
+#define _LIBUNWINDSTACK_DWARF_EH_FRAME_WITH_HDR_H
+
+#include <stdint.h>
+
+#include <unordered_map>
+
+#include <unwindstack/DwarfSection.h>
+
+namespace unwindstack {
+
+// Forward declarations.
+class Memory;
+
+template <typename AddressType>
+class DwarfEhFrameWithHdr : public DwarfSectionImpl<AddressType> {
+ public:
+  // Add these so that the protected members of DwarfSectionImpl
+  // can be accessed without needing a this->.
+  using DwarfSectionImpl<AddressType>::memory_;
+  using DwarfSectionImpl<AddressType>::pc_offset_;
+  using DwarfSectionImpl<AddressType>::entries_offset_;
+  using DwarfSectionImpl<AddressType>::entries_end_;
+  using DwarfSectionImpl<AddressType>::last_error_;
+  using DwarfSectionImpl<AddressType>::load_bias_;
+
+  struct FdeInfo {
+    AddressType pc;
+    uint64_t offset;
+  };
+
+  DwarfEhFrameWithHdr(Memory* memory) : DwarfSectionImpl<AddressType>(memory) {}
+  virtual ~DwarfEhFrameWithHdr() = default;
+
+  uint64_t GetCieOffsetFromFde32(uint32_t pointer) override {
+    return this->memory_.cur_offset() - pointer - 4;
+  }
+
+  uint64_t GetCieOffsetFromFde64(uint64_t pointer) override {
+    return this->memory_.cur_offset() - pointer - 8;
+  }
+
+  uint64_t AdjustPcFromFde(uint64_t pc) override {
+    // The eh_frame uses relative pcs.
+    return pc + this->memory_.cur_offset() - 4;
+  }
+
+  bool Init(uint64_t offset, uint64_t size, uint64_t load_bias) override;
+
+  const DwarfFde* GetFdeFromPc(uint64_t pc) override;
+
+  bool GetFdeOffsetFromPc(uint64_t pc, uint64_t* fde_offset);
+
+  const FdeInfo* GetFdeInfoFromIndex(size_t index);
+
+  bool GetFdeOffsetSequential(uint64_t pc, uint64_t* fde_offset);
+
+  bool GetFdeOffsetBinary(uint64_t pc, uint64_t* fde_offset, uint64_t total_entries);
+
+  void GetFdes(std::vector<const DwarfFde*>* fdes) override;
+
+ protected:
+  uint8_t version_;
+  uint8_t ptr_encoding_;
+  uint8_t table_encoding_;
+  size_t table_entry_size_;
+
+  uint64_t ptr_offset_;
+
+  uint64_t entries_data_offset_;
+  uint64_t cur_entries_offset_ = 0;
+
+  uint64_t fde_count_;
+  std::unordered_map<uint64_t, FdeInfo> fde_info_;
+};
+
+}  // namespace unwindstack
+
+#endif  // _LIBUNWINDSTACK_DWARF_EH_FRAME_WITH_HDR_H
diff --git a/libunwindstack/DwarfEncoding.h b/libunwindstack/DwarfEncoding.h
index 0ff3b8c..20db222 100644
--- a/libunwindstack/DwarfEncoding.h
+++ b/libunwindstack/DwarfEncoding.h
@@ -19,6 +19,8 @@
 
 #include <stdint.h>
 
+namespace unwindstack {
+
 enum DwarfEncoding : uint8_t {
   DW_EH_PE_omit = 0xff,
 
@@ -44,4 +46,6 @@
   DW_EH_PE_block = 0x0f,
 };
 
+}  // namespace unwindstack
+
 #endif  // _LIBUNWINDSTACK_DWARF_ENCODING_H
diff --git a/libunwindstack/DwarfError.h b/libunwindstack/DwarfError.h
deleted file mode 100644
index c00f17d..0000000
--- a/libunwindstack/DwarfError.h
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright (C) 2016 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 _LIBUNWINDSTACK_DWARF_ERROR_H
-#define _LIBUNWINDSTACK_DWARF_ERROR_H
-
-#include <stdint.h>
-
-enum DwarfError : uint8_t {
-  DWARF_ERROR_NONE,
-  DWARF_ERROR_MEMORY_INVALID,
-  DWARF_ERROR_ILLEGAL_VALUE,
-  DWARF_ERROR_ILLEGAL_STATE,
-  DWARF_ERROR_STACK_INDEX_NOT_VALID,
-  DWARF_ERROR_NOT_IMPLEMENTED,
-  DWARF_ERROR_TOO_MANY_ITERATIONS,
-  DWARF_ERROR_CFA_NOT_DEFINED,
-  DWARF_ERROR_UNSUPPORTED_VERSION,
-};
-
-#endif  // _LIBUNWINDSTACK_DWARF_ERROR_H
diff --git a/libunwindstack/DwarfLocation.h b/libunwindstack/DwarfLocation.h
deleted file mode 100644
index 062d125..0000000
--- a/libunwindstack/DwarfLocation.h
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright (C) 2016 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 _LIBUNWINDSTACK_DWARF_LOCATION_H
-#define _LIBUNWINDSTACK_DWARF_LOCATION_H
-
-#include <stdint.h>
-
-#include <unordered_map>
-
-enum DwarfLocationEnum : uint8_t {
-  DWARF_LOCATION_INVALID = 0,
-  DWARF_LOCATION_UNDEFINED,
-  DWARF_LOCATION_OFFSET,
-  DWARF_LOCATION_VAL_OFFSET,
-  DWARF_LOCATION_REGISTER,
-  DWARF_LOCATION_EXPRESSION,
-  DWARF_LOCATION_VAL_EXPRESSION,
-};
-
-struct DwarfLocation {
-  DwarfLocationEnum type;
-  uint64_t values[2];
-};
-
-typedef std::unordered_map<uint16_t, DwarfLocation> dwarf_loc_regs_t;
-
-#endif  // _LIBUNWINDSTACK_DWARF_LOCATION_H
diff --git a/libunwindstack/DwarfMemory.cpp b/libunwindstack/DwarfMemory.cpp
index a984fc6..6ffdc0d 100644
--- a/libunwindstack/DwarfMemory.cpp
+++ b/libunwindstack/DwarfMemory.cpp
@@ -18,13 +18,16 @@
 
 #include <string>
 
+#include <unwindstack/DwarfMemory.h>
+#include <unwindstack/Memory.h>
+
 #include "Check.h"
 #include "DwarfEncoding.h"
-#include "DwarfMemory.h"
-#include "Memory.h"
+
+namespace unwindstack {
 
 bool DwarfMemory::ReadBytes(void* dst, size_t num_bytes) {
-  if (!memory_->Read(cur_offset_, dst, num_bytes)) {
+  if (!memory_->ReadFully(cur_offset_, dst, num_bytes)) {
     return false;
   }
   cur_offset_ += num_bytes;
@@ -232,7 +235,7 @@
       return false;
   }
 
-  return AdjustEncodedValue(encoding & 0xf0, value);
+  return AdjustEncodedValue(encoding & 0x70, value);
 }
 
 // Instantiate all of the needed template functions.
@@ -246,3 +249,5 @@
 
 template bool DwarfMemory::ReadEncodedValue<uint32_t>(uint8_t, uint64_t*);
 template bool DwarfMemory::ReadEncodedValue<uint64_t>(uint8_t, uint64_t*);
+
+}  // namespace unwindstack
diff --git a/libunwindstack/DwarfMemory.h b/libunwindstack/DwarfMemory.h
deleted file mode 100644
index a304dd9..0000000
--- a/libunwindstack/DwarfMemory.h
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Copyright (C) 2016 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 _LIBUNWINDSTACK_DWARF_MEMORY_H
-#define _LIBUNWINDSTACK_DWARF_MEMORY_H
-
-#include <stdint.h>
-
-// Forward declarations.
-class Memory;
-
-class DwarfMemory {
- public:
-  DwarfMemory(Memory* memory) : memory_(memory) {}
-  virtual ~DwarfMemory() = default;
-
-  bool ReadBytes(void* dst, size_t num_bytes);
-
-  template <typename SignedType>
-  bool ReadSigned(uint64_t* value);
-
-  bool ReadULEB128(uint64_t* value);
-
-  bool ReadSLEB128(int64_t* value);
-
-  template <typename AddressType>
-  size_t GetEncodedSize(uint8_t encoding);
-
-  bool AdjustEncodedValue(uint8_t encoding, uint64_t* value);
-
-  template <typename AddressType>
-  bool ReadEncodedValue(uint8_t encoding, uint64_t* value);
-
-  uint64_t cur_offset() { return cur_offset_; }
-  void set_cur_offset(uint64_t cur_offset) { cur_offset_ = cur_offset; }
-
-  void set_pc_offset(uint64_t offset) { pc_offset_ = offset; }
-  void clear_pc_offset() { pc_offset_ = static_cast<uint64_t>(-1); }
-
-  void set_data_offset(uint64_t offset) { data_offset_ = offset; }
-  void clear_data_offset() { data_offset_ = static_cast<uint64_t>(-1); }
-
-  void set_func_offset(uint64_t offset) { func_offset_ = offset; }
-  void clear_func_offset() { func_offset_ = static_cast<uint64_t>(-1); }
-
-  void set_text_offset(uint64_t offset) { text_offset_ = offset; }
-  void clear_text_offset() { text_offset_ = static_cast<uint64_t>(-1); }
-
- private:
-  Memory* memory_;
-  uint64_t cur_offset_ = 0;
-
-  uint64_t pc_offset_ = static_cast<uint64_t>(-1);
-  uint64_t data_offset_ = static_cast<uint64_t>(-1);
-  uint64_t func_offset_ = static_cast<uint64_t>(-1);
-  uint64_t text_offset_ = static_cast<uint64_t>(-1);
-};
-
-#endif  // _LIBUNWINDSTACK_DWARF_MEMORY_H
diff --git a/libunwindstack/DwarfOp.cpp b/libunwindstack/DwarfOp.cpp
index 507ca08..5bc60b9 100644
--- a/libunwindstack/DwarfOp.cpp
+++ b/libunwindstack/DwarfOp.cpp
@@ -22,30 +22,65 @@
 
 #include <android-base/stringprintf.h>
 
-#include "DwarfError.h"
-#include "DwarfMemory.h"
+#include <unwindstack/DwarfError.h>
+#include <unwindstack/DwarfMemory.h>
+#include <unwindstack/Log.h>
+#include <unwindstack/Memory.h>
+#include <unwindstack/Regs.h>
+
 #include "DwarfOp.h"
-#include "Log.h"
-#include "Memory.h"
-#include "Regs.h"
+
+namespace unwindstack {
 
 template <typename AddressType>
 constexpr typename DwarfOp<AddressType>::OpCallback DwarfOp<AddressType>::kCallbackTable[256];
 
 template <typename AddressType>
-bool DwarfOp<AddressType>::Eval(uint64_t start, uint64_t end, uint8_t dwarf_version) {
-  uint32_t iterations = 0;
+bool DwarfOp<AddressType>::Eval(uint64_t start, uint64_t end) {
   is_register_ = false;
   stack_.clear();
   memory_->set_cur_offset(start);
+  dex_pc_set_ = false;
+
+  // Unroll the first Decode calls to be able to check for a special
+  // sequence of ops and values that indicate this is the dex pc.
+  // The pattern is:
+  //   OP_const4u (0x0c)  'D' 'E' 'X' '1'
+  //   OP_drop (0x13)
+  if (memory_->cur_offset() < end) {
+    if (!Decode()) {
+      return false;
+    }
+  } else {
+    return true;
+  }
+  bool check_for_drop;
+  if (cur_op_ == 0x0c && operands_.back() == 0x31584544) {
+    check_for_drop = true;
+  } else {
+    check_for_drop = false;
+  }
+  if (memory_->cur_offset() < end) {
+    if (!Decode()) {
+      return false;
+    }
+  } else {
+    return true;
+  }
+
+  if (check_for_drop && cur_op_ == 0x13) {
+    dex_pc_set_ = true;
+  }
+
+  uint32_t iterations = 2;
   while (memory_->cur_offset() < end) {
-    if (!Decode(dwarf_version)) {
+    if (!Decode()) {
       return false;
     }
     // To protect against a branch that creates an infinite loop,
     // terminate if the number of iterations gets too high.
     if (iterations++ == 1000) {
-      last_error_ = DWARF_ERROR_TOO_MANY_ITERATIONS;
+      last_error_.code = DWARF_ERROR_TOO_MANY_ITERATIONS;
       return false;
     }
   }
@@ -53,29 +88,24 @@
 }
 
 template <typename AddressType>
-bool DwarfOp<AddressType>::Decode(uint8_t dwarf_version) {
-  last_error_ = DWARF_ERROR_NONE;
+bool DwarfOp<AddressType>::Decode() {
+  last_error_.code = DWARF_ERROR_NONE;
   if (!memory_->ReadBytes(&cur_op_, 1)) {
-    last_error_ = DWARF_ERROR_MEMORY_INVALID;
+    last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+    last_error_.address = memory_->cur_offset();
     return false;
   }
 
   const auto* op = &kCallbackTable[cur_op_];
   const auto handle_func = op->handle_func;
   if (handle_func == nullptr) {
-    last_error_ = DWARF_ERROR_ILLEGAL_VALUE;
-    return false;
-  }
-
-  // Check for an unsupported opcode.
-  if (dwarf_version < op->supported_version) {
-    last_error_ = DWARF_ERROR_ILLEGAL_VALUE;
+    last_error_.code = DWARF_ERROR_ILLEGAL_VALUE;
     return false;
   }
 
   // Make sure that the required number of stack elements is available.
   if (stack_.size() < op->num_required_stack_values) {
-    last_error_ = DWARF_ERROR_STACK_INDEX_NOT_VALID;
+    last_error_.code = DWARF_ERROR_STACK_INDEX_NOT_VALID;
     return false;
   }
 
@@ -83,7 +113,8 @@
   for (size_t i = 0; i < op->num_operands; i++) {
     uint64_t value;
     if (!memory_->ReadEncodedValue<AddressType>(op->operands[i], &value)) {
-      last_error_ = DWARF_ERROR_MEMORY_INVALID;
+      last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+      last_error_.address = memory_->cur_offset();
       return false;
     }
     operands_.push_back(value);
@@ -138,8 +169,9 @@
   // Read the address and dereference it.
   AddressType addr = StackPop();
   AddressType value;
-  if (!regular_memory()->Read(addr, &value, sizeof(value))) {
-    last_error_ = DWARF_ERROR_MEMORY_INVALID;
+  if (!regular_memory()->ReadFully(addr, &value, sizeof(value))) {
+    last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+    last_error_.address = addr;
     return false;
   }
   stack_.push_front(value);
@@ -150,14 +182,15 @@
 bool DwarfOp<AddressType>::op_deref_size() {
   AddressType bytes_to_read = OperandAt(0);
   if (bytes_to_read > sizeof(AddressType) || bytes_to_read == 0) {
-    last_error_ = DWARF_ERROR_ILLEGAL_VALUE;
+    last_error_.code = DWARF_ERROR_ILLEGAL_VALUE;
     return false;
   }
   // Read the address and dereference it.
   AddressType addr = StackPop();
   AddressType value = 0;
-  if (!regular_memory()->Read(addr, &value, bytes_to_read)) {
-    last_error_ = DWARF_ERROR_MEMORY_INVALID;
+  if (!regular_memory()->ReadFully(addr, &value, bytes_to_read)) {
+    last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+    last_error_.address = addr;
     return false;
   }
   stack_.push_front(value);
@@ -195,7 +228,7 @@
 bool DwarfOp<AddressType>::op_pick() {
   AddressType index = OperandAt(0);
   if (index > StackSize()) {
-    last_error_ = DWARF_ERROR_STACK_INDEX_NOT_VALID;
+    last_error_.code = DWARF_ERROR_STACK_INDEX_NOT_VALID;
     return false;
   }
   stack_.push_front(StackAt(index));
@@ -240,7 +273,7 @@
 bool DwarfOp<AddressType>::op_div() {
   AddressType top = StackPop();
   if (top == 0) {
-    last_error_ = DWARF_ERROR_ILLEGAL_VALUE;
+    last_error_.code = DWARF_ERROR_ILLEGAL_VALUE;
     return false;
   }
   SignedType signed_divisor = static_cast<SignedType>(top);
@@ -260,7 +293,7 @@
 bool DwarfOp<AddressType>::op_mod() {
   AddressType top = StackPop();
   if (top == 0) {
-    last_error_ = DWARF_ERROR_ILLEGAL_VALUE;
+    last_error_.code = DWARF_ERROR_ILLEGAL_VALUE;
     return false;
   }
   stack_[0] %= top;
@@ -427,22 +460,22 @@
 template <typename AddressType>
 bool DwarfOp<AddressType>::op_breg() {
   uint16_t reg = cur_op() - 0x70;
-  if (reg >= regs_->total_regs()) {
-    last_error_ = DWARF_ERROR_ILLEGAL_VALUE;
+  if (reg >= regs_info_->Total()) {
+    last_error_.code = DWARF_ERROR_ILLEGAL_VALUE;
     return false;
   }
-  stack_.push_front((*regs_)[reg] + OperandAt(0));
+  stack_.push_front(regs_info_->Get(reg) + OperandAt(0));
   return true;
 }
 
 template <typename AddressType>
 bool DwarfOp<AddressType>::op_bregx() {
   AddressType reg = OperandAt(0);
-  if (reg >= regs_->total_regs()) {
-    last_error_ = DWARF_ERROR_ILLEGAL_VALUE;
+  if (reg >= regs_info_->Total()) {
+    last_error_.code = DWARF_ERROR_ILLEGAL_VALUE;
     return false;
   }
-  stack_.push_front((*regs_)[reg] + OperandAt(1));
+  stack_.push_front(regs_info_->Get(reg) + OperandAt(1));
   return true;
 }
 
@@ -453,10 +486,12 @@
 
 template <typename AddressType>
 bool DwarfOp<AddressType>::op_not_implemented() {
-  last_error_ = DWARF_ERROR_NOT_IMPLEMENTED;
+  last_error_.code = DWARF_ERROR_NOT_IMPLEMENTED;
   return false;
 }
 
 // Explicitly instantiate DwarfOp.
 template class DwarfOp<uint32_t>;
 template class DwarfOp<uint64_t>;
+
+}  // namespace unwindstack
diff --git a/libunwindstack/DwarfOp.h b/libunwindstack/DwarfOp.h
index 0f4b36d..4c69b3d 100644
--- a/libunwindstack/DwarfOp.h
+++ b/libunwindstack/DwarfOp.h
@@ -24,14 +24,12 @@
 #include <type_traits>
 #include <vector>
 
-#include "DwarfEncoding.h"
+#include <unwindstack/DwarfError.h>
 
-enum DwarfVersion : uint8_t {
-  DWARF_VERSION_2 = 2,
-  DWARF_VERSION_3 = 3,
-  DWARF_VERSION_4 = 4,
-  DWARF_VERSION_MAX = DWARF_VERSION_4,
-};
+#include "DwarfEncoding.h"
+#include "RegsInfo.h"
+
+namespace unwindstack {
 
 // Forward declarations.
 class DwarfMemory;
@@ -47,7 +45,6 @@
   struct OpCallback {
     const char* name;
     bool (DwarfOp::*handle_func)();
-    uint8_t supported_version;
     uint8_t num_required_stack_values;
     uint8_t num_operands;
     uint8_t operands[2];
@@ -58,18 +55,22 @@
       : memory_(memory), regular_memory_(regular_memory) {}
   virtual ~DwarfOp() = default;
 
-  bool Decode(uint8_t dwarf_version);
+  bool Decode();
 
-  bool Eval(uint64_t start, uint64_t end, uint8_t dwarf_version);
+  bool Eval(uint64_t start, uint64_t end);
 
   void GetLogInfo(uint64_t start, uint64_t end, std::vector<std::string>* lines);
 
   AddressType StackAt(size_t index) { return stack_[index]; }
   size_t StackSize() { return stack_.size(); }
 
-  void set_regs(RegsImpl<AddressType>* regs) { regs_ = regs; }
+  void set_regs_info(RegsInfo<AddressType>* regs_info) { regs_info_ = regs_info; }
 
-  DwarfError last_error() { return last_error_; }
+  const DwarfErrorData& last_error() { return last_error_; }
+  DwarfErrorCode LastErrorCode() { return last_error_.code; }
+  uint64_t LastErrorAddress() { return last_error_.address; }
+
+  bool dex_pc_set() { return dex_pc_set_; }
 
   bool is_register() { return is_register_; }
 
@@ -91,9 +92,10 @@
   DwarfMemory* memory_;
   Memory* regular_memory_;
 
-  RegsImpl<AddressType>* regs_;
+  RegsInfo<AddressType>* regs_info_;
+  bool dex_pc_set_ = false;
   bool is_register_ = false;
-  DwarfError last_error_ = DWARF_ERROR_NONE;
+  DwarfErrorData last_error_{DWARF_ERROR_NONE, 0};
   uint8_t cur_op_;
   std::vector<AddressType> operands_;
   std::deque<AddressType> stack_;
@@ -142,35 +144,32 @@
   bool op_not_implemented();
 
   constexpr static OpCallback kCallbackTable[256] = {
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0x00 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0x01 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0x02 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0x00 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0x01 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0x02 illegal op
       {
           // 0x03 DW_OP_addr
           "DW_OP_addr",
           &DwarfOp::op_push,
-          DWARF_VERSION_2,
           0,
           1,
           {DW_EH_PE_absptr},
       },
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0x04 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0x05 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0x04 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0x05 illegal op
       {
           // 0x06 DW_OP_deref
           "DW_OP_deref",
           &DwarfOp::op_deref,
-          DWARF_VERSION_2,
           1,
           0,
           {},
       },
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0x07 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0x07 illegal op
       {
           // 0x08 DW_OP_const1u
           "DW_OP_const1u",
           &DwarfOp::op_push,
-          DWARF_VERSION_2,
           0,
           1,
           {DW_EH_PE_udata1},
@@ -179,7 +178,6 @@
           // 0x09 DW_OP_const1s
           "DW_OP_const1s",
           &DwarfOp::op_push,
-          DWARF_VERSION_2,
           0,
           1,
           {DW_EH_PE_sdata1},
@@ -188,7 +186,6 @@
           // 0x0a DW_OP_const2u
           "DW_OP_const2u",
           &DwarfOp::op_push,
-          DWARF_VERSION_2,
           0,
           1,
           {DW_EH_PE_udata2},
@@ -197,7 +194,6 @@
           // 0x0b DW_OP_const2s
           "DW_OP_const2s",
           &DwarfOp::op_push,
-          DWARF_VERSION_2,
           0,
           1,
           {DW_EH_PE_sdata2},
@@ -206,7 +202,6 @@
           // 0x0c DW_OP_const4u
           "DW_OP_const4u",
           &DwarfOp::op_push,
-          DWARF_VERSION_2,
           0,
           1,
           {DW_EH_PE_udata4},
@@ -215,7 +210,6 @@
           // 0x0d DW_OP_const4s
           "DW_OP_const4s",
           &DwarfOp::op_push,
-          DWARF_VERSION_2,
           0,
           1,
           {DW_EH_PE_sdata4},
@@ -224,7 +218,6 @@
           // 0x0e DW_OP_const8u
           "DW_OP_const8u",
           &DwarfOp::op_push,
-          DWARF_VERSION_2,
           0,
           1,
           {DW_EH_PE_udata8},
@@ -233,7 +226,6 @@
           // 0x0f DW_OP_const8s
           "DW_OP_const8s",
           &DwarfOp::op_push,
-          DWARF_VERSION_2,
           0,
           1,
           {DW_EH_PE_sdata8},
@@ -242,7 +234,6 @@
           // 0x10 DW_OP_constu
           "DW_OP_constu",
           &DwarfOp::op_push,
-          DWARF_VERSION_2,
           0,
           1,
           {DW_EH_PE_uleb128},
@@ -251,7 +242,6 @@
           // 0x11 DW_OP_consts
           "DW_OP_consts",
           &DwarfOp::op_push,
-          DWARF_VERSION_2,
           0,
           1,
           {DW_EH_PE_sleb128},
@@ -260,7 +250,6 @@
           // 0x12 DW_OP_dup
           "DW_OP_dup",
           &DwarfOp::op_dup,
-          DWARF_VERSION_2,
           1,
           0,
           {},
@@ -269,7 +258,6 @@
           // 0x13 DW_OP_drop
           "DW_OP_drop",
           &DwarfOp::op_drop,
-          DWARF_VERSION_2,
           1,
           0,
           {},
@@ -278,7 +266,6 @@
           // 0x14 DW_OP_over
           "DW_OP_over",
           &DwarfOp::op_over,
-          DWARF_VERSION_2,
           2,
           0,
           {},
@@ -287,7 +274,6 @@
           // 0x15 DW_OP_pick
           "DW_OP_pick",
           &DwarfOp::op_pick,
-          DWARF_VERSION_2,
           0,
           1,
           {DW_EH_PE_udata1},
@@ -296,7 +282,6 @@
           // 0x16 DW_OP_swap
           "DW_OP_swap",
           &DwarfOp::op_swap,
-          DWARF_VERSION_2,
           2,
           0,
           {},
@@ -305,7 +290,6 @@
           // 0x17 DW_OP_rot
           "DW_OP_rot",
           &DwarfOp::op_rot,
-          DWARF_VERSION_2,
           3,
           0,
           {},
@@ -314,7 +298,6 @@
           // 0x18 DW_OP_xderef
           "DW_OP_xderef",
           &DwarfOp::op_not_implemented,
-          DWARF_VERSION_2,
           2,
           0,
           {},
@@ -323,7 +306,6 @@
           // 0x19 DW_OP_abs
           "DW_OP_abs",
           &DwarfOp::op_abs,
-          DWARF_VERSION_2,
           1,
           0,
           {},
@@ -332,7 +314,6 @@
           // 0x1a DW_OP_and
           "DW_OP_and",
           &DwarfOp::op_and,
-          DWARF_VERSION_2,
           2,
           0,
           {},
@@ -341,7 +322,6 @@
           // 0x1b DW_OP_div
           "DW_OP_div",
           &DwarfOp::op_div,
-          DWARF_VERSION_2,
           2,
           0,
           {},
@@ -350,7 +330,6 @@
           // 0x1c DW_OP_minus
           "DW_OP_minus",
           &DwarfOp::op_minus,
-          DWARF_VERSION_2,
           2,
           0,
           {},
@@ -359,7 +338,6 @@
           // 0x1d DW_OP_mod
           "DW_OP_mod",
           &DwarfOp::op_mod,
-          DWARF_VERSION_2,
           2,
           0,
           {},
@@ -368,7 +346,6 @@
           // 0x1e DW_OP_mul
           "DW_OP_mul",
           &DwarfOp::op_mul,
-          DWARF_VERSION_2,
           2,
           0,
           {},
@@ -377,7 +354,6 @@
           // 0x1f DW_OP_neg
           "DW_OP_neg",
           &DwarfOp::op_neg,
-          DWARF_VERSION_2,
           1,
           0,
           {},
@@ -386,7 +362,6 @@
           // 0x20 DW_OP_not
           "DW_OP_not",
           &DwarfOp::op_not,
-          DWARF_VERSION_2,
           1,
           0,
           {},
@@ -395,7 +370,6 @@
           // 0x21 DW_OP_or
           "DW_OP_or",
           &DwarfOp::op_or,
-          DWARF_VERSION_2,
           2,
           0,
           {},
@@ -404,7 +378,6 @@
           // 0x22 DW_OP_plus
           "DW_OP_plus",
           &DwarfOp::op_plus,
-          DWARF_VERSION_2,
           2,
           0,
           {},
@@ -413,7 +386,6 @@
           // 0x23 DW_OP_plus_uconst
           "DW_OP_plus_uconst",
           &DwarfOp::op_plus_uconst,
-          DWARF_VERSION_2,
           1,
           1,
           {DW_EH_PE_uleb128},
@@ -422,7 +394,6 @@
           // 0x24 DW_OP_shl
           "DW_OP_shl",
           &DwarfOp::op_shl,
-          DWARF_VERSION_2,
           2,
           0,
           {},
@@ -431,7 +402,6 @@
           // 0x25 DW_OP_shr
           "DW_OP_shr",
           &DwarfOp::op_shr,
-          DWARF_VERSION_2,
           2,
           0,
           {},
@@ -440,7 +410,6 @@
           // 0x26 DW_OP_shra
           "DW_OP_shra",
           &DwarfOp::op_shra,
-          DWARF_VERSION_2,
           2,
           0,
           {},
@@ -449,7 +418,6 @@
           // 0x27 DW_OP_xor
           "DW_OP_xor",
           &DwarfOp::op_xor,
-          DWARF_VERSION_2,
           2,
           0,
           {},
@@ -458,7 +426,6 @@
           // 0x28 DW_OP_bra
           "DW_OP_bra",
           &DwarfOp::op_bra,
-          DWARF_VERSION_2,
           1,
           1,
           {DW_EH_PE_sdata2},
@@ -467,7 +434,6 @@
           // 0x29 DW_OP_eq
           "DW_OP_eq",
           &DwarfOp::op_eq,
-          DWARF_VERSION_2,
           2,
           0,
           {},
@@ -476,7 +442,6 @@
           // 0x2a DW_OP_ge
           "DW_OP_ge",
           &DwarfOp::op_ge,
-          DWARF_VERSION_2,
           2,
           0,
           {},
@@ -485,7 +450,6 @@
           // 0x2b DW_OP_gt
           "DW_OP_gt",
           &DwarfOp::op_gt,
-          DWARF_VERSION_2,
           2,
           0,
           {},
@@ -494,7 +458,6 @@
           // 0x2c DW_OP_le
           "DW_OP_le",
           &DwarfOp::op_le,
-          DWARF_VERSION_2,
           2,
           0,
           {},
@@ -503,7 +466,6 @@
           // 0x2d DW_OP_lt
           "DW_OP_lt",
           &DwarfOp::op_lt,
-          DWARF_VERSION_2,
           2,
           0,
           {},
@@ -512,7 +474,6 @@
           // 0x2e DW_OP_ne
           "DW_OP_ne",
           &DwarfOp::op_ne,
-          DWARF_VERSION_2,
           2,
           0,
           {},
@@ -521,7 +482,6 @@
           // 0x2f DW_OP_skip
           "DW_OP_skip",
           &DwarfOp::op_skip,
-          DWARF_VERSION_2,
           0,
           1,
           {DW_EH_PE_sdata2},
@@ -530,7 +490,6 @@
           // 0x30 DW_OP_lit0
           "DW_OP_lit0",
           &DwarfOp::op_lit,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -539,7 +498,6 @@
           // 0x31 DW_OP_lit1
           "DW_OP_lit1",
           &DwarfOp::op_lit,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -548,7 +506,6 @@
           // 0x32 DW_OP_lit2
           "DW_OP_lit2",
           &DwarfOp::op_lit,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -557,7 +514,6 @@
           // 0x33 DW_OP_lit3
           "DW_OP_lit3",
           &DwarfOp::op_lit,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -566,7 +522,6 @@
           // 0x34 DW_OP_lit4
           "DW_OP_lit4",
           &DwarfOp::op_lit,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -575,7 +530,6 @@
           // 0x35 DW_OP_lit5
           "DW_OP_lit5",
           &DwarfOp::op_lit,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -584,7 +538,6 @@
           // 0x36 DW_OP_lit6
           "DW_OP_lit6",
           &DwarfOp::op_lit,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -593,7 +546,6 @@
           // 0x37 DW_OP_lit7
           "DW_OP_lit7",
           &DwarfOp::op_lit,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -602,7 +554,6 @@
           // 0x38 DW_OP_lit8
           "DW_OP_lit8",
           &DwarfOp::op_lit,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -611,7 +562,6 @@
           // 0x39 DW_OP_lit9
           "DW_OP_lit9",
           &DwarfOp::op_lit,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -620,7 +570,6 @@
           // 0x3a DW_OP_lit10
           "DW_OP_lit10",
           &DwarfOp::op_lit,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -629,7 +578,6 @@
           // 0x3b DW_OP_lit11
           "DW_OP_lit11",
           &DwarfOp::op_lit,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -638,7 +586,6 @@
           // 0x3c DW_OP_lit12
           "DW_OP_lit12",
           &DwarfOp::op_lit,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -647,7 +594,6 @@
           // 0x3d DW_OP_lit13
           "DW_OP_lit13",
           &DwarfOp::op_lit,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -656,7 +602,6 @@
           // 0x3e DW_OP_lit14
           "DW_OP_lit14",
           &DwarfOp::op_lit,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -665,7 +610,6 @@
           // 0x3f DW_OP_lit15
           "DW_OP_lit15",
           &DwarfOp::op_lit,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -674,7 +618,6 @@
           // 0x40 DW_OP_lit16
           "DW_OP_lit16",
           &DwarfOp::op_lit,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -683,7 +626,6 @@
           // 0x41 DW_OP_lit17
           "DW_OP_lit17",
           &DwarfOp::op_lit,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -692,7 +634,6 @@
           // 0x42 DW_OP_lit18
           "DW_OP_lit18",
           &DwarfOp::op_lit,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -701,7 +642,6 @@
           // 0x43 DW_OP_lit19
           "DW_OP_lit19",
           &DwarfOp::op_lit,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -710,7 +650,6 @@
           // 0x44 DW_OP_lit20
           "DW_OP_lit20",
           &DwarfOp::op_lit,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -719,7 +658,6 @@
           // 0x45 DW_OP_lit21
           "DW_OP_lit21",
           &DwarfOp::op_lit,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -728,7 +666,6 @@
           // 0x46 DW_OP_lit22
           "DW_OP_lit22",
           &DwarfOp::op_lit,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -737,7 +674,6 @@
           // 0x47 DW_OP_lit23
           "DW_OP_lit23",
           &DwarfOp::op_lit,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -746,7 +682,6 @@
           // 0x48 DW_OP_lit24
           "DW_OP_lit24",
           &DwarfOp::op_lit,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -755,7 +690,6 @@
           // 0x49 DW_OP_lit25
           "DW_OP_lit25",
           &DwarfOp::op_lit,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -764,7 +698,6 @@
           // 0x4a DW_OP_lit26
           "DW_OP_lit26",
           &DwarfOp::op_lit,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -773,7 +706,6 @@
           // 0x4b DW_OP_lit27
           "DW_OP_lit27",
           &DwarfOp::op_lit,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -782,7 +714,6 @@
           // 0x4c DW_OP_lit28
           "DW_OP_lit28",
           &DwarfOp::op_lit,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -791,7 +722,6 @@
           // 0x4d DW_OP_lit29
           "DW_OP_lit29",
           &DwarfOp::op_lit,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -800,7 +730,6 @@
           // 0x4e DW_OP_lit30
           "DW_OP_lit30",
           &DwarfOp::op_lit,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -809,7 +738,6 @@
           // 0x4f DW_OP_lit31
           "DW_OP_lit31",
           &DwarfOp::op_lit,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -818,7 +746,6 @@
           // 0x50 DW_OP_reg0
           "DW_OP_reg0",
           &DwarfOp::op_reg,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -827,7 +754,6 @@
           // 0x51 DW_OP_reg1
           "DW_OP_reg1",
           &DwarfOp::op_reg,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -836,7 +762,6 @@
           // 0x52 DW_OP_reg2
           "DW_OP_reg2",
           &DwarfOp::op_reg,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -845,7 +770,6 @@
           // 0x53 DW_OP_reg3
           "DW_OP_reg3",
           &DwarfOp::op_reg,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -854,7 +778,6 @@
           // 0x54 DW_OP_reg4
           "DW_OP_reg4",
           &DwarfOp::op_reg,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -863,7 +786,6 @@
           // 0x55 DW_OP_reg5
           "DW_OP_reg5",
           &DwarfOp::op_reg,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -872,7 +794,6 @@
           // 0x56 DW_OP_reg6
           "DW_OP_reg6",
           &DwarfOp::op_reg,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -881,7 +802,6 @@
           // 0x57 DW_OP_reg7
           "DW_OP_reg7",
           &DwarfOp::op_reg,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -890,7 +810,6 @@
           // 0x58 DW_OP_reg8
           "DW_OP_reg8",
           &DwarfOp::op_reg,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -899,7 +818,6 @@
           // 0x59 DW_OP_reg9
           "DW_OP_reg9",
           &DwarfOp::op_reg,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -908,7 +826,6 @@
           // 0x5a DW_OP_reg10
           "DW_OP_reg10",
           &DwarfOp::op_reg,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -917,7 +834,6 @@
           // 0x5b DW_OP_reg11
           "DW_OP_reg11",
           &DwarfOp::op_reg,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -926,7 +842,6 @@
           // 0x5c DW_OP_reg12
           "DW_OP_reg12",
           &DwarfOp::op_reg,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -935,7 +850,6 @@
           // 0x5d DW_OP_reg13
           "DW_OP_reg13",
           &DwarfOp::op_reg,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -944,7 +858,6 @@
           // 0x5e DW_OP_reg14
           "DW_OP_reg14",
           &DwarfOp::op_reg,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -953,7 +866,6 @@
           // 0x5f DW_OP_reg15
           "DW_OP_reg15",
           &DwarfOp::op_reg,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -962,7 +874,6 @@
           // 0x60 DW_OP_reg16
           "DW_OP_reg16",
           &DwarfOp::op_reg,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -971,7 +882,6 @@
           // 0x61 DW_OP_reg17
           "DW_OP_reg17",
           &DwarfOp::op_reg,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -980,7 +890,6 @@
           // 0x62 DW_OP_reg18
           "DW_OP_reg18",
           &DwarfOp::op_reg,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -989,7 +898,6 @@
           // 0x63 DW_OP_reg19
           "DW_OP_reg19",
           &DwarfOp::op_reg,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -998,7 +906,6 @@
           // 0x64 DW_OP_reg20
           "DW_OP_reg20",
           &DwarfOp::op_reg,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -1007,7 +914,6 @@
           // 0x65 DW_OP_reg21
           "DW_OP_reg21",
           &DwarfOp::op_reg,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -1016,7 +922,6 @@
           // 0x66 DW_OP_reg22
           "DW_OP_reg22",
           &DwarfOp::op_reg,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -1025,7 +930,6 @@
           // 0x67 DW_OP_reg23
           "DW_OP_reg23",
           &DwarfOp::op_reg,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -1034,7 +938,6 @@
           // 0x68 DW_OP_reg24
           "DW_OP_reg24",
           &DwarfOp::op_reg,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -1043,7 +946,6 @@
           // 0x69 DW_OP_reg25
           "DW_OP_reg25",
           &DwarfOp::op_reg,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -1052,7 +954,6 @@
           // 0x6a DW_OP_reg26
           "DW_OP_reg26",
           &DwarfOp::op_reg,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -1061,7 +962,6 @@
           // 0x6b DW_OP_reg27
           "DW_OP_reg27",
           &DwarfOp::op_reg,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -1070,7 +970,6 @@
           // 0x6c DW_OP_reg28
           "DW_OP_reg28",
           &DwarfOp::op_reg,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -1079,7 +978,6 @@
           // 0x6d DW_OP_reg29
           "DW_OP_reg29",
           &DwarfOp::op_reg,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -1088,7 +986,6 @@
           // 0x6e DW_OP_reg30
           "DW_OP_reg30",
           &DwarfOp::op_reg,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -1097,7 +994,6 @@
           // 0x6f DW_OP_reg31
           "DW_OP_reg31",
           &DwarfOp::op_reg,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -1106,7 +1002,6 @@
           // 0x70 DW_OP_breg0
           "DW_OP_breg0",
           &DwarfOp::op_breg,
-          DWARF_VERSION_2,
           0,
           1,
           {DW_EH_PE_sleb128},
@@ -1115,7 +1010,6 @@
           // 0x71 DW_OP_breg1
           "DW_OP_breg1",
           &DwarfOp::op_breg,
-          DWARF_VERSION_2,
           0,
           1,
           {DW_EH_PE_sleb128},
@@ -1124,7 +1018,6 @@
           // 0x72 DW_OP_breg2
           "DW_OP_breg2",
           &DwarfOp::op_breg,
-          DWARF_VERSION_2,
           0,
           1,
           {DW_EH_PE_sleb128},
@@ -1133,7 +1026,6 @@
           // 0x73 DW_OP_breg3
           "DW_OP_breg3",
           &DwarfOp::op_breg,
-          DWARF_VERSION_2,
           0,
           1,
           {DW_EH_PE_sleb128},
@@ -1142,7 +1034,6 @@
           // 0x74 DW_OP_breg4
           "DW_OP_breg4",
           &DwarfOp::op_breg,
-          DWARF_VERSION_2,
           0,
           1,
           {DW_EH_PE_sleb128},
@@ -1151,7 +1042,6 @@
           // 0x75 DW_OP_breg5
           "DW_OP_breg5",
           &DwarfOp::op_breg,
-          DWARF_VERSION_2,
           0,
           1,
           {DW_EH_PE_sleb128},
@@ -1160,7 +1050,6 @@
           // 0x76 DW_OP_breg6
           "DW_OP_breg6",
           &DwarfOp::op_breg,
-          DWARF_VERSION_2,
           0,
           1,
           {DW_EH_PE_sleb128},
@@ -1169,7 +1058,6 @@
           // 0x77 DW_OP_breg7
           "DW_OP_breg7",
           &DwarfOp::op_breg,
-          DWARF_VERSION_2,
           0,
           1,
           {DW_EH_PE_sleb128},
@@ -1178,7 +1066,6 @@
           // 0x78 DW_OP_breg8
           "DW_OP_breg8",
           &DwarfOp::op_breg,
-          DWARF_VERSION_2,
           0,
           1,
           {DW_EH_PE_sleb128},
@@ -1187,7 +1074,6 @@
           // 0x79 DW_OP_breg9
           "DW_OP_breg9",
           &DwarfOp::op_breg,
-          DWARF_VERSION_2,
           0,
           1,
           {DW_EH_PE_sleb128},
@@ -1196,7 +1082,6 @@
           // 0x7a DW_OP_breg10
           "DW_OP_breg10",
           &DwarfOp::op_breg,
-          DWARF_VERSION_2,
           0,
           1,
           {DW_EH_PE_sleb128},
@@ -1205,7 +1090,6 @@
           // 0x7b DW_OP_breg11
           "DW_OP_breg11",
           &DwarfOp::op_breg,
-          DWARF_VERSION_2,
           0,
           1,
           {DW_EH_PE_sleb128},
@@ -1214,7 +1098,6 @@
           // 0x7c DW_OP_breg12
           "DW_OP_breg12",
           &DwarfOp::op_breg,
-          DWARF_VERSION_2,
           0,
           1,
           {DW_EH_PE_sleb128},
@@ -1223,7 +1106,6 @@
           // 0x7d DW_OP_breg13
           "DW_OP_breg13",
           &DwarfOp::op_breg,
-          DWARF_VERSION_2,
           0,
           1,
           {DW_EH_PE_sleb128},
@@ -1232,7 +1114,6 @@
           // 0x7e DW_OP_breg14
           "DW_OP_breg14",
           &DwarfOp::op_breg,
-          DWARF_VERSION_2,
           0,
           1,
           {DW_EH_PE_sleb128},
@@ -1241,7 +1122,6 @@
           // 0x7f DW_OP_breg15
           "DW_OP_breg15",
           &DwarfOp::op_breg,
-          DWARF_VERSION_2,
           0,
           1,
           {DW_EH_PE_sleb128},
@@ -1250,7 +1130,6 @@
           // 0x80 DW_OP_breg16
           "DW_OP_breg16",
           &DwarfOp::op_breg,
-          DWARF_VERSION_2,
           0,
           1,
           {DW_EH_PE_sleb128},
@@ -1259,7 +1138,6 @@
           // 0x81 DW_OP_breg17
           "DW_OP_breg17",
           &DwarfOp::op_breg,
-          DWARF_VERSION_2,
           0,
           1,
           {DW_EH_PE_sleb128},
@@ -1268,7 +1146,6 @@
           // 0x82 DW_OP_breg18
           "DW_OP_breg18",
           &DwarfOp::op_breg,
-          DWARF_VERSION_2,
           0,
           1,
           {DW_EH_PE_sleb128},
@@ -1277,7 +1154,6 @@
           // 0x83 DW_OP_breg19
           "DW_OP_breg19",
           &DwarfOp::op_breg,
-          DWARF_VERSION_2,
           0,
           1,
           {DW_EH_PE_sleb128},
@@ -1286,7 +1162,6 @@
           // 0x84 DW_OP_breg20
           "DW_OP_breg20",
           &DwarfOp::op_breg,
-          DWARF_VERSION_2,
           0,
           1,
           {DW_EH_PE_sleb128},
@@ -1295,7 +1170,6 @@
           // 0x85 DW_OP_breg21
           "DW_OP_breg21",
           &DwarfOp::op_breg,
-          DWARF_VERSION_2,
           0,
           1,
           {DW_EH_PE_sleb128},
@@ -1304,7 +1178,6 @@
           // 0x86 DW_OP_breg22
           "DW_OP_breg22",
           &DwarfOp::op_breg,
-          DWARF_VERSION_2,
           0,
           1,
           {DW_EH_PE_sleb128},
@@ -1313,7 +1186,6 @@
           // 0x87 DW_OP_breg23
           "DW_OP_breg23",
           &DwarfOp::op_breg,
-          DWARF_VERSION_2,
           0,
           1,
           {DW_EH_PE_sleb128},
@@ -1322,7 +1194,6 @@
           // 0x88 DW_OP_breg24
           "DW_OP_breg24",
           &DwarfOp::op_breg,
-          DWARF_VERSION_2,
           0,
           1,
           {DW_EH_PE_sleb128},
@@ -1331,7 +1202,6 @@
           // 0x89 DW_OP_breg25
           "DW_OP_breg25",
           &DwarfOp::op_breg,
-          DWARF_VERSION_2,
           0,
           1,
           {DW_EH_PE_sleb128},
@@ -1340,7 +1210,6 @@
           // 0x8a DW_OP_breg26
           "DW_OP_breg26",
           &DwarfOp::op_breg,
-          DWARF_VERSION_2,
           0,
           1,
           {DW_EH_PE_sleb128},
@@ -1349,7 +1218,6 @@
           // 0x8b DW_OP_breg27
           "DW_OP_breg27",
           &DwarfOp::op_breg,
-          DWARF_VERSION_2,
           0,
           1,
           {DW_EH_PE_sleb128},
@@ -1358,7 +1226,6 @@
           // 0x8c DW_OP_breg28
           "DW_OP_breg28",
           &DwarfOp::op_breg,
-          DWARF_VERSION_2,
           0,
           1,
           {DW_EH_PE_sleb128},
@@ -1367,7 +1234,6 @@
           // 0x8d DW_OP_breg29
           "DW_OP_breg29",
           &DwarfOp::op_breg,
-          DWARF_VERSION_2,
           0,
           1,
           {DW_EH_PE_sleb128},
@@ -1376,7 +1242,6 @@
           // 0x8e DW_OP_breg30
           "DW_OP_breg30",
           &DwarfOp::op_breg,
-          DWARF_VERSION_2,
           0,
           1,
           {DW_EH_PE_sleb128},
@@ -1385,7 +1250,6 @@
           // 0x8f DW_OP_breg31
           "DW_OP_breg31",
           &DwarfOp::op_breg,
-          DWARF_VERSION_2,
           0,
           1,
           {DW_EH_PE_sleb128},
@@ -1394,7 +1258,6 @@
           // 0x90 DW_OP_regx
           "DW_OP_regx",
           &DwarfOp::op_regx,
-          DWARF_VERSION_2,
           0,
           1,
           {DW_EH_PE_uleb128},
@@ -1403,7 +1266,6 @@
           // 0x91 DW_OP_fbreg
           "DW_OP_fbreg",
           &DwarfOp::op_not_implemented,
-          DWARF_VERSION_2,
           0,
           1,
           {DW_EH_PE_sleb128},
@@ -1412,7 +1274,6 @@
           // 0x92 DW_OP_bregx
           "DW_OP_bregx",
           &DwarfOp::op_bregx,
-          DWARF_VERSION_2,
           0,
           2,
           {DW_EH_PE_uleb128, DW_EH_PE_sleb128},
@@ -1421,7 +1282,6 @@
           // 0x93 DW_OP_piece
           "DW_OP_piece",
           &DwarfOp::op_not_implemented,
-          DWARF_VERSION_2,
           0,
           1,
           {DW_EH_PE_uleb128},
@@ -1430,7 +1290,6 @@
           // 0x94 DW_OP_deref_size
           "DW_OP_deref_size",
           &DwarfOp::op_deref_size,
-          DWARF_VERSION_2,
           1,
           1,
           {DW_EH_PE_udata1},
@@ -1439,7 +1298,6 @@
           // 0x95 DW_OP_xderef_size
           "DW_OP_xderef_size",
           &DwarfOp::op_not_implemented,
-          DWARF_VERSION_2,
           0,
           1,
           {DW_EH_PE_udata1},
@@ -1448,7 +1306,6 @@
           // 0x96 DW_OP_nop
           "DW_OP_nop",
           &DwarfOp::op_nop,
-          DWARF_VERSION_2,
           0,
           0,
           {},
@@ -1457,7 +1314,6 @@
           // 0x97 DW_OP_push_object_address
           "DW_OP_push_object_address",
           &DwarfOp::op_not_implemented,
-          DWARF_VERSION_3,
           0,
           0,
           {},
@@ -1466,7 +1322,6 @@
           // 0x98 DW_OP_call2
           "DW_OP_call2",
           &DwarfOp::op_not_implemented,
-          DWARF_VERSION_3,
           0,
           1,
           {DW_EH_PE_udata2},
@@ -1475,7 +1330,6 @@
           // 0x99 DW_OP_call4
           "DW_OP_call4",
           &DwarfOp::op_not_implemented,
-          DWARF_VERSION_3,
           0,
           1,
           {DW_EH_PE_udata4},
@@ -1484,7 +1338,6 @@
           // 0x9a DW_OP_call_ref
           "DW_OP_call_ref",
           &DwarfOp::op_not_implemented,
-          DWARF_VERSION_3,
           0,
           0,  // Has a different sized operand (4 bytes or 8 bytes).
           {},
@@ -1493,7 +1346,6 @@
           // 0x9b DW_OP_form_tls_address
           "DW_OP_form_tls_address",
           &DwarfOp::op_not_implemented,
-          DWARF_VERSION_3,
           0,
           0,
           {},
@@ -1502,7 +1354,6 @@
           // 0x9c DW_OP_call_frame_cfa
           "DW_OP_call_frame_cfa",
           &DwarfOp::op_not_implemented,
-          DWARF_VERSION_3,
           0,
           0,
           {},
@@ -1511,7 +1362,6 @@
           // 0x9d DW_OP_bit_piece
           "DW_OP_bit_piece",
           &DwarfOp::op_not_implemented,
-          DWARF_VERSION_3,
           0,
           2,
           {DW_EH_PE_uleb128, DW_EH_PE_uleb128},
@@ -1520,7 +1370,6 @@
           // 0x9e DW_OP_implicit_value
           "DW_OP_implicit_value",
           &DwarfOp::op_not_implemented,
-          DWARF_VERSION_4,
           0,
           1,
           {DW_EH_PE_uleb128},
@@ -1529,108 +1378,109 @@
           // 0x9f DW_OP_stack_value
           "DW_OP_stack_value",
           &DwarfOp::op_not_implemented,
-          DWARF_VERSION_4,
           1,
           0,
           {},
       },
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xa0 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xa1 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xa2 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xa3 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xa4 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xa5 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xa6 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xa7 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xa8 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xa9 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xaa illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xab illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xac illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xad illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xae illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xaf illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xb0 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xb1 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xb2 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xb3 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xb4 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xb5 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xb6 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xb7 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xb8 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xb9 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xba illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xbb illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xbc illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xbd illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xbe illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xbf illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xc0 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xc1 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xc2 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xc3 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xc4 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xc5 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xc6 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xc7 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xc8 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xc9 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xca illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xcb illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xcc illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xcd illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xce illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xcf illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xd0 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xd1 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xd2 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xd3 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xd4 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xd5 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xd6 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xd7 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xd8 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xd9 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xda illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xdb illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xdc illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xdd illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xde illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xdf illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xe0 DW_OP_lo_user
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xe1 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xe2 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xe3 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xe4 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xe5 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xe6 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xe7 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xe8 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xe9 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xea illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xeb illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xec illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xed illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xee illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xef illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xf0 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xf1 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xf2 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xf3 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xf4 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xf5 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xf6 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xf7 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xf8 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xf9 illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xfa illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xfb illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xfc illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xfd illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xfe illegal op
-      {nullptr, nullptr, 0, 0, 0, {}},  // 0xff DW_OP_hi_user
+      {nullptr, nullptr, 0, 0, {}},  // 0xa0 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xa1 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xa2 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xa3 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xa4 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xa5 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xa6 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xa7 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xa8 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xa9 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xaa illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xab illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xac illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xad illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xae illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xaf illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xb0 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xb1 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xb2 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xb3 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xb4 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xb5 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xb6 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xb7 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xb8 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xb9 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xba illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xbb illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xbc illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xbd illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xbe illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xbf illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xc0 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xc1 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xc2 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xc3 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xc4 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xc5 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xc6 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xc7 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xc8 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xc9 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xca illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xcb illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xcc illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xcd illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xce illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xcf illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xd0 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xd1 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xd2 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xd3 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xd4 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xd5 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xd6 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xd7 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xd8 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xd9 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xda illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xdb illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xdc illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xdd illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xde illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xdf illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xe0 DW_OP_lo_user
+      {nullptr, nullptr, 0, 0, {}},  // 0xe1 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xe2 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xe3 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xe4 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xe5 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xe6 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xe7 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xe8 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xe9 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xea illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xeb illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xec illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xed illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xee illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xef illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xf0 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xf1 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xf2 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xf3 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xf4 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xf5 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xf6 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xf7 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xf8 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xf9 illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xfa illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xfb illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xfc illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xfd illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xfe illegal op
+      {nullptr, nullptr, 0, 0, {}},  // 0xff DW_OP_hi_user
   };
 };
 
+}  // namespace unwindstack
+
 #endif  // _LIBUNWINDSTACK_DWARF_OP_H
diff --git a/libunwindstack/DwarfSection.cpp b/libunwindstack/DwarfSection.cpp
index 4c98aa3..6061f61 100644
--- a/libunwindstack/DwarfSection.cpp
+++ b/libunwindstack/DwarfSection.cpp
@@ -16,224 +16,61 @@
 
 #include <stdint.h>
 
+#include <unwindstack/DwarfError.h>
+#include <unwindstack/DwarfLocation.h>
+#include <unwindstack/DwarfMemory.h>
+#include <unwindstack/DwarfSection.h>
+#include <unwindstack/DwarfStructs.h>
+#include <unwindstack/Log.h>
+#include <unwindstack/Memory.h>
+#include <unwindstack/Regs.h>
+
 #include "DwarfCfa.h"
-#include "DwarfError.h"
-#include "DwarfLocation.h"
-#include "DwarfMemory.h"
+#include "DwarfDebugFrame.h"
+#include "DwarfEhFrame.h"
+#include "DwarfEncoding.h"
 #include "DwarfOp.h"
-#include "DwarfSection.h"
-#include "DwarfStructs.h"
-#include "Log.h"
-#include "Memory.h"
-#include "Regs.h"
+#include "RegsInfo.h"
 
-const DwarfFde* DwarfSection::GetFdeFromPc(uint64_t pc) {
-  uint64_t fde_offset;
-  if (!GetFdeOffsetFromPc(pc, &fde_offset)) {
-    return nullptr;
-  }
-  const DwarfFde* fde = GetFdeFromOffset(fde_offset);
-  // Guaranteed pc >= pc_start, need to check pc in the fde range.
-  if (pc < fde->pc_end) {
-    return fde;
-  }
-  last_error_ = DWARF_ERROR_ILLEGAL_STATE;
-  return nullptr;
-}
+namespace unwindstack {
 
-bool DwarfSection::Step(uint64_t pc, Regs* regs, Memory* process_memory) {
-  const DwarfFde* fde = GetFdeFromPc(pc);
-  if (fde == nullptr || fde->cie == nullptr) {
-    last_error_ = DWARF_ERROR_ILLEGAL_STATE;
-    return false;
-  }
+DwarfSection::DwarfSection(Memory* memory) : memory_(memory) {}
 
-  // Now get the location information for this pc.
-  dwarf_loc_regs_t loc_regs;
-  if (!GetCfaLocationInfo(pc, fde, &loc_regs)) {
-    return false;
+bool DwarfSection::Step(uint64_t pc, Regs* regs, Memory* process_memory, bool* finished) {
+  // 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;
+    }
+    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);
+  return Eval(it->second.cie, process_memory, it->second, regs, finished);
 }
 
 template <typename AddressType>
-bool DwarfSectionImpl<AddressType>::EvalExpression(const DwarfLocation& loc, uint8_t version,
-                                                   Memory* regular_memory, AddressType* value) {
-  DwarfOp<AddressType> op(&memory_, regular_memory);
-
-  // Need to evaluate the op data.
-  uint64_t start = loc.values[1];
-  uint64_t end = start + loc.values[0];
-  if (!op.Eval(start, end, version)) {
-    last_error_ = op.last_error();
-    return false;
-  }
-  if (op.StackSize() == 0) {
-    last_error_ = DWARF_ERROR_ILLEGAL_STATE;
-    return false;
-  }
-  // We don't support an expression that evaluates to a register number.
-  if (op.is_register()) {
-    last_error_ = DWARF_ERROR_NOT_IMPLEMENTED;
-    return false;
-  }
-  *value = op.StackAt(0);
-  return true;
-}
-
-template <typename AddressType>
-bool DwarfSectionImpl<AddressType>::Eval(const DwarfCie* cie, Memory* regular_memory,
-                                         const dwarf_loc_regs_t& loc_regs, Regs* regs) {
-  RegsImpl<AddressType>* cur_regs = reinterpret_cast<RegsImpl<AddressType>*>(regs);
-  if (cie->return_address_register >= cur_regs->total_regs()) {
-    last_error_ = DWARF_ERROR_ILLEGAL_VALUE;
-    return false;
-  }
-
-  // Get the cfa value;
-  auto cfa_entry = loc_regs.find(CFA_REG);
-  if (cfa_entry == loc_regs.end()) {
-    last_error_ = DWARF_ERROR_CFA_NOT_DEFINED;
-    return false;
-  }
-
-  AddressType prev_pc = regs->pc();
-  AddressType prev_cfa = regs->sp();
-
-  AddressType cfa;
-  const DwarfLocation* loc = &cfa_entry->second;
-  // Only a few location types are valid for the cfa.
-  switch (loc->type) {
-    case DWARF_LOCATION_REGISTER:
-      if (loc->values[0] >= cur_regs->total_regs()) {
-        last_error_ = 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) {
-        cfa = prev_cfa;
-      } else {
-        cfa = (*cur_regs)[loc->values[0]];
-      }
-      cfa += loc->values[1];
-      break;
-    case DWARF_LOCATION_EXPRESSION:
-    case DWARF_LOCATION_VAL_EXPRESSION: {
-      AddressType value;
-      if (!EvalExpression(*loc, cie->version, regular_memory, &value)) {
-        return false;
-      }
-      if (loc->type == DWARF_LOCATION_EXPRESSION) {
-        if (!regular_memory->Read(value, &cfa, sizeof(AddressType))) {
-          last_error_ = DWARF_ERROR_MEMORY_INVALID;
-          return false;
-        }
-      } else {
-        cfa = value;
-      }
-      break;
-    }
-    default:
-      last_error_ = DWARF_ERROR_ILLEGAL_VALUE;
-      return false;
-  }
-
-  // This code is not guaranteed to work in cases where a register location
-  // is a double indirection to the actual value. For example, if r3 is set
-  // to r5 + 4, and r5 is set to CFA + 4, then this won't necessarily work
-  // because it does not guarantee that r5 is evaluated before r3.
-  // Check that this case does not exist, and error if it does.
-  bool return_address_undefined = false;
-  for (const auto& entry : loc_regs) {
-    uint16_t reg = entry.first;
-    // Already handled the CFA register.
-    if (reg == CFA_REG) continue;
-
-    if (reg >= cur_regs->total_regs()) {
-      // Skip this unknown register.
-      continue;
-    }
-
-    const DwarfLocation* loc = &entry.second;
-    switch (loc->type) {
-      case DWARF_LOCATION_OFFSET:
-        if (!regular_memory->Read(cfa + loc->values[0], &(*cur_regs)[reg], sizeof(AddressType))) {
-          last_error_ = DWARF_ERROR_MEMORY_INVALID;
-          return false;
-        }
-        break;
-      case DWARF_LOCATION_VAL_OFFSET:
-        (*cur_regs)[reg] = cfa + loc->values[0];
-        break;
-      case DWARF_LOCATION_REGISTER: {
-        uint16_t cur_reg = loc->values[0];
-        if (cur_reg >= cur_regs->total_regs()) {
-          last_error_ = DWARF_ERROR_ILLEGAL_VALUE;
-          return false;
-        }
-        if (loc_regs.find(cur_reg) != loc_regs.end()) {
-          // This is a double indirection, a register definition references
-          // another register which is also defined as something other
-          // than a register.
-          log(0,
-              "Invalid indirection: register %d references register %d which is "
-              "not a plain register.\n",
-              reg, cur_reg);
-          last_error_ = DWARF_ERROR_ILLEGAL_STATE;
-          return false;
-        }
-        (*cur_regs)[reg] = (*cur_regs)[cur_reg] + loc->values[1];
-        break;
-      }
-      case DWARF_LOCATION_EXPRESSION:
-      case DWARF_LOCATION_VAL_EXPRESSION: {
-        AddressType value;
-        if (!EvalExpression(*loc, cie->version, regular_memory, &value)) {
-          return false;
-        }
-        if (loc->type == DWARF_LOCATION_EXPRESSION) {
-          if (!regular_memory->Read(value, &(*cur_regs)[reg], sizeof(AddressType))) {
-            last_error_ = DWARF_ERROR_MEMORY_INVALID;
-            return false;
-          }
-        } else {
-          (*cur_regs)[reg] = value;
-        }
-        break;
-      }
-      case DWARF_LOCATION_UNDEFINED:
-        if (reg == cie->return_address_register) {
-          return_address_undefined = true;
-        }
-      default:
-        break;
-    }
-  }
-
-  // Find the return address location.
-  if (return_address_undefined) {
-    cur_regs->set_pc(0);
-  } else {
-    cur_regs->set_pc((*cur_regs)[cie->return_address_register]);
-  }
-  cur_regs->set_sp(cfa);
-  // Stop if the cfa and pc are the same.
-  return prev_cfa != cfa || prev_pc != cur_regs->pc();
-}
-
-template <typename AddressType>
-const DwarfCie* DwarfSectionImpl<AddressType>::GetCie(uint64_t offset) {
+const DwarfCie* DwarfSectionImpl<AddressType>::GetCieFromOffset(uint64_t offset) {
   auto cie_entry = cie_entries_.find(offset);
   if (cie_entry != cie_entries_.end()) {
     return &cie_entry->second;
   }
   DwarfCie* cie = &cie_entries_[offset];
   memory_.set_cur_offset(offset);
-  if (!FillInCie(cie)) {
+  if (!FillInCieHeader(cie) || !FillInCie(cie)) {
     // Erase the cached entry.
     cie_entries_.erase(offset);
     return nullptr;
@@ -242,17 +79,20 @@
 }
 
 template <typename AddressType>
-bool DwarfSectionImpl<AddressType>::FillInCie(DwarfCie* cie) {
+bool DwarfSectionImpl<AddressType>::FillInCieHeader(DwarfCie* cie) {
+  cie->lsda_encoding = DW_EH_PE_omit;
   uint32_t length32;
   if (!memory_.ReadBytes(&length32, sizeof(length32))) {
-    last_error_ = DWARF_ERROR_MEMORY_INVALID;
+    last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+    last_error_.address = memory_.cur_offset();
     return false;
   }
   if (length32 == static_cast<uint32_t>(-1)) {
     // 64 bit Cie
     uint64_t length64;
     if (!memory_.ReadBytes(&length64, sizeof(length64))) {
-      last_error_ = DWARF_ERROR_MEMORY_INVALID;
+      last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+      last_error_.address = memory_.cur_offset();
       return false;
     }
 
@@ -261,12 +101,13 @@
 
     uint64_t cie_id;
     if (!memory_.ReadBytes(&cie_id, sizeof(cie_id))) {
-      last_error_ = DWARF_ERROR_MEMORY_INVALID;
+      last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+      last_error_.address = memory_.cur_offset();
       return false;
     }
-    if (!IsCie64(cie_id)) {
+    if (cie_id != cie64_value_) {
       // This is not a Cie, something has gone horribly wrong.
-      last_error_ = DWARF_ERROR_ILLEGAL_VALUE;
+      last_error_.code = DWARF_ERROR_ILLEGAL_VALUE;
       return false;
     }
   } else {
@@ -276,24 +117,30 @@
 
     uint32_t cie_id;
     if (!memory_.ReadBytes(&cie_id, sizeof(cie_id))) {
-      last_error_ = DWARF_ERROR_MEMORY_INVALID;
+      last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+      last_error_.address = memory_.cur_offset();
       return false;
     }
-    if (!IsCie32(cie_id)) {
+    if (cie_id != cie32_value_) {
       // This is not a Cie, something has gone horribly wrong.
-      last_error_ = DWARF_ERROR_ILLEGAL_VALUE;
+      last_error_.code = DWARF_ERROR_ILLEGAL_VALUE;
       return false;
     }
   }
+  return true;
+}
 
+template <typename AddressType>
+bool DwarfSectionImpl<AddressType>::FillInCie(DwarfCie* cie) {
   if (!memory_.ReadBytes(&cie->version, sizeof(cie->version))) {
-    last_error_ = DWARF_ERROR_MEMORY_INVALID;
+    last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+    last_error_.address = memory_.cur_offset();
     return false;
   }
 
   if (cie->version != 1 && cie->version != 3 && cie->version != 4) {
     // Unrecognized version.
-    last_error_ = DWARF_ERROR_UNSUPPORTED_VERSION;
+    last_error_.code = DWARF_ERROR_UNSUPPORTED_VERSION;
     return false;
   }
 
@@ -301,7 +148,8 @@
   char aug_value;
   do {
     if (!memory_.ReadBytes(&aug_value, 1)) {
-      last_error_ = DWARF_ERROR_MEMORY_INVALID;
+      last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+      last_error_.address = memory_.cur_offset();
       return false;
     }
     cie->augmentation_string.push_back(aug_value);
@@ -313,20 +161,23 @@
 
     // Segment Size
     if (!memory_.ReadBytes(&cie->segment_size, 1)) {
-      last_error_ = DWARF_ERROR_MEMORY_INVALID;
+      last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+      last_error_.address = memory_.cur_offset();
       return false;
     }
   }
 
   // Code Alignment Factor
   if (!memory_.ReadULEB128(&cie->code_alignment_factor)) {
-    last_error_ = DWARF_ERROR_MEMORY_INVALID;
+    last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+    last_error_.address = memory_.cur_offset();
     return false;
   }
 
   // Data Alignment Factor
   if (!memory_.ReadSLEB128(&cie->data_alignment_factor)) {
-    last_error_ = DWARF_ERROR_MEMORY_INVALID;
+    last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+    last_error_.address = memory_.cur_offset();
     return false;
   }
 
@@ -334,12 +185,14 @@
     // Return Address is a single byte.
     uint8_t return_address_register;
     if (!memory_.ReadBytes(&return_address_register, 1)) {
-      last_error_ = DWARF_ERROR_MEMORY_INVALID;
+      last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+      last_error_.address = memory_.cur_offset();
       return false;
     }
     cie->return_address_register = return_address_register;
   } else if (!memory_.ReadULEB128(&cie->return_address_register)) {
-    last_error_ = DWARF_ERROR_MEMORY_INVALID;
+    last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+    last_error_.address = memory_.cur_offset();
     return false;
   }
 
@@ -350,7 +203,8 @@
 
   uint64_t aug_length;
   if (!memory_.ReadULEB128(&aug_length)) {
-    last_error_ = DWARF_ERROR_MEMORY_INVALID;
+    last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+    last_error_.address = memory_.cur_offset();
     return false;
   }
   cie->cfa_instructions_offset = memory_.cur_offset() + aug_length;
@@ -359,24 +213,29 @@
     switch (cie->augmentation_string[i]) {
       case 'L':
         if (!memory_.ReadBytes(&cie->lsda_encoding, 1)) {
-          last_error_ = DWARF_ERROR_MEMORY_INVALID;
+          last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+          last_error_.address = memory_.cur_offset();
           return false;
         }
         break;
       case 'P': {
         uint8_t encoding;
         if (!memory_.ReadBytes(&encoding, 1)) {
-          last_error_ = DWARF_ERROR_MEMORY_INVALID;
+          last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+          last_error_.address = memory_.cur_offset();
           return false;
         }
+        memory_.set_pc_offset(pc_offset_);
         if (!memory_.ReadEncodedValue<AddressType>(encoding, &cie->personality_handler)) {
-          last_error_ = DWARF_ERROR_MEMORY_INVALID;
+          last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+          last_error_.address = memory_.cur_offset();
           return false;
         }
       } break;
       case 'R':
         if (!memory_.ReadBytes(&cie->fde_address_encoding, 1)) {
-          last_error_ = DWARF_ERROR_MEMORY_INVALID;
+          last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+          last_error_.address = memory_.cur_offset();
           return false;
         }
         break;
@@ -393,7 +252,7 @@
   }
   DwarfFde* fde = &fde_entries_[offset];
   memory_.set_cur_offset(offset);
-  if (!FillInFde(fde)) {
+  if (!FillInFdeHeader(fde) || !FillInFde(fde)) {
     fde_entries_.erase(offset);
     return nullptr;
   }
@@ -401,10 +260,11 @@
 }
 
 template <typename AddressType>
-bool DwarfSectionImpl<AddressType>::FillInFde(DwarfFde* fde) {
+bool DwarfSectionImpl<AddressType>::FillInFdeHeader(DwarfFde* fde) {
   uint32_t length32;
   if (!memory_.ReadBytes(&length32, sizeof(length32))) {
-    last_error_ = DWARF_ERROR_MEMORY_INVALID;
+    last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+    last_error_.address = memory_.cur_offset();
     return false;
   }
 
@@ -412,19 +272,21 @@
     // 64 bit Fde.
     uint64_t length64;
     if (!memory_.ReadBytes(&length64, sizeof(length64))) {
-      last_error_ = DWARF_ERROR_MEMORY_INVALID;
+      last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+      last_error_.address = memory_.cur_offset();
       return false;
     }
     fde->cfa_instructions_end = memory_.cur_offset() + length64;
 
     uint64_t value64;
     if (!memory_.ReadBytes(&value64, sizeof(value64))) {
-      last_error_ = DWARF_ERROR_MEMORY_INVALID;
+      last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+      last_error_.address = memory_.cur_offset();
       return false;
     }
-    if (IsCie64(value64)) {
+    if (value64 == cie64_value_) {
       // This is a Cie, this means something has gone wrong.
-      last_error_ = DWARF_ERROR_ILLEGAL_VALUE;
+      last_error_.code = DWARF_ERROR_ILLEGAL_VALUE;
       return false;
     }
 
@@ -437,12 +299,13 @@
 
     uint32_t value32;
     if (!memory_.ReadBytes(&value32, sizeof(value32))) {
-      last_error_ = DWARF_ERROR_MEMORY_INVALID;
+      last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+      last_error_.address = memory_.cur_offset();
       return false;
     }
-    if (IsCie32(value32)) {
+    if (value32 == cie32_value_) {
       // This is a Cie, this means something has gone wrong.
-      last_error_ = DWARF_ERROR_ILLEGAL_VALUE;
+      last_error_.code = DWARF_ERROR_ILLEGAL_VALUE;
       return false;
     }
 
@@ -450,9 +313,14 @@
     // of the Fde information.
     fde->cie_offset = GetCieOffsetFromFde32(value32);
   }
+  return true;
+}
+
+template <typename AddressType>
+bool DwarfSectionImpl<AddressType>::FillInFde(DwarfFde* fde) {
   uint64_t cur_offset = memory_.cur_offset();
 
-  const DwarfCie* cie = GetCie(fde->cie_offset);
+  const DwarfCie* cie = GetCieFromOffset(fde->cie_offset);
   if (cie == nullptr) {
     return false;
   }
@@ -464,28 +332,33 @@
   }
   memory_.set_cur_offset(cur_offset);
 
-  if (!memory_.ReadEncodedValue<AddressType>(cie->fde_address_encoding & 0xf, &fde->pc_start)) {
-    last_error_ = DWARF_ERROR_MEMORY_INVALID;
-    return false;
-  }
+  // The load bias only applies to the start.
+  memory_.set_pc_offset(load_bias_);
+  bool valid = memory_.ReadEncodedValue<AddressType>(cie->fde_address_encoding, &fde->pc_start);
   fde->pc_start = AdjustPcFromFde(fde->pc_start);
 
-  if (!memory_.ReadEncodedValue<AddressType>(cie->fde_address_encoding & 0xf, &fde->pc_end)) {
-    last_error_ = DWARF_ERROR_MEMORY_INVALID;
+  memory_.set_pc_offset(0);
+  if (!valid || !memory_.ReadEncodedValue<AddressType>(cie->fde_address_encoding, &fde->pc_end)) {
+    last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+    last_error_.address = memory_.cur_offset();
     return false;
   }
   fde->pc_end += fde->pc_start;
+
   if (cie->augmentation_string.size() > 0 && cie->augmentation_string[0] == 'z') {
     // Augmentation Size
     uint64_t aug_length;
     if (!memory_.ReadULEB128(&aug_length)) {
-      last_error_ = DWARF_ERROR_MEMORY_INVALID;
+      last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+      last_error_.address = memory_.cur_offset();
       return false;
     }
     uint64_t cur_offset = memory_.cur_offset();
 
+    memory_.set_pc_offset(pc_offset_);
     if (!memory_.ReadEncodedValue<AddressType>(cie->lsda_encoding, &fde->lsda_address)) {
-      last_error_ = DWARF_ERROR_MEMORY_INVALID;
+      last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+      last_error_.address = memory_.cur_offset();
       return false;
     }
 
@@ -498,6 +371,185 @@
 }
 
 template <typename AddressType>
+bool DwarfSectionImpl<AddressType>::EvalExpression(const DwarfLocation& loc, Memory* regular_memory,
+                                                   AddressType* value,
+                                                   RegsInfo<AddressType>* regs_info,
+                                                   bool* is_dex_pc) {
+  DwarfOp<AddressType> op(&memory_, regular_memory);
+  op.set_regs_info(regs_info);
+
+  // Need to evaluate the op data.
+  uint64_t end = loc.values[1];
+  uint64_t start = end - loc.values[0];
+  if (!op.Eval(start, end)) {
+    last_error_ = op.last_error();
+    return false;
+  }
+  if (op.StackSize() == 0) {
+    last_error_.code = DWARF_ERROR_ILLEGAL_STATE;
+    return false;
+  }
+  // We don't support an expression that evaluates to a register number.
+  if (op.is_register()) {
+    last_error_.code = DWARF_ERROR_NOT_IMPLEMENTED;
+    return false;
+  }
+  *value = op.StackAt(0);
+  if (is_dex_pc != nullptr && op.dex_pc_set()) {
+    *is_dex_pc = true;
+  }
+  return true;
+}
+
+template <typename AddressType>
+struct EvalInfo {
+  const dwarf_loc_regs_t* loc_regs;
+  const DwarfCie* cie;
+  Memory* regular_memory;
+  AddressType cfa;
+  bool return_address_undefined = false;
+  RegsInfo<AddressType> regs_info;
+};
+
+template <typename AddressType>
+bool DwarfSectionImpl<AddressType>::EvalRegister(const DwarfLocation* loc, uint32_t reg,
+                                                 AddressType* reg_ptr, void* info) {
+  EvalInfo<AddressType>* eval_info = reinterpret_cast<EvalInfo<AddressType>*>(info);
+  Memory* regular_memory = eval_info->regular_memory;
+  switch (loc->type) {
+    case DWARF_LOCATION_OFFSET:
+      if (!regular_memory->ReadFully(eval_info->cfa + loc->values[0], reg_ptr, sizeof(AddressType))) {
+        last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+        last_error_.address = eval_info->cfa + loc->values[0];
+        return false;
+      }
+      break;
+    case DWARF_LOCATION_VAL_OFFSET:
+      *reg_ptr = eval_info->cfa + loc->values[0];
+      break;
+    case DWARF_LOCATION_REGISTER: {
+      uint32_t cur_reg = loc->values[0];
+      if (cur_reg >= eval_info->regs_info.Total()) {
+        last_error_.code = DWARF_ERROR_ILLEGAL_VALUE;
+        return false;
+      }
+      *reg_ptr = eval_info->regs_info.Get(cur_reg) + loc->values[1];
+      break;
+    }
+    case DWARF_LOCATION_EXPRESSION:
+    case DWARF_LOCATION_VAL_EXPRESSION: {
+      AddressType value;
+      bool is_dex_pc = false;
+      if (!EvalExpression(*loc, regular_memory, &value, &eval_info->regs_info, &is_dex_pc)) {
+        return false;
+      }
+      if (loc->type == DWARF_LOCATION_EXPRESSION) {
+        if (!regular_memory->ReadFully(value, reg_ptr, sizeof(AddressType))) {
+          last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+          last_error_.address = value;
+          return false;
+        }
+      } else {
+        *reg_ptr = value;
+        if (is_dex_pc) {
+          eval_info->regs_info.regs->set_dex_pc(value);
+        }
+      }
+      break;
+    }
+    case DWARF_LOCATION_UNDEFINED:
+      if (reg == eval_info->cie->return_address_register) {
+        eval_info->return_address_undefined = true;
+      }
+    default:
+      break;
+  }
+
+  return true;
+}
+
+template <typename AddressType>
+bool DwarfSectionImpl<AddressType>::Eval(const DwarfCie* cie, Memory* regular_memory,
+                                         const dwarf_loc_regs_t& loc_regs, Regs* regs,
+                                         bool* finished) {
+  RegsImpl<AddressType>* cur_regs = reinterpret_cast<RegsImpl<AddressType>*>(regs);
+  if (cie->return_address_register >= cur_regs->total_regs()) {
+    last_error_.code = DWARF_ERROR_ILLEGAL_VALUE;
+    return false;
+  }
+
+  // Get the cfa value;
+  auto cfa_entry = loc_regs.find(CFA_REG);
+  if (cfa_entry == loc_regs.end()) {
+    last_error_.code = DWARF_ERROR_CFA_NOT_DEFINED;
+    return false;
+  }
+
+  // Always set the dex pc to zero when evaluating.
+  cur_regs->set_dex_pc(0);
+
+  EvalInfo<AddressType> eval_info{.loc_regs = &loc_regs,
+                                  .cie = cie,
+                                  .regular_memory = regular_memory,
+                                  .regs_info = RegsInfo<AddressType>(cur_regs)};
+  const DwarfLocation* loc = &cfa_entry->second;
+  // Only a few location types are valid for the cfa.
+  switch (loc->type) {
+    case DWARF_LOCATION_REGISTER:
+      if (loc->values[0] >= cur_regs->total_regs()) {
+        last_error_.code = DWARF_ERROR_ILLEGAL_VALUE;
+        return false;
+      }
+      eval_info.cfa = (*cur_regs)[loc->values[0]];
+      eval_info.cfa += loc->values[1];
+      break;
+    case DWARF_LOCATION_VAL_EXPRESSION: {
+      AddressType value;
+      if (!EvalExpression(*loc, regular_memory, &value, &eval_info.regs_info, nullptr)) {
+        return false;
+      }
+      // There is only one type of valid expression for CFA evaluation.
+      eval_info.cfa = value;
+      break;
+    }
+    default:
+      last_error_.code = DWARF_ERROR_ILLEGAL_VALUE;
+      return false;
+  }
+
+  for (const auto& entry : loc_regs) {
+    uint32_t reg = entry.first;
+    // Already handled the CFA register.
+    if (reg == CFA_REG) continue;
+
+    AddressType* reg_ptr;
+    if (reg >= cur_regs->total_regs()) {
+      // Skip this unknown register.
+      continue;
+    }
+
+    reg_ptr = eval_info.regs_info.Save(reg);
+    if (!EvalRegister(&entry.second, reg, reg_ptr, &eval_info)) {
+      return false;
+    }
+  }
+
+  // Find the return address location.
+  if (eval_info.return_address_undefined) {
+    cur_regs->set_pc(0);
+  } else {
+    cur_regs->set_pc((*cur_regs)[cie->return_address_register]);
+  }
+
+  // If the pc was set to zero, consider this the final frame.
+  *finished = (cur_regs->pc() == 0) ? true : false;
+
+  cur_regs->set_sp(eval_info.cfa);
+
+  return true;
+}
+
+template <typename AddressType>
 bool DwarfSectionImpl<AddressType>::GetCfaLocationInfo(uint64_t pc, const DwarfFde* fde,
                                                        dwarf_loc_regs_t* loc_regs) {
   DwarfCfa<AddressType> cfa(&memory_, fde);
@@ -521,23 +573,248 @@
 }
 
 template <typename AddressType>
-bool DwarfSectionImpl<AddressType>::Log(uint8_t indent, uint64_t pc, uint64_t load_bias,
-                                        const DwarfFde* fde) {
+bool DwarfSectionImpl<AddressType>::Log(uint8_t indent, uint64_t pc, const DwarfFde* fde) {
   DwarfCfa<AddressType> cfa(&memory_, fde);
 
   // Always print the cie information.
   const DwarfCie* cie = fde->cie;
-  if (!cfa.Log(indent, pc, load_bias, cie->cfa_instructions_offset, cie->cfa_instructions_end)) {
+  if (!cfa.Log(indent, pc, cie->cfa_instructions_offset, cie->cfa_instructions_end)) {
     last_error_ = cfa.last_error();
     return false;
   }
-  if (!cfa.Log(indent, pc, load_bias, fde->cfa_instructions_offset, fde->cfa_instructions_end)) {
+  if (!cfa.Log(indent, pc, fde->cfa_instructions_offset, fde->cfa_instructions_end)) {
     last_error_ = cfa.last_error();
     return false;
   }
   return true;
 }
 
+template <typename AddressType>
+bool DwarfSectionImplNoHdr<AddressType>::Init(uint64_t offset, uint64_t size, uint64_t load_bias) {
+  load_bias_ = load_bias;
+  entries_offset_ = offset;
+  next_entries_offset_ = offset;
+  entries_end_ = offset + size;
+
+  memory_.clear_func_offset();
+  memory_.clear_text_offset();
+  memory_.set_cur_offset(offset);
+  memory_.set_data_offset(offset);
+  pc_offset_ = offset;
+
+  return true;
+}
+
+// Create a cached version of the fde information such that it is a std::map
+// that is indexed by end pc and contains a pair that represents the start pc
+// followed by the fde object. The fde pointers are owned by fde_entries_
+// and not by the map object.
+// It is possible for an fde to be represented by multiple entries in
+// the map. This can happen if the the start pc and end pc overlap already
+// existing entries. For example, if there is already an entry of 0x400, 0x200,
+// and an fde has a start pc of 0x100 and end pc of 0x500, two new entries
+// will be added: 0x200, 0x100 and 0x500, 0x400.
+template <typename AddressType>
+void DwarfSectionImplNoHdr<AddressType>::InsertFde(const DwarfFde* fde) {
+  uint64_t start = fde->pc_start;
+  uint64_t end = fde->pc_end;
+  auto it = fdes_.upper_bound(start);
+  bool add_element = false;
+  while (it != fdes_.end() && start < end) {
+    if (add_element) {
+      add_element = false;
+      if (end < it->second.first) {
+        if (it->first == end) {
+          return;
+        }
+        fdes_[end] = std::make_pair(start, fde);
+        return;
+      }
+      if (start != it->second.first) {
+        fdes_[it->second.first] = std::make_pair(start, fde);
+      }
+    }
+    if (start < it->first) {
+      if (end < it->second.first) {
+        if (it->first != end) {
+          fdes_[end] = std::make_pair(start, fde);
+        }
+        return;
+      }
+      add_element = true;
+    }
+    start = it->first;
+    ++it;
+  }
+  if (start < end) {
+    fdes_[end] = std::make_pair(start, fde);
+  }
+}
+
+template <typename AddressType>
+bool DwarfSectionImplNoHdr<AddressType>::GetNextCieOrFde(DwarfFde** fde_entry) {
+  uint64_t start_offset = next_entries_offset_;
+
+  memory_.set_cur_offset(next_entries_offset_);
+  uint32_t value32;
+  if (!memory_.ReadBytes(&value32, sizeof(value32))) {
+    last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+    last_error_.address = memory_.cur_offset();
+    return false;
+  }
+
+  uint64_t cie_offset;
+  uint8_t cie_fde_encoding;
+  bool entry_is_cie = false;
+  if (value32 == static_cast<uint32_t>(-1)) {
+    // 64 bit entry.
+    uint64_t value64;
+    if (!memory_.ReadBytes(&value64, sizeof(value64))) {
+      last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+      last_error_.address = memory_.cur_offset();
+      return false;
+    }
+
+    next_entries_offset_ = memory_.cur_offset() + value64;
+    // Read the Cie Id of a Cie or the pointer of the Fde.
+    if (!memory_.ReadBytes(&value64, sizeof(value64))) {
+      last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+      last_error_.address = memory_.cur_offset();
+      return false;
+    }
+
+    if (value64 == cie64_value_) {
+      entry_is_cie = true;
+      cie_fde_encoding = DW_EH_PE_sdata8;
+    } else {
+      cie_offset = this->GetCieOffsetFromFde64(value64);
+    }
+  } else {
+    next_entries_offset_ = memory_.cur_offset() + value32;
+
+    // 32 bit Cie
+    if (!memory_.ReadBytes(&value32, sizeof(value32))) {
+      last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+      last_error_.address = memory_.cur_offset();
+      return false;
+    }
+
+    if (value32 == cie32_value_) {
+      entry_is_cie = true;
+      cie_fde_encoding = DW_EH_PE_sdata4;
+    } else {
+      cie_offset = this->GetCieOffsetFromFde32(value32);
+    }
+  }
+
+  if (entry_is_cie) {
+    DwarfCie* cie = &cie_entries_[start_offset];
+    cie->lsda_encoding = DW_EH_PE_omit;
+    cie->cfa_instructions_end = next_entries_offset_;
+    cie->fde_address_encoding = cie_fde_encoding;
+
+    if (!this->FillInCie(cie)) {
+      cie_entries_.erase(start_offset);
+      return false;
+    }
+    *fde_entry = nullptr;
+  } else {
+    DwarfFde* fde = &fde_entries_[start_offset];
+    fde->cfa_instructions_end = next_entries_offset_;
+    fde->cie_offset = cie_offset;
+
+    if (!this->FillInFde(fde)) {
+      fde_entries_.erase(start_offset);
+      return false;
+    }
+    *fde_entry = fde;
+  }
+  return true;
+}
+
+template <typename AddressType>
+void DwarfSectionImplNoHdr<AddressType>::GetFdes(std::vector<const DwarfFde*>* fdes) {
+  // Loop through the already cached entries.
+  uint64_t entry_offset = entries_offset_;
+  while (entry_offset < next_entries_offset_) {
+    auto cie_it = cie_entries_.find(entry_offset);
+    if (cie_it != cie_entries_.end()) {
+      entry_offset = cie_it->second.cfa_instructions_end;
+    } else {
+      auto fde_it = fde_entries_.find(entry_offset);
+      if (fde_it == fde_entries_.end()) {
+        // No fde or cie at this entry, should not be possible.
+        return;
+      }
+      entry_offset = fde_it->second.cfa_instructions_end;
+      fdes->push_back(&fde_it->second);
+    }
+  }
+
+  while (next_entries_offset_ < entries_end_) {
+    DwarfFde* fde;
+    if (!GetNextCieOrFde(&fde)) {
+      break;
+    }
+    if (fde != nullptr) {
+      InsertFde(fde);
+      fdes->push_back(fde);
+    }
+
+    if (next_entries_offset_ < memory_.cur_offset()) {
+      // Simply consider the processing done in this case.
+      break;
+    }
+  }
+}
+
+template <typename AddressType>
+const DwarfFde* DwarfSectionImplNoHdr<AddressType>::GetFdeFromPc(uint64_t pc) {
+  // Search in the list of fdes we already have.
+  auto it = fdes_.upper_bound(pc);
+  if (it != fdes_.end()) {
+    if (pc >= it->second.first) {
+      return it->second.second;
+    }
+  }
+
+  // The section might have overlapping pcs in fdes, so it is necessary
+  // to do a linear search of the fdes by pc. As fdes are read, a cached
+  // search map is created.
+  while (next_entries_offset_ < entries_end_) {
+    DwarfFde* fde;
+    if (!GetNextCieOrFde(&fde)) {
+      return nullptr;
+    }
+    if (fde != nullptr) {
+      InsertFde(fde);
+      if (pc >= fde->pc_start && pc < fde->pc_end) {
+        return fde;
+      }
+    }
+
+    if (next_entries_offset_ < memory_.cur_offset()) {
+      // Simply consider the processing done in this case.
+      break;
+    }
+  }
+  return nullptr;
+}
+
 // Explicitly instantiate DwarfSectionImpl
 template class DwarfSectionImpl<uint32_t>;
 template class DwarfSectionImpl<uint64_t>;
+
+// Explicitly instantiate DwarfSectionImplNoHdr
+template class DwarfSectionImplNoHdr<uint32_t>;
+template class DwarfSectionImplNoHdr<uint64_t>;
+
+// Explicitly instantiate DwarfDebugFrame
+template class DwarfDebugFrame<uint32_t>;
+template class DwarfDebugFrame<uint64_t>;
+
+// Explicitly instantiate DwarfEhFrame
+template class DwarfEhFrame<uint32_t>;
+template class DwarfEhFrame<uint64_t>;
+
+}  // namespace unwindstack
diff --git a/libunwindstack/DwarfSection.h b/libunwindstack/DwarfSection.h
deleted file mode 100644
index e617601..0000000
--- a/libunwindstack/DwarfSection.h
+++ /dev/null
@@ -1,137 +0,0 @@
-/*
- * Copyright (C) 2016 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 _LIBUNWINDSTACK_DWARF_SECTION_H
-#define _LIBUNWINDSTACK_DWARF_SECTION_H
-
-#include <stdint.h>
-
-#include <iterator>
-#include <unordered_map>
-
-#include "DwarfError.h"
-#include "DwarfLocation.h"
-#include "DwarfMemory.h"
-#include "DwarfStructs.h"
-
-// Forward declarations.
-class Memory;
-class Regs;
-
-class DwarfSection {
- public:
-  DwarfSection(Memory* memory) : memory_(memory) {}
-  virtual ~DwarfSection() = default;
-
-  class iterator : public std::iterator<std::bidirectional_iterator_tag, DwarfFde*> {
-   public:
-    iterator(DwarfSection* section, size_t index) : section_(section), index_(index) {}
-
-    iterator& operator++() {
-      index_++;
-      return *this;
-    }
-    iterator& operator++(int increment) {
-      index_ += increment;
-      return *this;
-    }
-    iterator& operator--() {
-      index_--;
-      return *this;
-    }
-    iterator& operator--(int decrement) {
-      index_ -= decrement;
-      return *this;
-    }
-
-    bool operator==(const iterator& rhs) { return this->index_ == rhs.index_; }
-    bool operator!=(const iterator& rhs) { return this->index_ != rhs.index_; }
-
-    const DwarfFde* operator*() { return section_->GetFdeFromIndex(index_); }
-
-   private:
-    DwarfSection* section_ = nullptr;
-    size_t index_ = 0;
-  };
-
-  iterator begin() { return iterator(this, 0); }
-  iterator end() { return iterator(this, fde_count_); }
-
-  DwarfError last_error() { return last_error_; }
-
-  virtual bool Init(uint64_t offset, uint64_t size) = 0;
-
-  virtual bool Eval(const DwarfCie*, Memory*, const dwarf_loc_regs_t&, Regs*) = 0;
-
-  virtual bool GetFdeOffsetFromPc(uint64_t pc, uint64_t* fde_offset) = 0;
-
-  virtual bool Log(uint8_t indent, uint64_t pc, uint64_t load_bias, const DwarfFde* fde) = 0;
-
-  virtual const DwarfFde* GetFdeFromIndex(size_t index) = 0;
-
-  const DwarfFde* GetFdeFromPc(uint64_t pc);
-
-  virtual const DwarfFde* GetFdeFromOffset(uint64_t fde_offset) = 0;
-
-  virtual bool GetCfaLocationInfo(uint64_t pc, const DwarfFde* fde, dwarf_loc_regs_t* loc_regs) = 0;
-
-  virtual bool IsCie32(uint32_t value32) = 0;
-
-  virtual bool IsCie64(uint64_t value64) = 0;
-
-  virtual uint64_t GetCieOffsetFromFde32(uint32_t pointer) = 0;
-
-  virtual uint64_t GetCieOffsetFromFde64(uint64_t pointer) = 0;
-
-  virtual uint64_t AdjustPcFromFde(uint64_t pc) = 0;
-
-  bool Step(uint64_t pc, Regs* regs, Memory* process_memory);
-
- protected:
-  DwarfMemory memory_;
-  DwarfError last_error_ = DWARF_ERROR_NONE;
-
-  uint64_t fde_count_;
-  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_;
-};
-
-template <typename AddressType>
-class DwarfSectionImpl : public DwarfSection {
- public:
-  DwarfSectionImpl(Memory* memory) : DwarfSection(memory) {}
-  virtual ~DwarfSectionImpl() = default;
-
-  bool Eval(const DwarfCie* cie, Memory* regular_memory, const dwarf_loc_regs_t& loc_regs,
-            Regs* regs) override;
-
-  const DwarfCie* GetCie(uint64_t offset);
-  bool FillInCie(DwarfCie* cie);
-
-  const DwarfFde* GetFdeFromOffset(uint64_t offset) override;
-  bool FillInFde(DwarfFde* fde);
-
-  bool GetCfaLocationInfo(uint64_t pc, const DwarfFde* fde, dwarf_loc_regs_t* loc_regs) override;
-
-  bool Log(uint8_t indent, uint64_t pc, uint64_t load_bias, const DwarfFde* fde) override;
-
- protected:
-  bool EvalExpression(const DwarfLocation& loc, uint8_t version, Memory* regular_memory,
-                      AddressType* value);
-};
-
-#endif  // _LIBUNWINDSTACK_DWARF_SECTION_H
diff --git a/libunwindstack/DwarfStructs.h b/libunwindstack/DwarfStructs.h
deleted file mode 100644
index 87182c9..0000000
--- a/libunwindstack/DwarfStructs.h
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright (C) 2016 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 _LIBUNWINDSTACK_DWARF_STRUCTS_H
-#define _LIBUNWINDSTACK_DWARF_STRUCTS_H
-
-#include <stdint.h>
-
-#include <vector>
-
-#include "DwarfEncoding.h"
-
-struct DwarfCie {
-  uint8_t version = 0;
-  uint8_t fde_address_encoding = DW_EH_PE_absptr;
-  uint8_t lsda_encoding = DW_EH_PE_omit;
-  uint8_t segment_size = 0;
-  std::vector<char> augmentation_string;
-  uint64_t personality_handler = 0;
-  uint64_t cfa_instructions_offset = 0;
-  uint64_t cfa_instructions_end = 0;
-  uint64_t code_alignment_factor = 0;
-  int64_t data_alignment_factor = 0;
-  uint64_t return_address_register = 0;
-};
-
-struct DwarfFde {
-  uint64_t cie_offset = 0;
-  uint64_t cfa_instructions_offset = 0;
-  uint64_t cfa_instructions_end = 0;
-  uint64_t pc_start = 0;
-  uint64_t pc_end = 0;
-  uint64_t lsda_address = 0;
-  const DwarfCie* cie = nullptr;
-};
-
-constexpr uint16_t CFA_REG = static_cast<uint16_t>(-1);
-
-#endif  // _LIBUNWINDSTACK_DWARF_STRUCTS_H
diff --git a/libunwindstack/Elf.cpp b/libunwindstack/Elf.cpp
index ad1447a..4723606 100644
--- a/libunwindstack/Elf.cpp
+++ b/libunwindstack/Elf.cpp
@@ -18,19 +18,30 @@
 #include <string.h>
 
 #include <memory>
+#include <mutex>
 #include <string>
+#include <utility>
 
 #define LOG_TAG "unwind"
 #include <log/log.h>
 
-#include "Elf.h"
-#include "ElfInterface.h"
-#include "ElfInterfaceArm.h"
-#include "Machine.h"
-#include "Memory.h"
-#include "Regs.h"
+#include <unwindstack/Elf.h>
+#include <unwindstack/ElfInterface.h>
+#include <unwindstack/MapInfo.h>
+#include <unwindstack/Memory.h>
+#include <unwindstack/Regs.h>
 
-bool Elf::Init() {
+#include "ElfInterfaceArm.h"
+#include "Symbols.h"
+
+namespace unwindstack {
+
+bool Elf::cache_enabled_;
+std::unordered_map<std::string, std::pair<std::shared_ptr<Elf>, bool>>* Elf::cache_;
+std::mutex* Elf::cache_lock_;
+
+bool Elf::Init(bool init_gnu_debugdata) {
+  load_bias_ = 0;
   if (!memory_) {
     return false;
   }
@@ -40,9 +51,14 @@
     return false;
   }
 
-  valid_ = interface_->Init();
+  valid_ = interface_->Init(&load_bias_);
   if (valid_) {
-    interface_->InitHeaders();
+    interface_->InitHeaders(load_bias_);
+    if (init_gnu_debugdata) {
+      InitGnuDebugdata();
+    } else {
+      gnu_debugdata_interface_.reset(nullptr);
+    }
   } else {
     interface_.reset(nullptr);
   }
@@ -62,8 +78,13 @@
   if (gnu == nullptr) {
     return;
   }
-  if (gnu->Init()) {
-    gnu->InitHeaders();
+
+  // Ignore the load_bias from the compressed section, the correct load bias
+  // is in the uncompressed data.
+  uint64_t load_bias;
+  if (gnu->Init(&load_bias)) {
+    gnu->InitHeaders(load_bias);
+    interface_->SetGnuDebugdataInterface(gnu);
   } else {
     // Free all of the memory associated with the gnu_debugdata section.
     gnu_debugdata_memory_.reset(nullptr);
@@ -71,6 +92,91 @@
   }
 }
 
+bool Elf::GetSoname(std::string* name) {
+  std::lock_guard<std::mutex> guard(lock_);
+  return valid_ && interface_->GetSoname(name);
+}
+
+uint64_t Elf::GetRelPc(uint64_t pc, const MapInfo* map_info) {
+  return pc - map_info->start + load_bias_ + map_info->elf_offset;
+}
+
+bool Elf::GetFunctionName(uint64_t addr, std::string* name, uint64_t* func_offset) {
+  std::lock_guard<std::mutex> guard(lock_);
+  return valid_ && (interface_->GetFunctionName(addr, name, func_offset) ||
+                    (gnu_debugdata_interface_ &&
+                     gnu_debugdata_interface_->GetFunctionName(addr, name, func_offset)));
+}
+
+bool Elf::GetGlobalVariable(const std::string& name, uint64_t* memory_address) {
+  if (!valid_) {
+    return false;
+  }
+
+  if (!interface_->GetGlobalVariable(name, memory_address) &&
+      (gnu_debugdata_interface_ == nullptr ||
+       !gnu_debugdata_interface_->GetGlobalVariable(name, memory_address))) {
+    return false;
+  }
+
+  // Adjust by the load bias.
+  if (*memory_address < load_bias_) {
+    return false;
+  }
+
+  *memory_address -= load_bias_;
+
+  // If this winds up in the dynamic section, then we might need to adjust
+  // the address.
+  uint64_t dynamic_end = interface_->dynamic_vaddr() + interface_->dynamic_size();
+  if (*memory_address >= interface_->dynamic_vaddr() && *memory_address < dynamic_end) {
+    if (interface_->dynamic_vaddr() > interface_->dynamic_offset()) {
+      *memory_address -= interface_->dynamic_vaddr() - interface_->dynamic_offset();
+    } else {
+      *memory_address += interface_->dynamic_offset() - interface_->dynamic_vaddr();
+    }
+  }
+  return true;
+}
+
+void Elf::GetLastError(ErrorData* data) {
+  if (valid_) {
+    *data = interface_->last_error();
+  }
+}
+
+ErrorCode Elf::GetLastErrorCode() {
+  if (valid_) {
+    return interface_->LastErrorCode();
+  }
+  return ERROR_NONE;
+}
+
+uint64_t Elf::GetLastErrorAddress() {
+  if (valid_) {
+    return interface_->LastErrorAddress();
+  }
+  return 0;
+}
+
+// The relative pc is always relative to the start of the map from which it comes.
+bool Elf::Step(uint64_t rel_pc, uint64_t adjusted_rel_pc, Regs* regs, Memory* process_memory,
+               bool* finished) {
+  if (!valid_) {
+    return false;
+  }
+
+  // The relative pc expectd by StepIfSignalHandler is relative to the start of the elf.
+  if (regs->StepIfSignalHandler(rel_pc, this, process_memory)) {
+    *finished = false;
+    return true;
+  }
+
+  // Lock during the step which can update information in the object.
+  std::lock_guard<std::mutex> guard(lock_);
+  return interface_->Step(adjusted_rel_pc, regs, process_memory, finished);
+}
+
 bool Elf::IsValidElf(Memory* memory) {
   if (memory == nullptr) {
     return false;
@@ -78,7 +184,7 @@
 
   // Verify that this is a valid elf file.
   uint8_t e_ident[SELFMAG + 1];
-  if (!memory->Read(0, e_ident, SELFMAG)) {
+  if (!memory->ReadFully(0, e_ident, SELFMAG)) {
     return false;
   }
 
@@ -88,48 +194,189 @@
   return true;
 }
 
+void Elf::GetInfo(Memory* memory, bool* valid, uint64_t* size) {
+  if (!IsValidElf(memory)) {
+    *valid = false;
+    return;
+  }
+  *size = 0;
+  *valid = true;
+
+  // Now read the section header information.
+  uint8_t class_type;
+  if (!memory->ReadFully(EI_CLASS, &class_type, 1)) {
+    return;
+  }
+  if (class_type == ELFCLASS32) {
+    ElfInterface32::GetMaxSize(memory, size);
+  } else if (class_type == ELFCLASS64) {
+    ElfInterface64::GetMaxSize(memory, size);
+  } else {
+    *valid = false;
+  }
+}
+
+bool Elf::IsValidPc(uint64_t pc) {
+  if (!valid_ || pc < load_bias_) {
+    return false;
+  }
+
+  if (interface_->IsValidPc(pc)) {
+    return true;
+  }
+
+  if (gnu_debugdata_interface_ != nullptr && gnu_debugdata_interface_->IsValidPc(pc)) {
+    return true;
+  }
+
+  return false;
+}
+
 ElfInterface* Elf::CreateInterfaceFromMemory(Memory* memory) {
   if (!IsValidElf(memory)) {
     return nullptr;
   }
 
   std::unique_ptr<ElfInterface> interface;
-  if (!memory->Read(EI_CLASS, &class_type_, 1)) {
+  if (!memory->ReadFully(EI_CLASS, &class_type_, 1)) {
     return nullptr;
   }
   if (class_type_ == ELFCLASS32) {
     Elf32_Half e_machine;
-    if (!memory->Read(EI_NIDENT + sizeof(Elf32_Half), &e_machine, sizeof(e_machine))) {
-      return nullptr;
-    }
-
-    if (e_machine != EM_ARM && e_machine != EM_386) {
-      // Unsupported.
-      ALOGI("32 bit elf that is neither arm nor x86: e_machine = %d\n", e_machine);
+    if (!memory->ReadFully(EI_NIDENT + sizeof(Elf32_Half), &e_machine, sizeof(e_machine))) {
       return nullptr;
     }
 
     machine_type_ = e_machine;
     if (e_machine == EM_ARM) {
+      arch_ = ARCH_ARM;
       interface.reset(new ElfInterfaceArm(memory));
-    } else {
+    } else if (e_machine == EM_386) {
+      arch_ = ARCH_X86;
       interface.reset(new ElfInterface32(memory));
+    } else if (e_machine == EM_MIPS) {
+      arch_ = ARCH_MIPS;
+      interface.reset(new ElfInterface32(memory));
+    } else {
+      // Unsupported.
+      ALOGI("32 bit elf that is neither arm nor x86 nor mips: e_machine = %d\n", e_machine);
+      return nullptr;
     }
   } else if (class_type_ == ELFCLASS64) {
     Elf64_Half e_machine;
-    if (!memory->Read(EI_NIDENT + sizeof(Elf64_Half), &e_machine, sizeof(e_machine))) {
-      return nullptr;
-    }
-
-    if (e_machine != EM_AARCH64 && e_machine != EM_X86_64) {
-      // Unsupported.
-      ALOGI("64 bit elf that is neither aarch64 nor x86_64: e_machine = %d\n", e_machine);
+    if (!memory->ReadFully(EI_NIDENT + sizeof(Elf64_Half), &e_machine, sizeof(e_machine))) {
       return nullptr;
     }
 
     machine_type_ = e_machine;
+    if (e_machine == EM_AARCH64) {
+      arch_ = ARCH_ARM64;
+    } else if (e_machine == EM_X86_64) {
+      arch_ = ARCH_X86_64;
+    } else if (e_machine == EM_MIPS) {
+      arch_ = ARCH_MIPS64;
+    } else {
+      // Unsupported.
+      ALOGI("64 bit elf that is neither aarch64 nor x86_64 nor mips64: e_machine = %d\n",
+            e_machine);
+      return nullptr;
+    }
     interface.reset(new ElfInterface64(memory));
   }
 
   return interface.release();
 }
+
+uint64_t Elf::GetLoadBias(Memory* memory) {
+  if (!IsValidElf(memory)) {
+    return 0;
+  }
+
+  uint8_t class_type;
+  if (!memory->Read(EI_CLASS, &class_type, 1)) {
+    return 0;
+  }
+
+  if (class_type == ELFCLASS32) {
+    return ElfInterface::GetLoadBias<Elf32_Ehdr, Elf32_Phdr>(memory);
+  } else if (class_type == ELFCLASS64) {
+    return ElfInterface::GetLoadBias<Elf64_Ehdr, Elf64_Phdr>(memory);
+  }
+  return 0;
+}
+
+void Elf::SetCachingEnabled(bool enable) {
+  if (!cache_enabled_ && enable) {
+    cache_enabled_ = true;
+    cache_ = new std::unordered_map<std::string, std::pair<std::shared_ptr<Elf>, bool>>;
+    cache_lock_ = new std::mutex;
+  } else if (cache_enabled_ && !enable) {
+    cache_enabled_ = false;
+    delete cache_;
+    delete cache_lock_;
+  }
+}
+
+void Elf::CacheLock() {
+  cache_lock_->lock();
+}
+
+void Elf::CacheUnlock() {
+  cache_lock_->unlock();
+}
+
+void Elf::CacheAdd(MapInfo* info) {
+  // If elf_offset != 0, then cache both name:offset and name.
+  // The cached name is used to do lookups if multiple maps for the same
+  // named elf file exist.
+  // For example, if there are two maps boot.odex:1000 and boot.odex:2000
+  // where each reference the entire boot.odex, the cache will properly
+  // use the same cached elf object.
+
+  if (info->offset == 0 || info->elf_offset != 0) {
+    (*cache_)[info->name] = std::make_pair(info->elf, true);
+  }
+
+  if (info->offset != 0) {
+    // The second element in the pair indicates whether elf_offset should
+    // be set to offset when getting out of the cache.
+    (*cache_)[info->name + ':' + std::to_string(info->offset)] =
+        std::make_pair(info->elf, info->elf_offset != 0);
+  }
+}
+
+bool Elf::CacheAfterCreateMemory(MapInfo* info) {
+  if (info->name.empty() || info->offset == 0 || info->elf_offset == 0) {
+    return false;
+  }
+
+  auto entry = cache_->find(info->name);
+  if (entry == cache_->end()) {
+    return false;
+  }
+
+  // In this case, the whole file is the elf, and the name has already
+  // been cached. Add an entry at name:offset to get this directly out
+  // of the cache next time.
+  info->elf = entry->second.first;
+  (*cache_)[info->name + ':' + std::to_string(info->offset)] = std::make_pair(info->elf, true);
+  return true;
+}
+
+bool Elf::CacheGet(MapInfo* info) {
+  std::string name(info->name);
+  if (info->offset != 0) {
+    name += ':' + std::to_string(info->offset);
+  }
+  auto entry = cache_->find(name);
+  if (entry != cache_->end()) {
+    info->elf = entry->second.first;
+    if (entry->second.second) {
+      info->elf_offset = info->offset;
+    }
+    return true;
+  }
+  return false;
+}
+
+}  // namespace unwindstack
diff --git a/libunwindstack/Elf.h b/libunwindstack/Elf.h
deleted file mode 100644
index cff9dc4..0000000
--- a/libunwindstack/Elf.h
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * Copyright (C) 2016 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 _LIBUNWINDSTACK_ELF_H
-#define _LIBUNWINDSTACK_ELF_H
-
-#include <stddef.h>
-
-#include <memory>
-#include <string>
-
-#include "ElfInterface.h"
-#include "Memory.h"
-
-#if !defined(EM_AARCH64)
-#define EM_AARCH64 183
-#endif
-
-// Forward declaration.
-class Regs;
-
-class Elf {
- public:
-  Elf(Memory* memory) : memory_(memory) {}
-  virtual ~Elf() = default;
-
-  bool Init();
-
-  void InitGnuDebugdata();
-
-  bool GetSoname(std::string* name) {
-    return valid_ && interface_->GetSoname(name);
-  }
-
-  bool GetFunctionName(uint64_t addr, std::string* name, uint64_t* func_offset) {
-    return valid_ && (interface_->GetFunctionName(addr, name, func_offset) ||
-                      (gnu_debugdata_interface_ &&
-                       gnu_debugdata_interface_->GetFunctionName(addr, name, func_offset)));
-  }
-
-  bool Step(uint64_t rel_pc, Regs* regs, Memory* process_memory) {
-    return valid_ && (interface_->Step(rel_pc, regs, process_memory) ||
-                      (gnu_debugdata_interface_ &&
-                       gnu_debugdata_interface_->Step(rel_pc, regs, process_memory)));
-  }
-
-  ElfInterface* CreateInterfaceFromMemory(Memory* memory);
-
-  bool valid() { return valid_; }
-
-  uint32_t machine_type() { return machine_type_; }
-
-  uint8_t class_type() { return class_type_; }
-
-  Memory* memory() { return memory_.get(); }
-
-  ElfInterface* interface() { return interface_.get(); }
-
-  ElfInterface* gnu_debugdata_interface() { return gnu_debugdata_interface_.get(); }
-
-  static bool IsValidElf(Memory* memory);
-
- protected:
-  bool valid_ = false;
-  std::unique_ptr<ElfInterface> interface_;
-  std::unique_ptr<Memory> memory_;
-  uint32_t machine_type_;
-  uint8_t class_type_;
-
-  std::unique_ptr<Memory> gnu_debugdata_memory_;
-  std::unique_ptr<ElfInterface> gnu_debugdata_interface_;
-};
-
-#endif  // _LIBUNWINDSTACK_ELF_H
diff --git a/libunwindstack/ElfInterface.cpp b/libunwindstack/ElfInterface.cpp
index 7d38ee0..2c00456 100644
--- a/libunwindstack/ElfInterface.cpp
+++ b/libunwindstack/ElfInterface.cpp
@@ -19,26 +19,56 @@
 
 #include <memory>
 #include <string>
+#include <utility>
 
 #include <7zCrc.h>
 #include <Xz.h>
 #include <XzCrc64.h>
 
+#include <unwindstack/DwarfError.h>
+#include <unwindstack/DwarfSection.h>
+#include <unwindstack/ElfInterface.h>
+#include <unwindstack/Log.h>
+#include <unwindstack/Memory.h>
+#include <unwindstack/Regs.h>
+
 #include "DwarfDebugFrame.h"
 #include "DwarfEhFrame.h"
-#include "DwarfSection.h"
-#include "ElfInterface.h"
-#include "Log.h"
-#include "Memory.h"
-#include "Regs.h"
+#include "DwarfEhFrameWithHdr.h"
 #include "Symbols.h"
 
+namespace unwindstack {
+
 ElfInterface::~ElfInterface() {
   for (auto symbol : symbols_) {
     delete symbol;
   }
 }
 
+bool ElfInterface::IsValidPc(uint64_t pc) {
+  if (!pt_loads_.empty()) {
+    for (auto& entry : pt_loads_) {
+      uint64_t start = entry.second.table_offset;
+      uint64_t end = start + entry.second.table_size;
+      if (pc >= start && pc < end) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  // No PT_LOAD data, look for a fde for this pc in the section data.
+  if (debug_frame_ != nullptr && debug_frame_->GetFdeFromPc(pc) != nullptr) {
+    return true;
+  }
+
+  if (eh_frame_ != nullptr && eh_frame_->GetFdeFromPc(pc) != nullptr) {
+    return true;
+  }
+
+  return false;
+}
+
 Memory* ElfInterface::CreateGnuDebugdataMemory() {
   if (gnu_debugdata_offset_ == 0 || gnu_debugdata_size_ == 0) {
     return nullptr;
@@ -49,7 +79,7 @@
   Crc64GenerateTable();
 
   std::vector<uint8_t> src(gnu_debugdata_size_);
-  if (!memory_->Read(gnu_debugdata_offset_, src.data(), gnu_debugdata_size_)) {
+  if (!memory_->ReadFully(gnu_debugdata_offset_, src.data(), gnu_debugdata_size_)) {
     gnu_debugdata_offset_ = 0;
     gnu_debugdata_size_ = static_cast<uint64_t>(-1);
     return nullptr;
@@ -57,8 +87,8 @@
 
   ISzAlloc alloc;
   CXzUnpacker state;
-  alloc.Alloc = [](void*, size_t size) { return malloc(size); };
-  alloc.Free = [](void*, void* ptr) { return free(ptr); };
+  alloc.Alloc = [](ISzAllocPtr, size_t size) { return malloc(size); };
+  alloc.Free = [](ISzAllocPtr, void* ptr) { return free(ptr); };
 
   XzUnpacker_Construct(&state, &alloc);
 
@@ -76,7 +106,7 @@
       dst_remaining += 2 * gnu_debugdata_size_;
     }
     return_val = XzUnpacker_Code(&state, dst->GetPtr(dst_offset), &dst_remaining, &src[src_offset],
-                                 &src_remaining, CODER_FINISH_ANY, &status);
+                                 &src_remaining, true, CODER_FINISH_ANY, &status);
     src_offset += src_remaining;
     dst_offset += dst_remaining;
   } while (return_val == SZ_OK && status == CODER_STATUS_NOT_FINISHED);
@@ -94,19 +124,33 @@
 }
 
 template <typename AddressType>
-void ElfInterface::InitHeadersWithTemplate() {
-  if (eh_frame_offset_ != 0) {
-    eh_frame_.reset(new DwarfEhFrame<AddressType>(memory_));
-    if (!eh_frame_->Init(eh_frame_offset_, eh_frame_size_)) {
+void ElfInterface::InitHeadersWithTemplate(uint64_t load_bias) {
+  if (eh_frame_hdr_offset_ != 0) {
+    eh_frame_.reset(new DwarfEhFrameWithHdr<AddressType>(memory_));
+    if (!eh_frame_->Init(eh_frame_hdr_offset_, eh_frame_hdr_size_, load_bias)) {
       eh_frame_.reset(nullptr);
-      eh_frame_offset_ = 0;
-      eh_frame_size_ = static_cast<uint64_t>(-1);
     }
   }
 
+  if (eh_frame_.get() == nullptr && eh_frame_offset_ != 0) {
+    // If there is an eh_frame section without an eh_frame_hdr section,
+    // or using the frame hdr object failed to init.
+    eh_frame_.reset(new DwarfEhFrame<AddressType>(memory_));
+    if (!eh_frame_->Init(eh_frame_offset_, eh_frame_size_, load_bias)) {
+      eh_frame_.reset(nullptr);
+    }
+  }
+
+  if (eh_frame_.get() == nullptr) {
+    eh_frame_hdr_offset_ = 0;
+    eh_frame_hdr_size_ = static_cast<uint64_t>(-1);
+    eh_frame_offset_ = 0;
+    eh_frame_size_ = static_cast<uint64_t>(-1);
+  }
+
   if (debug_frame_offset_ != 0) {
     debug_frame_.reset(new DwarfDebugFrame<AddressType>(memory_));
-    if (!debug_frame_->Init(debug_frame_offset_, debug_frame_size_)) {
+    if (!debug_frame_->Init(debug_frame_offset_, debug_frame_size_, load_bias)) {
       debug_frame_.reset(nullptr);
       debug_frame_offset_ = 0;
       debug_frame_size_ = static_cast<uint64_t>(-1);
@@ -115,13 +159,15 @@
 }
 
 template <typename EhdrType, typename PhdrType, typename ShdrType>
-bool ElfInterface::ReadAllHeaders() {
+bool ElfInterface::ReadAllHeaders(uint64_t* load_bias) {
   EhdrType ehdr;
-  if (!memory_->Read(0, &ehdr, sizeof(ehdr))) {
+  if (!memory_->ReadFully(0, &ehdr, sizeof(ehdr))) {
+    last_error_.code = ERROR_MEMORY_INVALID;
+    last_error_.address = 0;
     return false;
   }
 
-  if (!ReadProgramHeaders<EhdrType, PhdrType>(ehdr)) {
+  if (!ReadProgramHeaders<EhdrType, PhdrType>(ehdr, load_bias)) {
     return false;
   }
 
@@ -134,67 +180,66 @@
 }
 
 template <typename EhdrType, typename PhdrType>
-bool ElfInterface::ReadProgramHeaders(const EhdrType& ehdr) {
+uint64_t ElfInterface::GetLoadBias(Memory* memory) {
+  EhdrType ehdr;
+  if (!memory->Read(0, &ehdr, sizeof(ehdr))) {
+    return false;
+  }
+
   uint64_t offset = ehdr.e_phoff;
   for (size_t i = 0; i < ehdr.e_phnum; i++, offset += ehdr.e_phentsize) {
     PhdrType phdr;
-    if (!memory_->ReadField(offset, &phdr, &phdr.p_type, sizeof(phdr.p_type))) {
-      return false;
+    if (!memory->Read(offset, &phdr, sizeof(phdr))) {
+      return 0;
     }
+    if (phdr.p_type == PT_LOAD && phdr.p_offset == 0) {
+      return phdr.p_vaddr;
+    }
+  }
+  return 0;
+}
 
-    if (HandleType(offset, phdr.p_type)) {
-      continue;
+template <typename EhdrType, typename PhdrType>
+bool ElfInterface::ReadProgramHeaders(const EhdrType& ehdr, uint64_t* load_bias) {
+  uint64_t offset = ehdr.e_phoff;
+  for (size_t i = 0; i < ehdr.e_phnum; i++, offset += ehdr.e_phentsize) {
+    PhdrType phdr;
+    if (!memory_->ReadFully(offset, &phdr, sizeof(phdr))) {
+      last_error_.code = ERROR_MEMORY_INVALID;
+      last_error_.address = offset;
+      return false;
     }
 
     switch (phdr.p_type) {
     case PT_LOAD:
     {
-      // Get the flags first, if this isn't an executable header, ignore it.
-      if (!memory_->ReadField(offset, &phdr, &phdr.p_flags, sizeof(phdr.p_flags))) {
-        return false;
-      }
       if ((phdr.p_flags & PF_X) == 0) {
         continue;
       }
 
-      if (!memory_->ReadField(offset, &phdr, &phdr.p_vaddr, sizeof(phdr.p_vaddr))) {
-        return false;
-      }
-      if (!memory_->ReadField(offset, &phdr, &phdr.p_offset, sizeof(phdr.p_offset))) {
-        return false;
-      }
-      if (!memory_->ReadField(offset, &phdr, &phdr.p_memsz, sizeof(phdr.p_memsz))) {
-        return false;
-      }
       pt_loads_[phdr.p_offset] = LoadInfo{phdr.p_offset, phdr.p_vaddr,
                                           static_cast<size_t>(phdr.p_memsz)};
       if (phdr.p_offset == 0) {
-        load_bias_ = phdr.p_vaddr;
+        *load_bias = phdr.p_vaddr;
       }
       break;
     }
 
     case PT_GNU_EH_FRAME:
-      if (!memory_->ReadField(offset, &phdr, &phdr.p_offset, sizeof(phdr.p_offset))) {
-        return false;
-      }
-      eh_frame_offset_ = phdr.p_offset;
-      if (!memory_->ReadField(offset, &phdr, &phdr.p_memsz, sizeof(phdr.p_memsz))) {
-        return false;
-      }
-      eh_frame_size_ = phdr.p_memsz;
+      // This is really the pointer to the .eh_frame_hdr section.
+      eh_frame_hdr_offset_ = phdr.p_offset;
+      eh_frame_hdr_size_ = phdr.p_memsz;
       break;
 
     case PT_DYNAMIC:
-      if (!memory_->ReadField(offset, &phdr, &phdr.p_offset, sizeof(phdr.p_offset))) {
-        return false;
-      }
       dynamic_offset_ = phdr.p_offset;
-      if (!memory_->ReadField(offset, &phdr, &phdr.p_memsz, sizeof(phdr.p_memsz))) {
-        return false;
-      }
+      dynamic_vaddr_ = phdr.p_vaddr;
       dynamic_size_ = phdr.p_memsz;
       break;
+
+    default:
+      HandleUnknownType(phdr.p_type, phdr.p_offset, phdr.p_filesz);
+      break;
     }
   }
   return true;
@@ -212,8 +257,7 @@
   ShdrType shdr;
   if (ehdr.e_shstrndx < ehdr.e_shnum) {
     uint64_t sh_offset = offset + ehdr.e_shstrndx * ehdr.e_shentsize;
-    if (memory_->ReadField(sh_offset, &shdr, &shdr.sh_offset, sizeof(shdr.sh_offset)) &&
-        memory_->ReadField(sh_offset, &shdr, &shdr.sh_size, sizeof(shdr.sh_size))) {
+    if (memory_->ReadFully(sh_offset, &shdr, sizeof(shdr))) {
       sec_offset = shdr.sh_offset;
       sec_size = shdr.sh_size;
     }
@@ -222,41 +266,34 @@
   // Skip the first header, it's always going to be NULL.
   offset += ehdr.e_shentsize;
   for (size_t i = 1; i < ehdr.e_shnum; i++, offset += ehdr.e_shentsize) {
-    if (!memory_->ReadField(offset, &shdr, &shdr.sh_type, sizeof(shdr.sh_type))) {
+    if (!memory_->Read(offset, &shdr, sizeof(shdr))) {
+      last_error_.code = ERROR_MEMORY_INVALID;
+      last_error_.address = offset;
       return false;
     }
 
     if (shdr.sh_type == SHT_SYMTAB || shdr.sh_type == SHT_DYNSYM) {
-      if (!memory_->Read(offset, &shdr, sizeof(shdr))) {
-        return false;
-      }
       // Need to go get the information about the section that contains
       // the string terminated names.
       ShdrType str_shdr;
       if (shdr.sh_link >= ehdr.e_shnum) {
+        last_error_.code = ERROR_UNWIND_INFO;
         return false;
       }
       uint64_t str_offset = ehdr.e_shoff + shdr.sh_link * ehdr.e_shentsize;
-      if (!memory_->ReadField(str_offset, &str_shdr, &str_shdr.sh_type, sizeof(str_shdr.sh_type))) {
+      if (!memory_->Read(str_offset, &str_shdr, sizeof(str_shdr))) {
+        last_error_.code = ERROR_MEMORY_INVALID;
+        last_error_.address = str_offset;
         return false;
       }
       if (str_shdr.sh_type != SHT_STRTAB) {
-        return false;
-      }
-      if (!memory_->ReadField(str_offset, &str_shdr, &str_shdr.sh_offset,
-                              sizeof(str_shdr.sh_offset))) {
-        return false;
-      }
-      if (!memory_->ReadField(str_offset, &str_shdr, &str_shdr.sh_size, sizeof(str_shdr.sh_size))) {
+        last_error_.code = ERROR_UNWIND_INFO;
         return false;
       }
       symbols_.push_back(new Symbols(shdr.sh_offset, shdr.sh_size, shdr.sh_entsize,
                                      str_shdr.sh_offset, str_shdr.sh_size));
     } else if (shdr.sh_type == SHT_PROGBITS && sec_size != 0) {
       // Look for the .debug_frame and .gnu_debugdata.
-      if (!memory_->ReadField(offset, &shdr, &shdr.sh_name, sizeof(shdr.sh_name))) {
-        return false;
-      }
       if (shdr.sh_name < sec_size) {
         std::string name;
         if (memory_->ReadString(sec_offset + shdr.sh_name, &name)) {
@@ -268,15 +305,23 @@
           } else if (name == ".gnu_debugdata") {
             offset_ptr = &gnu_debugdata_offset_;
             size_ptr = &gnu_debugdata_size_;
+          } else if (name == ".eh_frame") {
+            offset_ptr = &eh_frame_offset_;
+            size_ptr = &eh_frame_size_;
+          } else if (eh_frame_hdr_offset_ == 0 && name == ".eh_frame_hdr") {
+            offset_ptr = &eh_frame_hdr_offset_;
+            size_ptr = &eh_frame_hdr_size_;
           }
-          if (offset_ptr != nullptr &&
-              memory_->ReadField(offset, &shdr, &shdr.sh_offset, sizeof(shdr.sh_offset)) &&
-              memory_->ReadField(offset, &shdr, &shdr.sh_size, sizeof(shdr.sh_size))) {
+          if (offset_ptr != nullptr) {
             *offset_ptr = shdr.sh_offset;
             *size_ptr = shdr.sh_size;
           }
         }
       }
+    } else if (shdr.sh_type == SHT_STRTAB) {
+      // In order to read soname, keep track of address to offset mapping.
+      strtabs_.push_back(std::make_pair<uint64_t, uint64_t>(static_cast<uint64_t>(shdr.sh_addr),
+                                                            static_cast<uint64_t>(shdr.sh_offset)));
     }
   }
   return true;
@@ -295,7 +340,7 @@
   soname_type_ = SONAME_INVALID;
 
   uint64_t soname_offset = 0;
-  uint64_t strtab_offset = 0;
+  uint64_t strtab_addr = 0;
   uint64_t strtab_size = 0;
 
   // Find the soname location from the dynamic headers section.
@@ -303,12 +348,14 @@
   uint64_t offset = dynamic_offset_;
   uint64_t max_offset = offset + dynamic_size_;
   for (uint64_t offset = dynamic_offset_; offset < max_offset; offset += sizeof(DynType)) {
-    if (!memory_->Read(offset, &dyn, sizeof(dyn))) {
+    if (!memory_->ReadFully(offset, &dyn, sizeof(dyn))) {
+      last_error_.code = ERROR_MEMORY_INVALID;
+      last_error_.address = offset;
       return false;
     }
 
     if (dyn.d_tag == DT_STRTAB) {
-      strtab_offset = dyn.d_un.d_ptr;
+      strtab_addr = dyn.d_un.d_ptr;
     } else if (dyn.d_tag == DT_STRSZ) {
       strtab_size = dyn.d_un.d_val;
     } else if (dyn.d_tag == DT_SONAME) {
@@ -318,16 +365,22 @@
     }
   }
 
-  soname_offset += strtab_offset;
-  if (soname_offset >= strtab_offset + strtab_size) {
-    return false;
+  // Need to map the strtab address to the real offset.
+  for (const auto& entry : strtabs_) {
+    if (entry.first == strtab_addr) {
+      soname_offset = entry.second + soname_offset;
+      if (soname_offset >= entry.second + strtab_size) {
+        return false;
+      }
+      if (!memory_->ReadString(soname_offset, &soname_)) {
+        return false;
+      }
+      soname_type_ = SONAME_VALID;
+      *soname = soname_;
+      return true;
+    }
   }
-  if (!memory_->ReadString(soname_offset, &soname_)) {
-    return false;
-  }
-  soname_type_ = SONAME_VALID;
-  *soname = soname_;
-  return true;
+  return false;
 }
 
 template <typename SymType>
@@ -338,44 +391,116 @@
   }
 
   for (const auto symbol : symbols_) {
-    if (symbol->GetName<SymType>(addr, load_bias_, memory_, name, func_offset)) {
+    if (symbol->GetName<SymType>(addr, memory_, name, func_offset)) {
       return true;
     }
   }
   return false;
 }
 
-bool ElfInterface::Step(uint64_t pc, Regs* regs, Memory* process_memory) {
-  // Need to subtract off the load_bias to get the correct pc.
-  if (pc < load_bias_) {
+template <typename SymType>
+bool ElfInterface::GetGlobalVariableWithTemplate(const std::string& name, uint64_t* memory_address) {
+  if (symbols_.empty()) {
     return false;
   }
-  pc -= load_bias_;
 
-  // Try the eh_frame first.
-  DwarfSection* eh_frame = eh_frame_.get();
-  if (eh_frame != nullptr && eh_frame->Step(pc, regs, process_memory)) {
-    return true;
+  for (const auto symbol : symbols_) {
+    if (symbol->GetGlobal<SymType>(memory_, name, memory_address)) {
+      return true;
+    }
   }
-
-  // Try the debug_frame next.
-  DwarfSection* debug_frame = debug_frame_.get();
-  if (debug_frame != nullptr && debug_frame->Step(pc, regs, process_memory)) {
-    return true;
-  }
-
   return false;
 }
 
+bool ElfInterface::Step(uint64_t pc, Regs* regs, Memory* process_memory, bool* finished) {
+  last_error_.code = ERROR_NONE;
+  last_error_.address = 0;
+
+  // Try the debug_frame first since it contains the most specific unwind
+  // information.
+  DwarfSection* debug_frame = debug_frame_.get();
+  if (debug_frame != nullptr && debug_frame->Step(pc, regs, process_memory, finished)) {
+    return true;
+  }
+
+  // Try the eh_frame next.
+  DwarfSection* eh_frame = eh_frame_.get();
+  if (eh_frame != nullptr && eh_frame->Step(pc, regs, process_memory, finished)) {
+    return true;
+  }
+
+  if (gnu_debugdata_interface_ != nullptr &&
+      gnu_debugdata_interface_->Step(pc, regs, process_memory, finished)) {
+    return true;
+  }
+
+  // Set the error code based on the first error encountered.
+  DwarfSection* section = nullptr;
+  if (debug_frame_ != nullptr) {
+    section = debug_frame_.get();
+  } else if (eh_frame_ != nullptr) {
+    section = eh_frame_.get();
+  } else if (gnu_debugdata_interface_ != nullptr) {
+    last_error_ = gnu_debugdata_interface_->last_error();
+    return false;
+  } else {
+    return false;
+  }
+
+  // Convert the DWARF ERROR to an external error.
+  DwarfErrorCode code = section->LastErrorCode();
+  switch (code) {
+    case DWARF_ERROR_NONE:
+      last_error_.code = ERROR_NONE;
+      break;
+
+    case DWARF_ERROR_MEMORY_INVALID:
+      last_error_.code = ERROR_MEMORY_INVALID;
+      last_error_.address = section->LastErrorAddress();
+      break;
+
+    case DWARF_ERROR_ILLEGAL_VALUE:
+    case DWARF_ERROR_ILLEGAL_STATE:
+    case DWARF_ERROR_STACK_INDEX_NOT_VALID:
+    case DWARF_ERROR_TOO_MANY_ITERATIONS:
+    case DWARF_ERROR_CFA_NOT_DEFINED:
+    case DWARF_ERROR_NO_FDES:
+      last_error_.code = ERROR_UNWIND_INFO;
+      break;
+
+    case DWARF_ERROR_NOT_IMPLEMENTED:
+    case DWARF_ERROR_UNSUPPORTED_VERSION:
+      last_error_.code = ERROR_UNSUPPORTED;
+      break;
+  }
+  return false;
+}
+
+// This is an estimation of the size of the elf file using the location
+// of the section headers and size. This assumes that the section headers
+// are at the end of the elf file. If the elf has a load bias, the size
+// will be too large, but this is acceptable.
+template <typename EhdrType>
+void ElfInterface::GetMaxSizeWithTemplate(Memory* memory, uint64_t* size) {
+  EhdrType ehdr;
+  if (!memory->ReadFully(0, &ehdr, sizeof(ehdr))) {
+    return;
+  }
+  if (ehdr.e_shnum == 0) {
+    return;
+  }
+  *size = ehdr.e_shoff + ehdr.e_shentsize * ehdr.e_shnum;
+}
+
 // Instantiate all of the needed template functions.
-template void ElfInterface::InitHeadersWithTemplate<uint32_t>();
-template void ElfInterface::InitHeadersWithTemplate<uint64_t>();
+template void ElfInterface::InitHeadersWithTemplate<uint32_t>(uint64_t);
+template void ElfInterface::InitHeadersWithTemplate<uint64_t>(uint64_t);
 
-template bool ElfInterface::ReadAllHeaders<Elf32_Ehdr, Elf32_Phdr, Elf32_Shdr>();
-template bool ElfInterface::ReadAllHeaders<Elf64_Ehdr, Elf64_Phdr, Elf64_Shdr>();
+template bool ElfInterface::ReadAllHeaders<Elf32_Ehdr, Elf32_Phdr, Elf32_Shdr>(uint64_t*);
+template bool ElfInterface::ReadAllHeaders<Elf64_Ehdr, Elf64_Phdr, Elf64_Shdr>(uint64_t*);
 
-template bool ElfInterface::ReadProgramHeaders<Elf32_Ehdr, Elf32_Phdr>(const Elf32_Ehdr&);
-template bool ElfInterface::ReadProgramHeaders<Elf64_Ehdr, Elf64_Phdr>(const Elf64_Ehdr&);
+template bool ElfInterface::ReadProgramHeaders<Elf32_Ehdr, Elf32_Phdr>(const Elf32_Ehdr&, uint64_t*);
+template bool ElfInterface::ReadProgramHeaders<Elf64_Ehdr, Elf64_Phdr>(const Elf64_Ehdr&, uint64_t*);
 
 template bool ElfInterface::ReadSectionHeaders<Elf32_Ehdr, Elf32_Shdr>(const Elf32_Ehdr&);
 template bool ElfInterface::ReadSectionHeaders<Elf64_Ehdr, Elf64_Shdr>(const Elf64_Ehdr&);
@@ -387,3 +512,14 @@
                                                                    uint64_t*);
 template bool ElfInterface::GetFunctionNameWithTemplate<Elf64_Sym>(uint64_t, std::string*,
                                                                    uint64_t*);
+
+template bool ElfInterface::GetGlobalVariableWithTemplate<Elf32_Sym>(const std::string&, uint64_t*);
+template bool ElfInterface::GetGlobalVariableWithTemplate<Elf64_Sym>(const std::string&, uint64_t*);
+
+template void ElfInterface::GetMaxSizeWithTemplate<Elf32_Ehdr>(Memory*, uint64_t*);
+template void ElfInterface::GetMaxSizeWithTemplate<Elf64_Ehdr>(Memory*, uint64_t*);
+
+template uint64_t ElfInterface::GetLoadBias<Elf32_Ehdr, Elf32_Phdr>(Memory*);
+template uint64_t ElfInterface::GetLoadBias<Elf64_Ehdr, Elf64_Phdr>(Memory*);
+
+}  // namespace unwindstack
diff --git a/libunwindstack/ElfInterface.h b/libunwindstack/ElfInterface.h
deleted file mode 100644
index d0d0d28..0000000
--- a/libunwindstack/ElfInterface.h
+++ /dev/null
@@ -1,169 +0,0 @@
-/*
- * Copyright (C) 2016 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 _LIBUNWINDSTACK_ELF_INTERFACE_H
-#define _LIBUNWINDSTACK_ELF_INTERFACE_H
-
-#include <elf.h>
-#include <stdint.h>
-
-#include <memory>
-#include <string>
-#include <unordered_map>
-#include <vector>
-
-#include "DwarfSection.h"
-
-// Forward declarations.
-class Memory;
-class Regs;
-class Symbols;
-
-struct LoadInfo {
-  uint64_t offset;
-  uint64_t table_offset;
-  size_t table_size;
-};
-
-enum : uint8_t {
-  SONAME_UNKNOWN = 0,
-  SONAME_VALID,
-  SONAME_INVALID,
-};
-
-class ElfInterface {
- public:
-  ElfInterface(Memory* memory) : memory_(memory) {}
-  virtual ~ElfInterface();
-
-  virtual bool Init() = 0;
-
-  virtual void InitHeaders() = 0;
-
-  virtual bool GetSoname(std::string* name) = 0;
-
-  virtual bool GetFunctionName(uint64_t addr, std::string* name, uint64_t* offset) = 0;
-
-  virtual bool Step(uint64_t rel_pc, Regs* regs, Memory* process_memory);
-
-  Memory* CreateGnuDebugdataMemory();
-
-  Memory* memory() { return memory_; }
-
-  const std::unordered_map<uint64_t, LoadInfo>& pt_loads() { return pt_loads_; }
-  uint64_t load_bias() { return load_bias_; }
-  void set_load_bias(uint64_t load_bias) { load_bias_ = load_bias; }
-
-  uint64_t dynamic_offset() { return dynamic_offset_; }
-  uint64_t dynamic_size() { return dynamic_size_; }
-  uint64_t eh_frame_offset() { return eh_frame_offset_; }
-  uint64_t eh_frame_size() { return eh_frame_size_; }
-  uint64_t debug_frame_offset() { return debug_frame_offset_; }
-  uint64_t debug_frame_size() { return debug_frame_size_; }
-  uint64_t gnu_debugdata_offset() { return gnu_debugdata_offset_; }
-  uint64_t gnu_debugdata_size() { return gnu_debugdata_size_; }
-
-  DwarfSection* eh_frame() { return eh_frame_.get(); }
-  DwarfSection* debug_frame() { return debug_frame_.get(); }
-
- protected:
-  template <typename AddressType>
-  void InitHeadersWithTemplate();
-
-  template <typename EhdrType, typename PhdrType, typename ShdrType>
-  bool ReadAllHeaders();
-
-  template <typename EhdrType, typename PhdrType>
-  bool ReadProgramHeaders(const EhdrType& ehdr);
-
-  template <typename EhdrType, typename ShdrType>
-  bool ReadSectionHeaders(const EhdrType& ehdr);
-
-  template <typename DynType>
-  bool GetSonameWithTemplate(std::string* soname);
-
-  template <typename SymType>
-  bool GetFunctionNameWithTemplate(uint64_t addr, std::string* name, uint64_t* func_offset);
-
-  virtual bool HandleType(uint64_t, uint32_t) { return false; }
-
-  Memory* memory_;
-  std::unordered_map<uint64_t, LoadInfo> pt_loads_;
-  uint64_t load_bias_ = 0;
-
-  // Stored elf data.
-  uint64_t dynamic_offset_ = 0;
-  uint64_t dynamic_size_ = 0;
-
-  uint64_t eh_frame_offset_ = 0;
-  uint64_t eh_frame_size_ = 0;
-
-  uint64_t debug_frame_offset_ = 0;
-  uint64_t debug_frame_size_ = 0;
-
-  uint64_t gnu_debugdata_offset_ = 0;
-  uint64_t gnu_debugdata_size_ = 0;
-
-  uint8_t soname_type_ = SONAME_UNKNOWN;
-  std::string soname_;
-
-  std::unique_ptr<DwarfSection> eh_frame_;
-  std::unique_ptr<DwarfSection> debug_frame_;
-
-  std::vector<Symbols*> symbols_;
-};
-
-class ElfInterface32 : public ElfInterface {
- public:
-  ElfInterface32(Memory* memory) : ElfInterface(memory) {}
-  virtual ~ElfInterface32() = default;
-
-  bool Init() override {
-    return ElfInterface::ReadAllHeaders<Elf32_Ehdr, Elf32_Phdr, Elf32_Shdr>();
-  }
-
-  void InitHeaders() override { ElfInterface::InitHeadersWithTemplate<uint32_t>(); }
-
-  bool GetSoname(std::string* soname) override {
-    return ElfInterface::GetSonameWithTemplate<Elf32_Dyn>(soname);
-  }
-
-  bool GetFunctionName(uint64_t addr, std::string* name, uint64_t* func_offset) override {
-    return ElfInterface::GetFunctionNameWithTemplate<Elf32_Sym>(addr, name, func_offset);
-  }
-};
-
-class ElfInterface64 : public ElfInterface {
- public:
-  ElfInterface64(Memory* memory) : ElfInterface(memory) {}
-  virtual ~ElfInterface64() = default;
-
-  bool Init() override {
-    return ElfInterface::ReadAllHeaders<Elf64_Ehdr, Elf64_Phdr, Elf64_Shdr>();
-  }
-
-  void InitHeaders() override { ElfInterface::InitHeadersWithTemplate<uint64_t>(); }
-
-  bool GetSoname(std::string* soname) override {
-    return ElfInterface::GetSonameWithTemplate<Elf64_Dyn>(soname);
-  }
-
-  bool GetFunctionName(uint64_t addr, std::string* name, uint64_t* func_offset) override {
-    return ElfInterface::GetFunctionNameWithTemplate<Elf64_Sym>(addr, name, func_offset);
-  }
-};
-
-#endif  // _LIBUNWINDSTACK_ELF_INTERFACE_H
diff --git a/libunwindstack/ElfInterfaceArm.cpp b/libunwindstack/ElfInterfaceArm.cpp
index bab84cc..3dd5d54 100644
--- a/libunwindstack/ElfInterfaceArm.cpp
+++ b/libunwindstack/ElfInterfaceArm.cpp
@@ -17,24 +17,29 @@
 #include <elf.h>
 #include <stdint.h>
 
+#include <unwindstack/MachineArm.h>
+#include <unwindstack/Memory.h>
+#include <unwindstack/RegsArm.h>
+
 #include "ArmExidx.h"
-#include "ElfInterface.h"
 #include "ElfInterfaceArm.h"
-#include "Machine.h"
-#include "Memory.h"
-#include "Regs.h"
+
+namespace unwindstack {
+
+bool ElfInterfaceArm::Init(uint64_t* load_bias) {
+  if (!ElfInterface32::Init(load_bias)) {
+    return false;
+  }
+  load_bias_ = *load_bias;
+  return true;
+}
 
 bool ElfInterfaceArm::FindEntry(uint32_t pc, uint64_t* entry_offset) {
   if (start_offset_ == 0 || total_entries_ == 0) {
+    last_error_.code = ERROR_UNWIND_INFO;
     return false;
   }
 
-  // Need to subtract the load_bias from the pc.
-  if (pc < load_bias_) {
-    return false;
-  }
-  pc -= load_bias_;
-
   size_t first = 0;
   size_t last = total_entries_;
   while (first < last) {
@@ -60,12 +65,15 @@
     *entry_offset = start_offset_ + (last - 1) * 8;
     return true;
   }
+  last_error_.code = ERROR_UNWIND_INFO;
   return false;
 }
 
 bool ElfInterfaceArm::GetPrel31Addr(uint32_t offset, uint32_t* addr) {
   uint32_t data;
   if (!memory_->Read32(offset, &data)) {
+    last_error_.code = ERROR_MEMORY_INVALID;
+    last_error_.address = offset;
     return false;
   }
 
@@ -79,51 +87,98 @@
 #define PT_ARM_EXIDX 0x70000001
 #endif
 
-bool ElfInterfaceArm::HandleType(uint64_t offset, uint32_t type) {
+void ElfInterfaceArm::HandleUnknownType(uint32_t type, uint64_t ph_offset, uint64_t ph_filesz) {
   if (type != PT_ARM_EXIDX) {
-    return false;
+    return;
   }
 
-  Elf32_Phdr phdr;
-  if (!memory_->ReadField(offset, &phdr, &phdr.p_vaddr, sizeof(phdr.p_vaddr))) {
-    return true;
-  }
-  if (!memory_->ReadField(offset, &phdr, &phdr.p_memsz, sizeof(phdr.p_memsz))) {
-    return true;
-  }
-  // The load_bias_ should always be set by this time.
-  start_offset_ = phdr.p_vaddr - load_bias_;
-  total_entries_ = phdr.p_memsz / 8;
-  return true;
+  // The offset already takes into account the load bias.
+  start_offset_ = ph_offset;
+
+  // Always use filesz instead of memsz. In most cases they are the same,
+  // but some shared libraries wind up setting one correctly and not the other.
+  total_entries_ = ph_filesz / 8;
 }
 
-bool ElfInterfaceArm::Step(uint64_t pc, Regs* regs, Memory* process_memory) {
+bool ElfInterfaceArm::Step(uint64_t pc, Regs* regs, Memory* process_memory, bool* finished) {
   // Dwarf unwind information is precise about whether a pc is covered or not,
   // but arm unwind information only has ranges of pc. In order to avoid
   // incorrectly doing a bad unwind using arm unwind information for a
   // different function, always try and unwind with the dwarf information first.
-  return ElfInterface32::Step(pc, regs, process_memory) || StepExidx(pc, regs, process_memory);
+  return ElfInterface32::Step(pc, regs, process_memory, finished) ||
+         StepExidx(pc, regs, process_memory, finished);
 }
 
-bool ElfInterfaceArm::StepExidx(uint64_t pc, Regs* regs, Memory* process_memory) {
+bool ElfInterfaceArm::StepExidx(uint64_t pc, Regs* regs, Memory* process_memory, bool* finished) {
+  // Adjust the load bias to get the real relative pc.
+  if (pc < load_bias_) {
+    last_error_.code = ERROR_UNWIND_INFO;
+    return false;
+  }
+  pc -= load_bias_;
+
   RegsArm* regs_arm = reinterpret_cast<RegsArm*>(regs);
   uint64_t entry_offset;
   if (!FindEntry(pc, &entry_offset)) {
     return false;
   }
+
   ArmExidx arm(regs_arm, memory_, process_memory);
   arm.set_cfa(regs_arm->sp());
+  bool return_value = false;
   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.
+    *finished = (regs_arm->pc() == 0) ? true : false;
+  }
+
+  if (arm.status() == ARM_STATUS_NO_UNWIND) {
+    *finished = true;
+    return true;
+  }
+
+  if (!return_value) {
+    switch (arm.status()) {
+      case ARM_STATUS_NONE:
+      case ARM_STATUS_NO_UNWIND:
+      case ARM_STATUS_FINISH:
+        last_error_.code = ERROR_NONE;
+        break;
+
+      case ARM_STATUS_RESERVED:
+      case ARM_STATUS_SPARE:
+      case ARM_STATUS_TRUNCATED:
+      case ARM_STATUS_MALFORMED:
+      case ARM_STATUS_INVALID_ALIGNMENT:
+      case ARM_STATUS_INVALID_PERSONALITY:
+        last_error_.code = ERROR_UNWIND_INFO;
+        break;
+
+      case ARM_STATUS_READ_FAILED:
+        last_error_.code = ERROR_MEMORY_INVALID;
+        last_error_.address = arm.status_address();
+        break;
+    }
+  }
+  return return_value;
+}
+
+bool ElfInterfaceArm::GetFunctionName(uint64_t addr, 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, name, offset)) {
+    *offset &= ~1;
     return true;
   }
   return false;
 }
+
+}  // namespace unwindstack
diff --git a/libunwindstack/ElfInterfaceArm.h b/libunwindstack/ElfInterfaceArm.h
index ece694f..4c3a0c3 100644
--- a/libunwindstack/ElfInterfaceArm.h
+++ b/libunwindstack/ElfInterfaceArm.h
@@ -23,8 +23,10 @@
 #include <iterator>
 #include <unordered_map>
 
-#include "ElfInterface.h"
-#include "Memory.h"
+#include <unwindstack/ElfInterface.h>
+#include <unwindstack/Memory.h>
+
+namespace unwindstack {
 
 class ElfInterfaceArm : public ElfInterface32 {
  public:
@@ -62,29 +64,34 @@
   iterator begin() { return iterator(this, 0); }
   iterator end() { return iterator(this, total_entries_); }
 
+  bool Init(uint64_t* load_bias) override;
+
   bool GetPrel31Addr(uint32_t offset, uint32_t* addr);
 
   bool FindEntry(uint32_t pc, uint64_t* entry_offset);
 
-  bool HandleType(uint64_t offset, uint32_t type) override;
+  void HandleUnknownType(uint32_t type, uint64_t ph_offset, uint64_t ph_filesz) override;
 
-  bool Step(uint64_t pc, Regs* regs, Memory* process_memory) override;
+  bool Step(uint64_t pc, Regs* regs, Memory* process_memory, bool* finished) override;
 
-  bool StepExidx(uint64_t pc, Regs* regs, Memory* process_memory);
+  bool StepExidx(uint64_t pc, Regs* regs, Memory* process_memory, bool* finished);
+
+  bool GetFunctionName(uint64_t addr, std::string* name, uint64_t* offset) override;
 
   uint64_t start_offset() { return start_offset_; }
 
-  void set_start_offset(uint64_t start_offset) { start_offset_ = start_offset; }
-
   size_t total_entries() { return total_entries_; }
 
-  void set_total_entries(size_t total_entries) { total_entries_ = total_entries; }
+  void set_load_bias(uint64_t load_bias) { load_bias_ = load_bias; }
 
- private:
+ protected:
   uint64_t start_offset_ = 0;
   size_t total_entries_ = 0;
+  uint64_t load_bias_ = 0;
 
   std::unordered_map<size_t, uint32_t> addrs_;
 };
 
+}  // namespace unwindstack
+
 #endif  // _LIBUNWINDSTACK_ELF_INTERFACE_ARM_H
diff --git a/libunwindstack/JitDebug.cpp b/libunwindstack/JitDebug.cpp
new file mode 100644
index 0000000..0c319ec
--- /dev/null
+++ b/libunwindstack/JitDebug.cpp
@@ -0,0 +1,246 @@
+/*
+ * Copyright (C) 2017 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 <stdint.h>
+#include <sys/mman.h>
+
+#include <memory>
+#include <vector>
+
+#include <unwindstack/Elf.h>
+#include <unwindstack/JitDebug.h>
+#include <unwindstack/Maps.h>
+#include <unwindstack/Memory.h>
+
+// This implements the JIT Compilation Interface.
+// See https://sourceware.org/gdb/onlinedocs/gdb/JIT-Interface.html
+
+namespace unwindstack {
+
+struct JITCodeEntry32Pack {
+  uint32_t next;
+  uint32_t prev;
+  uint32_t symfile_addr;
+  uint64_t symfile_size;
+} __attribute__((packed));
+
+struct JITCodeEntry32Pad {
+  uint32_t next;
+  uint32_t prev;
+  uint32_t symfile_addr;
+  uint32_t pad;
+  uint64_t symfile_size;
+};
+
+struct JITCodeEntry64 {
+  uint64_t next;
+  uint64_t prev;
+  uint64_t symfile_addr;
+  uint64_t symfile_size;
+};
+
+struct JITDescriptorHeader {
+  uint32_t version;
+  uint32_t action_flag;
+};
+
+struct JITDescriptor32 {
+  JITDescriptorHeader header;
+  uint32_t relevant_entry;
+  uint32_t first_entry;
+};
+
+struct JITDescriptor64 {
+  JITDescriptorHeader header;
+  uint64_t relevant_entry;
+  uint64_t first_entry;
+};
+
+JitDebug::JitDebug(std::shared_ptr<Memory>& memory) : memory_(memory) {}
+
+JitDebug::JitDebug(std::shared_ptr<Memory>& memory, std::vector<std::string>& search_libs)
+    : memory_(memory), search_libs_(search_libs) {}
+
+JitDebug::~JitDebug() {
+  for (auto* elf : elf_list_) {
+    delete elf;
+  }
+}
+
+uint64_t JitDebug::ReadDescriptor32(uint64_t addr) {
+  JITDescriptor32 desc;
+  if (!memory_->ReadFully(addr, &desc, sizeof(desc))) {
+    return 0;
+  }
+
+  if (desc.header.version != 1 || desc.first_entry == 0) {
+    // Either unknown version, or no jit entries.
+    return 0;
+  }
+
+  return desc.first_entry;
+}
+
+uint64_t JitDebug::ReadDescriptor64(uint64_t addr) {
+  JITDescriptor64 desc;
+  if (!memory_->ReadFully(addr, &desc, sizeof(desc))) {
+    return 0;
+  }
+
+  if (desc.header.version != 1 || desc.first_entry == 0) {
+    // Either unknown version, or no jit entries.
+    return 0;
+  }
+
+  return desc.first_entry;
+}
+
+uint64_t JitDebug::ReadEntry32Pack(uint64_t* start, uint64_t* size) {
+  JITCodeEntry32Pack code;
+  if (!memory_->ReadFully(entry_addr_, &code, sizeof(code))) {
+    return 0;
+  }
+
+  *start = code.symfile_addr;
+  *size = code.symfile_size;
+  return code.next;
+}
+
+uint64_t JitDebug::ReadEntry32Pad(uint64_t* start, uint64_t* size) {
+  JITCodeEntry32Pad code;
+  if (!memory_->ReadFully(entry_addr_, &code, sizeof(code))) {
+    return 0;
+  }
+
+  *start = code.symfile_addr;
+  *size = code.symfile_size;
+  return code.next;
+}
+
+uint64_t JitDebug::ReadEntry64(uint64_t* start, uint64_t* size) {
+  JITCodeEntry64 code;
+  if (!memory_->ReadFully(entry_addr_, &code, sizeof(code))) {
+    return 0;
+  }
+
+  *start = code.symfile_addr;
+  *size = code.symfile_size;
+  return code.next;
+}
+
+void JitDebug::SetArch(ArchEnum arch) {
+  switch (arch) {
+    case ARCH_X86:
+      read_descriptor_func_ = &JitDebug::ReadDescriptor32;
+      read_entry_func_ = &JitDebug::ReadEntry32Pack;
+      break;
+
+    case ARCH_ARM:
+    case ARCH_MIPS:
+      read_descriptor_func_ = &JitDebug::ReadDescriptor32;
+      read_entry_func_ = &JitDebug::ReadEntry32Pad;
+      break;
+
+    case ARCH_ARM64:
+    case ARCH_X86_64:
+    case ARCH_MIPS64:
+      read_descriptor_func_ = &JitDebug::ReadDescriptor64;
+      read_entry_func_ = &JitDebug::ReadEntry64;
+      break;
+    case ARCH_UNKNOWN:
+      abort();
+  }
+}
+
+void JitDebug::Init(Maps* maps) {
+  if (initialized_) {
+    return;
+  }
+  // Regardless of what happens below, consider the init finished.
+  initialized_ = true;
+
+  const std::string descriptor_name("__jit_debug_descriptor");
+  for (MapInfo* info : *maps) {
+    if (!(info->flags & PROT_EXEC) || !(info->flags & PROT_READ) || info->offset != 0) {
+      continue;
+    }
+
+    if (!search_libs_.empty()) {
+      bool found = false;
+      const char* lib = basename(info->name.c_str());
+      for (std::string& name : search_libs_) {
+        if (strcmp(name.c_str(), lib) == 0) {
+          found = true;
+          break;
+        }
+      }
+      if (!found) {
+        continue;
+      }
+    }
+
+    Elf* elf = info->GetElf(memory_, true);
+    uint64_t descriptor_addr;
+    if (elf->GetGlobalVariable(descriptor_name, &descriptor_addr)) {
+      // Search for the first non-zero entry.
+      descriptor_addr += info->start;
+      entry_addr_ = (this->*read_descriptor_func_)(descriptor_addr);
+      if (entry_addr_ != 0) {
+        break;
+      }
+    }
+  }
+}
+
+Elf* JitDebug::GetElf(Maps* maps, uint64_t pc) {
+  // Use a single lock, this object should be used so infrequently that
+  // a fine grain lock is unnecessary.
+  std::lock_guard<std::mutex> guard(lock_);
+  if (!initialized_) {
+    Init(maps);
+  }
+
+  // Search the existing elf object first.
+  for (Elf* elf : elf_list_) {
+    if (elf->IsValidPc(pc)) {
+      return elf;
+    }
+  }
+
+  while (entry_addr_ != 0) {
+    uint64_t start;
+    uint64_t size;
+    entry_addr_ = (this->*read_entry_func_)(&start, &size);
+
+    Elf* elf = new Elf(new MemoryRange(memory_, start, size, 0));
+    elf->Init(true);
+    if (!elf->valid()) {
+      // The data is not formatted in a way we understand, do not attempt
+      // to process any other entries.
+      entry_addr_ = 0;
+      delete elf;
+      return nullptr;
+    }
+    elf_list_.push_back(elf);
+
+    if (elf->IsValidPc(pc)) {
+      return elf;
+    }
+  }
+  return nullptr;
+}
+
+}  // namespace unwindstack
diff --git a/libunwindstack/LocalUnwinder.cpp b/libunwindstack/LocalUnwinder.cpp
new file mode 100644
index 0000000..952b332
--- /dev/null
+++ b/libunwindstack/LocalUnwinder.cpp
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <pthread.h>
+#include <stdint.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <unwindstack/Elf.h>
+#include <unwindstack/LocalUnwinder.h>
+#include <unwindstack/MapInfo.h>
+#include <unwindstack/Maps.h>
+#include <unwindstack/Memory.h>
+#include <unwindstack/Regs.h>
+#include <unwindstack/RegsGetLocal.h>
+
+namespace unwindstack {
+
+bool LocalUnwinder::Init() {
+  pthread_rwlock_init(&maps_rwlock_, nullptr);
+
+  // Create the maps.
+  maps_.reset(new unwindstack::LocalUpdatableMaps());
+  if (!maps_->Parse()) {
+    maps_.reset();
+    return false;
+  }
+
+  process_memory_ = unwindstack::Memory::CreateProcessMemory(getpid());
+
+  return true;
+}
+
+bool LocalUnwinder::ShouldSkipLibrary(const std::string& map_name) {
+  for (const std::string& skip_library : skip_libraries_) {
+    if (skip_library == map_name) {
+      return true;
+    }
+  }
+  return false;
+}
+
+MapInfo* LocalUnwinder::GetMapInfo(uint64_t pc) {
+  pthread_rwlock_rdlock(&maps_rwlock_);
+  MapInfo* map_info = maps_->Find(pc);
+  pthread_rwlock_unlock(&maps_rwlock_);
+
+  if (map_info == nullptr) {
+    pthread_rwlock_wrlock(&maps_rwlock_);
+    // This is guaranteed not to invalidate any previous MapInfo objects so
+    // we don't need to worry about any MapInfo* values already in use.
+    if (maps_->Reparse()) {
+      map_info = maps_->Find(pc);
+    }
+    pthread_rwlock_unlock(&maps_rwlock_);
+  }
+
+  return map_info;
+}
+
+bool LocalUnwinder::Unwind(std::vector<LocalFrameData>* frame_info, size_t max_frames) {
+  std::unique_ptr<unwindstack::Regs> regs(unwindstack::Regs::CreateFromLocal());
+  unwindstack::RegsGetLocal(regs.get());
+
+  size_t num_frames = 0;
+  bool adjust_pc = false;
+  while (true) {
+    uint64_t cur_pc = regs->pc();
+    uint64_t cur_sp = regs->sp();
+
+    MapInfo* map_info = GetMapInfo(cur_pc);
+    if (map_info == nullptr) {
+      break;
+    }
+
+    Elf* elf = map_info->GetElf(process_memory_, true);
+    uint64_t rel_pc = elf->GetRelPc(cur_pc, map_info);
+    uint64_t step_pc = rel_pc;
+    uint64_t pc_adjustment;
+    if (adjust_pc) {
+      pc_adjustment = regs->GetPcAdjustment(rel_pc, elf);
+    } else {
+      pc_adjustment = 0;
+    }
+    step_pc -= pc_adjustment;
+    // Skip any locations that are within this library.
+    if (num_frames != 0 || !ShouldSkipLibrary(map_info->name)) {
+      // Add frame information.
+      std::string func_name;
+      uint64_t func_offset;
+      if (elf->GetFunctionName(rel_pc, &func_name, &func_offset)) {
+        frame_info->emplace_back(map_info, cur_pc - pc_adjustment, rel_pc - pc_adjustment,
+                                 func_name, func_offset);
+      } else {
+        frame_info->emplace_back(map_info, cur_pc - pc_adjustment, rel_pc - pc_adjustment, "", 0);
+      }
+      num_frames++;
+    }
+    if (!elf->valid()) {
+      break;
+    }
+    if (frame_info->size() == max_frames) {
+      break;
+    }
+
+    adjust_pc = true;
+    bool finished;
+    if (!elf->Step(rel_pc, step_pc, regs.get(), process_memory_.get(), &finished) || finished) {
+      break;
+    }
+    // pc and sp are the same, terminate the unwind.
+    if (cur_pc == regs->pc() && cur_sp == regs->sp()) {
+      break;
+    }
+  }
+  return num_frames != 0;
+}
+
+}  // namespace unwindstack
diff --git a/libunwindstack/Log.cpp b/libunwindstack/Log.cpp
index faeb66c..436e23c 100644
--- a/libunwindstack/Log.cpp
+++ b/libunwindstack/Log.cpp
@@ -25,7 +25,9 @@
 
 #include <android-base/stringprintf.h>
 
-#include "Log.h"
+#include <unwindstack/Log.h>
+
+namespace unwindstack {
 
 static bool g_print_to_stdout = false;
 
@@ -51,3 +53,5 @@
   }
   va_end(args);
 }
+
+}  // namespace unwindstack
diff --git a/libunwindstack/Log.h b/libunwindstack/Log.h
deleted file mode 100644
index 2d01aa8..0000000
--- a/libunwindstack/Log.h
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * Copyright (C) 2016 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 _LIBUNWINDSTACK_LOG_H
-#define _LIBUNWINDSTACK_LOG_H
-
-#include <stdint.h>
-
-void log_to_stdout(bool enable);
-void log(uint8_t indent, const char* format, ...);
-
-#endif  // _LIBUNWINDSTACK_LOG_H
diff --git a/libunwindstack/Machine.h b/libunwindstack/Machine.h
deleted file mode 100644
index 323ce80..0000000
--- a/libunwindstack/Machine.h
+++ /dev/null
@@ -1,133 +0,0 @@
-/*
- * Copyright (C) 2016 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 _LIBUNWINDSTACK_MACHINE_H
-#define _LIBUNWINDSTACK_MACHINE_H
-
-#include <stdint.h>
-
-enum ArmReg : uint16_t {
-  ARM_REG_R0 = 0,
-  ARM_REG_R1,
-  ARM_REG_R2,
-  ARM_REG_R3,
-  ARM_REG_R4,
-  ARM_REG_R5,
-  ARM_REG_R6,
-  ARM_REG_R7,
-  ARM_REG_R8,
-  ARM_REG_R9,
-  ARM_REG_R10,
-  ARM_REG_R11,
-  ARM_REG_R12,
-  ARM_REG_R13,
-  ARM_REG_R14,
-  ARM_REG_R15,
-  ARM_REG_LAST,
-
-  ARM_REG_SP = ARM_REG_R13,
-  ARM_REG_LR = ARM_REG_R14,
-  ARM_REG_PC = ARM_REG_R15,
-};
-
-enum Arm64Reg : uint16_t {
-  ARM64_REG_R0 = 0,
-  ARM64_REG_R1,
-  ARM64_REG_R2,
-  ARM64_REG_R3,
-  ARM64_REG_R4,
-  ARM64_REG_R5,
-  ARM64_REG_R6,
-  ARM64_REG_R7,
-  ARM64_REG_R8,
-  ARM64_REG_R9,
-  ARM64_REG_R10,
-  ARM64_REG_R11,
-  ARM64_REG_R12,
-  ARM64_REG_R13,
-  ARM64_REG_R14,
-  ARM64_REG_R15,
-  ARM64_REG_R16,
-  ARM64_REG_R17,
-  ARM64_REG_R18,
-  ARM64_REG_R19,
-  ARM64_REG_R20,
-  ARM64_REG_R21,
-  ARM64_REG_R22,
-  ARM64_REG_R23,
-  ARM64_REG_R24,
-  ARM64_REG_R25,
-  ARM64_REG_R26,
-  ARM64_REG_R27,
-  ARM64_REG_R28,
-  ARM64_REG_R29,
-  ARM64_REG_R30,
-  ARM64_REG_R31,
-  ARM64_REG_PC,
-  ARM64_REG_LAST,
-
-  ARM64_REG_SP = ARM64_REG_R31,
-  ARM64_REG_LR = ARM64_REG_R30,
-};
-
-enum X86Reg : uint16_t {
-  X86_REG_EAX = 0,
-  X86_REG_ECX,
-  X86_REG_EDX,
-  X86_REG_EBX,
-  X86_REG_ESP,
-  X86_REG_EBP,
-  X86_REG_ESI,
-  X86_REG_EDI,
-  X86_REG_EIP,
-  X86_REG_EFL,
-  X86_REG_CS,
-  X86_REG_SS,
-  X86_REG_DS,
-  X86_REG_ES,
-  X86_REG_FS,
-  X86_REG_GS,
-  X86_REG_LAST,
-
-  X86_REG_SP = X86_REG_ESP,
-  X86_REG_PC = X86_REG_EIP,
-};
-
-enum X86_64Reg : uint16_t {
-  X86_64_REG_RAX = 0,
-  X86_64_REG_RDX,
-  X86_64_REG_RCX,
-  X86_64_REG_RBX,
-  X86_64_REG_RSI,
-  X86_64_REG_RDI,
-  X86_64_REG_RBP,
-  X86_64_REG_RSP,
-  X86_64_REG_R8,
-  X86_64_REG_R9,
-  X86_64_REG_R10,
-  X86_64_REG_R11,
-  X86_64_REG_R12,
-  X86_64_REG_R13,
-  X86_64_REG_R14,
-  X86_64_REG_R15,
-  X86_64_REG_RIP,
-  X86_64_REG_LAST,
-
-  X86_64_REG_SP = X86_64_REG_RSP,
-  X86_64_REG_PC = X86_64_REG_RIP,
-};
-
-#endif  // _LIBUNWINDSTACK_MACHINE_H
diff --git a/libunwindstack/MapInfo.cpp b/libunwindstack/MapInfo.cpp
index d7b483d..39378a3 100644
--- a/libunwindstack/MapInfo.cpp
+++ b/libunwindstack/MapInfo.cpp
@@ -14,75 +14,162 @@
  * limitations under the License.
  */
 
+#include <sys/mman.h>
 #include <sys/types.h>
 #include <unistd.h>
 
 #include <memory>
+#include <mutex>
 #include <string>
 
-#include "Elf.h"
-#include "MapInfo.h"
-#include "Maps.h"
-#include "Memory.h"
+#include <unwindstack/Elf.h>
+#include <unwindstack/MapInfo.h>
+#include <unwindstack/Maps.h>
+#include <unwindstack/Memory.h>
 
-Memory* MapInfo::CreateMemory(pid_t pid) {
+namespace unwindstack {
+
+Memory* MapInfo::GetFileMemory() {
+  std::unique_ptr<MemoryFileAtOffset> memory(new MemoryFileAtOffset);
+  if (offset == 0) {
+    if (memory->Init(name, 0)) {
+      return memory.release();
+    }
+    return nullptr;
+  }
+
+  // There are two possibilities when the offset is non-zero.
+  // - There is an elf file embedded in a file.
+  // - The whole file is an elf file, and the offset needs to be saved.
+  //
+  // Map in just the part of the file for the map. If this is not
+  // a valid elf, then reinit as if the whole file is an elf file.
+  // If the offset is a valid elf, then determine the size of the map
+  // and reinit to that size. This is needed because the dynamic linker
+  // only maps in a portion of the original elf, and never the symbol
+  // file data.
+  uint64_t map_size = end - start;
+  if (!memory->Init(name, offset, map_size)) {
+    return nullptr;
+  }
+
+  bool valid;
+  uint64_t max_size;
+  Elf::GetInfo(memory.get(), &valid, &max_size);
+  if (!valid) {
+    // Init as if the whole file is an elf.
+    if (memory->Init(name, 0)) {
+      elf_offset = offset;
+      return memory.release();
+    }
+    return nullptr;
+  }
+
+  if (max_size > map_size) {
+    if (memory->Init(name, offset, max_size)) {
+      return memory.release();
+    }
+    // Try to reinit using the default map_size.
+    if (memory->Init(name, offset, map_size)) {
+      return memory.release();
+    }
+    return nullptr;
+  }
+  return memory.release();
+}
+
+Memory* MapInfo::CreateMemory(const std::shared_ptr<Memory>& process_memory) {
   if (end <= start) {
     return nullptr;
   }
 
   elf_offset = 0;
 
+  // Fail on device maps.
+  if (flags & MAPS_FLAGS_DEVICE_MAP) {
+    return nullptr;
+  }
+
   // First try and use the file associated with the info.
   if (!name.empty()) {
-    // Fail on device maps.
-    if (flags & MAPS_FLAGS_DEVICE_MAP) {
-      return nullptr;
+    Memory* memory = GetFileMemory();
+    if (memory != nullptr) {
+      return memory;
     }
+  }
 
-    std::unique_ptr<MemoryFileAtOffset> file_memory(new MemoryFileAtOffset);
-    uint64_t map_size;
-    if (offset != 0) {
-      // Only map in a piece of the file.
-      map_size = end - start;
-    } else {
-      map_size = UINT64_MAX;
+  // If the map isn't readable, don't bother trying to read from process memory.
+  if (!(flags & PROT_READ)) {
+    return nullptr;
+  }
+  return new MemoryRange(process_memory, start, end - start, 0);
+}
+
+Elf* MapInfo::GetElf(const std::shared_ptr<Memory>& process_memory, bool init_gnu_debugdata) {
+  // Make sure no other thread is trying to add the elf to this map.
+  std::lock_guard<std::mutex> guard(mutex_);
+
+  if (elf.get() != nullptr) {
+    return elf.get();
+  }
+
+  bool locked = false;
+  if (Elf::CachingEnabled() && !name.empty()) {
+    Elf::CacheLock();
+    locked = true;
+    if (Elf::CacheGet(this)) {
+      Elf::CacheUnlock();
+      return elf.get();
     }
-    if (file_memory->Init(name, offset, map_size)) {
-      // It's possible that a non-zero offset might not be pointing to
-      // valid elf data. Check if this is a valid elf, and if not assume
-      // that this was meant to incorporate the entire file.
-      if (offset != 0 && !Elf::IsValidElf(file_memory.get())) {
-        // Don't bother checking the validity that will happen on the elf init.
-        if (file_memory->Init(name, 0)) {
-          elf_offset = offset;
-          return file_memory.release();
-        }
-        // Fall through if the init fails.
+  }
+
+  Memory* memory = CreateMemory(process_memory);
+  if (locked) {
+    if (Elf::CacheAfterCreateMemory(this)) {
+      delete memory;
+      Elf::CacheUnlock();
+      return elf.get();
+    }
+  }
+  elf.reset(new Elf(memory));
+  // If the init fails, keep the elf around as an invalid object so we
+  // don't try to reinit the object.
+  elf->Init(init_gnu_debugdata);
+
+  if (locked) {
+    Elf::CacheAdd(this);
+    Elf::CacheUnlock();
+  }
+  return elf.get();
+}
+
+uint64_t MapInfo::GetLoadBias(const std::shared_ptr<Memory>& process_memory) {
+  uint64_t cur_load_bias = load_bias.load();
+  if (cur_load_bias != static_cast<uint64_t>(-1)) {
+    return cur_load_bias;
+  }
+
+  {
+    // Make sure no other thread is trying to add the elf to this map.
+    std::lock_guard<std::mutex> guard(mutex_);
+    if (elf != nullptr) {
+      if (elf->valid()) {
+        cur_load_bias = elf->GetLoadBias();
+        load_bias = cur_load_bias;
+        return cur_load_bias;
       } else {
-        return file_memory.release();
+        load_bias = 0;
+        return 0;
       }
     }
   }
 
-  Memory* memory = nullptr;
-  if (pid == getpid()) {
-    memory = new MemoryLocal();
-  } else {
-    memory = new MemoryRemote(pid);
-  }
-  return new MemoryRange(memory, start, end);
+  // Call lightweight static function that will only read enough of the
+  // elf data to get the load bias.
+  std::unique_ptr<Memory> memory(CreateMemory(process_memory));
+  cur_load_bias = Elf::GetLoadBias(memory.get());
+  load_bias = cur_load_bias;
+  return cur_load_bias;
 }
 
-Elf* MapInfo::GetElf(pid_t pid, bool init_gnu_debugdata) {
-  if (elf) {
-    return elf;
-  }
-
-  elf = new Elf(CreateMemory(pid));
-  if (elf->Init() && init_gnu_debugdata) {
-    elf->InitGnuDebugdata();
-  }
-  // If the init fails, keep the elf around as an invalid object so we
-  // don't try to reinit the object.
-  return elf;
-}
+}  // namespace unwindstack
diff --git a/libunwindstack/MapInfo.h b/libunwindstack/MapInfo.h
deleted file mode 100644
index 79a2ada..0000000
--- a/libunwindstack/MapInfo.h
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright (C) 2016 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 _LIBUNWINDSTACK_MAP_INFO_H
-#define _LIBUNWINDSTACK_MAP_INFO_H
-
-#include <stdint.h>
-
-#include <string>
-
-// Forward declarations.
-class Elf;
-class Memory;
-
-struct MapInfo {
-  uint64_t start;
-  uint64_t end;
-  uint64_t offset;
-  uint16_t flags;
-  std::string name;
-  Elf* elf = nullptr;
-  // This value is only non-zero if the offset is non-zero but there is
-  // no elf signature found at that offset. This indicates that the
-  // entire file is represented by the Memory object returned by CreateMemory,
-  // instead of a portion of the file.
-  uint64_t elf_offset;
-
-  Memory* CreateMemory(pid_t pid);
-  Elf* GetElf(pid_t pid, bool init_gnu_debugdata = false);
-};
-
-#endif  // _LIBUNWINDSTACK_MAP_INFO_H
diff --git a/libunwindstack/Maps.cpp b/libunwindstack/Maps.cpp
index b869918..e676a5a 100644
--- a/libunwindstack/Maps.cpp
+++ b/libunwindstack/Maps.cpp
@@ -24,12 +24,19 @@
 #include <unistd.h>
 
 #include <android-base/unique_fd.h>
+#include <procinfo/process_map.h>
 
+#include <algorithm>
+#include <cctype>
 #include <memory>
 #include <string>
 #include <vector>
 
-#include "Maps.h"
+#include <unwindstack/Elf.h>
+#include <unwindstack/Maps.h>
+#include <unwindstack/Memory.h>
+
+namespace unwindstack {
 
 MapInfo* Maps::Find(uint64_t pc) {
   if (maps_.empty()) {
@@ -39,7 +46,7 @@
   size_t last = maps_.size();
   while (first < last) {
     size_t index = (first + last) / 2;
-    MapInfo* cur = &maps_[index];
+    MapInfo* cur = maps_[index];
     if (pc >= cur->start && pc < cur->end) {
       return cur;
     } else if (pc < cur->start) {
@@ -51,148 +58,130 @@
   return nullptr;
 }
 
-bool Maps::ParseLine(const char* line, MapInfo* map_info) {
-  char permissions[5];
-  int name_pos;
-  // Linux /proc/<pid>/maps lines:
-  // 6f000000-6f01e000 rwxp 00000000 00:0c 16389419   /system/lib/libcomposer.so
-  if (sscanf(line, "%" SCNx64 "-%" SCNx64 " %4s %" SCNx64 " %*x:%*x %*d %n", &map_info->start,
-             &map_info->end, permissions, &map_info->offset, &name_pos) != 4) {
-    return false;
-  }
-  map_info->flags = PROT_NONE;
-  if (permissions[0] == 'r') {
-    map_info->flags |= PROT_READ;
-  }
-  if (permissions[1] == 'w') {
-    map_info->flags |= PROT_WRITE;
-  }
-  if (permissions[2] == 'x') {
-    map_info->flags |= PROT_EXEC;
-  }
-
-  if (line[name_pos] != '\0') {
-    map_info->name = &line[name_pos];
-    size_t length = map_info->name.length() - 1;
-    if (map_info->name[length] == '\n') {
-      map_info->name.erase(length);
-    }
-
-    // Mark a device map in /dev/and not in /dev/ashmem/ specially.
-    if (map_info->name.substr(0, 5) == "/dev/" && map_info->name.substr(5, 7) != "ashmem/") {
-      map_info->flags |= MAPS_FLAGS_DEVICE_MAP;
-    }
-  }
-
-  return true;
+bool Maps::Parse() {
+  return android::procinfo::ReadMapFile(
+      GetMapsFile(),
+      [&](uint64_t start, uint64_t end, uint16_t flags, uint64_t pgoff, const char* name) {
+        // Mark a device map in /dev/ and not in /dev/ashmem/ specially.
+        if (strncmp(name, "/dev/", 5) == 0 && strncmp(name + 5, "ashmem/", 7) != 0) {
+          flags |= unwindstack::MAPS_FLAGS_DEVICE_MAP;
+        }
+        maps_.push_back(new MapInfo(start, end, pgoff, flags, name));
+      });
 }
 
-bool Maps::Parse() {
-  std::unique_ptr<FILE, decltype(&fclose)> fp(fopen(GetMapsFile().c_str(), "re"), fclose);
-  if (!fp) {
-    return false;
-  }
+void Maps::Add(uint64_t start, uint64_t end, uint64_t offset, uint64_t flags,
+               const std::string& name, uint64_t load_bias) {
+  MapInfo* map_info = new MapInfo(start, end, offset, flags, name);
+  map_info->load_bias = load_bias;
+  maps_.push_back(map_info);
+}
 
-  bool valid = true;
-  char* line = nullptr;
-  size_t line_len;
-  while (getline(&line, &line_len, fp.get()) > 0) {
-    MapInfo map_info;
-    if (!ParseLine(line, &map_info)) {
-      valid = false;
-      break;
-    }
-
-    maps_.push_back(map_info);
-  }
-  free(line);
-
-  return valid;
+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.elf;
-    map.elf = nullptr;
+    delete map;
   }
 }
 
 bool BufferMaps::Parse() {
-  const char* start_of_line = buffer_;
-  do {
-    std::string line;
-    const char* end_of_line = strchr(start_of_line, '\n');
-    if (end_of_line == nullptr) {
-      line = start_of_line;
-    } else {
-      end_of_line++;
-      line = std::string(start_of_line, end_of_line - start_of_line);
-    }
-
-    MapInfo map_info;
-    if (!ParseLine(line.c_str(), &map_info)) {
-      return false;
-    }
-    maps_.push_back(map_info);
-
-    start_of_line = end_of_line;
-  } while (start_of_line != nullptr && *start_of_line != '\0');
-  return true;
+  std::string content(buffer_);
+  return android::procinfo::ReadMapFileContent(
+      &content[0],
+      [&](uint64_t start, uint64_t end, uint16_t flags, uint64_t pgoff, const char* name) {
+        // Mark a device map in /dev/ and not in /dev/ashmem/ specially.
+        if (strncmp(name, "/dev/", 5) == 0 && strncmp(name + 5, "ashmem/", 7) != 0) {
+          flags |= unwindstack::MAPS_FLAGS_DEVICE_MAP;
+        }
+        maps_.push_back(new MapInfo(start, end, pgoff, flags, name));
+      });
 }
 
 const std::string RemoteMaps::GetMapsFile() const {
   return "/proc/" + std::to_string(pid_) + "/maps";
 }
 
-bool OfflineMaps::Parse() {
-  // Format of maps information:
-  //   <uint64_t> StartOffset
-  //   <uint64_t> EndOffset
-  //   <uint64_t> offset
-  //   <uint16_t> flags
-  //   <uint16_t> MapNameLength
-  //   <VariableLengthValue> MapName
-  android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(file_.c_str(), O_RDONLY)));
-  if (fd == -1) {
+const std::string LocalUpdatableMaps::GetMapsFile() const {
+  return "/proc/self/maps";
+}
+
+bool LocalUpdatableMaps::Reparse() {
+  // New maps will be added at the end without deleting the old ones.
+  size_t last_map_idx = maps_.size();
+  if (!Parse()) {
+    // Delete any maps added by the Parse call.
+    for (size_t i = last_map_idx; i < maps_.size(); i++) {
+      delete maps_[i];
+    }
+    maps_.resize(last_map_idx);
     return false;
   }
 
-  std::vector<char> name;
-  while (true) {
-    MapInfo map_info;
-    ssize_t bytes = TEMP_FAILURE_RETRY(read(fd, &map_info.start, sizeof(map_info.start)));
-    if (bytes == 0) {
+  size_t total_entries = maps_.size();
+  size_t search_map_idx = 0;
+  for (size_t new_map_idx = last_map_idx; new_map_idx < maps_.size(); new_map_idx++) {
+    MapInfo* new_map_info = maps_[new_map_idx];
+    uint64_t start = new_map_info->start;
+    uint64_t end = new_map_info->end;
+    uint64_t flags = new_map_info->flags;
+    std::string* name = &new_map_info->name;
+    for (size_t old_map_idx = search_map_idx; old_map_idx < last_map_idx; old_map_idx++) {
+      MapInfo* info = maps_[old_map_idx];
+      if (start == info->start && end == info->end && flags == info->flags && *name == info->name) {
+        // No need to check
+        search_map_idx = old_map_idx + 1;
+        delete new_map_info;
+        maps_[new_map_idx] = nullptr;
+        total_entries--;
+        break;
+      } else if (info->start > start) {
+        // Stop, there isn't going to be a match.
+        search_map_idx = old_map_idx;
+        break;
+      }
+
+      // Never delete these maps, they may be in use. The assumption is
+      // that there will only every be a handfull of these so waiting
+      // to destroy them is not too expensive.
+      saved_maps_.push_back(info);
+      maps_[old_map_idx] = nullptr;
+      total_entries--;
+    }
+    if (search_map_idx >= last_map_idx) {
       break;
     }
-    if (bytes == -1 || bytes != sizeof(map_info.start)) {
-      return false;
-    }
-    bytes = TEMP_FAILURE_RETRY(read(fd, &map_info.end, sizeof(map_info.end)));
-    if (bytes == -1 || bytes != sizeof(map_info.end)) {
-      return false;
-    }
-    bytes = TEMP_FAILURE_RETRY(read(fd, &map_info.offset, sizeof(map_info.offset)));
-    if (bytes == -1 || bytes != sizeof(map_info.offset)) {
-      return false;
-    }
-    bytes = TEMP_FAILURE_RETRY(read(fd, &map_info.flags, sizeof(map_info.flags)));
-    if (bytes == -1 || bytes != sizeof(map_info.flags)) {
-      return false;
-    }
-    uint16_t len;
-    bytes = TEMP_FAILURE_RETRY(read(fd, &len, sizeof(len)));
-    if (bytes == -1 || bytes != sizeof(len)) {
-      return false;
-    }
-    if (len > 0) {
-      name.resize(len);
-      bytes = TEMP_FAILURE_RETRY(read(fd, name.data(), len));
-      if (bytes == -1 || bytes != len) {
-        return false;
-      }
-      map_info.name = std::string(name.data(), len);
-    }
-    maps_.push_back(map_info);
   }
+
+  // Now move out any of the maps that never were found.
+  for (size_t i = search_map_idx; i < last_map_idx; i++) {
+    saved_maps_.push_back(maps_[i]);
+    maps_[i] = nullptr;
+    total_entries--;
+  }
+
+  // Sort all of the values such that the nullptrs wind up at the end, then
+  // resize them away.
+  std::sort(maps_.begin(), maps_.end(), [](const auto* a, const auto* b) {
+    if (a == nullptr) {
+      return false;
+    } else if (b == nullptr) {
+      return true;
+    }
+    return a->start < b->start;
+  });
+  maps_.resize(total_entries);
+
   return true;
 }
+
+LocalUpdatableMaps::~LocalUpdatableMaps() {
+  for (auto map_info : saved_maps_) {
+    delete map_info;
+  }
+}
+
+}  // namespace unwindstack
diff --git a/libunwindstack/Maps.h b/libunwindstack/Maps.h
deleted file mode 100644
index 239b64a..0000000
--- a/libunwindstack/Maps.h
+++ /dev/null
@@ -1,107 +0,0 @@
-/*
- * Copyright (C) 2016 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 _LIBUNWINDSTACK_MAPS_H
-#define _LIBUNWINDSTACK_MAPS_H
-
-#include <sys/types.h>
-#include <unistd.h>
-
-#include <string>
-#include <vector>
-
-#include "Elf.h"
-#include "MapInfo.h"
-
-// 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;
-
-class Maps {
- public:
-  Maps() = default;
-  virtual ~Maps();
-
-  MapInfo* Find(uint64_t pc);
-
-  bool ParseLine(const char* line, MapInfo* map_info);
-
-  virtual bool Parse();
-
-  virtual const std::string GetMapsFile() const { return ""; }
-
-  typedef std::vector<MapInfo>::iterator iterator;
-  iterator begin() { return maps_.begin(); }
-  iterator end() { return maps_.end(); }
-
-  typedef std::vector<MapInfo>::const_iterator const_iterator;
-  const_iterator begin() const { return maps_.begin(); }
-  const_iterator end() const { return maps_.end(); }
-
-  size_t Total() { return maps_.size(); }
-
- protected:
-  std::vector<MapInfo> maps_;
-};
-
-class RemoteMaps : public Maps {
- public:
-  RemoteMaps(pid_t pid) : pid_(pid) {}
-  virtual ~RemoteMaps() = default;
-
-  virtual const std::string GetMapsFile() const override;
-
- private:
-  pid_t pid_;
-};
-
-class LocalMaps : public RemoteMaps {
- public:
-  LocalMaps() : RemoteMaps(getpid()) {}
-  virtual ~LocalMaps() = default;
-};
-
-class BufferMaps : public Maps {
- public:
-  BufferMaps(const char* buffer) : buffer_(buffer) {}
-  virtual ~BufferMaps() = default;
-
-  bool Parse() override;
-
- private:
-  const char* buffer_;
-};
-
-class FileMaps : public Maps {
- public:
-  FileMaps(const std::string& file) : file_(file) {}
-  virtual ~FileMaps() = default;
-
-  const std::string GetMapsFile() const override { return file_; }
-
- protected:
-  const std::string file_;
-};
-
-class OfflineMaps : public FileMaps {
- public:
-  OfflineMaps(const std::string& file) : FileMaps(file) {}
-  virtual ~OfflineMaps() = default;
-
-  bool Parse() override;
-};
-
-#endif  // _LIBUNWINDSTACK_MAPS_H
diff --git a/libunwindstack/Memory.cpp b/libunwindstack/Memory.cpp
index 9e46509..beb2aad 100644
--- a/libunwindstack/Memory.cpp
+++ b/libunwindstack/Memory.cpp
@@ -28,14 +28,133 @@
 
 #include <android-base/unique_fd.h>
 
-#include "Memory.h"
+#include <unwindstack/Memory.h>
+
+#include "Check.h"
+
+namespace unwindstack {
+
+static size_t ProcessVmRead(pid_t pid, uint64_t remote_src, void* dst, size_t len) {
+
+  // Split up the remote read across page boundaries.
+  // From the manpage:
+  //   A partial read/write may result if one of the remote_iov elements points to an invalid
+  //   memory region in the remote process.
+  //
+  //   Partial transfers apply at the granularity of iovec elements.  These system calls won't
+  //   perform a partial transfer that splits a single iovec element.
+  constexpr size_t kMaxIovecs = 64;
+  struct iovec src_iovs[kMaxIovecs];
+
+  uint64_t cur = remote_src;
+  size_t total_read = 0;
+  while (len > 0) {
+    struct iovec dst_iov = {
+        .iov_base = &reinterpret_cast<uint8_t*>(dst)[total_read], .iov_len = len,
+    };
+
+    size_t iovecs_used = 0;
+    while (len > 0) {
+      if (iovecs_used == kMaxIovecs) {
+        break;
+      }
+
+      // struct iovec uses void* for iov_base.
+      if (cur >= UINTPTR_MAX) {
+        errno = EFAULT;
+        return total_read;
+      }
+
+      src_iovs[iovecs_used].iov_base = reinterpret_cast<void*>(cur);
+
+      uintptr_t misalignment = cur & (getpagesize() - 1);
+      size_t iov_len = getpagesize() - misalignment;
+      iov_len = std::min(iov_len, len);
+
+      len -= iov_len;
+      if (__builtin_add_overflow(cur, iov_len, &cur)) {
+        errno = EFAULT;
+        return total_read;
+      }
+
+      src_iovs[iovecs_used].iov_len = iov_len;
+      ++iovecs_used;
+    }
+
+    ssize_t rc = process_vm_readv(pid, &dst_iov, 1, src_iovs, iovecs_used, 0);
+    if (rc == -1) {
+      return total_read;
+    }
+    total_read += rc;
+  }
+  return total_read;
+}
+
+static bool PtraceReadLong(pid_t pid, uint64_t addr, long* value) {
+  // ptrace() returns -1 and sets errno when the operation fails.
+  // To disambiguate -1 from a valid result, we clear errno beforehand.
+  errno = 0;
+  *value = ptrace(PTRACE_PEEKTEXT, pid, reinterpret_cast<void*>(addr), nullptr);
+  if (*value == -1 && errno) {
+    return false;
+  }
+  return true;
+}
+
+static size_t PtraceRead(pid_t pid, uint64_t addr, void* dst, size_t bytes) {
+  // Make sure that there is no overflow.
+  uint64_t max_size;
+  if (__builtin_add_overflow(addr, bytes, &max_size)) {
+    return 0;
+  }
+
+  size_t bytes_read = 0;
+  long data;
+  size_t align_bytes = addr & (sizeof(long) - 1);
+  if (align_bytes != 0) {
+    if (!PtraceReadLong(pid, addr & ~(sizeof(long) - 1), &data)) {
+      return 0;
+    }
+    size_t copy_bytes = std::min(sizeof(long) - align_bytes, bytes);
+    memcpy(dst, reinterpret_cast<uint8_t*>(&data) + align_bytes, copy_bytes);
+    addr += copy_bytes;
+    dst = reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(dst) + copy_bytes);
+    bytes -= copy_bytes;
+    bytes_read += copy_bytes;
+  }
+
+  for (size_t i = 0; i < bytes / sizeof(long); i++) {
+    if (!PtraceReadLong(pid, addr, &data)) {
+      return bytes_read;
+    }
+    memcpy(dst, &data, sizeof(long));
+    dst = reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(dst) + sizeof(long));
+    addr += sizeof(long);
+    bytes_read += sizeof(long);
+  }
+
+  size_t left_over = bytes & (sizeof(long) - 1);
+  if (left_over) {
+    if (!PtraceReadLong(pid, addr, &data)) {
+      return bytes_read;
+    }
+    memcpy(dst, &data, left_over);
+    bytes_read += left_over;
+  }
+  return bytes_read;
+}
+
+bool Memory::ReadFully(uint64_t addr, void* dst, size_t size) {
+  size_t rc = Read(addr, dst, size);
+  return rc == size;
+}
 
 bool Memory::ReadString(uint64_t addr, std::string* string, uint64_t max_read) {
   string->clear();
   uint64_t bytes_read = 0;
   while (bytes_read < max_read) {
     uint8_t value;
-    if (!Read(addr, &value, sizeof(value))) {
+    if (!ReadFully(addr, &value, sizeof(value))) {
       return false;
     }
     if (value == '\0') {
@@ -48,16 +167,24 @@
   return false;
 }
 
-bool MemoryBuffer::Read(uint64_t addr, void* dst, size_t size) {
-  uint64_t last_read_byte;
-  if (__builtin_add_overflow(size, addr, &last_read_byte)) {
-    return false;
+std::shared_ptr<Memory> Memory::CreateProcessMemory(pid_t pid) {
+  if (pid == getpid()) {
+    return std::shared_ptr<Memory>(new MemoryLocal());
   }
-  if (last_read_byte > raw_.size()) {
-    return false;
+  return std::shared_ptr<Memory>(new MemoryRemote(pid));
+}
+
+size_t MemoryBuffer::Read(uint64_t addr, void* dst, size_t size) {
+  if (addr >= raw_.size()) {
+    return 0;
   }
-  memcpy(dst, &raw_[addr], size);
-  return true;
+
+  size_t bytes_left = raw_.size() - static_cast<size_t>(addr);
+  const unsigned char* actual_base = static_cast<const unsigned char*>(raw_.data()) + addr;
+  size_t actual_len = std::min(bytes_left, size);
+
+  memcpy(dst, actual_base, actual_len);
+  return actual_len;
 }
 
 uint8_t* MemoryBuffer::GetPtr(size_t offset) {
@@ -118,138 +245,145 @@
   return true;
 }
 
-bool MemoryFileAtOffset::Read(uint64_t addr, void* dst, size_t size) {
-  uint64_t max_size;
-  if (__builtin_add_overflow(addr, size, &max_size) || max_size > size_) {
-    return false;
+size_t MemoryFileAtOffset::Read(uint64_t addr, void* dst, size_t size) {
+  if (addr >= size_) {
+    return 0;
   }
-  memcpy(dst, &data_[addr], size);
-  return true;
+
+  size_t bytes_left = size_ - static_cast<size_t>(addr);
+  const unsigned char* actual_base = static_cast<const unsigned char*>(data_) + addr;
+  size_t actual_len = std::min(bytes_left, size);
+
+  memcpy(dst, actual_base, actual_len);
+  return actual_len;
 }
 
-bool MemoryRemote::PtraceRead(uint64_t addr, long* value) {
+size_t MemoryRemote::Read(uint64_t addr, void* dst, size_t size) {
 #if !defined(__LP64__)
-  // Cannot read an address greater than 32 bits.
+  // Cannot read an address greater than 32 bits in a 32 bit context.
   if (addr > UINT32_MAX) {
-    return false;
+    return 0;
   }
 #endif
-  // ptrace() returns -1 and sets errno when the operation fails.
-  // To disambiguate -1 from a valid result, we clear errno beforehand.
-  errno = 0;
-  *value = ptrace(PTRACE_PEEKTEXT, pid_, reinterpret_cast<void*>(addr), nullptr);
-  if (*value == -1 && errno) {
-    return false;
+
+  size_t (*read_func)(pid_t, uint64_t, void*, size_t) =
+      reinterpret_cast<size_t (*)(pid_t, uint64_t, void*, size_t)>(read_redirect_func_.load());
+  if (read_func != nullptr) {
+    return read_func(pid_, addr, dst, size);
+  } else {
+    // Prefer process_vm_read, try it first. If it doesn't work, use the
+    // ptrace function. If at least one of them returns at least some data,
+    // set that as the permanent function to use.
+    // This assumes that if process_vm_read works once, it will continue
+    // to work.
+    size_t bytes = ProcessVmRead(pid_, addr, dst, size);
+    if (bytes > 0) {
+      read_redirect_func_ = reinterpret_cast<uintptr_t>(ProcessVmRead);
+      return bytes;
+    }
+    bytes = PtraceRead(pid_, addr, dst, size);
+    if (bytes > 0) {
+      read_redirect_func_ = reinterpret_cast<uintptr_t>(PtraceRead);
+    }
+    return bytes;
   }
-  return true;
 }
 
-bool MemoryRemote::Read(uint64_t addr, void* dst, size_t bytes) {
-  // Make sure that there is no overflow.
-  uint64_t max_size;
-  if (__builtin_add_overflow(addr, bytes, &max_size)) {
-    return false;
-  }
-
-  size_t bytes_read = 0;
-  long data;
-  size_t align_bytes = addr & (sizeof(long) - 1);
-  if (align_bytes != 0) {
-    if (!PtraceRead(addr & ~(sizeof(long) - 1), &data)) {
-      return false;
-    }
-    size_t copy_bytes = std::min(sizeof(long) - align_bytes, bytes);
-    memcpy(dst, reinterpret_cast<uint8_t*>(&data) + align_bytes, copy_bytes);
-    addr += copy_bytes;
-    dst = reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(dst) + copy_bytes);
-    bytes -= copy_bytes;
-    bytes_read += copy_bytes;
-  }
-
-  for (size_t i = 0; i < bytes / sizeof(long); i++) {
-    if (!PtraceRead(addr, &data)) {
-      return false;
-    }
-    memcpy(dst, &data, sizeof(long));
-    dst = reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(dst) + sizeof(long));
-    addr += sizeof(long);
-    bytes_read += sizeof(long);
-  }
-
-  size_t left_over = bytes & (sizeof(long) - 1);
-  if (left_over) {
-    if (!PtraceRead(addr, &data)) {
-      return false;
-    }
-    memcpy(dst, &data, left_over);
-    bytes_read += left_over;
-  }
-  return true;
+size_t MemoryLocal::Read(uint64_t addr, void* dst, size_t size) {
+  return ProcessVmRead(getpid(), addr, dst, size);
 }
 
-bool MemoryLocal::Read(uint64_t addr, void* dst, size_t size) {
-  // Make sure that there is no overflow.
-  uint64_t max_size;
-  if (__builtin_add_overflow(addr, size, &max_size)) {
-    return false;
+MemoryRange::MemoryRange(const std::shared_ptr<Memory>& memory, uint64_t begin, uint64_t length,
+                         uint64_t offset)
+    : memory_(memory), begin_(begin), length_(length), offset_(offset) {}
+
+size_t MemoryRange::Read(uint64_t addr, void* dst, size_t size) {
+  if (addr < offset_) {
+    return 0;
   }
 
-  // The process_vm_readv call will not always work on remote
-  // processes, so only use it for reads from the current pid.
-  // Use this method to avoid crashes if an address is invalid since
-  // unwind data could try to access any part of the address space.
-  struct iovec local_io;
-  local_io.iov_base = dst;
-  local_io.iov_len = size;
-
-  struct iovec remote_io;
-  remote_io.iov_base = reinterpret_cast<void*>(static_cast<uintptr_t>(addr));
-  remote_io.iov_len = size;
-
-  ssize_t bytes_read = process_vm_readv(getpid(), &local_io, 1, &remote_io, 1, 0);
-  if (bytes_read == -1) {
-    return false;
+  uint64_t read_offset = addr - offset_;
+  if (read_offset >= length_) {
+    return 0;
   }
-  return static_cast<size_t>(bytes_read) == size;
+
+  uint64_t read_length = std::min(static_cast<uint64_t>(size), length_ - read_offset);
+  uint64_t read_addr;
+  if (__builtin_add_overflow(read_offset, begin_, &read_addr)) {
+    return 0;
+  }
+
+  return memory_->Read(read_addr, dst, read_length);
 }
 
 bool MemoryOffline::Init(const std::string& file, uint64_t offset) {
-  if (!MemoryFileAtOffset::Init(file, offset)) {
+  auto memory_file = std::make_shared<MemoryFileAtOffset>();
+  if (!memory_file->Init(file, offset)) {
     return false;
   }
+
   // The first uint64_t value is the start of memory.
-  if (!MemoryFileAtOffset::Read(0, &start_, sizeof(start_))) {
+  uint64_t start;
+  if (!memory_file->ReadFully(0, &start, sizeof(start))) {
     return false;
   }
-  // Subtract the first 64 bit value from the total size.
-  size_ -= sizeof(start_);
+
+  uint64_t size = memory_file->Size();
+  if (__builtin_sub_overflow(size, sizeof(start), &size)) {
+    return false;
+  }
+
+  memory_ = std::make_unique<MemoryRange>(memory_file, sizeof(start), size, start);
   return true;
 }
 
-bool MemoryOffline::Read(uint64_t addr, void* dst, size_t size) {
-  uint64_t max_size;
-  if (__builtin_add_overflow(addr, size, &max_size)) {
-    return false;
+size_t MemoryOffline::Read(uint64_t addr, void* dst, size_t size) {
+  if (!memory_) {
+    return 0;
   }
 
-  uint64_t real_size;
-  if (__builtin_add_overflow(start_, offset_, &real_size) ||
-      __builtin_add_overflow(real_size, size_, &real_size)) {
-    return false;
-  }
-
-  if (addr < start_ || max_size > real_size) {
-    return false;
-  }
-  memcpy(dst, &data_[addr + offset_ - start_ + sizeof(start_)], size);
-  return true;
+  return memory_->Read(addr, dst, size);
 }
 
-bool MemoryRange::Read(uint64_t addr, void* dst, size_t size) {
-  uint64_t max_read;
-  if (__builtin_add_overflow(addr, size, &max_read) || max_read > length_) {
-    return false;
-  }
-  // The check above guarantees that addr + begin_ will not overflow.
-  return memory_->Read(addr + begin_, dst, size);
+MemoryOfflineBuffer::MemoryOfflineBuffer(const uint8_t* data, uint64_t start, uint64_t end)
+    : data_(data), start_(start), end_(end) {}
+
+void MemoryOfflineBuffer::Reset(const uint8_t* data, uint64_t start, uint64_t end) {
+  data_ = data;
+  start_ = start;
+  end_ = end;
 }
+
+size_t MemoryOfflineBuffer::Read(uint64_t addr, void* dst, size_t size) {
+  if (addr < start_ || addr >= end_) {
+    return 0;
+  }
+
+  size_t read_length = std::min(size, static_cast<size_t>(end_ - addr));
+  memcpy(dst, &data_[addr - start_], read_length);
+  return read_length;
+}
+
+MemoryOfflineParts::~MemoryOfflineParts() {
+  for (auto memory : memories_) {
+    delete memory;
+  }
+}
+
+size_t MemoryOfflineParts::Read(uint64_t addr, void* dst, size_t size) {
+  if (memories_.empty()) {
+    return 0;
+  }
+
+  // Do a read on each memory object, no support for reading across the
+  // different memory objects.
+  for (MemoryOffline* memory : memories_) {
+    size_t bytes = memory->Read(addr, dst, size);
+    if (bytes != 0) {
+      return bytes;
+    }
+  }
+  return 0;
+}
+
+}  // namespace unwindstack
diff --git a/libunwindstack/Memory.h b/libunwindstack/Memory.h
deleted file mode 100644
index e353cab..0000000
--- a/libunwindstack/Memory.h
+++ /dev/null
@@ -1,146 +0,0 @@
-/*
- * Copyright (C) 2016 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 _LIBUNWINDSTACK_MEMORY_H
-#define _LIBUNWINDSTACK_MEMORY_H
-
-#include <stdint.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include <string>
-#include <vector>
-
-#include "Check.h"
-
-class Memory {
- public:
-  Memory() = default;
-  virtual ~Memory() = default;
-
-  virtual bool ReadString(uint64_t addr, std::string* string, uint64_t max_read = UINT64_MAX);
-
-  virtual bool Read(uint64_t addr, void* dst, size_t size) = 0;
-
-  inline bool ReadField(uint64_t addr, void* start, void* field, size_t size) {
-    if (reinterpret_cast<uintptr_t>(field) < reinterpret_cast<uintptr_t>(start)) {
-      return false;
-    }
-    uint64_t offset = reinterpret_cast<uintptr_t>(field) - reinterpret_cast<uintptr_t>(start);
-    if (__builtin_add_overflow(addr, offset, &offset)) {
-      return false;
-    }
-    // The read will check if offset + size overflows.
-    return Read(offset, field, size);
-  }
-
-  inline bool Read32(uint64_t addr, uint32_t* dst) {
-    return Read(addr, dst, sizeof(uint32_t));
-  }
-
-  inline bool Read64(uint64_t addr, uint64_t* dst) {
-    return Read(addr, dst, sizeof(uint64_t));
-  }
-};
-
-class MemoryBuffer : public Memory {
- public:
-  MemoryBuffer() = default;
-  virtual ~MemoryBuffer() = default;
-
-  bool Read(uint64_t addr, void* dst, size_t size) override;
-
-  uint8_t* GetPtr(size_t offset);
-
-  void Resize(size_t size) { raw_.resize(size); }
-
-  uint64_t Size() { return raw_.size(); }
-
- private:
-  std::vector<uint8_t> raw_;
-};
-
-class MemoryFileAtOffset : public Memory {
- public:
-  MemoryFileAtOffset() = default;
-  virtual ~MemoryFileAtOffset();
-
-  bool Init(const std::string& file, uint64_t offset, uint64_t size = UINT64_MAX);
-
-  bool Read(uint64_t addr, void* dst, size_t size) override;
-
-  void Clear();
-
- protected:
-  size_t size_ = 0;
-  size_t offset_ = 0;
-  uint8_t* data_ = nullptr;
-};
-
-class MemoryOffline : public MemoryFileAtOffset {
- public:
-  MemoryOffline() = default;
-  virtual ~MemoryOffline() = default;
-
-  bool Init(const std::string& file, uint64_t offset);
-
-  bool Read(uint64_t addr, void* dst, size_t size) override;
-
- private:
-  uint64_t start_;
-};
-
-class MemoryRemote : public Memory {
- public:
-  MemoryRemote(pid_t pid) : pid_(pid) {}
-  virtual ~MemoryRemote() = default;
-
-  bool Read(uint64_t addr, void* dst, size_t size) override;
-
-  pid_t pid() { return pid_; }
-
- protected:
-  virtual bool PtraceRead(uint64_t addr, long* value);
-
- private:
-  pid_t pid_;
-};
-
-class MemoryLocal : public Memory {
- public:
-  MemoryLocal() = default;
-  virtual ~MemoryLocal() = default;
-
-  bool Read(uint64_t addr, void* dst, size_t size) override;
-};
-
-class MemoryRange : public Memory {
- public:
-  MemoryRange(Memory* memory, uint64_t begin, uint64_t end)
-      : memory_(memory), begin_(begin), length_(end - begin) {
-    CHECK(end > begin);
-  }
-  virtual ~MemoryRange() { delete memory_; }
-
-  bool Read(uint64_t addr, void* dst, size_t size) override;
-
- private:
-  Memory* memory_;
-  uint64_t begin_;
-  uint64_t length_;
-};
-
-#endif  // _LIBUNWINDSTACK_MEMORY_H
diff --git a/libunwindstack/OWNERS b/libunwindstack/OWNERS
new file mode 100644
index 0000000..6f7e4a3
--- /dev/null
+++ b/libunwindstack/OWNERS
@@ -0,0 +1 @@
+cferris@google.com
diff --git a/libunwindstack/Regs.cpp b/libunwindstack/Regs.cpp
index da22b07..c7dec52 100644
--- a/libunwindstack/Regs.cpp
+++ b/libunwindstack/Regs.cpp
@@ -14,197 +14,36 @@
  * limitations under the License.
  */
 
-#include <elf.h>
 #include <stdint.h>
 #include <sys/ptrace.h>
 #include <sys/uio.h>
 
 #include <vector>
 
-#include "Check.h"
-#include "Elf.h"
-#include "ElfInterface.h"
-#include "Machine.h"
-#include "MapInfo.h"
-#include "Regs.h"
-#include "User.h"
+#include <unwindstack/Elf.h>
+#include <unwindstack/MapInfo.h>
+#include <unwindstack/Regs.h>
+#include <unwindstack/RegsArm.h>
+#include <unwindstack/RegsArm64.h>
+#include <unwindstack/RegsMips.h>
+#include <unwindstack/RegsMips64.h>
+#include <unwindstack/RegsX86.h>
+#include <unwindstack/RegsX86_64.h>
+#include <unwindstack/UserArm.h>
+#include <unwindstack/UserArm64.h>
+#include <unwindstack/UserMips.h>
+#include <unwindstack/UserMips64.h>
+#include <unwindstack/UserX86.h>
+#include <unwindstack/UserX86_64.h>
 
-template <typename AddressType>
-uint64_t RegsImpl<AddressType>::GetRelPc(Elf* elf, const MapInfo* map_info) {
-  uint64_t load_bias = 0;
-  if (elf->valid()) {
-    load_bias = elf->interface()->load_bias();
-  }
+namespace unwindstack {
 
-  return pc_ - map_info->start + load_bias + map_info->elf_offset;
-}
-
-template <typename AddressType>
-bool RegsImpl<AddressType>::GetReturnAddressFromDefault(Memory* memory, uint64_t* value) {
-  switch (return_loc_.type) {
-  case LOCATION_REGISTER:
-    CHECK(return_loc_.value < total_regs_);
-    *value = regs_[return_loc_.value];
-    return true;
-  case LOCATION_SP_OFFSET:
-    AddressType return_value;
-    if (!memory->Read(sp_ + return_loc_.value, &return_value, sizeof(return_value))) {
-      return false;
-    }
-    *value = return_value;
-    return true;
-  case LOCATION_UNKNOWN:
-  default:
-    return false;
-  }
-}
-
-RegsArm::RegsArm()
-    : RegsImpl<uint32_t>(ARM_REG_LAST, ARM_REG_SP, Location(LOCATION_REGISTER, ARM_REG_LR)) {}
-
-uint64_t RegsArm::GetAdjustedPc(uint64_t rel_pc, Elf* elf) {
-  if (!elf->valid()) {
-    return rel_pc;
-  }
-
-  uint64_t load_bias = elf->interface()->load_bias();
-  if (rel_pc < load_bias) {
-    return rel_pc;
-  }
-  uint64_t adjusted_rel_pc = rel_pc - load_bias;
-
-  if (adjusted_rel_pc < 5) {
-    return rel_pc;
-  }
-
-  if (adjusted_rel_pc & 1) {
-    // This is a thumb instruction, it could be 2 or 4 bytes.
-    uint32_t value;
-    if (rel_pc < 5 || !elf->memory()->Read(adjusted_rel_pc - 5, &value, sizeof(value)) ||
-        (value & 0xe000f000) != 0xe000f000) {
-      return rel_pc - 2;
-    }
-  }
-  return rel_pc - 4;
-}
-
-RegsArm64::RegsArm64()
-    : RegsImpl<uint64_t>(ARM64_REG_LAST, ARM64_REG_SP, Location(LOCATION_REGISTER, ARM64_REG_LR)) {}
-
-uint64_t RegsArm64::GetAdjustedPc(uint64_t rel_pc, Elf* elf) {
-  if (!elf->valid()) {
-    return rel_pc;
-  }
-
-  if (rel_pc < 4) {
-    return rel_pc;
-  }
-  return rel_pc - 4;
-}
-
-RegsX86::RegsX86()
-    : RegsImpl<uint32_t>(X86_REG_LAST, X86_REG_SP, Location(LOCATION_SP_OFFSET, -4)) {}
-
-uint64_t RegsX86::GetAdjustedPc(uint64_t rel_pc, Elf* elf) {
-  if (!elf->valid()) {
-    return rel_pc;
-  }
-
-  if (rel_pc == 0) {
-    return 0;
-  }
-  return rel_pc - 1;
-}
-
-RegsX86_64::RegsX86_64()
-    : RegsImpl<uint64_t>(X86_64_REG_LAST, X86_64_REG_SP, Location(LOCATION_SP_OFFSET, -8)) {}
-
-uint64_t RegsX86_64::GetAdjustedPc(uint64_t rel_pc, Elf* elf) {
-  if (!elf->valid()) {
-    return rel_pc;
-  }
-
-  if (rel_pc == 0) {
-    return 0;
-  }
-
-  return rel_pc - 1;
-}
-
-static Regs* ReadArm(void* remote_data) {
-  arm_user_regs* user = reinterpret_cast<arm_user_regs*>(remote_data);
-
-  RegsArm* regs = new RegsArm();
-  memcpy(regs->RawData(), &user->regs[0], ARM_REG_LAST * sizeof(uint32_t));
-
-  regs->set_pc(user->regs[ARM_REG_PC]);
-  regs->set_sp(user->regs[ARM_REG_SP]);
-
-  return regs;
-}
-
-static Regs* ReadArm64(void* remote_data) {
-  arm64_user_regs* user = reinterpret_cast<arm64_user_regs*>(remote_data);
-
-  RegsArm64* regs = new RegsArm64();
-  memcpy(regs->RawData(), &user->regs[0], (ARM64_REG_R31 + 1) * sizeof(uint64_t));
-  regs->set_pc(user->pc);
-  regs->set_sp(user->sp);
-
-  return regs;
-}
-
-static Regs* ReadX86(void* remote_data) {
-  x86_user_regs* user = reinterpret_cast<x86_user_regs*>(remote_data);
-
-  RegsX86* regs = new RegsX86();
-  (*regs)[X86_REG_EAX] = user->eax;
-  (*regs)[X86_REG_EBX] = user->ebx;
-  (*regs)[X86_REG_ECX] = user->ecx;
-  (*regs)[X86_REG_EDX] = user->edx;
-  (*regs)[X86_REG_EBP] = user->ebp;
-  (*regs)[X86_REG_EDI] = user->edi;
-  (*regs)[X86_REG_ESI] = user->esi;
-  (*regs)[X86_REG_ESP] = user->esp;
-  (*regs)[X86_REG_EIP] = user->eip;
-
-  regs->set_pc(user->eip);
-  regs->set_sp(user->esp);
-
-  return regs;
-}
-
-static Regs* ReadX86_64(void* remote_data) {
-  x86_64_user_regs* user = reinterpret_cast<x86_64_user_regs*>(remote_data);
-
-  RegsX86_64* regs = new RegsX86_64();
-  (*regs)[X86_64_REG_RAX] = user->rax;
-  (*regs)[X86_64_REG_RBX] = user->rbx;
-  (*regs)[X86_64_REG_RCX] = user->rcx;
-  (*regs)[X86_64_REG_RDX] = user->rdx;
-  (*regs)[X86_64_REG_R8] = user->r8;
-  (*regs)[X86_64_REG_R9] = user->r9;
-  (*regs)[X86_64_REG_R10] = user->r10;
-  (*regs)[X86_64_REG_R11] = user->r11;
-  (*regs)[X86_64_REG_R12] = user->r12;
-  (*regs)[X86_64_REG_R13] = user->r13;
-  (*regs)[X86_64_REG_R14] = user->r14;
-  (*regs)[X86_64_REG_R15] = user->r15;
-  (*regs)[X86_64_REG_RDI] = user->rdi;
-  (*regs)[X86_64_REG_RSI] = user->rsi;
-  (*regs)[X86_64_REG_RBP] = user->rbp;
-  (*regs)[X86_64_REG_RSP] = user->rsp;
-  (*regs)[X86_64_REG_RIP] = user->rip;
-
-  regs->set_pc(user->rip);
-  regs->set_sp(user->rsp);
-
-  return regs;
-}
+// The largest user structure.
+constexpr size_t MAX_USER_REGS_SIZE = sizeof(mips64_user_regs) + 10;
 
 // This function assumes that reg_data is already aligned to a 64 bit value.
 // If not this could crash with an unaligned access.
-Regs* Regs::RemoteGet(pid_t pid, uint32_t* machine_type) {
+Regs* Regs::RemoteGet(pid_t pid) {
   // Make the buffer large enough to contain the largest registers type.
   std::vector<uint64_t> buffer(MAX_USER_REGS_SIZE / sizeof(uint64_t));
   struct iovec io;
@@ -217,17 +56,77 @@
 
   switch (io.iov_len) {
   case sizeof(x86_user_regs):
-    *machine_type = EM_386;
-    return ReadX86(buffer.data());
+    return RegsX86::Read(buffer.data());
   case sizeof(x86_64_user_regs):
-    *machine_type = EM_X86_64;
-    return ReadX86_64(buffer.data());
+    return RegsX86_64::Read(buffer.data());
   case sizeof(arm_user_regs):
-    *machine_type = EM_ARM;
-    return ReadArm(buffer.data());
+    return RegsArm::Read(buffer.data());
   case sizeof(arm64_user_regs):
-    *machine_type = EM_AARCH64;
-    return ReadArm64(buffer.data());
+    return RegsArm64::Read(buffer.data());
+  case sizeof(mips_user_regs):
+    return RegsMips::Read(buffer.data());
+  case sizeof(mips64_user_regs):
+    return RegsMips64::Read(buffer.data());
   }
   return nullptr;
 }
+
+Regs* Regs::CreateFromUcontext(ArchEnum arch, void* ucontext) {
+  switch (arch) {
+    case ARCH_X86:
+      return RegsX86::CreateFromUcontext(ucontext);
+    case ARCH_X86_64:
+      return RegsX86_64::CreateFromUcontext(ucontext);
+    case ARCH_ARM:
+      return RegsArm::CreateFromUcontext(ucontext);
+    case ARCH_ARM64:
+      return RegsArm64::CreateFromUcontext(ucontext);
+    case ARCH_MIPS:
+      return RegsMips::CreateFromUcontext(ucontext);
+    case ARCH_MIPS64:
+      return RegsMips64::CreateFromUcontext(ucontext);
+    case ARCH_UNKNOWN:
+    default:
+      return nullptr;
+  }
+}
+
+ArchEnum Regs::CurrentArch() {
+#if defined(__arm__)
+  return ARCH_ARM;
+#elif defined(__aarch64__)
+  return ARCH_ARM64;
+#elif defined(__i386__)
+  return ARCH_X86;
+#elif defined(__x86_64__)
+  return ARCH_X86_64;
+#elif defined(__mips__) && !defined(__LP64__)
+  return ARCH_MIPS;
+#elif defined(__mips__) && defined(__LP64__)
+  return ARCH_MIPS64;
+#else
+  abort();
+#endif
+}
+
+Regs* Regs::CreateFromLocal() {
+  Regs* regs;
+#if defined(__arm__)
+  regs = new RegsArm();
+#elif defined(__aarch64__)
+  regs = new RegsArm64();
+#elif defined(__i386__)
+  regs = new RegsX86();
+#elif defined(__x86_64__)
+  regs = new RegsX86_64();
+#elif defined(__mips__) && !defined(__LP64__)
+  regs = new RegsMips();
+#elif defined(__mips__) && defined(__LP64__)
+  regs = new RegsMips64();
+#else
+  abort();
+#endif
+  return regs;
+}
+
+}  // namespace unwindstack
diff --git a/libunwindstack/Regs.h b/libunwindstack/Regs.h
deleted file mode 100644
index 8f5a721..0000000
--- a/libunwindstack/Regs.h
+++ /dev/null
@@ -1,127 +0,0 @@
-/*
- * Copyright (C) 2016 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 _LIBUNWINDSTACK_REGS_H
-#define _LIBUNWINDSTACK_REGS_H
-
-#include <stdint.h>
-
-#include <vector>
-
-// Forward declarations.
-class Elf;
-struct MapInfo;
-
-class Regs {
- public:
-  enum LocationEnum : uint8_t {
-    LOCATION_UNKNOWN = 0,
-    LOCATION_REGISTER,
-    LOCATION_SP_OFFSET,
-  };
-
-  struct Location {
-    Location(LocationEnum type, int16_t value) : type(type), value(value) {}
-
-    LocationEnum type;
-    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) {}
-  virtual ~Regs() = default;
-
-  virtual void* RawData() = 0;
-  virtual uint64_t pc() = 0;
-  virtual uint64_t sp() = 0;
-
-  virtual bool GetReturnAddressFromDefault(Memory* memory, uint64_t* value) = 0;
-
-  virtual uint64_t GetRelPc(Elf* elf, const MapInfo* map_info) = 0;
-
-  virtual uint64_t GetAdjustedPc(uint64_t rel_pc, Elf* elf) = 0;
-
-  uint16_t sp_reg() { return sp_reg_; }
-  uint16_t total_regs() { return total_regs_; }
-
-  static Regs* RemoteGet(pid_t pid, uint32_t* machine_type);
-
- protected:
-  uint16_t total_regs_;
-  uint16_t sp_reg_;
-  Location return_loc_;
-};
-
-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) {}
-  virtual ~RegsImpl() = default;
-
-  uint64_t GetRelPc(Elf* elf, const MapInfo* map_info) override;
-
-  bool GetReturnAddressFromDefault(Memory* memory, uint64_t* value) override;
-
-  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; }
-
-  inline AddressType& operator[](size_t reg) { return regs_[reg]; }
-
-  void* RawData() override { return regs_.data(); }
-
- protected:
-  AddressType pc_;
-  AddressType sp_;
-  std::vector<AddressType> regs_;
-};
-
-class RegsArm : public RegsImpl<uint32_t> {
- public:
-  RegsArm();
-  virtual ~RegsArm() = default;
-
-  uint64_t GetAdjustedPc(uint64_t rel_pc, Elf* elf) override;
-};
-
-class RegsArm64 : public RegsImpl<uint64_t> {
- public:
-  RegsArm64();
-  virtual ~RegsArm64() = default;
-
-  uint64_t GetAdjustedPc(uint64_t rel_pc, Elf* elf) override;
-};
-
-class RegsX86 : public RegsImpl<uint32_t> {
- public:
-  RegsX86();
-  virtual ~RegsX86() = default;
-
-  uint64_t GetAdjustedPc(uint64_t rel_pc, Elf* elf) override;
-};
-
-class RegsX86_64 : public RegsImpl<uint64_t> {
- public:
-  RegsX86_64();
-  virtual ~RegsX86_64() = default;
-
-  uint64_t GetAdjustedPc(uint64_t rel_pc, Elf* elf) override;
-};
-
-#endif  // _LIBUNWINDSTACK_REGS_H
diff --git a/libunwindstack/RegsArm.cpp b/libunwindstack/RegsArm.cpp
new file mode 100644
index 0000000..de22bde
--- /dev/null
+++ b/libunwindstack/RegsArm.cpp
@@ -0,0 +1,204 @@
+/*
+ * Copyright (C) 2016 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 <stdint.h>
+
+#include <functional>
+
+#include <unwindstack/Elf.h>
+#include <unwindstack/MachineArm.h>
+#include <unwindstack/MapInfo.h>
+#include <unwindstack/Memory.h>
+#include <unwindstack/RegsArm.h>
+#include <unwindstack/UcontextArm.h>
+#include <unwindstack/UserArm.h>
+
+namespace unwindstack {
+
+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) {
+    if (rel_pc < 2) {
+      return 0;
+    }
+    return 2;
+  }
+  uint64_t adjusted_rel_pc = rel_pc - load_bias;
+  if (adjusted_rel_pc < 5) {
+    if (adjusted_rel_pc < 2) {
+      return 0;
+    }
+    return 2;
+  }
+
+  if (adjusted_rel_pc & 1) {
+    // This is a thumb instruction, it could be 2 or 4 bytes.
+    uint32_t value;
+    if (!elf->memory()->ReadFully(adjusted_rel_pc - 5, &value, sizeof(value)) ||
+        (value & 0xe000f000) != 0xe000f000) {
+      return 2;
+    }
+  }
+  return 4;
+}
+
+bool RegsArm::SetPcFromReturnAddress(Memory*) {
+  uint32_t lr = regs_[ARM_REG_LR];
+  if (regs_[ARM_REG_PC] == lr) {
+    return false;
+  }
+
+  regs_[ARM_REG_PC] = lr;
+  return true;
+}
+
+void RegsArm::IterateRegisters(std::function<void(const char*, uint64_t)> fn) {
+  fn("r0", regs_[ARM_REG_R0]);
+  fn("r1", regs_[ARM_REG_R1]);
+  fn("r2", regs_[ARM_REG_R2]);
+  fn("r3", regs_[ARM_REG_R3]);
+  fn("r4", regs_[ARM_REG_R4]);
+  fn("r5", regs_[ARM_REG_R5]);
+  fn("r6", regs_[ARM_REG_R6]);
+  fn("r7", regs_[ARM_REG_R7]);
+  fn("r8", regs_[ARM_REG_R8]);
+  fn("r9", regs_[ARM_REG_R9]);
+  fn("r10", regs_[ARM_REG_R10]);
+  fn("r11", regs_[ARM_REG_R11]);
+  fn("ip", regs_[ARM_REG_R12]);
+  fn("sp", regs_[ARM_REG_SP]);
+  fn("lr", regs_[ARM_REG_LR]);
+  fn("pc", regs_[ARM_REG_PC]);
+}
+
+Regs* RegsArm::Read(void* remote_data) {
+  arm_user_regs* user = reinterpret_cast<arm_user_regs*>(remote_data);
+
+  RegsArm* regs = new RegsArm();
+  memcpy(regs->RawData(), &user->regs[0], ARM_REG_LAST * sizeof(uint32_t));
+  return regs;
+}
+
+Regs* RegsArm::CreateFromUcontext(void* ucontext) {
+  arm_ucontext_t* arm_ucontext = reinterpret_cast<arm_ucontext_t*>(ucontext);
+
+  RegsArm* regs = new RegsArm();
+  memcpy(regs->RawData(), &arm_ucontext->uc_mcontext.regs[0], ARM_REG_LAST * sizeof(uint32_t));
+  return regs;
+}
+
+bool RegsArm::StepIfSignalHandler(uint64_t rel_pc, Elf* elf, Memory* process_memory) {
+  uint32_t data;
+  Memory* elf_memory = elf->memory();
+  // Read from elf memory since it is usually more expensive to read from
+  // process memory.
+  if (!elf_memory->ReadFully(rel_pc, &data, sizeof(data))) {
+    return false;
+  }
+
+  uint64_t offset = 0;
+  if (data == 0xe3a07077 || data == 0xef900077 || data == 0xdf002777) {
+    uint64_t sp = regs_[ARM_REG_SP];
+    // non-RT sigreturn call.
+    // __restore:
+    //
+    // Form 1 (arm):
+    // 0x77 0x70              mov r7, #0x77
+    // 0xa0 0xe3              svc 0x00000000
+    //
+    // Form 2 (arm):
+    // 0x77 0x00 0x90 0xef    svc 0x00900077
+    //
+    // Form 3 (thumb):
+    // 0x77 0x27              movs r7, #77
+    // 0x00 0xdf              svc 0
+    if (!process_memory->ReadFully(sp, &data, sizeof(data))) {
+      return false;
+    }
+    if (data == 0x5ac3c35a) {
+      // SP + uc_mcontext offset + r0 offset.
+      offset = sp + 0x14 + 0xc;
+    } else {
+      // SP + r0 offset
+      offset = sp + 0xc;
+    }
+  } else if (data == 0xe3a070ad || data == 0xef9000ad || data == 0xdf0027ad) {
+    uint64_t sp = regs_[ARM_REG_SP];
+    // RT sigreturn call.
+    // __restore_rt:
+    //
+    // Form 1 (arm):
+    // 0xad 0x70      mov r7, #0xad
+    // 0xa0 0xe3      svc 0x00000000
+    //
+    // Form 2 (arm):
+    // 0xad 0x00 0x90 0xef    svc 0x009000ad
+    //
+    // Form 3 (thumb):
+    // 0xad 0x27              movs r7, #ad
+    // 0x00 0xdf              svc 0
+    if (!process_memory->ReadFully(sp, &data, sizeof(data))) {
+      return false;
+    }
+    if (data == sp + 8) {
+      // SP + 8 + sizeof(siginfo_t) + uc_mcontext_offset + r0 offset
+      offset = sp + 8 + 0x80 + 0x14 + 0xc;
+    } else {
+      // SP + sizeof(siginfo_t) + uc_mcontext_offset + r0 offset
+      offset = sp + 0x80 + 0x14 + 0xc;
+    }
+  }
+  if (offset == 0) {
+    return false;
+  }
+
+  if (!process_memory->ReadFully(offset, regs_.data(), sizeof(uint32_t) * ARM_REG_LAST)) {
+    return false;
+  }
+  return true;
+}
+
+Regs* RegsArm::Clone() {
+  return new RegsArm(*this);
+}
+
+}  // namespace unwindstack
diff --git a/libunwindstack/RegsArm64.cpp b/libunwindstack/RegsArm64.cpp
new file mode 100644
index 0000000..a68f6e0
--- /dev/null
+++ b/libunwindstack/RegsArm64.cpp
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2016 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 <stdint.h>
+
+#include <functional>
+
+#include <unwindstack/Elf.h>
+#include <unwindstack/MachineArm64.h>
+#include <unwindstack/MapInfo.h>
+#include <unwindstack/Memory.h>
+#include <unwindstack/RegsArm64.h>
+#include <unwindstack/UcontextArm64.h>
+#include <unwindstack/UserArm64.h>
+
+namespace unwindstack {
+
+RegsArm64::RegsArm64()
+    : RegsImpl<uint64_t>(ARM64_REG_LAST, Location(LOCATION_REGISTER, ARM64_REG_LR)) {}
+
+ArchEnum RegsArm64::Arch() {
+  return ARCH_ARM64;
+}
+
+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;
+}
+
+bool RegsArm64::SetPcFromReturnAddress(Memory*) {
+  uint64_t lr = regs_[ARM64_REG_LR];
+  if (regs_[ARM64_REG_PC] == lr) {
+    return false;
+  }
+
+  regs_[ARM64_REG_PC] = lr;
+  return true;
+}
+
+void RegsArm64::IterateRegisters(std::function<void(const char*, uint64_t)> fn) {
+  fn("x0", regs_[ARM64_REG_R0]);
+  fn("x1", regs_[ARM64_REG_R1]);
+  fn("x2", regs_[ARM64_REG_R2]);
+  fn("x3", regs_[ARM64_REG_R3]);
+  fn("x4", regs_[ARM64_REG_R4]);
+  fn("x5", regs_[ARM64_REG_R5]);
+  fn("x6", regs_[ARM64_REG_R6]);
+  fn("x7", regs_[ARM64_REG_R7]);
+  fn("x8", regs_[ARM64_REG_R8]);
+  fn("x9", regs_[ARM64_REG_R9]);
+  fn("x10", regs_[ARM64_REG_R10]);
+  fn("x11", regs_[ARM64_REG_R11]);
+  fn("x12", regs_[ARM64_REG_R12]);
+  fn("x13", regs_[ARM64_REG_R13]);
+  fn("x14", regs_[ARM64_REG_R14]);
+  fn("x15", regs_[ARM64_REG_R15]);
+  fn("x16", regs_[ARM64_REG_R16]);
+  fn("x17", regs_[ARM64_REG_R17]);
+  fn("x18", regs_[ARM64_REG_R18]);
+  fn("x19", regs_[ARM64_REG_R19]);
+  fn("x20", regs_[ARM64_REG_R20]);
+  fn("x21", regs_[ARM64_REG_R21]);
+  fn("x22", regs_[ARM64_REG_R22]);
+  fn("x23", regs_[ARM64_REG_R23]);
+  fn("x24", regs_[ARM64_REG_R24]);
+  fn("x25", regs_[ARM64_REG_R25]);
+  fn("x26", regs_[ARM64_REG_R26]);
+  fn("x27", regs_[ARM64_REG_R27]);
+  fn("x28", regs_[ARM64_REG_R28]);
+  fn("x29", regs_[ARM64_REG_R29]);
+  fn("sp", regs_[ARM64_REG_SP]);
+  fn("lr", regs_[ARM64_REG_LR]);
+  fn("pc", regs_[ARM64_REG_PC]);
+}
+
+Regs* RegsArm64::Read(void* remote_data) {
+  arm64_user_regs* user = reinterpret_cast<arm64_user_regs*>(remote_data);
+
+  RegsArm64* regs = new RegsArm64();
+  memcpy(regs->RawData(), &user->regs[0], (ARM64_REG_R31 + 1) * sizeof(uint64_t));
+  uint64_t* reg_data = reinterpret_cast<uint64_t*>(regs->RawData());
+  reg_data[ARM64_REG_PC] = user->pc;
+  reg_data[ARM64_REG_SP] = user->sp;
+  return regs;
+}
+
+Regs* RegsArm64::CreateFromUcontext(void* ucontext) {
+  arm64_ucontext_t* arm64_ucontext = reinterpret_cast<arm64_ucontext_t*>(ucontext);
+
+  RegsArm64* regs = new RegsArm64();
+  memcpy(regs->RawData(), &arm64_ucontext->uc_mcontext.regs[0], ARM64_REG_LAST * sizeof(uint64_t));
+  return regs;
+}
+
+bool RegsArm64::StepIfSignalHandler(uint64_t rel_pc, Elf* elf, Memory* process_memory) {
+  uint64_t data;
+  Memory* elf_memory = elf->memory();
+  // Read from elf memory since it is usually more expensive to read from
+  // process memory.
+  if (!elf_memory->ReadFully(rel_pc, &data, sizeof(data))) {
+    return false;
+  }
+
+  // Look for the kernel sigreturn function.
+  // __kernel_rt_sigreturn:
+  // 0xd2801168     mov x8, #0x8b
+  // 0xd4000001     svc #0x0
+  if (data != 0xd4000001d2801168ULL) {
+    return false;
+  }
+
+  // SP + sizeof(siginfo_t) + uc_mcontext offset + X0 offset.
+  if (!process_memory->ReadFully(regs_[ARM64_REG_SP] + 0x80 + 0xb0 + 0x08, regs_.data(),
+                                 sizeof(uint64_t) * ARM64_REG_LAST)) {
+    return false;
+  }
+  return true;
+}
+
+Regs* RegsArm64::Clone() {
+  return new RegsArm64(*this);
+}
+
+}  // namespace unwindstack
diff --git a/libunwindstack/RegsInfo.h b/libunwindstack/RegsInfo.h
new file mode 100644
index 0000000..47825f5
--- /dev/null
+++ b/libunwindstack/RegsInfo.h
@@ -0,0 +1,66 @@
+/*
+ * 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 _LIBUNWINDSTACK_REGS_INFO_H
+#define _LIBUNWINDSTACK_REGS_INFO_H
+
+#include <stdint.h>
+
+#include <unwindstack/Regs.h>
+
+namespace unwindstack {
+
+template <typename AddressType>
+struct RegsInfo {
+  RegsInfo(RegsImpl<AddressType>* regs) : regs(regs) {}
+
+  RegsImpl<AddressType>* regs = nullptr;
+  uint64_t saved_reg_map = 0;
+  AddressType saved_regs[64];
+
+  inline AddressType Get(uint32_t reg) {
+    if (IsSaved(reg)) {
+      return saved_regs[reg];
+    }
+    return (*regs)[reg];
+  }
+
+  inline AddressType* Save(uint32_t reg) {
+    if (reg > sizeof(saved_regs) / sizeof(AddressType)) {
+      // This should never happen as since all currently supported
+      // architectures have the total number of registers < 64.
+      abort();
+    }
+    saved_reg_map |= 1 << reg;
+    saved_regs[reg] = (*regs)[reg];
+    return &(*regs)[reg];
+  }
+
+  inline bool IsSaved(uint32_t reg) {
+    if (reg > sizeof(saved_regs) / sizeof(AddressType)) {
+      // This should never happen as since all currently supported
+      // architectures have the total number of registers < 64.
+      abort();
+    }
+    return saved_reg_map & (1 << reg);
+  }
+
+  inline uint16_t Total() { return regs->total_regs(); }
+};
+
+}  // namespace unwindstack
+
+#endif  // _LIBUNWINDSTACK_REGS_INFO_H
diff --git a/libunwindstack/RegsMips.cpp b/libunwindstack/RegsMips.cpp
new file mode 100644
index 0000000..2e6908c
--- /dev/null
+++ b/libunwindstack/RegsMips.cpp
@@ -0,0 +1,180 @@
+/*
+ * Copyright (C) 2017 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 <stdint.h>
+
+#include <functional>
+
+#include <unwindstack/Elf.h>
+#include <unwindstack/MachineMips.h>
+#include <unwindstack/MapInfo.h>
+#include <unwindstack/Memory.h>
+#include <unwindstack/RegsMips.h>
+#include <unwindstack/UcontextMips.h>
+#include <unwindstack/UserMips.h>
+
+namespace unwindstack {
+
+RegsMips::RegsMips()
+    : RegsImpl<uint32_t>(MIPS_REG_LAST, Location(LOCATION_REGISTER, MIPS_REG_RA)) {}
+
+ArchEnum RegsMips::Arch() {
+  return ARCH_MIPS;
+}
+
+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;
+}
+
+bool RegsMips::SetPcFromReturnAddress(Memory*) {
+  uint32_t ra = regs_[MIPS_REG_RA];
+  if (regs_[MIPS_REG_PC] == ra) {
+    return false;
+  }
+
+  regs_[MIPS_REG_PC] = ra;
+  return true;
+}
+
+void RegsMips::IterateRegisters(std::function<void(const char*, uint64_t)> fn) {
+  fn("r0", regs_[MIPS_REG_R0]);
+  fn("r1", regs_[MIPS_REG_R1]);
+  fn("r2", regs_[MIPS_REG_R2]);
+  fn("r3", regs_[MIPS_REG_R3]);
+  fn("r4", regs_[MIPS_REG_R4]);
+  fn("r5", regs_[MIPS_REG_R5]);
+  fn("r6", regs_[MIPS_REG_R6]);
+  fn("r7", regs_[MIPS_REG_R7]);
+  fn("r8", regs_[MIPS_REG_R8]);
+  fn("r9", regs_[MIPS_REG_R9]);
+  fn("r10", regs_[MIPS_REG_R10]);
+  fn("r11", regs_[MIPS_REG_R11]);
+  fn("r12", regs_[MIPS_REG_R12]);
+  fn("r13", regs_[MIPS_REG_R13]);
+  fn("r14", regs_[MIPS_REG_R14]);
+  fn("r15", regs_[MIPS_REG_R15]);
+  fn("r16", regs_[MIPS_REG_R16]);
+  fn("r17", regs_[MIPS_REG_R17]);
+  fn("r18", regs_[MIPS_REG_R18]);
+  fn("r19", regs_[MIPS_REG_R19]);
+  fn("r20", regs_[MIPS_REG_R20]);
+  fn("r21", regs_[MIPS_REG_R21]);
+  fn("r22", regs_[MIPS_REG_R22]);
+  fn("r23", regs_[MIPS_REG_R23]);
+  fn("r24", regs_[MIPS_REG_R24]);
+  fn("r25", regs_[MIPS_REG_R25]);
+  fn("r26", regs_[MIPS_REG_R26]);
+  fn("r27", regs_[MIPS_REG_R27]);
+  fn("r28", regs_[MIPS_REG_R28]);
+  fn("sp", regs_[MIPS_REG_SP]);
+  fn("r30", regs_[MIPS_REG_R30]);
+  fn("ra", regs_[MIPS_REG_RA]);
+  fn("pc", regs_[MIPS_REG_PC]);
+}
+
+Regs* RegsMips::Read(void* remote_data) {
+  mips_user_regs* user = reinterpret_cast<mips_user_regs*>(remote_data);
+  RegsMips* regs = new RegsMips();
+  uint32_t* reg_data = reinterpret_cast<uint32_t*>(regs->RawData());
+
+  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];
+  return regs;
+}
+
+Regs* RegsMips::CreateFromUcontext(void* ucontext) {
+  mips_ucontext_t* mips_ucontext = reinterpret_cast<mips_ucontext_t*>(ucontext);
+
+  RegsMips* regs = new RegsMips();
+  // Copy 64 bit sc_regs over to 32 bit regs
+  for (int i = 0; i < 32; i++) {
+      (*regs)[MIPS_REG_R0 + i] = mips_ucontext->uc_mcontext.sc_regs[i];
+  }
+  (*regs)[MIPS_REG_PC] = mips_ucontext->uc_mcontext.sc_pc;
+  return regs;
+}
+
+bool RegsMips::StepIfSignalHandler(uint64_t rel_pc, Elf* elf, Memory* process_memory) {
+  uint64_t data;
+  uint64_t offset = 0;
+  Memory* elf_memory = elf->memory();
+  // Read from elf memory since it is usually more expensive to read from
+  // process memory.
+  if (!elf_memory->Read(rel_pc, &data, sizeof(data))) {
+    return false;
+  }
+
+  // Look for the kernel sigreturn functions.
+  // __vdso_rt_sigreturn:
+  // 0x24021061     li  v0, 0x1061
+  // 0x0000000c     syscall
+  // __vdso_sigreturn:
+  // 0x24021017     li  v0, 0x1017
+  // 0x0000000c     syscall
+  if (data == 0x0000000c24021061ULL) {
+    // vdso_rt_sigreturn => read rt_sigframe
+    // offset = siginfo offset + sizeof(siginfo) + uc_mcontext offset + sc_pc offset
+    offset = 24 + 128 + 24 + 8;
+  } else if (data == 0x0000000c24021017LL) {
+    // vdso_sigreturn => read sigframe
+    // offset = sigcontext offset + sc_pc offset
+    offset = 24 + 8;
+  } else {
+    return false;
+  }
+
+  // read sc_pc and sc_regs[32] from stack
+  uint64_t values[MIPS_REG_LAST];
+  if (!process_memory->Read(regs_[MIPS_REG_SP] + offset, values, sizeof(values))) {
+    return false;
+  }
+
+  // Copy 64 bit sc_pc over to 32 bit regs_[MIPS_REG_PC]
+  regs_[MIPS_REG_PC] = values[0];
+
+  // Copy 64 bit sc_regs over to 32 bit regs
+  for (int i = 0; i < 32; i++) {
+      regs_[MIPS_REG_R0 + i] = values[1 + i];
+  }
+  return true;
+}
+
+Regs* RegsMips::Clone() {
+  return new RegsMips(*this);
+}
+
+}  // namespace unwindstack
diff --git a/libunwindstack/RegsMips64.cpp b/libunwindstack/RegsMips64.cpp
new file mode 100644
index 0000000..0b835a1
--- /dev/null
+++ b/libunwindstack/RegsMips64.cpp
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2017 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 <stdint.h>
+
+#include <functional>
+
+#include <unwindstack/Elf.h>
+#include <unwindstack/MachineMips64.h>
+#include <unwindstack/MapInfo.h>
+#include <unwindstack/Memory.h>
+#include <unwindstack/RegsMips64.h>
+#include <unwindstack/UcontextMips64.h>
+#include <unwindstack/UserMips64.h>
+
+namespace unwindstack {
+
+RegsMips64::RegsMips64()
+    : RegsImpl<uint64_t>(MIPS64_REG_LAST, Location(LOCATION_REGISTER, MIPS64_REG_RA)) {}
+
+ArchEnum RegsMips64::Arch() {
+  return ARCH_MIPS64;
+}
+
+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;
+}
+
+bool RegsMips64::SetPcFromReturnAddress(Memory*) {
+  uint64_t ra = regs_[MIPS64_REG_RA];
+  if (regs_[MIPS64_REG_PC] == ra) {
+    return false;
+  }
+
+  regs_[MIPS64_REG_PC] = ra;
+  return true;
+}
+
+void RegsMips64::IterateRegisters(std::function<void(const char*, uint64_t)> fn) {
+  fn("r0", regs_[MIPS64_REG_R0]);
+  fn("r1", regs_[MIPS64_REG_R1]);
+  fn("r2", regs_[MIPS64_REG_R2]);
+  fn("r3", regs_[MIPS64_REG_R3]);
+  fn("r4", regs_[MIPS64_REG_R4]);
+  fn("r5", regs_[MIPS64_REG_R5]);
+  fn("r6", regs_[MIPS64_REG_R6]);
+  fn("r7", regs_[MIPS64_REG_R7]);
+  fn("r8", regs_[MIPS64_REG_R8]);
+  fn("r9", regs_[MIPS64_REG_R9]);
+  fn("r10", regs_[MIPS64_REG_R10]);
+  fn("r11", regs_[MIPS64_REG_R11]);
+  fn("r12", regs_[MIPS64_REG_R12]);
+  fn("r13", regs_[MIPS64_REG_R13]);
+  fn("r14", regs_[MIPS64_REG_R14]);
+  fn("r15", regs_[MIPS64_REG_R15]);
+  fn("r16", regs_[MIPS64_REG_R16]);
+  fn("r17", regs_[MIPS64_REG_R17]);
+  fn("r18", regs_[MIPS64_REG_R18]);
+  fn("r19", regs_[MIPS64_REG_R19]);
+  fn("r20", regs_[MIPS64_REG_R20]);
+  fn("r21", regs_[MIPS64_REG_R21]);
+  fn("r22", regs_[MIPS64_REG_R22]);
+  fn("r23", regs_[MIPS64_REG_R23]);
+  fn("r24", regs_[MIPS64_REG_R24]);
+  fn("r25", regs_[MIPS64_REG_R25]);
+  fn("r26", regs_[MIPS64_REG_R26]);
+  fn("r27", regs_[MIPS64_REG_R27]);
+  fn("r28", regs_[MIPS64_REG_R28]);
+  fn("sp", regs_[MIPS64_REG_SP]);
+  fn("r30", regs_[MIPS64_REG_R30]);
+  fn("ra", regs_[MIPS64_REG_RA]);
+  fn("pc", regs_[MIPS64_REG_PC]);
+}
+
+Regs* RegsMips64::Read(void* remote_data) {
+  mips64_user_regs* user = reinterpret_cast<mips64_user_regs*>(remote_data);
+  RegsMips64* regs = new RegsMips64();
+  uint64_t* reg_data = reinterpret_cast<uint64_t*>(regs->RawData());
+
+  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];
+  return regs;
+}
+
+Regs* RegsMips64::CreateFromUcontext(void* ucontext) {
+  mips64_ucontext_t* mips64_ucontext = reinterpret_cast<mips64_ucontext_t*>(ucontext);
+
+  RegsMips64* regs = new RegsMips64();
+  // 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;
+  return regs;
+}
+
+bool RegsMips64::StepIfSignalHandler(uint64_t rel_pc, Elf* elf, Memory* process_memory) {
+  uint64_t data;
+  Memory* elf_memory = elf->memory();
+  // Read from elf memory since it is usually more expensive to read from
+  // process memory.
+  if (!elf_memory->Read(rel_pc, &data, sizeof(data))) {
+    return false;
+  }
+
+  // Look for the kernel sigreturn function.
+  // __vdso_rt_sigreturn:
+  // 0x2402145b     li  v0, 0x145b
+  // 0x0000000c     syscall
+  if (data != 0x0000000c2402145bULL) {
+    return false;
+  }
+
+  // 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_
+  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))) {
+    return false;
+  }
+  return true;
+}
+
+Regs* RegsMips64::Clone() {
+  return new RegsMips64(*this);
+}
+
+}  // namespace unwindstack
diff --git a/libunwindstack/RegsX86.cpp b/libunwindstack/RegsX86.cpp
new file mode 100644
index 0000000..9bb39d1
--- /dev/null
+++ b/libunwindstack/RegsX86.cpp
@@ -0,0 +1,186 @@
+/*
+ * Copyright (C) 2016 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 <stdint.h>
+
+#include <functional>
+
+#include <unwindstack/Elf.h>
+#include <unwindstack/MachineX86.h>
+#include <unwindstack/MapInfo.h>
+#include <unwindstack/Memory.h>
+#include <unwindstack/RegsX86.h>
+#include <unwindstack/UcontextX86.h>
+#include <unwindstack/UserX86.h>
+
+namespace unwindstack {
+
+RegsX86::RegsX86() : RegsImpl<uint32_t>(X86_REG_LAST, Location(LOCATION_SP_OFFSET, -4)) {}
+
+ArchEnum RegsX86::Arch() {
+  return ARCH_X86;
+}
+
+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;
+}
+
+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(regs_[X86_REG_SP], &new_pc, sizeof(new_pc)) ||
+      new_pc == regs_[X86_REG_PC]) {
+    return false;
+  }
+
+  regs_[X86_REG_PC] = new_pc;
+  return true;
+}
+
+void RegsX86::IterateRegisters(std::function<void(const char*, uint64_t)> fn) {
+  fn("eax", regs_[X86_REG_EAX]);
+  fn("ebx", regs_[X86_REG_EBX]);
+  fn("ecx", regs_[X86_REG_ECX]);
+  fn("edx", regs_[X86_REG_EDX]);
+  fn("ebp", regs_[X86_REG_EBP]);
+  fn("edi", regs_[X86_REG_EDI]);
+  fn("esi", regs_[X86_REG_ESI]);
+  fn("esp", regs_[X86_REG_ESP]);
+  fn("eip", regs_[X86_REG_EIP]);
+}
+
+Regs* RegsX86::Read(void* user_data) {
+  x86_user_regs* user = reinterpret_cast<x86_user_regs*>(user_data);
+
+  RegsX86* regs = new RegsX86();
+  (*regs)[X86_REG_EAX] = user->eax;
+  (*regs)[X86_REG_EBX] = user->ebx;
+  (*regs)[X86_REG_ECX] = user->ecx;
+  (*regs)[X86_REG_EDX] = user->edx;
+  (*regs)[X86_REG_EBP] = user->ebp;
+  (*regs)[X86_REG_EDI] = user->edi;
+  (*regs)[X86_REG_ESI] = user->esi;
+  (*regs)[X86_REG_ESP] = user->esp;
+  (*regs)[X86_REG_EIP] = user->eip;
+
+  return regs;
+}
+
+void RegsX86::SetFromUcontext(x86_ucontext_t* ucontext) {
+  // Put the registers in the expected order.
+  regs_[X86_REG_EDI] = ucontext->uc_mcontext.edi;
+  regs_[X86_REG_ESI] = ucontext->uc_mcontext.esi;
+  regs_[X86_REG_EBP] = ucontext->uc_mcontext.ebp;
+  regs_[X86_REG_ESP] = ucontext->uc_mcontext.esp;
+  regs_[X86_REG_EBX] = ucontext->uc_mcontext.ebx;
+  regs_[X86_REG_EDX] = ucontext->uc_mcontext.edx;
+  regs_[X86_REG_ECX] = ucontext->uc_mcontext.ecx;
+  regs_[X86_REG_EAX] = ucontext->uc_mcontext.eax;
+  regs_[X86_REG_EIP] = ucontext->uc_mcontext.eip;
+}
+
+Regs* RegsX86::CreateFromUcontext(void* ucontext) {
+  x86_ucontext_t* x86_ucontext = reinterpret_cast<x86_ucontext_t*>(ucontext);
+
+  RegsX86* regs = new RegsX86();
+  regs->SetFromUcontext(x86_ucontext);
+  return regs;
+}
+
+bool RegsX86::StepIfSignalHandler(uint64_t rel_pc, Elf* elf, Memory* process_memory) {
+  uint64_t data;
+  Memory* elf_memory = elf->memory();
+  // Read from elf memory since it is usually more expensive to read from
+  // process memory.
+  if (!elf_memory->ReadFully(rel_pc, &data, sizeof(data))) {
+    return false;
+  }
+
+  if (data == 0x80cd00000077b858ULL) {
+    // Without SA_SIGINFO set, the return sequence is:
+    //
+    //   __restore:
+    //   0x58                            pop %eax
+    //   0xb8 0x77 0x00 0x00 0x00        movl 0x77,%eax
+    //   0xcd 0x80                       int 0x80
+    //
+    // SP points at arguments:
+    //   int signum
+    //   struct sigcontext (same format as mcontext)
+    struct x86_mcontext_t context;
+    if (!process_memory->ReadFully(regs_[X86_REG_SP] + 4, &context, sizeof(context))) {
+      return false;
+    }
+    regs_[X86_REG_EBP] = context.ebp;
+    regs_[X86_REG_ESP] = context.esp;
+    regs_[X86_REG_EBX] = context.ebx;
+    regs_[X86_REG_EDX] = context.edx;
+    regs_[X86_REG_ECX] = context.ecx;
+    regs_[X86_REG_EAX] = context.eax;
+    regs_[X86_REG_EIP] = context.eip;
+    return true;
+  } else if ((data & 0x00ffffffffffffffULL) == 0x0080cd000000adb8ULL) {
+    // With SA_SIGINFO set, the return sequence is:
+    //
+    //   __restore_rt:
+    //   0xb8 0xad 0x00 0x00 0x00        movl 0xad,%eax
+    //   0xcd 0x80                       int 0x80
+    //
+    // SP points at arguments:
+    //   int signum
+    //   siginfo*
+    //   ucontext*
+
+    // Get the location of the sigcontext data.
+    uint32_t 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.
+    x86_ucontext_t x86_ucontext;
+    if (!process_memory->ReadFully(ptr + 0x14, &x86_ucontext.uc_mcontext, sizeof(x86_mcontext_t))) {
+      return false;
+    }
+    SetFromUcontext(&x86_ucontext);
+    return true;
+  }
+  return false;
+}
+
+Regs* RegsX86::Clone() {
+  return new RegsX86(*this);
+}
+
+}  // namespace unwindstack
diff --git a/libunwindstack/RegsX86_64.cpp b/libunwindstack/RegsX86_64.cpp
new file mode 100644
index 0000000..ebad3f4
--- /dev/null
+++ b/libunwindstack/RegsX86_64.cpp
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2016 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 <stdint.h>
+
+#include <functional>
+
+#include <unwindstack/Elf.h>
+#include <unwindstack/MachineX86_64.h>
+#include <unwindstack/MapInfo.h>
+#include <unwindstack/Memory.h>
+#include <unwindstack/RegsX86_64.h>
+#include <unwindstack/UcontextX86_64.h>
+#include <unwindstack/UserX86_64.h>
+
+namespace unwindstack {
+
+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::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;
+}
+
+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(regs_[X86_64_REG_SP], &new_pc, sizeof(new_pc)) ||
+      new_pc == regs_[X86_64_REG_PC]) {
+    return false;
+  }
+
+  regs_[X86_64_REG_PC] = new_pc;
+  return true;
+}
+
+void RegsX86_64::IterateRegisters(std::function<void(const char*, uint64_t)> fn) {
+  fn("rax", regs_[X86_64_REG_RAX]);
+  fn("rbx", regs_[X86_64_REG_RBX]);
+  fn("rcx", regs_[X86_64_REG_RCX]);
+  fn("rdx", regs_[X86_64_REG_RDX]);
+  fn("r8", regs_[X86_64_REG_R8]);
+  fn("r9", regs_[X86_64_REG_R9]);
+  fn("r10", regs_[X86_64_REG_R10]);
+  fn("r11", regs_[X86_64_REG_R11]);
+  fn("r12", regs_[X86_64_REG_R12]);
+  fn("r13", regs_[X86_64_REG_R13]);
+  fn("r14", regs_[X86_64_REG_R14]);
+  fn("r15", regs_[X86_64_REG_R15]);
+  fn("rdi", regs_[X86_64_REG_RDI]);
+  fn("rsi", regs_[X86_64_REG_RSI]);
+  fn("rbp", regs_[X86_64_REG_RBP]);
+  fn("rsp", regs_[X86_64_REG_RSP]);
+  fn("rip", regs_[X86_64_REG_RIP]);
+}
+
+Regs* RegsX86_64::Read(void* remote_data) {
+  x86_64_user_regs* user = reinterpret_cast<x86_64_user_regs*>(remote_data);
+
+  RegsX86_64* regs = new RegsX86_64();
+  (*regs)[X86_64_REG_RAX] = user->rax;
+  (*regs)[X86_64_REG_RBX] = user->rbx;
+  (*regs)[X86_64_REG_RCX] = user->rcx;
+  (*regs)[X86_64_REG_RDX] = user->rdx;
+  (*regs)[X86_64_REG_R8] = user->r8;
+  (*regs)[X86_64_REG_R9] = user->r9;
+  (*regs)[X86_64_REG_R10] = user->r10;
+  (*regs)[X86_64_REG_R11] = user->r11;
+  (*regs)[X86_64_REG_R12] = user->r12;
+  (*regs)[X86_64_REG_R13] = user->r13;
+  (*regs)[X86_64_REG_R14] = user->r14;
+  (*regs)[X86_64_REG_R15] = user->r15;
+  (*regs)[X86_64_REG_RDI] = user->rdi;
+  (*regs)[X86_64_REG_RSI] = user->rsi;
+  (*regs)[X86_64_REG_RBP] = user->rbp;
+  (*regs)[X86_64_REG_RSP] = user->rsp;
+  (*regs)[X86_64_REG_RIP] = user->rip;
+
+  return regs;
+}
+
+void RegsX86_64::SetFromUcontext(x86_64_ucontext_t* ucontext) {
+  // R8-R15
+  memcpy(&regs_[X86_64_REG_R8], &ucontext->uc_mcontext.r8, 8 * sizeof(uint64_t));
+
+  // Rest of the registers.
+  regs_[X86_64_REG_RDI] = ucontext->uc_mcontext.rdi;
+  regs_[X86_64_REG_RSI] = ucontext->uc_mcontext.rsi;
+  regs_[X86_64_REG_RBP] = ucontext->uc_mcontext.rbp;
+  regs_[X86_64_REG_RBX] = ucontext->uc_mcontext.rbx;
+  regs_[X86_64_REG_RDX] = ucontext->uc_mcontext.rdx;
+  regs_[X86_64_REG_RAX] = ucontext->uc_mcontext.rax;
+  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;
+}
+
+Regs* RegsX86_64::CreateFromUcontext(void* ucontext) {
+  x86_64_ucontext_t* x86_64_ucontext = reinterpret_cast<x86_64_ucontext_t*>(ucontext);
+
+  RegsX86_64* regs = new RegsX86_64();
+  regs->SetFromUcontext(x86_64_ucontext);
+  return regs;
+}
+
+bool RegsX86_64::StepIfSignalHandler(uint64_t rel_pc, Elf* elf, Memory* process_memory) {
+  uint64_t data;
+  Memory* elf_memory = elf->memory();
+  // Read from elf memory since it is usually more expensive to read from
+  // process memory.
+  if (!elf_memory->ReadFully(rel_pc, &data, sizeof(data)) || data != 0x0f0000000fc0c748) {
+    return false;
+  }
+
+  uint16_t data2;
+  if (!elf_memory->ReadFully(rel_pc + 8, &data2, sizeof(data2)) || data2 != 0x0f05) {
+    return false;
+  }
+
+  // __restore_rt:
+  // 0x48 0xc7 0xc0 0x0f 0x00 0x00 0x00   mov $0xf,%rax
+  // 0x0f 0x05                            syscall
+  // 0x0f                                 nopl 0x0($rax)
+
+  // 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(regs_[X86_64_REG_SP] + 0x28, &x86_64_ucontext.uc_mcontext,
+                                 sizeof(x86_64_mcontext_t))) {
+    return false;
+  }
+  SetFromUcontext(&x86_64_ucontext);
+  return true;
+}
+
+Regs* RegsX86_64::Clone() {
+  return new RegsX86_64(*this);
+}
+
+}  // namespace unwindstack
diff --git a/libunwindstack/Symbols.cpp b/libunwindstack/Symbols.cpp
index edc2187..14ebdbb 100644
--- a/libunwindstack/Symbols.cpp
+++ b/libunwindstack/Symbols.cpp
@@ -19,10 +19,13 @@
 
 #include <string>
 
+#include <unwindstack/Memory.h>
+
 #include "Check.h"
-#include "Memory.h"
 #include "Symbols.h"
 
+namespace unwindstack {
+
 Symbols::Symbols(uint64_t offset, uint64_t size, uint64_t entry_size, uint64_t str_offset,
                  uint64_t str_size)
     : cur_offset_(offset),
@@ -51,10 +54,7 @@
 }
 
 template <typename SymType>
-bool Symbols::GetName(uint64_t addr, uint64_t load_bias, Memory* elf_memory, std::string* name,
-                      uint64_t* func_offset) {
-  addr += load_bias;
-
+bool Symbols::GetName(uint64_t addr, Memory* elf_memory, std::string* name, uint64_t* func_offset) {
   if (symbols_.size() != 0) {
     const Info* info = GetInfoFromCache(addr);
     if (info) {
@@ -68,7 +68,7 @@
   bool return_value = false;
   while (cur_offset_ + entry_size_ <= end_) {
     SymType entry;
-    if (!elf_memory->Read(cur_offset_, &entry, sizeof(entry))) {
+    if (!elf_memory->ReadFully(cur_offset_, &entry, sizeof(entry))) {
       // Stop all processing, something looks like it is corrupted.
       cur_offset_ = UINT64_MAX;
       return false;
@@ -78,9 +78,6 @@
     if (entry.st_shndx != SHN_UNDEF && ELF32_ST_TYPE(entry.st_info) == STT_FUNC) {
       // Treat st_value as virtual address.
       uint64_t start_offset = entry.st_value;
-      if (entry.st_shndx != SHN_ABS) {
-        start_offset += load_bias;
-      }
       uint64_t end_offset = start_offset + entry.st_size;
 
       // Cache the value.
@@ -105,6 +102,35 @@
   return return_value;
 }
 
+template <typename SymType>
+bool Symbols::GetGlobal(Memory* elf_memory, const std::string& name, uint64_t* memory_address) {
+  uint64_t cur_offset = offset_;
+  while (cur_offset + entry_size_ <= end_) {
+    SymType entry;
+    if (!elf_memory->ReadFully(cur_offset, &entry, sizeof(entry))) {
+      return false;
+    }
+    cur_offset += entry_size_;
+
+    if (entry.st_shndx != SHN_UNDEF && ELF32_ST_TYPE(entry.st_info) == STT_OBJECT &&
+        ELF32_ST_BIND(entry.st_info) == STB_GLOBAL) {
+      uint64_t str_offset = str_offset_ + entry.st_name;
+      if (str_offset < str_end_) {
+        std::string symbol;
+        if (elf_memory->ReadString(str_offset, &symbol, str_end_ - str_offset) && symbol == name) {
+          *memory_address = entry.st_value;
+          return true;
+        }
+      }
+    }
+  }
+  return false;
+}
+
 // Instantiate all of the needed template functions.
-template bool Symbols::GetName<Elf32_Sym>(uint64_t, uint64_t, Memory*, std::string*, uint64_t*);
-template bool Symbols::GetName<Elf64_Sym>(uint64_t, uint64_t, Memory*, std::string*, uint64_t*);
+template bool Symbols::GetName<Elf32_Sym>(uint64_t, Memory*, std::string*, uint64_t*);
+template bool Symbols::GetName<Elf64_Sym>(uint64_t, Memory*, std::string*, uint64_t*);
+
+template bool Symbols::GetGlobal<Elf32_Sym>(Memory*, const std::string&, uint64_t*);
+template bool Symbols::GetGlobal<Elf64_Sym>(Memory*, const std::string&, uint64_t*);
+}  // namespace unwindstack
diff --git a/libunwindstack/Symbols.h b/libunwindstack/Symbols.h
index 3c0d033..7fcd067 100644
--- a/libunwindstack/Symbols.h
+++ b/libunwindstack/Symbols.h
@@ -22,6 +22,8 @@
 #include <string>
 #include <vector>
 
+namespace unwindstack {
+
 // Forward declaration.
 class Memory;
 
@@ -42,8 +44,10 @@
   const Info* GetInfoFromCache(uint64_t addr);
 
   template <typename SymType>
-  bool GetName(uint64_t addr, uint64_t load_bias, Memory* elf_memory, std::string* name,
-               uint64_t* func_offset);
+  bool GetName(uint64_t addr, Memory* elf_memory, std::string* name, uint64_t* func_offset);
+
+  template <typename SymType>
+  bool GetGlobal(Memory* elf_memory, const std::string& name, uint64_t* memory_address);
 
   void ClearCache() {
     symbols_.clear();
@@ -61,4 +65,6 @@
   std::vector<Info> symbols_;
 };
 
+}  // namespace unwindstack
+
 #endif  // _LIBUNWINDSTACK_SYMBOLS_H
diff --git a/libunwindstack/Unwinder.cpp b/libunwindstack/Unwinder.cpp
new file mode 100644
index 0000000..099cc9e
--- /dev/null
+++ b/libunwindstack/Unwinder.cpp
@@ -0,0 +1,313 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#define _GNU_SOURCE 1
+#include <elf.h>
+#include <inttypes.h>
+#include <stdint.h>
+#include <string.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <algorithm>
+
+#include <android-base/stringprintf.h>
+
+#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)
+#include <unwindstack/DexFiles.h>
+#endif
+
+namespace unwindstack {
+
+// Inject extra 'virtual' frame that represents the dex pc data.
+// The dex pc is a magic register defined in the Mterp interpreter,
+// and thus it will be restored/observed in the frame after it.
+// Adding the dex frame first here will create something like:
+//   #7 pc 0015fa20 core.vdex   java.util.Arrays.binarySearch+8
+//   #8 pc 006b1ba1 libartd.so  ExecuteMterpImpl+14625
+//   #9 pc 0039a1ef libartd.so  art::interpreter::Execute+719
+void Unwinder::FillInDexFrame() {
+  size_t frame_num = frames_.size();
+  frames_.resize(frame_num + 1);
+  FrameData* frame = &frames_.at(frame_num);
+  frame->num = frame_num;
+
+  uint64_t dex_pc = regs_->dex_pc();
+  frame->pc = dex_pc;
+  frame->sp = regs_->sp();
+
+  MapInfo* info = maps_->Find(dex_pc);
+  if (info != nullptr) {
+    frame->map_start = info->start;
+    frame->map_end = info->end;
+    frame->map_offset = info->offset;
+    frame->map_load_bias = info->load_bias;
+    frame->map_flags = info->flags;
+    if (resolve_names_) {
+      frame->map_name = info->name;
+    }
+    frame->rel_pc = dex_pc - info->start;
+  } else {
+    frame->rel_pc = dex_pc;
+    return;
+  }
+
+  if (!resolve_names_) {
+    return;
+  }
+
+#if !defined(NO_LIBDEXFILE_SUPPORT)
+  if (dex_files_ == nullptr) {
+    return;
+  }
+
+  dex_files_->GetMethodInformation(maps_, info, dex_pc, &frame->function_name,
+                                   &frame->function_offset);
+#endif
+}
+
+void Unwinder::FillInFrame(MapInfo* map_info, Elf* elf, uint64_t rel_pc, uint64_t func_pc,
+                           uint64_t pc_adjustment) {
+  size_t frame_num = frames_.size();
+  frames_.resize(frame_num + 1);
+  FrameData* frame = &frames_.at(frame_num);
+  frame->num = frame_num;
+  frame->sp = regs_->sp();
+  frame->rel_pc = rel_pc - pc_adjustment;
+  frame->pc = regs_->pc() - pc_adjustment;
+
+  if (map_info == nullptr) {
+    return;
+  }
+
+  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;
+  frame->map_flags = map_info->flags;
+  frame->map_load_bias = elf->GetLoadBias();
+
+  if (!resolve_names_ ||
+      !elf->GetFunctionName(func_pc, &frame->function_name, &frame->function_offset)) {
+    frame->function_name = "";
+    frame->function_offset = 0;
+  }
+}
+
+static bool ShouldStop(const std::vector<std::string>* map_suffixes_to_ignore,
+                       std::string& map_name) {
+  if (map_suffixes_to_ignore == nullptr) {
+    return false;
+  }
+  auto pos = map_name.find_last_of('.');
+  if (pos == std::string::npos) {
+    return false;
+  }
+
+  return std::find(map_suffixes_to_ignore->begin(), map_suffixes_to_ignore->end(),
+                   map_name.substr(pos + 1)) != map_suffixes_to_ignore->end();
+}
+
+void Unwinder::Unwind(const std::vector<std::string>* initial_map_names_to_skip,
+                      const std::vector<std::string>* map_suffixes_to_ignore) {
+  frames_.clear();
+  last_error_.code = ERROR_NONE;
+  last_error_.address = 0;
+
+  bool return_address_attempt = false;
+  bool adjust_pc = false;
+  std::unique_ptr<JitDebug> jit_debug;
+  for (; frames_.size() < max_frames_;) {
+    uint64_t cur_pc = regs_->pc();
+    uint64_t cur_sp = regs_->sp();
+
+    MapInfo* map_info = maps_->Find(regs_->pc());
+    uint64_t pc_adjustment = 0;
+    uint64_t step_pc;
+    uint64_t rel_pc;
+    Elf* elf;
+    if (map_info == nullptr) {
+      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);
+      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 -= pc_adjustment;
+
+      // If the pc is in an invalid elf file, try and get an Elf object
+      // using the jit debug information.
+      if (!elf->valid() && jit_debug_ != nullptr) {
+        uint64_t adjusted_jit_pc = regs_->pc() - pc_adjustment;
+        Elf* jit_elf = jit_debug_->GetElf(maps_, adjusted_jit_pc);
+        if (jit_elf != nullptr) {
+          // The jit debug information requires a non relative adjusted pc.
+          step_pc = adjusted_jit_pc;
+          elf = jit_elf;
+        }
+      }
+    }
+
+    if (map_info == nullptr || initial_map_names_to_skip == nullptr ||
+        std::find(initial_map_names_to_skip->begin(), initial_map_names_to_skip->end(),
+                  basename(map_info->name.c_str())) == initial_map_names_to_skip->end()) {
+      if (regs_->dex_pc() != 0) {
+        // Add a frame to represent the dex file.
+        FillInDexFrame();
+        // Clear the dex pc so that we don't repeat this frame later.
+        regs_->set_dex_pc(0);
+      }
+
+      FillInFrame(map_info, elf, rel_pc, step_pc, pc_adjustment);
+
+      // Once a frame is added, stop skipping frames.
+      initial_map_names_to_skip = nullptr;
+    }
+    adjust_pc = true;
+
+    bool stepped;
+    bool in_device_map = false;
+    if (map_info == nullptr) {
+      stepped = false;
+    } else {
+      if (map_info->flags & MAPS_FLAGS_DEVICE_MAP) {
+        // Do not stop here, fall through in case we are
+        // in the speculative unwind path and need to remove
+        // some of the speculative frames.
+        stepped = false;
+        in_device_map = true;
+      } else {
+        MapInfo* sp_info = maps_->Find(regs_->sp());
+        if (sp_info != nullptr && sp_info->flags & MAPS_FLAGS_DEVICE_MAP) {
+          // Do not stop here, fall through in case we are
+          // in the speculative unwind path and need to remove
+          // some of the speculative frames.
+          stepped = false;
+          in_device_map = true;
+        } else {
+          bool finished;
+          stepped = elf->Step(rel_pc, step_pc, regs_, process_memory_.get(), &finished);
+          elf->GetLastError(&last_error_);
+          if (stepped && finished) {
+            break;
+          }
+        }
+      }
+    }
+
+    if (!stepped) {
+      if (return_address_attempt) {
+        // Remove the speculative frame.
+        frames_.pop_back();
+        break;
+      } else if (in_device_map) {
+        // Do not attempt any other unwinding, pc or sp is in a device
+        // map.
+        break;
+      } else {
+        // Steping didn't work, try this secondary method.
+        if (!regs_->SetPcFromReturnAddress(process_memory_.get())) {
+          break;
+        }
+        return_address_attempt = true;
+      }
+    } else {
+      return_address_attempt = false;
+      if (max_frames_ == frames_.size()) {
+        last_error_.code = ERROR_MAX_FRAMES_EXCEEDED;
+      }
+    }
+
+    // If the pc and sp didn't change, then consider everything stopped.
+    if (cur_pc == regs_->pc() && cur_sp == regs_->sp()) {
+      last_error_.code = ERROR_REPEATED_FRAME;
+      break;
+    }
+  }
+}
+
+std::string Unwinder::FormatFrame(size_t frame_num) {
+  if (frame_num >= frames_.size()) {
+    return "";
+  }
+  return FormatFrame(frames_[frame_num], regs_->Is32Bit());
+}
+
+std::string Unwinder::FormatFrame(const FrameData& frame, bool is32bit) {
+  std::string data;
+
+  if (is32bit) {
+    data += android::base::StringPrintf("  #%02zu pc %08" PRIx64, frame.num, frame.rel_pc);
+  } else {
+    data += android::base::StringPrintf("  #%02zu pc %016" PRIx64, frame.num, frame.rel_pc);
+  }
+
+  if (frame.map_offset != 0) {
+    data += android::base::StringPrintf(" (offset 0x%" PRIx64 ")", frame.map_offset);
+  }
+
+  if (frame.map_start == frame.map_end) {
+    // No valid map associated with this frame.
+    data += "  <unknown>";
+  } else if (!frame.map_name.empty()) {
+    data += "  " + frame.map_name;
+  } else {
+    data += android::base::StringPrintf("  <anonymous:%" PRIx64 ">", frame.map_start);
+  }
+  if (!frame.function_name.empty()) {
+    data += " (" + frame.function_name;
+    if (frame.function_offset != 0) {
+      data += android::base::StringPrintf("+%" PRId64, frame.function_offset);
+    }
+    data += ')';
+  }
+  return data;
+}
+
+void Unwinder::SetJitDebug(JitDebug* jit_debug, ArchEnum arch) {
+  jit_debug->SetArch(arch);
+  jit_debug_ = jit_debug;
+}
+
+#if !defined(NO_LIBDEXFILE_SUPPORT)
+void Unwinder::SetDexFiles(DexFiles* dex_files, ArchEnum arch) {
+  dex_files->SetArch(arch);
+  dex_files_ = dex_files;
+}
+#endif
+
+}  // namespace unwindstack
diff --git a/libunwindstack/User.h b/libunwindstack/User.h
deleted file mode 100644
index a695467..0000000
--- a/libunwindstack/User.h
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *  * Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- *  * Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in
- *    the documentation and/or other materials provided with the
- *    distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
- * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
- * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
- * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
- * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
- * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#ifndef _LIBUNWINDSTACK_USER_H
-#define _LIBUNWINDSTACK_USER_H
-
-struct x86_user_regs {
-  uint32_t ebx;
-  uint32_t ecx;
-  uint32_t edx;
-  uint32_t esi;
-  uint32_t edi;
-  uint32_t ebp;
-  uint32_t eax;
-  uint32_t xds;
-  uint32_t xes;
-  uint32_t xfs;
-  uint32_t xgs;
-  uint32_t orig_eax;
-  uint32_t eip;
-  uint32_t xcs;
-  uint32_t eflags;
-  uint32_t esp;
-  uint32_t xss;
-};
-
-struct x86_64_user_regs {
-  uint64_t r15;
-  uint64_t r14;
-  uint64_t r13;
-  uint64_t r12;
-  uint64_t rbp;
-  uint64_t rbx;
-  uint64_t r11;
-  uint64_t r10;
-  uint64_t r9;
-  uint64_t r8;
-  uint64_t rax;
-  uint64_t rcx;
-  uint64_t rdx;
-  uint64_t rsi;
-  uint64_t rdi;
-  uint64_t orig_rax;
-  uint64_t rip;
-  uint64_t cs;
-  uint64_t eflags;
-  uint64_t rsp;
-  uint64_t ss;
-  uint64_t fs_base;
-  uint64_t gs_base;
-  uint64_t ds;
-  uint64_t es;
-  uint64_t fs;
-  uint64_t gs;
-};
-
-struct arm_user_regs {
-  uint32_t regs[18];
-};
-
-struct arm64_user_regs {
-  uint64_t regs[31];
-  uint64_t sp;
-  uint64_t pc;
-  uint64_t pstate;
-};
-
-// The largest user structure.
-constexpr size_t MAX_USER_REGS_SIZE = sizeof(arm64_user_regs) + 10;
-
-#endif  // _LIBUNWINDSTACK_USER_H
diff --git a/libunwindstack/include/unwindstack/DexFiles.h b/libunwindstack/include/unwindstack/DexFiles.h
new file mode 100644
index 0000000..26f5d35
--- /dev/null
+++ b/libunwindstack/include/unwindstack/DexFiles.h
@@ -0,0 +1,78 @@
+/*
+ * 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 _LIBUNWINDSTACK_DEX_FILES_H
+#define _LIBUNWINDSTACK_DEX_FILES_H
+
+#include <stdint.h>
+
+#include <memory>
+#include <mutex>
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+namespace unwindstack {
+
+// Forward declarations.
+class DexFile;
+class Maps;
+struct MapInfo;
+class Memory;
+enum ArchEnum : uint8_t;
+
+class DexFiles {
+ public:
+  explicit DexFiles(std::shared_ptr<Memory>& memory);
+  DexFiles(std::shared_ptr<Memory>& memory, std::vector<std::string>& search_libs);
+  ~DexFiles();
+
+  DexFile* GetDexFile(uint64_t dex_file_offset, MapInfo* info);
+
+  void GetMethodInformation(Maps* maps, MapInfo* info, uint64_t dex_pc, std::string* method_name,
+                            uint64_t* method_offset);
+
+  void SetArch(ArchEnum arch);
+
+ private:
+  void Init(Maps* maps);
+
+  bool GetAddr(size_t index, uint64_t* addr);
+
+  uint64_t ReadEntryPtr32(uint64_t addr);
+
+  uint64_t ReadEntryPtr64(uint64_t addr);
+
+  bool ReadEntry32();
+
+  bool ReadEntry64();
+
+  std::shared_ptr<Memory> memory_;
+  std::vector<std::string> search_libs_;
+
+  std::mutex lock_;
+  bool initialized_ = false;
+  std::unordered_map<uint64_t, DexFile*> files_;
+
+  uint64_t entry_addr_ = 0;
+  uint64_t (DexFiles::*read_entry_ptr_func_)(uint64_t) = nullptr;
+  bool (DexFiles::*read_entry_func_)() = nullptr;
+  std::vector<uint64_t> addrs_;
+};
+
+}  // namespace unwindstack
+
+#endif  // _LIBUNWINDSTACK_DEX_FILES_H
diff --git a/libunwindstack/include/unwindstack/DwarfError.h b/libunwindstack/include/unwindstack/DwarfError.h
new file mode 100644
index 0000000..763e2cb
--- /dev/null
+++ b/libunwindstack/include/unwindstack/DwarfError.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2016 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 _LIBUNWINDSTACK_DWARF_ERROR_H
+#define _LIBUNWINDSTACK_DWARF_ERROR_H
+
+#include <stdint.h>
+
+namespace unwindstack {
+
+enum DwarfErrorCode : uint8_t {
+  DWARF_ERROR_NONE,
+  DWARF_ERROR_MEMORY_INVALID,
+  DWARF_ERROR_ILLEGAL_VALUE,
+  DWARF_ERROR_ILLEGAL_STATE,
+  DWARF_ERROR_STACK_INDEX_NOT_VALID,
+  DWARF_ERROR_NOT_IMPLEMENTED,
+  DWARF_ERROR_TOO_MANY_ITERATIONS,
+  DWARF_ERROR_CFA_NOT_DEFINED,
+  DWARF_ERROR_UNSUPPORTED_VERSION,
+  DWARF_ERROR_NO_FDES,
+};
+
+struct DwarfErrorData {
+  DwarfErrorCode code;
+  uint64_t address;
+};
+
+}  // namespace unwindstack
+
+#endif  // _LIBUNWINDSTACK_DWARF_ERROR_H
diff --git a/libunwindstack/include/unwindstack/DwarfLocation.h b/libunwindstack/include/unwindstack/DwarfLocation.h
new file mode 100644
index 0000000..3d50ccf
--- /dev/null
+++ b/libunwindstack/include/unwindstack/DwarfLocation.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2016 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 _LIBUNWINDSTACK_DWARF_LOCATION_H
+#define _LIBUNWINDSTACK_DWARF_LOCATION_H
+
+#include <stdint.h>
+
+#include <unordered_map>
+
+namespace unwindstack {
+
+struct DwarfCie;
+
+enum DwarfLocationEnum : uint8_t {
+  DWARF_LOCATION_INVALID = 0,
+  DWARF_LOCATION_UNDEFINED,
+  DWARF_LOCATION_OFFSET,
+  DWARF_LOCATION_VAL_OFFSET,
+  DWARF_LOCATION_REGISTER,
+  DWARF_LOCATION_EXPRESSION,
+  DWARF_LOCATION_VAL_EXPRESSION,
+};
+
+struct DwarfLocation {
+  DwarfLocationEnum type;
+  uint64_t values[2];
+};
+
+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
+
+#endif  // _LIBUNWINDSTACK_DWARF_LOCATION_H
diff --git a/libunwindstack/include/unwindstack/DwarfMemory.h b/libunwindstack/include/unwindstack/DwarfMemory.h
new file mode 100644
index 0000000..8dd8d2b
--- /dev/null
+++ b/libunwindstack/include/unwindstack/DwarfMemory.h
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2016 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 _LIBUNWINDSTACK_DWARF_MEMORY_H
+#define _LIBUNWINDSTACK_DWARF_MEMORY_H
+
+#include <stdint.h>
+
+namespace unwindstack {
+
+// Forward declarations.
+class Memory;
+
+class DwarfMemory {
+ public:
+  DwarfMemory(Memory* memory) : memory_(memory) {}
+  virtual ~DwarfMemory() = default;
+
+  bool ReadBytes(void* dst, size_t num_bytes);
+
+  template <typename SignedType>
+  bool ReadSigned(uint64_t* value);
+
+  bool ReadULEB128(uint64_t* value);
+
+  bool ReadSLEB128(int64_t* value);
+
+  template <typename AddressType>
+  size_t GetEncodedSize(uint8_t encoding);
+
+  bool AdjustEncodedValue(uint8_t encoding, uint64_t* value);
+
+  template <typename AddressType>
+  bool ReadEncodedValue(uint8_t encoding, uint64_t* value);
+
+  uint64_t cur_offset() { return cur_offset_; }
+  void set_cur_offset(uint64_t cur_offset) { cur_offset_ = cur_offset; }
+
+  void set_pc_offset(uint64_t offset) { pc_offset_ = offset; }
+  void clear_pc_offset() { pc_offset_ = static_cast<uint64_t>(-1); }
+
+  void set_data_offset(uint64_t offset) { data_offset_ = offset; }
+  void clear_data_offset() { data_offset_ = static_cast<uint64_t>(-1); }
+
+  void set_func_offset(uint64_t offset) { func_offset_ = offset; }
+  void clear_func_offset() { func_offset_ = static_cast<uint64_t>(-1); }
+
+  void set_text_offset(uint64_t offset) { text_offset_ = offset; }
+  void clear_text_offset() { text_offset_ = static_cast<uint64_t>(-1); }
+
+ private:
+  Memory* memory_;
+  uint64_t cur_offset_ = 0;
+
+  uint64_t pc_offset_ = static_cast<uint64_t>(-1);
+  uint64_t data_offset_ = static_cast<uint64_t>(-1);
+  uint64_t func_offset_ = static_cast<uint64_t>(-1);
+  uint64_t text_offset_ = static_cast<uint64_t>(-1);
+};
+
+}  // namespace unwindstack
+
+#endif  // _LIBUNWINDSTACK_DWARF_MEMORY_H
diff --git a/libunwindstack/include/unwindstack/DwarfSection.h b/libunwindstack/include/unwindstack/DwarfSection.h
new file mode 100644
index 0000000..e9942de
--- /dev/null
+++ b/libunwindstack/include/unwindstack/DwarfSection.h
@@ -0,0 +1,196 @@
+/*
+ * Copyright (C) 2016 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 _LIBUNWINDSTACK_DWARF_SECTION_H
+#define _LIBUNWINDSTACK_DWARF_SECTION_H
+
+#include <stdint.h>
+
+#include <iterator>
+#include <map>
+#include <unordered_map>
+
+#include <unwindstack/DwarfError.h>
+#include <unwindstack/DwarfLocation.h>
+#include <unwindstack/DwarfMemory.h>
+#include <unwindstack/DwarfStructs.h>
+
+namespace unwindstack {
+
+// Forward declarations.
+class Memory;
+class Regs;
+template <typename AddressType>
+struct RegsInfo;
+
+class DwarfSection {
+ public:
+  DwarfSection(Memory* memory);
+  virtual ~DwarfSection() = default;
+
+  class iterator : public std::iterator<std::bidirectional_iterator_tag, DwarfFde*> {
+   public:
+    iterator(DwarfSection* section, size_t index) : index_(index) {
+      section->GetFdes(&fdes_);
+      if (index_ == static_cast<size_t>(-1)) {
+        index_ = fdes_.size();
+      }
+    }
+
+    iterator& operator++() {
+      index_++;
+      return *this;
+    }
+    iterator& operator++(int increment) {
+      index_ += increment;
+      return *this;
+    }
+    iterator& operator--() {
+      index_--;
+      return *this;
+    }
+    iterator& operator--(int decrement) {
+      index_ -= decrement;
+      return *this;
+    }
+
+    bool operator==(const iterator& rhs) { return this->index_ == rhs.index_; }
+    bool operator!=(const iterator& rhs) { return this->index_ != rhs.index_; }
+
+    const DwarfFde* operator*() {
+      if (index_ > fdes_.size()) return nullptr;
+      return fdes_[index_];
+    }
+
+   private:
+    std::vector<const DwarfFde*> fdes_;
+    size_t index_ = 0;
+  };
+
+  iterator begin() { return iterator(this, 0); }
+  iterator end() { return iterator(this, static_cast<size_t>(-1)); }
+
+  DwarfErrorCode LastErrorCode() { return last_error_.code; }
+  uint64_t LastErrorAddress() { return last_error_.address; }
+
+  virtual bool Init(uint64_t offset, uint64_t size, uint64_t load_bias) = 0;
+
+  virtual bool Eval(const DwarfCie*, Memory*, const dwarf_loc_regs_t&, Regs*, bool*) = 0;
+
+  virtual bool Log(uint8_t indent, uint64_t pc, const DwarfFde* fde) = 0;
+
+  virtual void GetFdes(std::vector<const DwarfFde*>* fdes) = 0;
+
+  virtual const DwarfFde* GetFdeFromPc(uint64_t pc) = 0;
+
+  virtual bool GetCfaLocationInfo(uint64_t pc, const DwarfFde* fde, dwarf_loc_regs_t* loc_regs) = 0;
+
+  virtual uint64_t GetCieOffsetFromFde32(uint32_t pointer) = 0;
+
+  virtual uint64_t GetCieOffsetFromFde64(uint64_t pointer) = 0;
+
+  virtual uint64_t AdjustPcFromFde(uint64_t pc) = 0;
+
+  bool Step(uint64_t pc, Regs* regs, Memory* process_memory, bool* finished);
+
+ protected:
+  DwarfMemory memory_;
+  DwarfErrorData last_error_{DWARF_ERROR_NONE, 0};
+
+  uint32_t cie32_value_ = 0;
+  uint64_t cie64_value_ = 0;
+
+  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>
+class DwarfSectionImpl : public DwarfSection {
+ public:
+  DwarfSectionImpl(Memory* memory) : DwarfSection(memory) {}
+  virtual ~DwarfSectionImpl() = default;
+
+  const DwarfCie* GetCieFromOffset(uint64_t offset);
+
+  const DwarfFde* GetFdeFromOffset(uint64_t offset);
+
+  bool EvalRegister(const DwarfLocation* loc, uint32_t reg, AddressType* reg_ptr, void* info);
+
+  bool Eval(const DwarfCie* cie, Memory* regular_memory, const dwarf_loc_regs_t& loc_regs,
+            Regs* regs, bool* finished) override;
+
+  bool GetCfaLocationInfo(uint64_t pc, const DwarfFde* fde, dwarf_loc_regs_t* loc_regs) override;
+
+  bool Log(uint8_t indent, uint64_t pc, const DwarfFde* fde) override;
+
+ protected:
+  bool FillInCieHeader(DwarfCie* cie);
+
+  bool FillInCie(DwarfCie* cie);
+
+  bool FillInFdeHeader(DwarfFde* fde);
+
+  bool FillInFde(DwarfFde* fde);
+
+  bool EvalExpression(const DwarfLocation& loc, Memory* regular_memory, AddressType* value,
+                      RegsInfo<AddressType>* regs_info, bool* is_dex_pc);
+
+  uint64_t load_bias_ = 0;
+  uint64_t entries_offset_ = 0;
+  uint64_t entries_end_ = 0;
+  uint64_t pc_offset_ = 0;
+};
+
+template <typename AddressType>
+class DwarfSectionImplNoHdr : public DwarfSectionImpl<AddressType> {
+ public:
+  // Add these so that the protected members of DwarfSectionImpl
+  // can be accessed without needing a this->.
+  using DwarfSectionImpl<AddressType>::memory_;
+  using DwarfSectionImpl<AddressType>::pc_offset_;
+  using DwarfSectionImpl<AddressType>::entries_offset_;
+  using DwarfSectionImpl<AddressType>::entries_end_;
+  using DwarfSectionImpl<AddressType>::last_error_;
+  using DwarfSectionImpl<AddressType>::load_bias_;
+  using DwarfSectionImpl<AddressType>::cie_entries_;
+  using DwarfSectionImpl<AddressType>::fde_entries_;
+  using DwarfSectionImpl<AddressType>::cie32_value_;
+  using DwarfSectionImpl<AddressType>::cie64_value_;
+
+  DwarfSectionImplNoHdr(Memory* memory) : DwarfSectionImpl<AddressType>(memory) {}
+  virtual ~DwarfSectionImplNoHdr() = default;
+
+  bool Init(uint64_t offset, uint64_t size, uint64_t load_bias) override;
+
+  const DwarfFde* GetFdeFromPc(uint64_t pc) override;
+
+  void GetFdes(std::vector<const DwarfFde*>* fdes) override;
+
+ protected:
+  bool GetNextCieOrFde(DwarfFde** fde_entry);
+
+  void InsertFde(const DwarfFde* fde);
+
+  uint64_t next_entries_offset_ = 0;
+
+  std::map<uint64_t, std::pair<uint64_t, const DwarfFde*>> fdes_;
+};
+
+}  // namespace unwindstack
+
+#endif  // _LIBUNWINDSTACK_DWARF_SECTION_H
diff --git a/libunwindstack/include/unwindstack/DwarfStructs.h b/libunwindstack/include/unwindstack/DwarfStructs.h
new file mode 100644
index 0000000..4b481f0
--- /dev/null
+++ b/libunwindstack/include/unwindstack/DwarfStructs.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2016 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 _LIBUNWINDSTACK_DWARF_STRUCTS_H
+#define _LIBUNWINDSTACK_DWARF_STRUCTS_H
+
+#include <stdint.h>
+
+#include <vector>
+
+namespace unwindstack {
+
+struct DwarfCie {
+  uint8_t version = 0;
+  uint8_t fde_address_encoding = 0;
+  uint8_t lsda_encoding = 0;
+  uint8_t segment_size = 0;
+  std::vector<char> augmentation_string;
+  uint64_t personality_handler = 0;
+  uint64_t cfa_instructions_offset = 0;
+  uint64_t cfa_instructions_end = 0;
+  uint64_t code_alignment_factor = 0;
+  int64_t data_alignment_factor = 0;
+  uint64_t return_address_register = 0;
+};
+
+struct DwarfFde {
+  uint64_t cie_offset = 0;
+  uint64_t cfa_instructions_offset = 0;
+  uint64_t cfa_instructions_end = 0;
+  uint64_t pc_start = 0;
+  uint64_t pc_end = 0;
+  uint64_t lsda_address = 0;
+  const DwarfCie* cie = nullptr;
+};
+
+constexpr uint16_t CFA_REG = static_cast<uint16_t>(-1);
+
+}  // namespace unwindstack
+
+#endif  // _LIBUNWINDSTACK_DWARF_STRUCTS_H
diff --git a/libunwindstack/include/unwindstack/Elf.h b/libunwindstack/include/unwindstack/Elf.h
new file mode 100644
index 0000000..f4cdbda
--- /dev/null
+++ b/libunwindstack/include/unwindstack/Elf.h
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2016 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 _LIBUNWINDSTACK_ELF_H
+#define _LIBUNWINDSTACK_ELF_H
+
+#include <stddef.h>
+
+#include <memory>
+#include <mutex>
+#include <string>
+#include <unordered_map>
+#include <utility>
+
+#include <unwindstack/ElfInterface.h>
+#include <unwindstack/Memory.h>
+
+#if !defined(EM_AARCH64)
+#define EM_AARCH64 183
+#endif
+
+namespace unwindstack {
+
+// Forward declaration.
+struct MapInfo;
+class Regs;
+
+enum ArchEnum : uint8_t {
+  ARCH_UNKNOWN = 0,
+  ARCH_ARM,
+  ARCH_ARM64,
+  ARCH_X86,
+  ARCH_X86_64,
+  ARCH_MIPS,
+  ARCH_MIPS64,
+};
+
+class Elf {
+ public:
+  Elf(Memory* memory) : memory_(memory) {}
+  virtual ~Elf() = default;
+
+  bool Init(bool init_gnu_debugdata);
+
+  void InitGnuDebugdata();
+
+  bool GetSoname(std::string* name);
+
+  bool GetFunctionName(uint64_t addr, std::string* name, uint64_t* func_offset);
+
+  bool GetGlobalVariable(const std::string& name, uint64_t* memory_address);
+
+  uint64_t GetRelPc(uint64_t pc, const MapInfo* map_info);
+
+  bool Step(uint64_t rel_pc, uint64_t adjusted_rel_pc, Regs* regs, Memory* process_memory,
+            bool* finished);
+
+  ElfInterface* CreateInterfaceFromMemory(Memory* memory);
+
+  uint64_t GetLoadBias() { return load_bias_; }
+
+  bool IsValidPc(uint64_t pc);
+
+  void GetLastError(ErrorData* data);
+  ErrorCode GetLastErrorCode();
+  uint64_t GetLastErrorAddress();
+
+  bool valid() { return valid_; }
+
+  uint32_t machine_type() { return machine_type_; }
+
+  uint8_t class_type() { return class_type_; }
+
+  ArchEnum arch() { return arch_; }
+
+  Memory* memory() { return memory_.get(); }
+
+  ElfInterface* interface() { return interface_.get(); }
+
+  ElfInterface* gnu_debugdata_interface() { return gnu_debugdata_interface_.get(); }
+
+  static bool IsValidElf(Memory* memory);
+
+  static void GetInfo(Memory* memory, bool* valid, uint64_t* size);
+
+  static uint64_t GetLoadBias(Memory* memory);
+
+  static void SetCachingEnabled(bool enable);
+  static bool CachingEnabled() { return cache_enabled_; }
+
+  static void CacheLock();
+  static void CacheUnlock();
+  static void CacheAdd(MapInfo* info);
+  static bool CacheGet(MapInfo* info);
+  static bool CacheAfterCreateMemory(MapInfo* info);
+
+ protected:
+  bool valid_ = false;
+  uint64_t load_bias_ = 0;
+  std::unique_ptr<ElfInterface> interface_;
+  std::unique_ptr<Memory> memory_;
+  uint32_t machine_type_;
+  uint8_t class_type_;
+  ArchEnum arch_;
+  // Protect calls that can modify internal state of the interface object.
+  std::mutex lock_;
+
+  std::unique_ptr<Memory> gnu_debugdata_memory_;
+  std::unique_ptr<ElfInterface> gnu_debugdata_interface_;
+
+  static bool cache_enabled_;
+  static std::unordered_map<std::string, std::pair<std::shared_ptr<Elf>, bool>>* cache_;
+  static std::mutex* cache_lock_;
+};
+
+}  // namespace unwindstack
+
+#endif  // _LIBUNWINDSTACK_ELF_H
diff --git a/libunwindstack/include/unwindstack/ElfInterface.h b/libunwindstack/include/unwindstack/ElfInterface.h
new file mode 100644
index 0000000..5c1210d
--- /dev/null
+++ b/libunwindstack/include/unwindstack/ElfInterface.h
@@ -0,0 +1,222 @@
+/*
+ * Copyright (C) 2016 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 _LIBUNWINDSTACK_ELF_INTERFACE_H
+#define _LIBUNWINDSTACK_ELF_INTERFACE_H
+
+#include <elf.h>
+#include <stdint.h>
+
+#include <memory>
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+#include <unwindstack/DwarfSection.h>
+#include <unwindstack/Error.h>
+
+namespace unwindstack {
+
+// Forward declarations.
+class Memory;
+class Regs;
+class Symbols;
+
+struct LoadInfo {
+  uint64_t offset;
+  uint64_t table_offset;
+  size_t table_size;
+};
+
+enum : uint8_t {
+  SONAME_UNKNOWN = 0,
+  SONAME_VALID,
+  SONAME_INVALID,
+};
+
+class ElfInterface {
+ public:
+  ElfInterface(Memory* memory) : memory_(memory) {}
+  virtual ~ElfInterface();
+
+  virtual bool Init(uint64_t* load_bias) = 0;
+
+  virtual void InitHeaders(uint64_t load_bias) = 0;
+
+  virtual bool GetSoname(std::string* name) = 0;
+
+  virtual bool GetFunctionName(uint64_t addr, std::string* name, uint64_t* offset) = 0;
+
+  virtual bool GetGlobalVariable(const std::string& name, uint64_t* memory_address) = 0;
+
+  virtual bool Step(uint64_t rel_pc, Regs* regs, Memory* process_memory, bool* finished);
+
+  virtual bool IsValidPc(uint64_t pc);
+
+  Memory* CreateGnuDebugdataMemory();
+
+  Memory* memory() { return memory_; }
+
+  const std::unordered_map<uint64_t, LoadInfo>& pt_loads() { return pt_loads_; }
+
+  void SetGnuDebugdataInterface(ElfInterface* interface) { gnu_debugdata_interface_ = interface; }
+
+  uint64_t dynamic_offset() { return dynamic_offset_; }
+  uint64_t dynamic_vaddr() { return dynamic_vaddr_; }
+  uint64_t dynamic_size() { return dynamic_size_; }
+  uint64_t eh_frame_hdr_offset() { return eh_frame_hdr_offset_; }
+  uint64_t eh_frame_hdr_size() { return eh_frame_hdr_size_; }
+  uint64_t eh_frame_offset() { return eh_frame_offset_; }
+  uint64_t eh_frame_size() { return eh_frame_size_; }
+  uint64_t debug_frame_offset() { return debug_frame_offset_; }
+  uint64_t debug_frame_size() { return debug_frame_size_; }
+  uint64_t gnu_debugdata_offset() { return gnu_debugdata_offset_; }
+  uint64_t gnu_debugdata_size() { return gnu_debugdata_size_; }
+
+  DwarfSection* eh_frame() { return eh_frame_.get(); }
+  DwarfSection* debug_frame() { return debug_frame_.get(); }
+
+  const ErrorData& last_error() { return last_error_; }
+  ErrorCode LastErrorCode() { return last_error_.code; }
+  uint64_t LastErrorAddress() { return last_error_.address; }
+
+  template <typename EhdrType, typename PhdrType>
+  static uint64_t GetLoadBias(Memory* memory);
+
+ protected:
+  template <typename AddressType>
+  void InitHeadersWithTemplate(uint64_t load_bias);
+
+  template <typename EhdrType, typename PhdrType, typename ShdrType>
+  bool ReadAllHeaders(uint64_t* load_bias);
+
+  template <typename EhdrType, typename PhdrType>
+  bool ReadProgramHeaders(const EhdrType& ehdr, uint64_t* load_bias);
+
+  template <typename EhdrType, typename ShdrType>
+  bool ReadSectionHeaders(const EhdrType& ehdr);
+
+  template <typename DynType>
+  bool GetSonameWithTemplate(std::string* soname);
+
+  template <typename SymType>
+  bool GetFunctionNameWithTemplate(uint64_t addr, std::string* name, uint64_t* func_offset);
+
+  template <typename SymType>
+  bool GetGlobalVariableWithTemplate(const std::string& name, uint64_t* memory_address);
+
+  virtual void HandleUnknownType(uint32_t, uint64_t, uint64_t) {}
+
+  template <typename EhdrType>
+  static void GetMaxSizeWithTemplate(Memory* memory, uint64_t* size);
+
+  Memory* memory_;
+  std::unordered_map<uint64_t, LoadInfo> pt_loads_;
+
+  // Stored elf data.
+  uint64_t dynamic_offset_ = 0;
+  uint64_t dynamic_vaddr_ = 0;
+  uint64_t dynamic_size_ = 0;
+
+  uint64_t eh_frame_hdr_offset_ = 0;
+  uint64_t eh_frame_hdr_size_ = 0;
+
+  uint64_t eh_frame_offset_ = 0;
+  uint64_t eh_frame_size_ = 0;
+
+  uint64_t debug_frame_offset_ = 0;
+  uint64_t debug_frame_size_ = 0;
+
+  uint64_t gnu_debugdata_offset_ = 0;
+  uint64_t gnu_debugdata_size_ = 0;
+
+  uint8_t soname_type_ = SONAME_UNKNOWN;
+  std::string soname_;
+
+  ErrorData last_error_{ERROR_NONE, 0};
+
+  std::unique_ptr<DwarfSection> eh_frame_;
+  std::unique_ptr<DwarfSection> debug_frame_;
+  // The Elf object owns the gnu_debugdata interface object.
+  ElfInterface* gnu_debugdata_interface_ = nullptr;
+
+  std::vector<Symbols*> symbols_;
+  std::vector<std::pair<uint64_t, uint64_t>> strtabs_;
+};
+
+class ElfInterface32 : public ElfInterface {
+ public:
+  ElfInterface32(Memory* memory) : ElfInterface(memory) {}
+  virtual ~ElfInterface32() = default;
+
+  bool Init(uint64_t* load_bias) override {
+    return ElfInterface::ReadAllHeaders<Elf32_Ehdr, Elf32_Phdr, Elf32_Shdr>(load_bias);
+  }
+
+  void InitHeaders(uint64_t load_bias) override {
+    ElfInterface::InitHeadersWithTemplate<uint32_t>(load_bias);
+  }
+
+  bool GetSoname(std::string* soname) override {
+    return ElfInterface::GetSonameWithTemplate<Elf32_Dyn>(soname);
+  }
+
+  bool GetFunctionName(uint64_t addr, std::string* name, uint64_t* func_offset) override {
+    return ElfInterface::GetFunctionNameWithTemplate<Elf32_Sym>(addr, name, func_offset);
+  }
+
+  bool GetGlobalVariable(const std::string& name, uint64_t* memory_address) override {
+    return ElfInterface::GetGlobalVariableWithTemplate<Elf32_Sym>(name, memory_address);
+  }
+
+  static void GetMaxSize(Memory* memory, uint64_t* size) {
+    GetMaxSizeWithTemplate<Elf32_Ehdr>(memory, size);
+  }
+};
+
+class ElfInterface64 : public ElfInterface {
+ public:
+  ElfInterface64(Memory* memory) : ElfInterface(memory) {}
+  virtual ~ElfInterface64() = default;
+
+  bool Init(uint64_t* load_bias) override {
+    return ElfInterface::ReadAllHeaders<Elf64_Ehdr, Elf64_Phdr, Elf64_Shdr>(load_bias);
+  }
+
+  void InitHeaders(uint64_t load_bias) override {
+    ElfInterface::InitHeadersWithTemplate<uint64_t>(load_bias);
+  }
+
+  bool GetSoname(std::string* soname) override {
+    return ElfInterface::GetSonameWithTemplate<Elf64_Dyn>(soname);
+  }
+
+  bool GetFunctionName(uint64_t addr, std::string* name, uint64_t* func_offset) override {
+    return ElfInterface::GetFunctionNameWithTemplate<Elf64_Sym>(addr, name, func_offset);
+  }
+
+  bool GetGlobalVariable(const std::string& name, uint64_t* memory_address) override {
+    return ElfInterface::GetGlobalVariableWithTemplate<Elf64_Sym>(name, memory_address);
+  }
+
+  static void GetMaxSize(Memory* memory, uint64_t* size) {
+    GetMaxSizeWithTemplate<Elf64_Ehdr>(memory, size);
+  }
+};
+
+}  // namespace unwindstack
+
+#endif  // _LIBUNWINDSTACK_ELF_INTERFACE_H
diff --git a/libunwindstack/include/unwindstack/Error.h b/libunwindstack/include/unwindstack/Error.h
new file mode 100644
index 0000000..6ed0e0f
--- /dev/null
+++ b/libunwindstack/include/unwindstack/Error.h
@@ -0,0 +1,42 @@
+/*
+ * 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 _LIBUNWINDSTACK_ERROR_H
+#define _LIBUNWINDSTACK_ERROR_H
+
+#include <stdint.h>
+
+namespace unwindstack {
+
+enum ErrorCode : uint8_t {
+  ERROR_NONE,                 // No error.
+  ERROR_MEMORY_INVALID,       // Memory read failed.
+  ERROR_UNWIND_INFO,          // Unable to use unwind information to unwind.
+  ERROR_UNSUPPORTED,          // Encountered unsupported feature.
+  ERROR_INVALID_MAP,          // Unwind in an invalid map.
+  ERROR_MAX_FRAMES_EXCEEDED,  // The number of frames exceed the total allowed.
+  ERROR_REPEATED_FRAME,       // The last frame has the same pc/sp as the next.
+};
+
+struct ErrorData {
+  ErrorCode code;
+  uint64_t address;  // Only valid when code is ERROR_MEMORY_INVALID.
+                     // Indicates the failing address.
+};
+
+}  // namespace unwindstack
+
+#endif  // _LIBUNWINDSTACK_ERROR_H
diff --git a/libunwindstack/include/unwindstack/JitDebug.h b/libunwindstack/include/unwindstack/JitDebug.h
new file mode 100644
index 0000000..0bcd0b0
--- /dev/null
+++ b/libunwindstack/include/unwindstack/JitDebug.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2017 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 _LIBUNWINDSTACK_JIT_DEBUG_H
+#define _LIBUNWINDSTACK_JIT_DEBUG_H
+
+#include <stdint.h>
+
+#include <memory>
+#include <mutex>
+#include <string>
+#include <vector>
+
+namespace unwindstack {
+
+// Forward declarations.
+class Elf;
+class Maps;
+class Memory;
+enum ArchEnum : uint8_t;
+
+class JitDebug {
+ public:
+  explicit JitDebug(std::shared_ptr<Memory>& memory);
+  JitDebug(std::shared_ptr<Memory>& memory, std::vector<std::string>& search_libs);
+  ~JitDebug();
+
+  Elf* GetElf(Maps* maps, uint64_t pc);
+
+  void SetArch(ArchEnum arch);
+
+ private:
+  void Init(Maps* maps);
+
+  std::shared_ptr<Memory> memory_;
+  uint64_t entry_addr_ = 0;
+  bool initialized_ = false;
+  std::vector<Elf*> elf_list_;
+  std::vector<std::string> search_libs_;
+
+  std::mutex lock_;
+
+  uint64_t (JitDebug::*read_descriptor_func_)(uint64_t) = nullptr;
+  uint64_t (JitDebug::*read_entry_func_)(uint64_t*, uint64_t*) = nullptr;
+
+  uint64_t ReadDescriptor32(uint64_t);
+  uint64_t ReadDescriptor64(uint64_t);
+
+  uint64_t ReadEntry32Pack(uint64_t* start, uint64_t* size);
+  uint64_t ReadEntry32Pad(uint64_t* start, uint64_t* size);
+  uint64_t ReadEntry64(uint64_t* start, uint64_t* size);
+};
+
+}  // namespace unwindstack
+
+#endif  // _LIBUNWINDSTACK_JIT_DEBUG_H
diff --git a/libunwindstack/include/unwindstack/LocalUnwinder.h b/libunwindstack/include/unwindstack/LocalUnwinder.h
new file mode 100644
index 0000000..80bb53e
--- /dev/null
+++ b/libunwindstack/include/unwindstack/LocalUnwinder.h
@@ -0,0 +1,86 @@
+/*
+ * 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 _LIBUNWINDSTACK_LOCAL_UNWINDER_H
+#define _LIBUNWINDSTACK_LOCAL_UNWINDER_H
+
+#include <pthread.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <unwindstack/Error.h>
+#include <unwindstack/Maps.h>
+#include <unwindstack/Memory.h>
+
+namespace unwindstack {
+
+// Forward declarations.
+class Elf;
+struct MapInfo;
+
+struct LocalFrameData {
+  LocalFrameData(MapInfo* map_info, uint64_t pc, uint64_t rel_pc, const std::string& function_name,
+                 uint64_t function_offset)
+      : map_info(map_info),
+        pc(pc),
+        rel_pc(rel_pc),
+        function_name(function_name),
+        function_offset(function_offset) {}
+
+  MapInfo* map_info;
+  uint64_t pc;
+  uint64_t rel_pc;
+  std::string function_name;
+  uint64_t function_offset;
+};
+
+// This is a specialized class that should only be used for doing local unwinds.
+// The Unwind call can be made as multiple times on the same object, and it can
+// be called by multiple threads at the same time.
+// It is designed to be used in debugging circumstances to get a stack trace
+// as fast as possible.
+class LocalUnwinder {
+ public:
+  LocalUnwinder() = default;
+  LocalUnwinder(const std::vector<std::string>& skip_libraries) : skip_libraries_(skip_libraries) {}
+  ~LocalUnwinder() = default;
+
+  bool Init();
+
+  bool Unwind(std::vector<LocalFrameData>* frame_info, size_t max_frames);
+
+  bool ShouldSkipLibrary(const std::string& map_name);
+
+  MapInfo* GetMapInfo(uint64_t pc);
+
+  ErrorCode LastErrorCode() { return last_error_.code; }
+  uint64_t LastErrorAddress() { return last_error_.address; }
+
+ private:
+  pthread_rwlock_t maps_rwlock_;
+  std::unique_ptr<LocalUpdatableMaps> maps_ = nullptr;
+  std::shared_ptr<Memory> process_memory_;
+  std::vector<std::string> skip_libraries_;
+  ErrorData last_error_;
+};
+
+}  // namespace unwindstack
+
+#endif  // _LIBUNWINDSTACK_LOCAL_UNWINDER_H
diff --git a/libunwindstack/include/unwindstack/Log.h b/libunwindstack/include/unwindstack/Log.h
new file mode 100644
index 0000000..aa1219c
--- /dev/null
+++ b/libunwindstack/include/unwindstack/Log.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2016 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 _LIBUNWINDSTACK_LOG_H
+#define _LIBUNWINDSTACK_LOG_H
+
+#include <stdint.h>
+
+namespace unwindstack {
+
+void log_to_stdout(bool enable);
+void log(uint8_t indent, const char* format, ...);
+
+}  // namespace unwindstack
+
+#endif  // _LIBUNWINDSTACK_LOG_H
diff --git a/libunwindstack/include/unwindstack/MachineArm.h b/libunwindstack/include/unwindstack/MachineArm.h
new file mode 100644
index 0000000..3f902b1
--- /dev/null
+++ b/libunwindstack/include/unwindstack/MachineArm.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2017 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 _LIBUNWINDSTACK_MACHINE_ARM_H
+#define _LIBUNWINDSTACK_MACHINE_ARM_H
+
+#include <stdint.h>
+
+namespace unwindstack {
+
+enum ArmReg : uint16_t {
+  ARM_REG_R0 = 0,
+  ARM_REG_R1,
+  ARM_REG_R2,
+  ARM_REG_R3,
+  ARM_REG_R4,
+  ARM_REG_R5,
+  ARM_REG_R6,
+  ARM_REG_R7,
+  ARM_REG_R8,
+  ARM_REG_R9,
+  ARM_REG_R10,
+  ARM_REG_R11,
+  ARM_REG_R12,
+  ARM_REG_R13,
+  ARM_REG_R14,
+  ARM_REG_R15,
+  ARM_REG_LAST,
+
+  ARM_REG_SP = ARM_REG_R13,
+  ARM_REG_LR = ARM_REG_R14,
+  ARM_REG_PC = ARM_REG_R15,
+};
+
+}  // namespace unwindstack
+
+#endif  // _LIBUNWINDSTACK_MACHINE_ARM_H
diff --git a/libunwindstack/include/unwindstack/MachineArm64.h b/libunwindstack/include/unwindstack/MachineArm64.h
new file mode 100644
index 0000000..e8b778b
--- /dev/null
+++ b/libunwindstack/include/unwindstack/MachineArm64.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2017 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 _LIBUNWINDSTACK_MACHINE_ARM64_H
+#define _LIBUNWINDSTACK_MACHINE_ARM64_H
+
+#include <stdint.h>
+
+namespace unwindstack {
+
+enum Arm64Reg : uint16_t {
+  ARM64_REG_R0 = 0,
+  ARM64_REG_R1,
+  ARM64_REG_R2,
+  ARM64_REG_R3,
+  ARM64_REG_R4,
+  ARM64_REG_R5,
+  ARM64_REG_R6,
+  ARM64_REG_R7,
+  ARM64_REG_R8,
+  ARM64_REG_R9,
+  ARM64_REG_R10,
+  ARM64_REG_R11,
+  ARM64_REG_R12,
+  ARM64_REG_R13,
+  ARM64_REG_R14,
+  ARM64_REG_R15,
+  ARM64_REG_R16,
+  ARM64_REG_R17,
+  ARM64_REG_R18,
+  ARM64_REG_R19,
+  ARM64_REG_R20,
+  ARM64_REG_R21,
+  ARM64_REG_R22,
+  ARM64_REG_R23,
+  ARM64_REG_R24,
+  ARM64_REG_R25,
+  ARM64_REG_R26,
+  ARM64_REG_R27,
+  ARM64_REG_R28,
+  ARM64_REG_R29,
+  ARM64_REG_R30,
+  ARM64_REG_R31,
+  ARM64_REG_PC,
+  ARM64_REG_LAST,
+
+  ARM64_REG_SP = ARM64_REG_R31,
+  ARM64_REG_LR = ARM64_REG_R30,
+};
+
+}  // namespace unwindstack
+
+#endif  // _LIBUNWINDSTACK_MACHINE_ARM64_H
diff --git a/libunwindstack/include/unwindstack/MachineMips.h b/libunwindstack/include/unwindstack/MachineMips.h
new file mode 100644
index 0000000..2dfb1e9
--- /dev/null
+++ b/libunwindstack/include/unwindstack/MachineMips.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2017 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 _LIBUNWINDSTACK_MACHINE_MIPS_H
+#define _LIBUNWINDSTACK_MACHINE_MIPS_H
+
+#include <stdint.h>
+
+namespace unwindstack {
+
+enum MipsReg : uint16_t {
+  MIPS_REG_R0 = 0,
+  MIPS_REG_R1,
+  MIPS_REG_R2,
+  MIPS_REG_R3,
+  MIPS_REG_R4,
+  MIPS_REG_R5,
+  MIPS_REG_R6,
+  MIPS_REG_R7,
+  MIPS_REG_R8,
+  MIPS_REG_R9,
+  MIPS_REG_R10,
+  MIPS_REG_R11,
+  MIPS_REG_R12,
+  MIPS_REG_R13,
+  MIPS_REG_R14,
+  MIPS_REG_R15,
+  MIPS_REG_R16,
+  MIPS_REG_R17,
+  MIPS_REG_R18,
+  MIPS_REG_R19,
+  MIPS_REG_R20,
+  MIPS_REG_R21,
+  MIPS_REG_R22,
+  MIPS_REG_R23,
+  MIPS_REG_R24,
+  MIPS_REG_R25,
+  MIPS_REG_R26,
+  MIPS_REG_R27,
+  MIPS_REG_R28,
+  MIPS_REG_R29,
+  MIPS_REG_R30,
+  MIPS_REG_R31,
+  MIPS_REG_PC,
+  MIPS_REG_LAST,
+
+  MIPS_REG_SP = MIPS_REG_R29,
+  MIPS_REG_RA = MIPS_REG_R31,
+};
+
+}  // namespace unwindstack
+
+#endif  // _LIBUNWINDSTACK_MACHINE_MIPS_H
\ No newline at end of file
diff --git a/libunwindstack/include/unwindstack/MachineMips64.h b/libunwindstack/include/unwindstack/MachineMips64.h
new file mode 100644
index 0000000..34addf2
--- /dev/null
+++ b/libunwindstack/include/unwindstack/MachineMips64.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2017 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 _LIBUNWINDSTACK_MACHINE_MIPS64_H
+#define _LIBUNWINDSTACK_MACHINE_MIPS64_H
+
+#include <stdint.h>
+
+namespace unwindstack {
+
+enum Mips64Reg : uint16_t {
+  MIPS64_REG_R0 = 0,
+  MIPS64_REG_R1,
+  MIPS64_REG_R2,
+  MIPS64_REG_R3,
+  MIPS64_REG_R4,
+  MIPS64_REG_R5,
+  MIPS64_REG_R6,
+  MIPS64_REG_R7,
+  MIPS64_REG_R8,
+  MIPS64_REG_R9,
+  MIPS64_REG_R10,
+  MIPS64_REG_R11,
+  MIPS64_REG_R12,
+  MIPS64_REG_R13,
+  MIPS64_REG_R14,
+  MIPS64_REG_R15,
+  MIPS64_REG_R16,
+  MIPS64_REG_R17,
+  MIPS64_REG_R18,
+  MIPS64_REG_R19,
+  MIPS64_REG_R20,
+  MIPS64_REG_R21,
+  MIPS64_REG_R22,
+  MIPS64_REG_R23,
+  MIPS64_REG_R24,
+  MIPS64_REG_R25,
+  MIPS64_REG_R26,
+  MIPS64_REG_R27,
+  MIPS64_REG_R28,
+  MIPS64_REG_R29,
+  MIPS64_REG_R30,
+  MIPS64_REG_R31,
+  MIPS64_REG_PC,
+  MIPS64_REG_LAST,
+
+  MIPS64_REG_SP = MIPS64_REG_R29,
+  MIPS64_REG_RA = MIPS64_REG_R31,
+};
+
+}  // namespace unwindstack
+
+#endif  // _LIBUNWINDSTACK_MACHINE_MIPS64_H
\ No newline at end of file
diff --git a/libunwindstack/include/unwindstack/MachineX86.h b/libunwindstack/include/unwindstack/MachineX86.h
new file mode 100644
index 0000000..02adb98
--- /dev/null
+++ b/libunwindstack/include/unwindstack/MachineX86.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2017 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 _LIBUNWINDSTACK_MACHINE_X86_H
+#define _LIBUNWINDSTACK_MACHINE_X86_H
+
+#include <stdint.h>
+
+namespace unwindstack {
+
+// Matches the numbers for the registers as generated by compilers.
+// If this is changed, then unwinding will fail.
+enum X86Reg : uint16_t {
+  X86_REG_EAX = 0,
+  X86_REG_ECX = 1,
+  X86_REG_EDX = 2,
+  X86_REG_EBX = 3,
+  X86_REG_ESP = 4,
+  X86_REG_EBP = 5,
+  X86_REG_ESI = 6,
+  X86_REG_EDI = 7,
+  X86_REG_EIP = 8,
+  X86_REG_EFL = 9,
+  X86_REG_CS = 10,
+  X86_REG_SS = 11,
+  X86_REG_DS = 12,
+  X86_REG_ES = 13,
+  X86_REG_FS = 14,
+  X86_REG_GS = 15,
+  X86_REG_LAST,
+
+  X86_REG_SP = X86_REG_ESP,
+  X86_REG_PC = X86_REG_EIP,
+};
+
+}  // namespace unwindstack
+
+#endif  // _LIBUNWINDSTACK_MACHINE_X86_H
diff --git a/libunwindstack/include/unwindstack/MachineX86_64.h b/libunwindstack/include/unwindstack/MachineX86_64.h
new file mode 100644
index 0000000..af33fea
--- /dev/null
+++ b/libunwindstack/include/unwindstack/MachineX86_64.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2017 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 _LIBUNWINDSTACK_MACHINE_X86_64_H
+#define _LIBUNWINDSTACK_MACHINE_X86_64_H
+
+#include <stdint.h>
+
+namespace unwindstack {
+
+// Matches the numbers for the registers as generated by compilers.
+// If this is changed, then unwinding will fail.
+enum X86_64Reg : uint16_t {
+  X86_64_REG_RAX = 0,
+  X86_64_REG_RDX = 1,
+  X86_64_REG_RCX = 2,
+  X86_64_REG_RBX = 3,
+  X86_64_REG_RSI = 4,
+  X86_64_REG_RDI = 5,
+  X86_64_REG_RBP = 6,
+  X86_64_REG_RSP = 7,
+  X86_64_REG_R8 = 8,
+  X86_64_REG_R9 = 9,
+  X86_64_REG_R10 = 10,
+  X86_64_REG_R11 = 11,
+  X86_64_REG_R12 = 12,
+  X86_64_REG_R13 = 13,
+  X86_64_REG_R14 = 14,
+  X86_64_REG_R15 = 15,
+  X86_64_REG_RIP = 16,
+  X86_64_REG_LAST,
+
+  X86_64_REG_SP = X86_64_REG_RSP,
+  X86_64_REG_PC = X86_64_REG_RIP,
+};
+
+}  // namespace unwindstack
+
+#endif  // _LIBUNWINDSTACK_MACHINE_X86_64_H
diff --git a/libunwindstack/include/unwindstack/MapInfo.h b/libunwindstack/include/unwindstack/MapInfo.h
new file mode 100644
index 0000000..ac0df41
--- /dev/null
+++ b/libunwindstack/include/unwindstack/MapInfo.h
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2016 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 _LIBUNWINDSTACK_MAP_INFO_H
+#define _LIBUNWINDSTACK_MAP_INFO_H
+
+#include <stdint.h>
+
+#include <atomic>
+#include <memory>
+#include <mutex>
+#include <string>
+
+#include <unwindstack/Elf.h>
+
+namespace unwindstack {
+
+// Forward declarations.
+class Memory;
+
+struct MapInfo {
+  MapInfo() = default;
+  MapInfo(uint64_t start, uint64_t end) : start(start), end(end) {}
+  MapInfo(uint64_t start, uint64_t end, uint64_t offset, uint64_t flags, const char* name)
+      : start(start),
+        end(end),
+        offset(offset),
+        flags(flags),
+        name(name),
+        load_bias(static_cast<uint64_t>(-1)) {}
+  MapInfo(uint64_t start, uint64_t end, uint64_t offset, uint64_t flags, const std::string& name)
+      : start(start),
+        end(end),
+        offset(offset),
+        flags(flags),
+        name(name),
+        load_bias(static_cast<uint64_t>(-1)) {}
+  ~MapInfo() = default;
+
+  uint64_t start = 0;
+  uint64_t end = 0;
+  uint64_t offset = 0;
+  uint16_t flags = 0;
+  std::string name;
+  std::shared_ptr<Elf> elf;
+  // This value is only non-zero if the offset is non-zero but there is
+  // no elf signature found at that offset. This indicates that the
+  // entire file is represented by the Memory object returned by CreateMemory,
+  // instead of a portion of the file.
+  uint64_t elf_offset = 0;
+
+  std::atomic_uint64_t load_bias;
+
+  // This function guarantees it will never return nullptr.
+  Elf* GetElf(const std::shared_ptr<Memory>& process_memory, bool init_gnu_debugdata = false);
+
+  uint64_t GetLoadBias(const std::shared_ptr<Memory>& process_memory);
+
+ private:
+  MapInfo(const MapInfo&) = delete;
+  void operator=(const MapInfo&) = delete;
+
+  Memory* GetFileMemory();
+
+  Memory* CreateMemory(const std::shared_ptr<Memory>& process_memory);
+
+  // Protect the creation of the elf object.
+  std::mutex mutex_;
+};
+
+}  // namespace unwindstack
+
+#endif  // _LIBUNWINDSTACK_MAP_INFO_H
diff --git a/libunwindstack/include/unwindstack/Maps.h b/libunwindstack/include/unwindstack/Maps.h
new file mode 100644
index 0000000..67fbed2
--- /dev/null
+++ b/libunwindstack/include/unwindstack/Maps.h
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2016 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 _LIBUNWINDSTACK_MAPS_H
+#define _LIBUNWINDSTACK_MAPS_H
+
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <string>
+#include <vector>
+
+#include <unwindstack/MapInfo.h>
+
+namespace unwindstack {
+
+// 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:
+  Maps() = default;
+  virtual ~Maps();
+
+  MapInfo* Find(uint64_t pc);
+
+  virtual bool Parse();
+
+  virtual const std::string GetMapsFile() const { return ""; }
+
+  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(); }
+
+  typedef std::vector<MapInfo*>::const_iterator const_iterator;
+  const_iterator begin() const { return maps_.begin(); }
+  const_iterator end() const { return maps_.end(); }
+
+  size_t Total() { return maps_.size(); }
+
+  MapInfo* Get(size_t index) {
+    if (index >= maps_.size()) return nullptr;
+    return maps_[index];
+  }
+
+ protected:
+  std::vector<MapInfo*> maps_;
+};
+
+class RemoteMaps : public Maps {
+ public:
+  RemoteMaps(pid_t pid) : pid_(pid) {}
+  virtual ~RemoteMaps() = default;
+
+  virtual const std::string GetMapsFile() const override;
+
+ private:
+  pid_t pid_;
+};
+
+class LocalMaps : public RemoteMaps {
+ public:
+  LocalMaps() : RemoteMaps(getpid()) {}
+  virtual ~LocalMaps() = default;
+};
+
+class LocalUpdatableMaps : public Maps {
+ public:
+  LocalUpdatableMaps() : Maps() {}
+  virtual ~LocalUpdatableMaps();
+
+  bool Reparse();
+
+  const std::string GetMapsFile() const override;
+
+ private:
+  std::vector<MapInfo*> saved_maps_;
+};
+
+class BufferMaps : public Maps {
+ public:
+  BufferMaps(const char* buffer) : buffer_(buffer) {}
+  virtual ~BufferMaps() = default;
+
+  bool Parse() override;
+
+ private:
+  const char* buffer_;
+};
+
+class FileMaps : public Maps {
+ public:
+  FileMaps(const std::string& file) : file_(file) {}
+  virtual ~FileMaps() = default;
+
+  const std::string GetMapsFile() const override { return file_; }
+
+ protected:
+  const std::string file_;
+};
+
+}  // namespace unwindstack
+
+#endif  // _LIBUNWINDSTACK_MAPS_H
diff --git a/libunwindstack/include/unwindstack/Memory.h b/libunwindstack/include/unwindstack/Memory.h
new file mode 100644
index 0000000..dee5e98
--- /dev/null
+++ b/libunwindstack/include/unwindstack/Memory.h
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2016 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 _LIBUNWINDSTACK_MEMORY_H
+#define _LIBUNWINDSTACK_MEMORY_H
+
+#include <stdint.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <atomic>
+#include <memory>
+#include <string>
+#include <vector>
+
+namespace unwindstack {
+
+class Memory {
+ public:
+  Memory() = default;
+  virtual ~Memory() = default;
+
+  static std::shared_ptr<Memory> CreateProcessMemory(pid_t pid);
+
+  virtual bool ReadString(uint64_t addr, std::string* string, uint64_t max_read = UINT64_MAX);
+
+  virtual size_t Read(uint64_t addr, void* dst, size_t size) = 0;
+
+  bool ReadFully(uint64_t addr, void* dst, size_t size);
+
+  inline bool Read32(uint64_t addr, uint32_t* dst) {
+    return ReadFully(addr, dst, sizeof(uint32_t));
+  }
+
+  inline bool Read64(uint64_t addr, uint64_t* dst) {
+    return ReadFully(addr, dst, sizeof(uint64_t));
+  }
+};
+
+class MemoryBuffer : public Memory {
+ public:
+  MemoryBuffer() = default;
+  virtual ~MemoryBuffer() = default;
+
+  size_t Read(uint64_t addr, void* dst, size_t size) override;
+
+  uint8_t* GetPtr(size_t offset);
+
+  void Resize(size_t size) { raw_.resize(size); }
+
+  uint64_t Size() { return raw_.size(); }
+
+ private:
+  std::vector<uint8_t> raw_;
+};
+
+class MemoryFileAtOffset : public Memory {
+ public:
+  MemoryFileAtOffset() = default;
+  virtual ~MemoryFileAtOffset();
+
+  bool Init(const std::string& file, uint64_t offset, uint64_t size = UINT64_MAX);
+
+  size_t Read(uint64_t addr, void* dst, size_t size) override;
+
+  size_t Size() { return size_; }
+
+  void Clear();
+
+ protected:
+  size_t size_ = 0;
+  size_t offset_ = 0;
+  uint8_t* data_ = nullptr;
+};
+
+class MemoryRemote : public Memory {
+ public:
+  MemoryRemote(pid_t pid) : pid_(pid), read_redirect_func_(0) {}
+  virtual ~MemoryRemote() = default;
+
+  size_t Read(uint64_t addr, void* dst, size_t size) override;
+
+  pid_t pid() { return pid_; }
+
+ private:
+  pid_t pid_;
+  std::atomic_uintptr_t read_redirect_func_;
+};
+
+class MemoryLocal : public Memory {
+ public:
+  MemoryLocal() = default;
+  virtual ~MemoryLocal() = default;
+
+  size_t Read(uint64_t addr, void* dst, size_t size) override;
+};
+
+// MemoryRange maps one address range onto another.
+// The range [src_begin, src_begin + length) in the underlying Memory is mapped onto offset,
+// such that range.read(offset) is equivalent to underlying.read(src_begin).
+class MemoryRange : public Memory {
+ public:
+  MemoryRange(const std::shared_ptr<Memory>& memory, uint64_t begin, uint64_t length,
+              uint64_t offset);
+  virtual ~MemoryRange() = default;
+
+  size_t Read(uint64_t addr, void* dst, size_t size) override;
+
+ private:
+  std::shared_ptr<Memory> memory_;
+  uint64_t begin_;
+  uint64_t length_;
+  uint64_t offset_;
+};
+
+class MemoryOffline : public Memory {
+ public:
+  MemoryOffline() = default;
+  virtual ~MemoryOffline() = default;
+
+  bool Init(const std::string& file, uint64_t offset);
+
+  size_t Read(uint64_t addr, void* dst, size_t size) override;
+
+ private:
+  std::unique_ptr<MemoryRange> memory_;
+};
+
+class MemoryOfflineBuffer : public Memory {
+ public:
+  MemoryOfflineBuffer(const uint8_t* data, uint64_t start, uint64_t end);
+  virtual ~MemoryOfflineBuffer() = default;
+
+  void Reset(const uint8_t* data, uint64_t start, uint64_t end);
+
+  size_t Read(uint64_t addr, void* dst, size_t size) override;
+
+ private:
+  const uint8_t* data_;
+  uint64_t start_;
+  uint64_t end_;
+};
+
+class MemoryOfflineParts : public Memory {
+ public:
+  MemoryOfflineParts() = default;
+  virtual ~MemoryOfflineParts();
+
+  void Add(MemoryOffline* memory) { memories_.push_back(memory); }
+
+  size_t Read(uint64_t addr, void* dst, size_t size) override;
+
+ private:
+  std::vector<MemoryOffline*> memories_;
+};
+
+}  // namespace unwindstack
+
+#endif  // _LIBUNWINDSTACK_MEMORY_H
diff --git a/libunwindstack/include/unwindstack/Regs.h b/libunwindstack/include/unwindstack/Regs.h
new file mode 100644
index 0000000..878ced3
--- /dev/null
+++ b/libunwindstack/include/unwindstack/Regs.h
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2016 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 _LIBUNWINDSTACK_REGS_H
+#define _LIBUNWINDSTACK_REGS_H
+
+#include <stdint.h>
+
+#include <functional>
+#include <string>
+#include <vector>
+
+namespace unwindstack {
+
+// Forward declarations.
+class Elf;
+enum ArchEnum : uint8_t;
+class Memory;
+
+class Regs {
+ public:
+  enum LocationEnum : uint8_t {
+    LOCATION_UNKNOWN = 0,
+    LOCATION_REGISTER,
+    LOCATION_SP_OFFSET,
+  };
+
+  struct Location {
+    Location(LocationEnum type, int16_t value) : type(type), value(value) {}
+
+    LocationEnum type;
+    int16_t value;
+  };
+
+  Regs(uint16_t total_regs, const Location& return_loc)
+      : total_regs_(total_regs), return_loc_(return_loc) {}
+  virtual ~Regs() = default;
+
+  virtual ArchEnum Arch() = 0;
+
+  virtual bool Is32Bit() = 0;
+
+  virtual void* RawData() = 0;
+  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; }
+
+  virtual uint64_t GetPcAdjustment(uint64_t rel_pc, Elf* elf) = 0;
+
+  virtual bool StepIfSignalHandler(uint64_t rel_pc, Elf* elf, Memory* process_memory) = 0;
+
+  virtual bool SetPcFromReturnAddress(Memory* process_memory) = 0;
+
+  virtual void IterateRegisters(std::function<void(const char*, uint64_t)>) = 0;
+
+  uint16_t total_regs() { return total_regs_; }
+
+  virtual Regs* Clone() = 0;
+
+  static ArchEnum CurrentArch();
+  static Regs* RemoteGet(pid_t pid);
+  static Regs* CreateFromUcontext(ArchEnum arch, void* ucontext);
+  static Regs* CreateFromLocal();
+
+ protected:
+  uint16_t total_regs_;
+  Location return_loc_;
+  uint64_t dex_pc_ = 0;
+};
+
+template <typename AddressType>
+class RegsImpl : public Regs {
+ public:
+  RegsImpl(uint16_t total_regs, Location return_loc)
+      : Regs(total_regs, return_loc), regs_(total_regs) {}
+  virtual ~RegsImpl() = default;
+
+  bool Is32Bit() override { return sizeof(AddressType) == sizeof(uint32_t); }
+
+  inline AddressType& operator[](size_t reg) { return regs_[reg]; }
+
+  void* RawData() override { return regs_.data(); }
+
+  virtual void IterateRegisters(std::function<void(const char*, uint64_t)> fn) override {
+    for (size_t i = 0; i < regs_.size(); ++i) {
+      fn(std::to_string(i).c_str(), regs_[i]);
+    }
+  }
+
+ protected:
+  std::vector<AddressType> regs_;
+};
+
+}  // namespace unwindstack
+
+#endif  // _LIBUNWINDSTACK_REGS_H
diff --git a/libunwindstack/include/unwindstack/RegsArm.h b/libunwindstack/include/unwindstack/RegsArm.h
new file mode 100644
index 0000000..44f6744
--- /dev/null
+++ b/libunwindstack/include/unwindstack/RegsArm.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2016 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 _LIBUNWINDSTACK_REGS_ARM_H
+#define _LIBUNWINDSTACK_REGS_ARM_H
+
+#include <stdint.h>
+
+#include <functional>
+
+#include <unwindstack/Elf.h>
+#include <unwindstack/Regs.h>
+
+namespace unwindstack {
+
+// Forward declarations.
+class Memory;
+
+class RegsArm : public RegsImpl<uint32_t> {
+ public:
+  RegsArm();
+  virtual ~RegsArm() = default;
+
+  ArchEnum Arch() override final;
+
+  uint64_t GetPcAdjustment(uint64_t rel_pc, Elf* elf) override;
+
+  bool SetPcFromReturnAddress(Memory* process_memory) override;
+
+  bool StepIfSignalHandler(uint64_t rel_pc, Elf* elf, Memory* process_memory) override;
+
+  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;
+
+  Regs* Clone() override final;
+
+  static Regs* Read(void* data);
+
+  static Regs* CreateFromUcontext(void* ucontext);
+};
+
+}  // namespace unwindstack
+
+#endif  // _LIBUNWINDSTACK_REGS_ARM_H
diff --git a/libunwindstack/include/unwindstack/RegsArm64.h b/libunwindstack/include/unwindstack/RegsArm64.h
new file mode 100644
index 0000000..a72f91f
--- /dev/null
+++ b/libunwindstack/include/unwindstack/RegsArm64.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2016 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 _LIBUNWINDSTACK_REGS_ARM64_H
+#define _LIBUNWINDSTACK_REGS_ARM64_H
+
+#include <stdint.h>
+
+#include <functional>
+
+#include <unwindstack/Elf.h>
+#include <unwindstack/Regs.h>
+
+namespace unwindstack {
+
+// Forward declarations.
+class Memory;
+
+class RegsArm64 : public RegsImpl<uint64_t> {
+ public:
+  RegsArm64();
+  virtual ~RegsArm64() = default;
+
+  ArchEnum Arch() override final;
+
+  uint64_t GetPcAdjustment(uint64_t rel_pc, Elf* elf) override;
+
+  bool SetPcFromReturnAddress(Memory* process_memory) override;
+
+  bool StepIfSignalHandler(uint64_t rel_pc, Elf* elf, Memory* process_memory) override;
+
+  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;
+
+  Regs* Clone() override final;
+
+  static Regs* Read(void* data);
+
+  static Regs* CreateFromUcontext(void* ucontext);
+};
+
+}  // namespace unwindstack
+
+#endif  // _LIBUNWINDSTACK_REGS_ARM64_H
diff --git a/libunwindstack/include/unwindstack/RegsGetLocal.h b/libunwindstack/include/unwindstack/RegsGetLocal.h
new file mode 100644
index 0000000..81c0af3
--- /dev/null
+++ b/libunwindstack/include/unwindstack/RegsGetLocal.h
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _LIBUNWINDSTACK_REGS_GET_LOCAL_H
+#define _LIBUNWINDSTACK_REGS_GET_LOCAL_H
+
+namespace unwindstack {
+
+#if defined(__arm__)
+
+inline __always_inline void RegsGetLocal(Regs* regs) {
+  void* reg_data = regs->RawData();
+  asm volatile(
+      ".align 2\n"
+      "bx pc\n"
+      "nop\n"
+      ".code 32\n"
+      "stmia %[base], {r0-r12}\n"
+      "add %[base], #52\n"
+      "mov r1, r13\n"
+      "mov r2, r14\n"
+      "mov r3, r15\n"
+      "stmia %[base], {r1-r3}\n"
+      "orr %[base], pc, #1\n"
+      "bx %[base]\n"
+      : [base] "+r"(reg_data)
+      :
+      : "memory");
+}
+
+#elif defined(__aarch64__)
+
+inline __always_inline void RegsGetLocal(Regs* regs) {
+  void* reg_data = regs->RawData();
+  asm volatile(
+      "1:\n"
+      "stp x0, x1, [%[base], #0]\n"
+      "stp x2, x3, [%[base], #16]\n"
+      "stp x4, x5, [%[base], #32]\n"
+      "stp x6, x7, [%[base], #48]\n"
+      "stp x8, x9, [%[base], #64]\n"
+      "stp x10, x11, [%[base], #80]\n"
+      "stp x12, x13, [%[base], #96]\n"
+      "stp x14, x15, [%[base], #112]\n"
+      "stp x16, x17, [%[base], #128]\n"
+      "stp x18, x19, [%[base], #144]\n"
+      "stp x20, x21, [%[base], #160]\n"
+      "stp x22, x23, [%[base], #176]\n"
+      "stp x24, x25, [%[base], #192]\n"
+      "stp x26, x27, [%[base], #208]\n"
+      "stp x28, x29, [%[base], #224]\n"
+      "str x30, [%[base], #240]\n"
+      "mov x12, sp\n"
+      "adr x13, 1b\n"
+      "stp x12, x13, [%[base], #248]\n"
+      : [base] "+r"(reg_data)
+      :
+      : "x12", "x13", "memory");
+}
+
+#elif defined(__i386__) || defined(__x86_64__) || defined(__mips__)
+
+extern "C" void AsmGetRegs(void* regs);
+
+inline void RegsGetLocal(Regs* regs) {
+  AsmGetRegs(regs->RawData());
+}
+
+#endif
+
+}  // namespace unwindstack
+
+#endif  // _LIBUNWINDSTACK_REGS_GET_LOCAL_H
diff --git a/libunwindstack/include/unwindstack/RegsMips.h b/libunwindstack/include/unwindstack/RegsMips.h
new file mode 100644
index 0000000..c9dd202
--- /dev/null
+++ b/libunwindstack/include/unwindstack/RegsMips.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2017 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 _LIBUNWINDSTACK_REGS_MIPS_H
+#define _LIBUNWINDSTACK_REGS_MIPS_H
+
+#include <stdint.h>
+
+#include <functional>
+
+#include <unwindstack/Elf.h>
+#include <unwindstack/Regs.h>
+
+namespace unwindstack {
+
+// Forward declarations.
+class Memory;
+
+class RegsMips : public RegsImpl<uint32_t> {
+ public:
+  RegsMips();
+  virtual ~RegsMips() = default;
+
+  ArchEnum Arch() override final;
+
+  uint64_t GetPcAdjustment(uint64_t rel_pc, Elf* elf) override;
+
+  bool SetPcFromReturnAddress(Memory* process_memory) override;
+
+  bool StepIfSignalHandler(uint64_t rel_pc, Elf* elf, Memory* process_memory) override;
+
+  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;
+
+  Regs* Clone() override final;
+
+  static Regs* Read(void* data);
+
+  static Regs* CreateFromUcontext(void* ucontext);
+};
+
+}  // namespace unwindstack
+
+#endif  // _LIBUNWINDSTACK_REGS_MIPS_H
diff --git a/libunwindstack/include/unwindstack/RegsMips64.h b/libunwindstack/include/unwindstack/RegsMips64.h
new file mode 100644
index 0000000..7c42812
--- /dev/null
+++ b/libunwindstack/include/unwindstack/RegsMips64.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2017 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 _LIBUNWINDSTACK_REGS_MIPS64_H
+#define _LIBUNWINDSTACK_REGS_MIPS64_H
+
+#include <stdint.h>
+
+#include <functional>
+
+#include <unwindstack/Elf.h>
+#include <unwindstack/Regs.h>
+
+namespace unwindstack {
+
+// Forward declarations.
+class Memory;
+
+class RegsMips64 : public RegsImpl<uint64_t> {
+ public:
+  RegsMips64();
+  virtual ~RegsMips64() = default;
+
+  ArchEnum Arch() override final;
+
+  uint64_t GetPcAdjustment(uint64_t rel_pc, Elf* elf) override;
+
+  bool SetPcFromReturnAddress(Memory* process_memory) override;
+
+  bool StepIfSignalHandler(uint64_t rel_pc, Elf* elf, Memory* process_memory) override;
+
+  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;
+
+  Regs* Clone() override final;
+
+  static Regs* Read(void* data);
+
+  static Regs* CreateFromUcontext(void* ucontext);
+};
+
+}  // namespace unwindstack
+
+#endif  // _LIBUNWINDSTACK_REGS_MIPS64_H
diff --git a/libunwindstack/include/unwindstack/RegsX86.h b/libunwindstack/include/unwindstack/RegsX86.h
new file mode 100644
index 0000000..d19e449
--- /dev/null
+++ b/libunwindstack/include/unwindstack/RegsX86.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2016 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 _LIBUNWINDSTACK_REGS_X86_H
+#define _LIBUNWINDSTACK_REGS_X86_H
+
+#include <stdint.h>
+
+#include <functional>
+
+#include <unwindstack/Elf.h>
+#include <unwindstack/Regs.h>
+
+namespace unwindstack {
+
+// Forward declarations.
+class Memory;
+struct x86_ucontext_t;
+
+class RegsX86 : public RegsImpl<uint32_t> {
+ public:
+  RegsX86();
+  virtual ~RegsX86() = default;
+
+  ArchEnum Arch() override final;
+
+  uint64_t GetPcAdjustment(uint64_t rel_pc, Elf* elf) 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);
+
+  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;
+
+  Regs* Clone() override final;
+
+  static Regs* Read(void* data);
+
+  static Regs* CreateFromUcontext(void* ucontext);
+};
+
+}  // namespace unwindstack
+
+#endif  // _LIBUNWINDSTACK_REGS_X86_H
diff --git a/libunwindstack/include/unwindstack/RegsX86_64.h b/libunwindstack/include/unwindstack/RegsX86_64.h
new file mode 100644
index 0000000..dc9a220
--- /dev/null
+++ b/libunwindstack/include/unwindstack/RegsX86_64.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2016 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 _LIBUNWINDSTACK_REGS_X86_64_H
+#define _LIBUNWINDSTACK_REGS_X86_64_H
+
+#include <stdint.h>
+
+#include <functional>
+
+#include <unwindstack/Elf.h>
+#include <unwindstack/Regs.h>
+
+namespace unwindstack {
+
+// Forward declarations.
+class Memory;
+struct x86_64_ucontext_t;
+
+class RegsX86_64 : public RegsImpl<uint64_t> {
+ public:
+  RegsX86_64();
+  virtual ~RegsX86_64() = default;
+
+  ArchEnum Arch() override final;
+
+  uint64_t GetPcAdjustment(uint64_t rel_pc, Elf* elf) 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);
+
+  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;
+
+  Regs* Clone() override final;
+
+  static Regs* Read(void* data);
+
+  static Regs* CreateFromUcontext(void* ucontext);
+};
+
+}  // namespace unwindstack
+
+#endif  // _LIBUNWINDSTACK_REGS_X86_64_H
diff --git a/libunwindstack/include/unwindstack/UcontextArm.h b/libunwindstack/include/unwindstack/UcontextArm.h
new file mode 100644
index 0000000..7d1ec3b
--- /dev/null
+++ b/libunwindstack/include/unwindstack/UcontextArm.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _LIBUNWINDSTACK_UCONTEXT_ARM_H
+#define _LIBUNWINDSTACK_UCONTEXT_ARM_H
+
+#include <stdint.h>
+
+#include <unwindstack/MachineArm.h>
+
+namespace unwindstack {
+
+struct arm_stack_t {
+  uint32_t ss_sp;    // void __user*
+  int32_t ss_flags;  // int
+  uint32_t ss_size;  // size_t
+};
+
+struct arm_mcontext_t {
+  uint32_t trap_no;             // unsigned long
+  uint32_t error_code;          // unsigned long
+  uint32_t oldmask;             // unsigned long
+  uint32_t regs[ARM_REG_LAST];  // unsigned long
+  uint32_t cpsr;                // unsigned long
+  uint32_t fault_address;       // unsigned long
+};
+
+struct arm_ucontext_t {
+  uint32_t uc_flags;  // unsigned long
+  uint32_t uc_link;   // struct ucontext*
+  arm_stack_t uc_stack;
+  arm_mcontext_t uc_mcontext;
+  // Nothing else is used, so don't define it.
+};
+
+}  // namespace unwindstack
+
+#endif  // _LIBUNWINDSTACK_UCONTEXT_ARM_H
diff --git a/libunwindstack/include/unwindstack/UcontextArm64.h b/libunwindstack/include/unwindstack/UcontextArm64.h
new file mode 100644
index 0000000..a68be3b
--- /dev/null
+++ b/libunwindstack/include/unwindstack/UcontextArm64.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _LIBUNWINDSTACK_UCONTEXT_ARM64_H
+#define _LIBUNWINDSTACK_UCONTEXT_ARM64_H
+
+#include <stdint.h>
+
+#include <unwindstack/MachineArm64.h>
+
+namespace unwindstack {
+
+struct arm64_stack_t {
+  uint64_t ss_sp;    // void __user*
+  int32_t ss_flags;  // int
+  uint64_t ss_size;  // size_t
+};
+
+struct arm64_sigset_t {
+  uint64_t sig;  // unsigned long
+};
+
+struct arm64_mcontext_t {
+  uint64_t fault_address;         // __u64
+  uint64_t regs[ARM64_REG_LAST];  // __u64
+  uint64_t pstate;                // __u64
+  // Nothing else is used, so don't define it.
+};
+
+struct arm64_ucontext_t {
+  uint64_t uc_flags;  // unsigned long
+  uint64_t uc_link;   // struct ucontext*
+  arm64_stack_t uc_stack;
+  arm64_sigset_t uc_sigmask;
+  // The kernel adds extra padding after uc_sigmask to match glibc sigset_t on ARM64.
+  char __padding[128 - sizeof(arm64_sigset_t)];
+  // The full structure requires 16 byte alignment, but our partial structure
+  // doesn't, so force the alignment.
+  arm64_mcontext_t uc_mcontext __attribute__((aligned(16)));
+};
+
+}  // namespace unwindstack
+
+#endif  // _LIBUNWINDSTACK_UCONTEXT_ARM64_H
diff --git a/libunwindstack/include/unwindstack/UcontextMips.h b/libunwindstack/include/unwindstack/UcontextMips.h
new file mode 100644
index 0000000..02e33b6
--- /dev/null
+++ b/libunwindstack/include/unwindstack/UcontextMips.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _LIBUNWINDSTACK_UCONTEXT_MIPS_H
+#define _LIBUNWINDSTACK_UCONTEXT_MIPS_H
+
+#include <stdint.h>
+
+#include <unwindstack/MachineMips.h>
+
+namespace unwindstack {
+
+struct mips_stack_t {
+  uint32_t ss_sp;    // void __user*
+  uint32_t ss_size;  // size_t
+  int32_t ss_flags;  // int
+};
+
+struct mips_mcontext_t {
+  uint32_t sc_regmask;
+  uint32_t sc_status;
+  uint64_t sc_pc;
+  uint64_t sc_regs[32];
+  // Nothing else is used, so don't define it.
+};
+
+struct mips_ucontext_t {
+  uint32_t uc_flags;  // unsigned long
+  uint32_t uc_link;   // struct ucontext*
+  mips_stack_t uc_stack;
+  mips_mcontext_t uc_mcontext;
+  // Nothing else is used, so don't define it.
+};
+
+}  // namespace unwindstack
+
+#endif  // _LIBUNWINDSTACK_UCONTEXT_MIPS_H
diff --git a/libunwindstack/include/unwindstack/UcontextMips64.h b/libunwindstack/include/unwindstack/UcontextMips64.h
new file mode 100644
index 0000000..5b92a55
--- /dev/null
+++ b/libunwindstack/include/unwindstack/UcontextMips64.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _LIBUNWINDSTACK_UCONTEXT_MIPS64_H
+#define _LIBUNWINDSTACK_UCONTEXT_MIPS64_H
+
+#include <stdint.h>
+
+#include <unwindstack/MachineMips64.h>
+
+namespace unwindstack {
+
+struct mips64_stack_t {
+  uint64_t ss_sp;    // void __user*
+  uint64_t ss_size;  // size_t
+  int32_t ss_flags;  // int
+};
+
+struct mips64_mcontext_t {
+  uint64_t sc_regs[32];
+  uint64_t sc_fpregs[32];
+  uint64_t sc_mdhi;
+  uint64_t sc_hi1;
+  uint64_t sc_hi2;
+  uint64_t sc_hi3;
+  uint64_t sc_mdlo;
+  uint64_t sc_lo1;
+  uint64_t sc_lo2;
+  uint64_t sc_lo3;
+  uint64_t sc_pc;
+  // Nothing else is used, so don't define it.
+};
+
+struct mips64_ucontext_t {
+  uint64_t uc_flags;  // unsigned long
+  uint64_t uc_link;   // struct ucontext*
+  mips64_stack_t uc_stack;
+  mips64_mcontext_t uc_mcontext;
+  // Nothing else is used, so don't define it.
+};
+
+}  // namespace unwindstack
+
+#endif  // _LIBUNWINDSTACK_UCONTEXT_MIPS64_H
diff --git a/libunwindstack/include/unwindstack/UcontextX86.h b/libunwindstack/include/unwindstack/UcontextX86.h
new file mode 100644
index 0000000..c96ebb7
--- /dev/null
+++ b/libunwindstack/include/unwindstack/UcontextX86.h
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _LIBUNWINDSTACK_UCONTEXT_X86_H
+#define _LIBUNWINDSTACK_UCONTEXT_X86_H
+
+#include <stdint.h>
+
+#include <unwindstack/MachineX86.h>
+
+namespace unwindstack {
+
+struct x86_stack_t {
+  uint32_t ss_sp;    // void __user*
+  int32_t ss_flags;  // int
+  uint32_t ss_size;  // size_t
+};
+
+struct x86_mcontext_t {
+  uint32_t gs;
+  uint32_t fs;
+  uint32_t es;
+  uint32_t ds;
+  uint32_t edi;
+  uint32_t esi;
+  uint32_t ebp;
+  uint32_t esp;
+  uint32_t ebx;
+  uint32_t edx;
+  uint32_t ecx;
+  uint32_t eax;
+  uint32_t trapno;
+  uint32_t err;
+  uint32_t eip;
+  uint32_t cs;
+  uint32_t efl;
+  uint32_t uesp;
+  uint32_t ss;
+  // Only care about the registers, skip everything else.
+};
+
+struct x86_ucontext_t {
+  uint32_t uc_flags;  // unsigned long
+  uint32_t uc_link;   // struct ucontext*
+  x86_stack_t uc_stack;
+  x86_mcontext_t uc_mcontext;
+  // Nothing else is used, so don't define it.
+};
+
+}  // namespace unwindstack
+
+#endif  // _LIBUNWINDSTACK_UCONTEXT_X86_H
diff --git a/libunwindstack/include/unwindstack/UcontextX86_64.h b/libunwindstack/include/unwindstack/UcontextX86_64.h
new file mode 100644
index 0000000..4e163e5
--- /dev/null
+++ b/libunwindstack/include/unwindstack/UcontextX86_64.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _LIBUNWINDSTACK_UCONTEXT_X86_64_H
+#define _LIBUNWINDSTACK_UCONTEXT_X86_64_H
+
+#include <stdint.h>
+
+#include <unwindstack/MachineX86_64.h>
+
+namespace unwindstack {
+
+struct x86_64_stack_t {
+  uint64_t ss_sp;    // void __user*
+  int32_t ss_flags;  // int
+  int32_t pad;
+  uint64_t ss_size;  // size_t
+};
+
+struct x86_64_mcontext_t {
+  uint64_t r8;
+  uint64_t r9;
+  uint64_t r10;
+  uint64_t r11;
+  uint64_t r12;
+  uint64_t r13;
+  uint64_t r14;
+  uint64_t r15;
+  uint64_t rdi;
+  uint64_t rsi;
+  uint64_t rbp;
+  uint64_t rbx;
+  uint64_t rdx;
+  uint64_t rax;
+  uint64_t rcx;
+  uint64_t rsp;
+  uint64_t rip;
+  uint64_t efl;
+  uint64_t csgsfs;
+  uint64_t err;
+  uint64_t trapno;
+  uint64_t oldmask;
+  uint64_t cr2;
+  // Only care about the registers, skip everything else.
+};
+
+struct x86_64_ucontext_t {
+  uint64_t uc_flags;  // unsigned long
+  uint64_t uc_link;   // struct ucontext*
+  x86_64_stack_t uc_stack;
+  x86_64_mcontext_t uc_mcontext;
+  // Nothing else is used, so don't define it.
+};
+
+}  // namespace unwindstack
+
+#endif  // _LIBUNWINDSTACK_UCONTEXT_X86_64_H
diff --git a/libunwindstack/include/unwindstack/Unwinder.h b/libunwindstack/include/unwindstack/Unwinder.h
new file mode 100644
index 0000000..56b0581
--- /dev/null
+++ b/libunwindstack/include/unwindstack/Unwinder.h
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2017 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 _LIBUNWINDSTACK_UNWINDER_H
+#define _LIBUNWINDSTACK_UNWINDER_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <unwindstack/Error.h>
+#include <unwindstack/Maps.h>
+#include <unwindstack/Memory.h>
+#include <unwindstack/Regs.h>
+
+namespace unwindstack {
+
+// Forward declarations.
+class DexFiles;
+class Elf;
+class JitDebug;
+enum ArchEnum : uint8_t;
+
+struct FrameData {
+  size_t num;
+
+  uint64_t rel_pc;
+  uint64_t pc;
+  uint64_t sp;
+
+  std::string function_name;
+  uint64_t function_offset = 0;
+
+  std::string map_name;
+  uint64_t map_offset = 0;
+  uint64_t map_start = 0;
+  uint64_t map_end = 0;
+  uint64_t map_load_bias = 0;
+  int map_flags = 0;
+};
+
+class Unwinder {
+ public:
+  Unwinder(size_t max_frames, Maps* maps, Regs* regs, std::shared_ptr<Memory> process_memory)
+      : max_frames_(max_frames), maps_(maps), regs_(regs), process_memory_(process_memory) {
+    frames_.reserve(max_frames);
+  }
+  ~Unwinder() = default;
+
+  void Unwind(const std::vector<std::string>* initial_map_names_to_skip = nullptr,
+              const std::vector<std::string>* map_suffixes_to_ignore = nullptr);
+
+  size_t NumFrames() { return frames_.size(); }
+
+  const std::vector<FrameData>& frames() { return frames_; }
+
+  std::string FormatFrame(size_t frame_num);
+  static std::string FormatFrame(const FrameData& frame, bool is32bit);
+
+  void SetJitDebug(JitDebug* jit_debug, ArchEnum arch);
+
+  // Disabling the resolving of names results in the function name being
+  // set to an empty string and the function offset being set to zero.
+  void SetResolveNames(bool resolve) { resolve_names_ = resolve; }
+
+#if !defined(NO_LIBDEXFILE_SUPPORT)
+  void SetDexFiles(DexFiles* dex_files, ArchEnum arch);
+#endif
+
+  ErrorCode LastErrorCode() { return last_error_.code; }
+  uint64_t LastErrorAddress() { return last_error_.address; }
+
+ private:
+  void FillInDexFrame();
+  void FillInFrame(MapInfo* map_info, Elf* elf, uint64_t rel_pc, uint64_t func_pc,
+                   uint64_t pc_adjustment);
+
+  size_t max_frames_;
+  Maps* maps_;
+  Regs* regs_;
+  std::vector<FrameData> frames_;
+  std::shared_ptr<Memory> process_memory_;
+  JitDebug* jit_debug_ = nullptr;
+#if !defined(NO_LIBDEXFILE_SUPPORT)
+  DexFiles* dex_files_ = nullptr;
+#endif
+  bool resolve_names_ = true;
+  ErrorData last_error_;
+};
+
+}  // namespace unwindstack
+
+#endif  // _LIBUNWINDSTACK_UNWINDER_H
diff --git a/libunwindstack/include/unwindstack/UserArm.h b/libunwindstack/include/unwindstack/UserArm.h
new file mode 100644
index 0000000..7388c03
--- /dev/null
+++ b/libunwindstack/include/unwindstack/UserArm.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _LIBUNWINDSTACK_USER_ARM_H
+#define _LIBUNWINDSTACK_USER_ARM_H
+
+namespace unwindstack {
+
+struct arm_user_regs {
+  uint32_t regs[18];
+};
+
+}  // namespace unwindstack
+
+#endif  // _LIBUNWINDSTACK_USER_ARM_H
diff --git a/libunwindstack/include/unwindstack/UserArm64.h b/libunwindstack/include/unwindstack/UserArm64.h
new file mode 100644
index 0000000..d74983f
--- /dev/null
+++ b/libunwindstack/include/unwindstack/UserArm64.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _LIBUNWINDSTACK_USER_ARM64_H
+#define _LIBUNWINDSTACK_USER_ARM64_H
+
+namespace unwindstack {
+
+struct arm64_user_regs {
+  uint64_t regs[31];
+  uint64_t sp;
+  uint64_t pc;
+  uint64_t pstate;
+};
+
+}  // namespace unwindstack
+
+#endif  // _LIBUNWINDSTACK_USER_ARM64_H
diff --git a/libunwindstack/include/unwindstack/UserMips.h b/libunwindstack/include/unwindstack/UserMips.h
new file mode 100644
index 0000000..184be4f
--- /dev/null
+++ b/libunwindstack/include/unwindstack/UserMips.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _LIBUNWINDSTACK_USER_MIPS_H
+#define _LIBUNWINDSTACK_USER_MIPS_H
+
+namespace unwindstack {
+
+enum Mips32UserReg : uint16_t {
+  MIPS32_EF_R0 = 6,
+  MIPS32_EF_CP0_EPC = 40,
+};
+
+struct mips_user_regs {
+  uint32_t regs[45];
+};
+
+}  // namespace unwindstack
+
+#endif  // _LIBUNWINDSTACK_USER_MIPS_H
diff --git a/libunwindstack/include/unwindstack/UserMips64.h b/libunwindstack/include/unwindstack/UserMips64.h
new file mode 100644
index 0000000..c46befd
--- /dev/null
+++ b/libunwindstack/include/unwindstack/UserMips64.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _LIBUNWINDSTACK_USER_MIPS64_H
+#define _LIBUNWINDSTACK_USER_MIPS64_H
+
+namespace unwindstack {
+
+enum Mips64UserReg : uint16_t {
+  MIPS64_EF_R0 = 0,
+  MIPS64_EF_CP0_EPC = 34,
+};
+
+struct mips64_user_regs {
+  uint64_t regs[45];
+};
+
+}  // namespace unwindstack
+
+#endif  // _LIBUNWINDSTACK_USER_MIPS64_H
diff --git a/libunwindstack/include/unwindstack/UserX86.h b/libunwindstack/include/unwindstack/UserX86.h
new file mode 100644
index 0000000..a040560
--- /dev/null
+++ b/libunwindstack/include/unwindstack/UserX86.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _LIBUNWINDSTACK_USER_X86_H
+#define _LIBUNWINDSTACK_USER_X86_H
+
+namespace unwindstack {
+
+struct x86_user_regs {
+  uint32_t ebx;
+  uint32_t ecx;
+  uint32_t edx;
+  uint32_t esi;
+  uint32_t edi;
+  uint32_t ebp;
+  uint32_t eax;
+  uint32_t xds;
+  uint32_t xes;
+  uint32_t xfs;
+  uint32_t xgs;
+  uint32_t orig_eax;
+  uint32_t eip;
+  uint32_t xcs;
+  uint32_t eflags;
+  uint32_t esp;
+  uint32_t xss;
+};
+
+}  // namespace unwindstack
+
+#endif  // _LIBUNWINDSTACK_USER_X86_H
diff --git a/libunwindstack/include/unwindstack/UserX86_64.h b/libunwindstack/include/unwindstack/UserX86_64.h
new file mode 100644
index 0000000..b80d201
--- /dev/null
+++ b/libunwindstack/include/unwindstack/UserX86_64.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _LIBUNWINDSTACK_USER_X86_64_H
+#define _LIBUNWINDSTACK_USER_X86_64_H
+
+namespace unwindstack {
+
+struct x86_64_user_regs {
+  uint64_t r15;
+  uint64_t r14;
+  uint64_t r13;
+  uint64_t r12;
+  uint64_t rbp;
+  uint64_t rbx;
+  uint64_t r11;
+  uint64_t r10;
+  uint64_t r9;
+  uint64_t r8;
+  uint64_t rax;
+  uint64_t rcx;
+  uint64_t rdx;
+  uint64_t rsi;
+  uint64_t rdi;
+  uint64_t orig_rax;
+  uint64_t rip;
+  uint64_t cs;
+  uint64_t eflags;
+  uint64_t rsp;
+  uint64_t ss;
+  uint64_t fs_base;
+  uint64_t gs_base;
+  uint64_t ds;
+  uint64_t es;
+  uint64_t fs;
+  uint64_t gs;
+};
+
+}  // namespace unwindstack
+
+#endif  // _LIBUNWINDSTACK_USER_X86_64_H
diff --git a/libunwindstack/tests/ArmExidxDecodeTest.cpp b/libunwindstack/tests/ArmExidxDecodeTest.cpp
index 4fff48e..5f3d1ea 100644
--- a/libunwindstack/tests/ArmExidxDecodeTest.cpp
+++ b/libunwindstack/tests/ArmExidxDecodeTest.cpp
@@ -23,18 +23,19 @@
 
 #include <gtest/gtest.h>
 
+#include <unwindstack/Log.h>
+#include <unwindstack/RegsArm.h>
+
 #include "ArmExidx.h"
-#include "Regs.h"
-#include "Log.h"
 
 #include "LogFake.h"
 #include "MemoryFake.h"
 
+namespace unwindstack {
+
 class ArmExidxDecodeTest : public ::testing::TestWithParam<std::string> {
  protected:
   void Init(Memory* process_memory = nullptr) {
-    TearDown();
-
     if (process_memory == nullptr) {
       process_memory = &process_memory_;
     }
@@ -47,8 +48,8 @@
     regs_arm_->set_sp(0);
 
     exidx_.reset(new ArmExidx(regs_arm_.get(), &elf_memory_, process_memory));
-    if (log_) {
-      exidx_->set_log(true);
+    if (log_ != ARM_LOG_NONE) {
+      exidx_->set_log(log_);
       exidx_->set_log_indent(0);
       exidx_->set_log_skip_execution(false);
     }
@@ -57,14 +58,20 @@
   }
 
   void SetUp() override {
-    if (GetParam() != "no_logging") {
-      log_ = false;
+    if (GetParam() == "no_logging") {
+      log_ = ARM_LOG_NONE;
+    } else if (GetParam() == "register_logging") {
+      log_ = ARM_LOG_BY_REG;
     } else {
-      log_ = true;
+      log_ = ARM_LOG_FULL;
     }
-    ResetLogs();
     elf_memory_.Clear();
     process_memory_.Clear();
+    ResetExidx();
+  }
+
+  void ResetExidx() {
+    ResetLogs();
     Init();
   }
 
@@ -74,7 +81,7 @@
 
   MemoryFake elf_memory_;
   MemoryFake process_memory_;
-  bool log_;
+  ArmLogType log_;
 };
 
 TEST_P(ArmExidxDecodeTest, vsp_incr) {
@@ -83,38 +90,59 @@
   ASSERT_TRUE(exidx_->Decode());
   ASSERT_FALSE(exidx_->pc_set());
   ASSERT_EQ("", GetFakeLogBuf());
-  if (log_) {
-    ASSERT_EQ("4 unwind vsp = vsp + 4\n", GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+      ASSERT_EQ("4 unwind vsp = vsp + 4\n", GetFakeLogPrint());
+      break;
+    case ARM_LOG_BY_REG:
+      exidx_->LogByReg();
+      ASSERT_EQ("4 unwind cfa = r13 + 4\n", GetFakeLogPrint());
+      break;
   }
   ASSERT_EQ(0x10004U, exidx_->cfa());
 
-  ResetLogs();
+  ResetExidx();
   data_->clear();
   data_->push_back(0x01);
   ASSERT_TRUE(exidx_->Decode());
   ASSERT_FALSE(exidx_->pc_set());
   ASSERT_EQ("", GetFakeLogBuf());
-  if (log_) {
-    ASSERT_EQ("4 unwind vsp = vsp + 8\n", GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+      ASSERT_EQ("4 unwind vsp = vsp + 8\n", GetFakeLogPrint());
+      break;
+    case ARM_LOG_BY_REG:
+      exidx_->LogByReg();
+      ASSERT_EQ("4 unwind cfa = r13 + 8\n", GetFakeLogPrint());
+      break;
   }
-  ASSERT_EQ(0x1000cU, exidx_->cfa());
+  ASSERT_EQ(0x10008U, exidx_->cfa());
 
-  ResetLogs();
+  ResetExidx();
   data_->clear();
   data_->push_back(0x3f);
   ASSERT_TRUE(exidx_->Decode());
   ASSERT_FALSE(exidx_->pc_set());
   ASSERT_EQ("", GetFakeLogBuf());
-  if (log_) {
-    ASSERT_EQ("4 unwind vsp = vsp + 256\n", GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+      ASSERT_EQ("4 unwind vsp = vsp + 256\n", GetFakeLogPrint());
+      break;
+    case ARM_LOG_BY_REG:
+      exidx_->LogByReg();
+      ASSERT_EQ("4 unwind cfa = r13 + 256\n", GetFakeLogPrint());
+      break;
   }
-  ASSERT_EQ(0x1010cU, exidx_->cfa());
+  ASSERT_EQ(0x10100U, exidx_->cfa());
 }
 
 TEST_P(ArmExidxDecodeTest, vsp_decr) {
@@ -123,38 +151,59 @@
   ASSERT_TRUE(exidx_->Decode());
   ASSERT_FALSE(exidx_->pc_set());
   ASSERT_EQ("", GetFakeLogBuf());
-  if (log_) {
-    ASSERT_EQ("4 unwind vsp = vsp - 4\n", GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+      ASSERT_EQ("4 unwind vsp = vsp - 4\n", GetFakeLogPrint());
+      break;
+    case ARM_LOG_BY_REG:
+      exidx_->LogByReg();
+      ASSERT_EQ("4 unwind cfa = r13 - 4\n", GetFakeLogPrint());
+      break;
   }
   ASSERT_EQ(0xfffcU, exidx_->cfa());
 
-  ResetLogs();
+  ResetExidx();
   data_->clear();
   data_->push_back(0x41);
   ASSERT_TRUE(exidx_->Decode());
   ASSERT_FALSE(exidx_->pc_set());
   ASSERT_EQ("", GetFakeLogBuf());
-  if (log_) {
-    ASSERT_EQ("4 unwind vsp = vsp - 8\n", GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+      ASSERT_EQ("4 unwind vsp = vsp - 8\n", GetFakeLogPrint());
+      break;
+    case ARM_LOG_BY_REG:
+      exidx_->LogByReg();
+      ASSERT_EQ("4 unwind cfa = r13 - 8\n", GetFakeLogPrint());
+      break;
   }
-  ASSERT_EQ(0xfff4U, exidx_->cfa());
+  ASSERT_EQ(0xfff8U, exidx_->cfa());
 
-  ResetLogs();
+  ResetExidx();
   data_->clear();
   data_->push_back(0x7f);
   ASSERT_TRUE(exidx_->Decode());
   ASSERT_FALSE(exidx_->pc_set());
   ASSERT_EQ("", GetFakeLogBuf());
-  if (log_) {
-    ASSERT_EQ("4 unwind vsp = vsp - 256\n", GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+      ASSERT_EQ("4 unwind vsp = vsp - 256\n", GetFakeLogPrint());
+      break;
+    case ARM_LOG_BY_REG:
+      exidx_->LogByReg();
+      ASSERT_EQ("4 unwind cfa = r13 - 256\n", GetFakeLogPrint());
+      break;
   }
-  ASSERT_EQ(0xfef4U, exidx_->cfa());
+  ASSERT_EQ(0xff00U, exidx_->cfa());
 }
 
 TEST_P(ArmExidxDecodeTest, refuse_unwind) {
@@ -163,10 +212,14 @@
   data_->push_back(0x00);
   ASSERT_FALSE(exidx_->Decode());
   ASSERT_EQ("", GetFakeLogBuf());
-  if (log_) {
-    ASSERT_EQ("4 unwind Refuse to unwind\n", GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+    case ARM_LOG_BY_REG:
+      ASSERT_EQ("4 unwind Refuse to unwind\n", GetFakeLogPrint());
+      break;
   }
   ASSERT_EQ(ARM_STATUS_NO_UNWIND, exidx_->status());
 }
@@ -179,29 +232,60 @@
   ASSERT_TRUE(exidx_->Decode());
   ASSERT_TRUE(exidx_->pc_set());
   ASSERT_EQ("", GetFakeLogBuf());
-  if (log_) {
-    ASSERT_EQ("4 unwind pop {r15}\n", GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+      ASSERT_EQ("4 unwind pop {r15}\n", GetFakeLogPrint());
+      break;
+    case ARM_LOG_BY_REG:
+      exidx_->LogByReg();
+      ASSERT_EQ(
+          "4 unwind cfa = r13 + 4\n"
+          "4 unwind r15 = [cfa - 4]\n",
+          GetFakeLogPrint());
+      break;
   }
   ASSERT_EQ(0x10004U, exidx_->cfa());
   ASSERT_EQ(0x10U, (*exidx_->regs())[15]);
 
-  ResetLogs();
+  ResetExidx();
   data_->push_back(0x8f);
   data_->push_back(0xff);
   for (size_t i = 0; i < 12; i++) {
-    process_memory_.SetData32(0x10004 + i * 4, i + 0x20);
+    process_memory_.SetData32(0x10000 + i * 4, i + 0x20);
   }
   exidx_->set_pc_set(false);
   ASSERT_TRUE(exidx_->Decode());
   ASSERT_TRUE(exidx_->pc_set());
   ASSERT_EQ("", GetFakeLogBuf());
-  if (log_) {
-    ASSERT_EQ("4 unwind pop {r4, r5, r6, r7, r8, r9, r10, r11, r12, r13, r14, r15}\n",
-              GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+      ASSERT_EQ("4 unwind pop {r4, r5, r6, r7, r8, r9, r10, r11, r12, r13, r14, r15}\n",
+                GetFakeLogPrint());
+      break;
+    case ARM_LOG_BY_REG:
+      exidx_->LogByReg();
+      ASSERT_EQ(
+          "4 unwind cfa = r13 + 48\n"
+          "4 unwind r4 = [cfa - 48]\n"
+          "4 unwind r5 = [cfa - 44]\n"
+          "4 unwind r6 = [cfa - 40]\n"
+          "4 unwind r7 = [cfa - 36]\n"
+          "4 unwind r8 = [cfa - 32]\n"
+          "4 unwind r9 = [cfa - 28]\n"
+          "4 unwind r10 = [cfa - 24]\n"
+          "4 unwind r11 = [cfa - 20]\n"
+          "4 unwind r12 = [cfa - 16]\n"
+          "4 unwind r13 = [cfa - 12]\n"
+          "4 unwind r14 = [cfa - 8]\n"
+          "4 unwind r15 = [cfa - 4]\n",
+          GetFakeLogPrint());
+      break;
   }
   // Popping r13 results in a modified cfa.
   ASSERT_EQ(0x29U, exidx_->cfa());
@@ -219,7 +303,7 @@
   ASSERT_EQ(0x2aU, (*exidx_->regs())[14]);
   ASSERT_EQ(0x2bU, (*exidx_->regs())[15]);
 
-  ResetLogs();
+  ResetExidx();
   exidx_->set_cfa(0x10034);
   data_->push_back(0x81);
   data_->push_back(0x28);
@@ -230,10 +314,22 @@
   ASSERT_TRUE(exidx_->Decode());
   ASSERT_FALSE(exidx_->pc_set());
   ASSERT_EQ("", GetFakeLogBuf());
-  if (log_) {
-    ASSERT_EQ("4 unwind pop {r7, r9, r12}\n", GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+      ASSERT_EQ("4 unwind pop {r7, r9, r12}\n", GetFakeLogPrint());
+      break;
+    case ARM_LOG_BY_REG:
+      exidx_->LogByReg();
+      ASSERT_EQ(
+          "4 unwind cfa = r13 + 12\n"
+          "4 unwind r7 = [cfa - 12]\n"
+          "4 unwind r9 = [cfa - 8]\n"
+          "4 unwind r12 = [cfa - 4]\n",
+          GetFakeLogPrint());
+      break;
   }
   ASSERT_EQ(0x10040U, exidx_->cfa());
   ASSERT_EQ(0x11U, (*exidx_->regs())[7]);
@@ -252,34 +348,63 @@
   ASSERT_TRUE(exidx_->Decode());
   ASSERT_FALSE(exidx_->pc_set());
   ASSERT_EQ("", GetFakeLogBuf());
-  if (log_) {
-    ASSERT_EQ("4 unwind vsp = r0\n", GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+      ASSERT_EQ("4 unwind vsp = r0\n", GetFakeLogPrint());
+      break;
+    case ARM_LOG_BY_REG:
+      exidx_->LogByReg();
+      ASSERT_EQ("4 unwind cfa = r0\n", GetFakeLogPrint());
+      break;
   }
   ASSERT_EQ(1U, exidx_->cfa());
 
-  ResetLogs();
+  ResetExidx();
+  exidx_->set_cfa(0x100);
+  for (size_t i = 0; i < 15; i++) {
+    (*regs_arm_)[i] = i + 1;
+  }
   data_->push_back(0x93);
   ASSERT_TRUE(exidx_->Decode());
   ASSERT_FALSE(exidx_->pc_set());
   ASSERT_EQ("", GetFakeLogBuf());
-  if (log_) {
-    ASSERT_EQ("4 unwind vsp = r3\n", GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+      ASSERT_EQ("4 unwind vsp = r3\n", GetFakeLogPrint());
+      break;
+    case ARM_LOG_BY_REG:
+      exidx_->LogByReg();
+      ASSERT_EQ("4 unwind cfa = r3\n", GetFakeLogPrint());
+      break;
   }
   ASSERT_EQ(4U, exidx_->cfa());
 
-  ResetLogs();
+  ResetExidx();
+  exidx_->set_cfa(0x100);
+  for (size_t i = 0; i < 15; i++) {
+    (*regs_arm_)[i] = i + 1;
+  }
   data_->push_back(0x9e);
   ASSERT_TRUE(exidx_->Decode());
   ASSERT_FALSE(exidx_->pc_set());
   ASSERT_EQ("", GetFakeLogBuf());
-  if (log_) {
-    ASSERT_EQ("4 unwind vsp = r14\n", GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+      ASSERT_EQ("4 unwind vsp = r14\n", GetFakeLogPrint());
+      break;
+    case ARM_LOG_BY_REG:
+      exidx_->LogByReg();
+      ASSERT_EQ("4 unwind cfa = r14\n", GetFakeLogPrint());
+      break;
   }
   ASSERT_EQ(15U, exidx_->cfa());
 }
@@ -289,22 +414,30 @@
   data_->push_back(0x9d);
   ASSERT_FALSE(exidx_->Decode());
   ASSERT_EQ("", GetFakeLogBuf());
-  if (log_) {
-    ASSERT_EQ("4 unwind [Reserved]\n", GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+    case ARM_LOG_BY_REG:
+      ASSERT_EQ("4 unwind [Reserved]\n", GetFakeLogPrint());
+      break;
   }
   ASSERT_EQ(ARM_STATUS_RESERVED, exidx_->status());
 
   // 10011111: Reserved as prefix for Intel Wireless MMX register to register moves
-  ResetLogs();
+  ResetExidx();
   data_->push_back(0x9f);
   ASSERT_FALSE(exidx_->Decode());
   ASSERT_EQ("", GetFakeLogBuf());
-  if (log_) {
-    ASSERT_EQ("4 unwind [Reserved]\n", GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+    case ARM_LOG_BY_REG:
+      ASSERT_EQ("4 unwind [Reserved]\n", GetFakeLogPrint());
+      break;
   }
   ASSERT_EQ(ARM_STATUS_RESERVED, exidx_->status());
 }
@@ -316,53 +449,93 @@
   ASSERT_TRUE(exidx_->Decode());
   ASSERT_FALSE(exidx_->pc_set());
   ASSERT_EQ("", GetFakeLogBuf());
-  if (log_) {
-    ASSERT_EQ("4 unwind pop {r4}\n", GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+      ASSERT_EQ("4 unwind pop {r4}\n", GetFakeLogPrint());
+      break;
+    case ARM_LOG_BY_REG:
+      exidx_->LogByReg();
+      ASSERT_EQ(
+          "4 unwind cfa = r13 + 4\n"
+          "4 unwind r4 = [cfa - 4]\n",
+          GetFakeLogPrint());
+      break;
   }
   ASSERT_EQ(0x10004U, exidx_->cfa());
   ASSERT_EQ(0x14U, (*exidx_->regs())[4]);
 
-  ResetLogs();
+  ResetExidx();
   data_->push_back(0xa3);
-  process_memory_.SetData32(0x10004, 0x20);
-  process_memory_.SetData32(0x10008, 0x30);
-  process_memory_.SetData32(0x1000c, 0x40);
-  process_memory_.SetData32(0x10010, 0x50);
+  process_memory_.SetData32(0x10000, 0x20);
+  process_memory_.SetData32(0x10004, 0x30);
+  process_memory_.SetData32(0x10008, 0x40);
+  process_memory_.SetData32(0x1000c, 0x50);
   ASSERT_TRUE(exidx_->Decode());
   ASSERT_FALSE(exidx_->pc_set());
   ASSERT_EQ("", GetFakeLogBuf());
-  if (log_) {
-    ASSERT_EQ("4 unwind pop {r4-r7}\n", GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+      ASSERT_EQ("4 unwind pop {r4-r7}\n", GetFakeLogPrint());
+      break;
+    case ARM_LOG_BY_REG:
+      exidx_->LogByReg();
+      ASSERT_EQ(
+          "4 unwind cfa = r13 + 16\n"
+          "4 unwind r4 = [cfa - 16]\n"
+          "4 unwind r5 = [cfa - 12]\n"
+          "4 unwind r6 = [cfa - 8]\n"
+          "4 unwind r7 = [cfa - 4]\n",
+          GetFakeLogPrint());
+      break;
   }
-  ASSERT_EQ(0x10014U, exidx_->cfa());
+  ASSERT_EQ(0x10010U, exidx_->cfa());
   ASSERT_EQ(0x20U, (*exidx_->regs())[4]);
   ASSERT_EQ(0x30U, (*exidx_->regs())[5]);
   ASSERT_EQ(0x40U, (*exidx_->regs())[6]);
   ASSERT_EQ(0x50U, (*exidx_->regs())[7]);
 
-  ResetLogs();
+  ResetExidx();
   data_->push_back(0xa7);
-  process_memory_.SetData32(0x10014, 0x41);
-  process_memory_.SetData32(0x10018, 0x51);
-  process_memory_.SetData32(0x1001c, 0x61);
-  process_memory_.SetData32(0x10020, 0x71);
-  process_memory_.SetData32(0x10024, 0x81);
-  process_memory_.SetData32(0x10028, 0x91);
-  process_memory_.SetData32(0x1002c, 0xa1);
-  process_memory_.SetData32(0x10030, 0xb1);
+  process_memory_.SetData32(0x10000, 0x41);
+  process_memory_.SetData32(0x10004, 0x51);
+  process_memory_.SetData32(0x10008, 0x61);
+  process_memory_.SetData32(0x1000c, 0x71);
+  process_memory_.SetData32(0x10010, 0x81);
+  process_memory_.SetData32(0x10014, 0x91);
+  process_memory_.SetData32(0x10018, 0xa1);
+  process_memory_.SetData32(0x1001c, 0xb1);
   ASSERT_TRUE(exidx_->Decode());
   ASSERT_FALSE(exidx_->pc_set());
   ASSERT_EQ("", GetFakeLogBuf());
-  if (log_) {
-    ASSERT_EQ("4 unwind pop {r4-r11}\n", GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+      ASSERT_EQ("4 unwind pop {r4-r11}\n", GetFakeLogPrint());
+      break;
+    case ARM_LOG_BY_REG:
+      exidx_->LogByReg();
+      ASSERT_EQ(
+          "4 unwind cfa = r13 + 32\n"
+          "4 unwind r4 = [cfa - 32]\n"
+          "4 unwind r5 = [cfa - 28]\n"
+          "4 unwind r6 = [cfa - 24]\n"
+          "4 unwind r7 = [cfa - 20]\n"
+          "4 unwind r8 = [cfa - 16]\n"
+          "4 unwind r9 = [cfa - 12]\n"
+          "4 unwind r10 = [cfa - 8]\n"
+          "4 unwind r11 = [cfa - 4]\n",
+          GetFakeLogPrint());
+      break;
   }
-  ASSERT_EQ(0x10034U, exidx_->cfa());
+  ASSERT_EQ(0x10020U, exidx_->cfa());
   ASSERT_EQ(0x41U, (*exidx_->regs())[4]);
   ASSERT_EQ(0x51U, (*exidx_->regs())[5]);
   ASSERT_EQ(0x61U, (*exidx_->regs())[6]);
@@ -381,57 +554,100 @@
   ASSERT_TRUE(exidx_->Decode());
   ASSERT_FALSE(exidx_->pc_set());
   ASSERT_EQ("", GetFakeLogBuf());
-  if (log_) {
-    ASSERT_EQ("4 unwind pop {r4, r14}\n", GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+      ASSERT_EQ("4 unwind pop {r4, r14}\n", GetFakeLogPrint());
+      break;
+    case ARM_LOG_BY_REG:
+      exidx_->LogByReg();
+      ASSERT_EQ(
+          "4 unwind cfa = r13 + 8\n"
+          "4 unwind r4 = [cfa - 8]\n"
+          "4 unwind r14 = [cfa - 4]\n",
+          GetFakeLogPrint());
+      break;
   }
   ASSERT_EQ(0x10008U, exidx_->cfa());
   ASSERT_EQ(0x12U, (*exidx_->regs())[4]);
   ASSERT_EQ(0x22U, (*exidx_->regs())[14]);
 
-  ResetLogs();
+  ResetExidx();
   data_->push_back(0xab);
-  process_memory_.SetData32(0x10008, 0x1);
-  process_memory_.SetData32(0x1000c, 0x2);
-  process_memory_.SetData32(0x10010, 0x3);
-  process_memory_.SetData32(0x10014, 0x4);
-  process_memory_.SetData32(0x10018, 0x5);
+  process_memory_.SetData32(0x10000, 0x1);
+  process_memory_.SetData32(0x10004, 0x2);
+  process_memory_.SetData32(0x10008, 0x3);
+  process_memory_.SetData32(0x1000c, 0x4);
+  process_memory_.SetData32(0x10010, 0x5);
   ASSERT_TRUE(exidx_->Decode());
   ASSERT_FALSE(exidx_->pc_set());
   ASSERT_EQ("", GetFakeLogBuf());
-  if (log_) {
-    ASSERT_EQ("4 unwind pop {r4-r7, r14}\n", GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+      ASSERT_EQ("4 unwind pop {r4-r7, r14}\n", GetFakeLogPrint());
+      break;
+    case ARM_LOG_BY_REG:
+      exidx_->LogByReg();
+      ASSERT_EQ(
+          "4 unwind cfa = r13 + 20\n"
+          "4 unwind r4 = [cfa - 20]\n"
+          "4 unwind r5 = [cfa - 16]\n"
+          "4 unwind r6 = [cfa - 12]\n"
+          "4 unwind r7 = [cfa - 8]\n"
+          "4 unwind r14 = [cfa - 4]\n",
+          GetFakeLogPrint());
+      break;
   }
-  ASSERT_EQ(0x1001cU, exidx_->cfa());
+  ASSERT_EQ(0x10014U, exidx_->cfa());
   ASSERT_EQ(0x1U, (*exidx_->regs())[4]);
   ASSERT_EQ(0x2U, (*exidx_->regs())[5]);
   ASSERT_EQ(0x3U, (*exidx_->regs())[6]);
   ASSERT_EQ(0x4U, (*exidx_->regs())[7]);
   ASSERT_EQ(0x5U, (*exidx_->regs())[14]);
 
-  ResetLogs();
+  ResetExidx();
   data_->push_back(0xaf);
-  process_memory_.SetData32(0x1001c, 0x1a);
-  process_memory_.SetData32(0x10020, 0x2a);
-  process_memory_.SetData32(0x10024, 0x3a);
-  process_memory_.SetData32(0x10028, 0x4a);
-  process_memory_.SetData32(0x1002c, 0x5a);
-  process_memory_.SetData32(0x10030, 0x6a);
-  process_memory_.SetData32(0x10034, 0x7a);
-  process_memory_.SetData32(0x10038, 0x8a);
-  process_memory_.SetData32(0x1003c, 0x9a);
+  process_memory_.SetData32(0x10000, 0x1a);
+  process_memory_.SetData32(0x10004, 0x2a);
+  process_memory_.SetData32(0x10008, 0x3a);
+  process_memory_.SetData32(0x1000c, 0x4a);
+  process_memory_.SetData32(0x10010, 0x5a);
+  process_memory_.SetData32(0x10014, 0x6a);
+  process_memory_.SetData32(0x10018, 0x7a);
+  process_memory_.SetData32(0x1001c, 0x8a);
+  process_memory_.SetData32(0x10020, 0x9a);
   ASSERT_TRUE(exidx_->Decode());
   ASSERT_FALSE(exidx_->pc_set());
   ASSERT_EQ("", GetFakeLogBuf());
-  if (log_) {
-    ASSERT_EQ("4 unwind pop {r4-r11, r14}\n", GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+      ASSERT_EQ("4 unwind pop {r4-r11, r14}\n", GetFakeLogPrint());
+      break;
+    case ARM_LOG_BY_REG:
+      exidx_->LogByReg();
+      ASSERT_EQ(
+          "4 unwind cfa = r13 + 36\n"
+          "4 unwind r4 = [cfa - 36]\n"
+          "4 unwind r5 = [cfa - 32]\n"
+          "4 unwind r6 = [cfa - 28]\n"
+          "4 unwind r7 = [cfa - 24]\n"
+          "4 unwind r8 = [cfa - 20]\n"
+          "4 unwind r9 = [cfa - 16]\n"
+          "4 unwind r10 = [cfa - 12]\n"
+          "4 unwind r11 = [cfa - 8]\n"
+          "4 unwind r14 = [cfa - 4]\n",
+          GetFakeLogPrint());
+      break;
   }
-  ASSERT_EQ(0x10040U, exidx_->cfa());
+  ASSERT_EQ(0x10024U, exidx_->cfa());
   ASSERT_EQ(0x1aU, (*exidx_->regs())[4]);
   ASSERT_EQ(0x2aU, (*exidx_->regs())[5]);
   ASSERT_EQ(0x3aU, (*exidx_->regs())[6]);
@@ -448,10 +664,17 @@
   data_->push_back(0xb0);
   ASSERT_FALSE(exidx_->Decode());
   ASSERT_EQ("", GetFakeLogBuf());
-  if (log_) {
-    ASSERT_EQ("4 unwind finish\n", GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+      ASSERT_EQ("4 unwind finish\n", GetFakeLogPrint());
+      break;
+    case ARM_LOG_BY_REG:
+      exidx_->LogByReg();
+      ASSERT_EQ("4 unwind cfa = r13\n", GetFakeLogPrint());
+      break;
   }
   ASSERT_EQ(0x10000U, exidx_->cfa());
   ASSERT_EQ(ARM_STATUS_FINISH, exidx_->status());
@@ -463,10 +686,14 @@
   data_->push_back(0x00);
   ASSERT_FALSE(exidx_->Decode());
   ASSERT_EQ("", GetFakeLogBuf());
-  if (log_) {
-    ASSERT_EQ("4 unwind Spare\n", GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+    case ARM_LOG_BY_REG:
+      ASSERT_EQ("4 unwind Spare\n", GetFakeLogPrint());
+      break;
   }
   ASSERT_EQ(0x10000U, exidx_->cfa());
   ASSERT_EQ(ARM_STATUS_SPARE, exidx_->status());
@@ -474,15 +701,19 @@
   // 10110001 xxxxyyyy: Spare (xxxx != 0000)
   for (size_t x = 1; x < 16; x++) {
     for (size_t y = 0; y < 16; y++) {
-      ResetLogs();
+      ResetExidx();
       data_->push_back(0xb1);
       data_->push_back((x << 4) | y);
       ASSERT_FALSE(exidx_->Decode()) << "x, y = " << x << ", " << y;
       ASSERT_EQ("", GetFakeLogBuf()) << "x, y = " << x << ", " << y;
-      if (log_) {
-        ASSERT_EQ("4 unwind Spare\n", GetFakeLogPrint()) << "x, y = " << x << ", " << y;
-      } else {
-        ASSERT_EQ("", GetFakeLogPrint());
+      switch (log_) {
+        case ARM_LOG_NONE:
+          ASSERT_EQ("", GetFakeLogPrint());
+          break;
+        case ARM_LOG_FULL:
+        case ARM_LOG_BY_REG:
+          ASSERT_EQ("4 unwind Spare\n", GetFakeLogPrint()) << "x, y = " << x << ", " << y;
+          break;
       }
       ASSERT_EQ(0x10000U, exidx_->cfa()) << "x, y = " << x << ", " << y;
       ASSERT_EQ(ARM_STATUS_SPARE, exidx_->status());
@@ -491,29 +722,37 @@
 
   // 101101nn: Spare
   for (size_t n = 0; n < 4; n++) {
-    ResetLogs();
+    ResetExidx();
     data_->push_back(0xb4 | n);
     ASSERT_FALSE(exidx_->Decode()) << "n = " << n;
     ASSERT_EQ("", GetFakeLogBuf()) << "n = " << n;
-    if (log_) {
-      ASSERT_EQ("4 unwind Spare\n", GetFakeLogPrint()) << "n = " << n;
-    } else {
-      ASSERT_EQ("", GetFakeLogPrint());
+    switch (log_) {
+      case ARM_LOG_NONE:
+        ASSERT_EQ("", GetFakeLogPrint());
+        break;
+      case ARM_LOG_FULL:
+      case ARM_LOG_BY_REG:
+        ASSERT_EQ("4 unwind Spare\n", GetFakeLogPrint()) << "n = " << n;
+        break;
     }
     ASSERT_EQ(0x10000U, exidx_->cfa()) << "n = " << n;
     ASSERT_EQ(ARM_STATUS_SPARE, exidx_->status());
   }
 
   // 11000111 00000000: Spare
-  ResetLogs();
+  ResetExidx();
   data_->push_back(0xc7);
   data_->push_back(0x00);
   ASSERT_FALSE(exidx_->Decode());
   ASSERT_EQ("", GetFakeLogBuf());
-  if (log_) {
-    ASSERT_EQ("4 unwind Spare\n", GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+    case ARM_LOG_BY_REG:
+      ASSERT_EQ("4 unwind Spare\n", GetFakeLogPrint());
+      break;
   }
   ASSERT_EQ(0x10000U, exidx_->cfa());
   ASSERT_EQ(ARM_STATUS_SPARE, exidx_->status());
@@ -521,15 +760,19 @@
   // 11000111 xxxxyyyy: Spare (xxxx != 0000)
   for (size_t x = 1; x < 16; x++) {
     for (size_t y = 0; y < 16; y++) {
-      ResetLogs();
+      ResetExidx();
       data_->push_back(0xc7);
       data_->push_back(0x10);
       ASSERT_FALSE(exidx_->Decode()) << "x, y = " << x << ", " << y;
       ASSERT_EQ("", GetFakeLogBuf()) << "x, y = " << x << ", " << y;
-      if (log_) {
-        ASSERT_EQ("4 unwind Spare\n", GetFakeLogPrint()) << "x, y = " << x << ", " << y;
-      } else {
-        ASSERT_EQ("", GetFakeLogPrint());
+      switch (log_) {
+        case ARM_LOG_NONE:
+          ASSERT_EQ("", GetFakeLogPrint());
+          break;
+        case ARM_LOG_FULL:
+        case ARM_LOG_BY_REG:
+          ASSERT_EQ("4 unwind Spare\n", GetFakeLogPrint()) << "x, y = " << x << ", " << y;
+          break;
       }
       ASSERT_EQ(0x10000U, exidx_->cfa()) << "x, y = " << x << ", " << y;
       ASSERT_EQ(ARM_STATUS_SPARE, exidx_->status());
@@ -538,14 +781,18 @@
 
   // 11001yyy: Spare (yyy != 000, 001)
   for (size_t y = 2; y < 8; y++) {
-    ResetLogs();
+    ResetExidx();
     data_->push_back(0xc8 | y);
     ASSERT_FALSE(exidx_->Decode()) << "y = " << y;
     ASSERT_EQ("", GetFakeLogBuf()) << "y = " << y;
-    if (log_) {
-      ASSERT_EQ("4 unwind Spare\n", GetFakeLogPrint()) << "y = " << y;
-    } else {
-      ASSERT_EQ("", GetFakeLogPrint());
+    switch (log_) {
+      case ARM_LOG_NONE:
+        ASSERT_EQ("", GetFakeLogPrint());
+        break;
+      case ARM_LOG_FULL:
+      case ARM_LOG_BY_REG:
+        ASSERT_EQ("4 unwind Spare\n", GetFakeLogPrint()) << "y = " << y;
+        break;
     }
     ASSERT_EQ(0x10000U, exidx_->cfa()) << "y = " << y;
     ASSERT_EQ(ARM_STATUS_SPARE, exidx_->status());
@@ -554,14 +801,18 @@
   // 11xxxyyy: Spare (xxx != 000, 001, 010)
   for (size_t x = 3; x < 8; x++) {
     for (size_t y = 0; y < 8; y++) {
-      ResetLogs();
+      ResetExidx();
       data_->push_back(0xc0 | (x << 3) | y);
       ASSERT_FALSE(exidx_->Decode()) << "x, y = " << x << ", " << y;
       ASSERT_EQ("", GetFakeLogBuf()) << "x, y = " << x << ", " << y;
-      if (log_) {
-        ASSERT_EQ("4 unwind Spare\n", GetFakeLogPrint()) << "x, y = " << x << ", " << y;
-      } else {
-        ASSERT_EQ("", GetFakeLogPrint());
+      switch (log_) {
+        case ARM_LOG_NONE:
+          ASSERT_EQ("", GetFakeLogPrint());
+          break;
+        case ARM_LOG_FULL:
+        case ARM_LOG_BY_REG:
+          ASSERT_EQ("4 unwind Spare\n", GetFakeLogPrint()) << "x, y = " << x << ", " << y;
+          break;
       }
       ASSERT_EQ(0x10000U, exidx_->cfa()) << "x, y = " << x << ", " << y;
       ASSERT_EQ(ARM_STATUS_SPARE, exidx_->status());
@@ -577,47 +828,81 @@
   ASSERT_TRUE(exidx_->Decode());
   ASSERT_FALSE(exidx_->pc_set());
   ASSERT_EQ("", GetFakeLogBuf());
-  if (log_) {
-    ASSERT_EQ("4 unwind pop {r0}\n", GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+      ASSERT_EQ("4 unwind pop {r0}\n", GetFakeLogPrint());
+      break;
+    case ARM_LOG_BY_REG:
+      exidx_->LogByReg();
+      ASSERT_EQ(
+          "4 unwind cfa = r13 + 4\n"
+          "4 unwind r0 = [cfa - 4]\n",
+          GetFakeLogPrint());
+      break;
   }
   ASSERT_EQ(0x10004U, exidx_->cfa());
   ASSERT_EQ(0x45U, (*exidx_->regs())[0]);
 
-  ResetLogs();
+  ResetExidx();
   data_->push_back(0xb1);
   data_->push_back(0x0a);
-  process_memory_.SetData32(0x10004, 0x23);
-  process_memory_.SetData32(0x10008, 0x24);
+  process_memory_.SetData32(0x10000, 0x23);
+  process_memory_.SetData32(0x10004, 0x24);
   ASSERT_TRUE(exidx_->Decode());
   ASSERT_FALSE(exidx_->pc_set());
   ASSERT_EQ("", GetFakeLogBuf());
-  if (log_) {
-    ASSERT_EQ("4 unwind pop {r1, r3}\n", GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+      ASSERT_EQ("4 unwind pop {r1, r3}\n", GetFakeLogPrint());
+      break;
+    case ARM_LOG_BY_REG:
+      exidx_->LogByReg();
+      ASSERT_EQ(
+          "4 unwind cfa = r13 + 8\n"
+          "4 unwind r1 = [cfa - 8]\n"
+          "4 unwind r3 = [cfa - 4]\n",
+          GetFakeLogPrint());
+      break;
   }
-  ASSERT_EQ(0x1000cU, exidx_->cfa());
+  ASSERT_EQ(0x10008U, exidx_->cfa());
   ASSERT_EQ(0x23U, (*exidx_->regs())[1]);
   ASSERT_EQ(0x24U, (*exidx_->regs())[3]);
 
-  ResetLogs();
+  ResetExidx();
   data_->push_back(0xb1);
   data_->push_back(0x0f);
-  process_memory_.SetData32(0x1000c, 0x65);
-  process_memory_.SetData32(0x10010, 0x54);
-  process_memory_.SetData32(0x10014, 0x43);
-  process_memory_.SetData32(0x10018, 0x32);
+  process_memory_.SetData32(0x10000, 0x65);
+  process_memory_.SetData32(0x10004, 0x54);
+  process_memory_.SetData32(0x10008, 0x43);
+  process_memory_.SetData32(0x1000c, 0x32);
   ASSERT_TRUE(exidx_->Decode());
   ASSERT_FALSE(exidx_->pc_set());
   ASSERT_EQ("", GetFakeLogBuf());
-  if (log_) {
-    ASSERT_EQ("4 unwind pop {r0, r1, r2, r3}\n", GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+      ASSERT_EQ("4 unwind pop {r0, r1, r2, r3}\n", GetFakeLogPrint());
+      break;
+    case ARM_LOG_BY_REG:
+      exidx_->LogByReg();
+      ASSERT_EQ(
+          "4 unwind cfa = r13 + 16\n"
+          "4 unwind r0 = [cfa - 16]\n"
+          "4 unwind r1 = [cfa - 12]\n"
+          "4 unwind r2 = [cfa - 8]\n"
+          "4 unwind r3 = [cfa - 4]\n",
+          GetFakeLogPrint());
+      break;
   }
-  ASSERT_EQ(0x1001cU, exidx_->cfa());
+  ASSERT_EQ(0x10010U, exidx_->cfa());
   ASSERT_EQ(0x65U, (*exidx_->regs())[0]);
   ASSERT_EQ(0x54U, (*exidx_->regs())[1]);
   ASSERT_EQ(0x43U, (*exidx_->regs())[2]);
@@ -631,28 +916,42 @@
   ASSERT_TRUE(exidx_->Decode());
   ASSERT_FALSE(exidx_->pc_set());
   ASSERT_EQ("", GetFakeLogBuf());
-  if (log_) {
-    ASSERT_EQ("4 unwind vsp = vsp + 1024\n", GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+      ASSERT_EQ("4 unwind vsp = vsp + 1024\n", GetFakeLogPrint());
+      break;
+    case ARM_LOG_BY_REG:
+      exidx_->LogByReg();
+      ASSERT_EQ("4 unwind cfa = r13 + 1024\n", GetFakeLogPrint());
+      break;
   }
   ASSERT_EQ(0x10400U, exidx_->cfa());
 
-  ResetLogs();
+  ResetExidx();
   data_->push_back(0xb2);
   data_->push_back(0xff);
   data_->push_back(0x02);
   ASSERT_TRUE(exidx_->Decode());
   ASSERT_FALSE(exidx_->pc_set());
   ASSERT_EQ("", GetFakeLogBuf());
-  if (log_) {
-    ASSERT_EQ("4 unwind vsp = vsp + 2048\n", GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+      ASSERT_EQ("4 unwind vsp = vsp + 2048\n", GetFakeLogPrint());
+      break;
+    case ARM_LOG_BY_REG:
+      exidx_->LogByReg();
+      ASSERT_EQ("4 unwind cfa = r13 + 2048\n", GetFakeLogPrint());
+      break;
   }
-  ASSERT_EQ(0x10c00U, exidx_->cfa());
+  ASSERT_EQ(0x10800U, exidx_->cfa());
 
-  ResetLogs();
+  ResetExidx();
   data_->push_back(0xb2);
   data_->push_back(0xff);
   data_->push_back(0x82);
@@ -660,12 +959,19 @@
   ASSERT_TRUE(exidx_->Decode());
   ASSERT_FALSE(exidx_->pc_set());
   ASSERT_EQ("", GetFakeLogBuf());
-  if (log_) {
-    ASSERT_EQ("4 unwind vsp = vsp + 3147776\n", GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+      ASSERT_EQ("4 unwind vsp = vsp + 3147776\n", GetFakeLogPrint());
+      break;
+    case ARM_LOG_BY_REG:
+      exidx_->LogByReg();
+      ASSERT_EQ("4 unwind cfa = r13 + 3147776\n", GetFakeLogPrint());
+      break;
   }
-  ASSERT_EQ(0x311400U, exidx_->cfa());
+  ASSERT_EQ(0x310800U, exidx_->cfa());
 }
 
 TEST_P(ArmExidxDecodeTest, pop_vfp_fstmfdx) {
@@ -675,25 +981,37 @@
   ASSERT_TRUE(exidx_->Decode());
   ASSERT_FALSE(exidx_->pc_set());
   ASSERT_EQ("", GetFakeLogBuf());
-  if (log_) {
-    ASSERT_EQ("4 unwind pop {d0}\n", GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+      ASSERT_EQ("4 unwind pop {d0}\n", GetFakeLogPrint());
+      break;
+    case ARM_LOG_BY_REG:
+      ASSERT_EQ("4 unwind Unsupported DX register display\n", GetFakeLogPrint());
+      break;
   }
   ASSERT_EQ(0x1000cU, exidx_->cfa());
 
-  ResetLogs();
+  ResetExidx();
   data_->push_back(0xb3);
   data_->push_back(0x48);
   ASSERT_TRUE(exidx_->Decode());
   ASSERT_FALSE(exidx_->pc_set());
   ASSERT_EQ("", GetFakeLogBuf());
-  if (log_) {
-    ASSERT_EQ("4 unwind pop {d4-d12}\n", GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+      ASSERT_EQ("4 unwind pop {d4-d12}\n", GetFakeLogPrint());
+      break;
+    case ARM_LOG_BY_REG:
+      ASSERT_EQ("4 unwind Unsupported DX register display\n", GetFakeLogPrint());
+      break;
   }
-  ASSERT_EQ(0x10058U, exidx_->cfa());
+  ASSERT_EQ(0x1004cU, exidx_->cfa());
 }
 
 TEST_P(ArmExidxDecodeTest, pop_vfp8_fstmfdx) {
@@ -702,36 +1020,54 @@
   ASSERT_TRUE(exidx_->Decode());
   ASSERT_FALSE(exidx_->pc_set());
   ASSERT_EQ("", GetFakeLogBuf());
-  if (log_) {
-    ASSERT_EQ("4 unwind pop {d8}\n", GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+      ASSERT_EQ("4 unwind pop {d8}\n", GetFakeLogPrint());
+      break;
+    case ARM_LOG_BY_REG:
+      ASSERT_EQ("4 unwind Unsupported DX register display\n", GetFakeLogPrint());
+      break;
   }
   ASSERT_EQ(0x1000cU, exidx_->cfa());
 
-  ResetLogs();
+  ResetExidx();
   data_->push_back(0xbb);
   ASSERT_TRUE(exidx_->Decode());
   ASSERT_FALSE(exidx_->pc_set());
   ASSERT_EQ("", GetFakeLogBuf());
-  if (log_) {
-    ASSERT_EQ("4 unwind pop {d8-d11}\n", GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+      ASSERT_EQ("4 unwind pop {d8-d11}\n", GetFakeLogPrint());
+      break;
+    case ARM_LOG_BY_REG:
+      ASSERT_EQ("4 unwind Unsupported DX register display\n", GetFakeLogPrint());
+      break;
   }
-  ASSERT_EQ(0x10030U, exidx_->cfa());
+  ASSERT_EQ(0x10024U, exidx_->cfa());
 
-  ResetLogs();
+  ResetExidx();
   data_->push_back(0xbf);
   ASSERT_TRUE(exidx_->Decode());
   ASSERT_FALSE(exidx_->pc_set());
   ASSERT_EQ("", GetFakeLogBuf());
-  if (log_) {
-    ASSERT_EQ("4 unwind pop {d8-d15}\n", GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+      ASSERT_EQ("4 unwind pop {d8-d15}\n", GetFakeLogPrint());
+      break;
+    case ARM_LOG_BY_REG:
+      ASSERT_EQ("4 unwind Unsupported DX register display\n", GetFakeLogPrint());
+      break;
   }
-  ASSERT_EQ(0x10074U, exidx_->cfa());
+  ASSERT_EQ(0x10044U, exidx_->cfa());
 }
 
 TEST_P(ArmExidxDecodeTest, pop_mmx_wr10) {
@@ -740,36 +1076,54 @@
   ASSERT_TRUE(exidx_->Decode());
   ASSERT_FALSE(exidx_->pc_set());
   ASSERT_EQ("", GetFakeLogBuf());
-  if (log_) {
-    ASSERT_EQ("4 unwind pop {wR10}\n", GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+      ASSERT_EQ("4 unwind pop {wR10}\n", GetFakeLogPrint());
+      break;
+    case ARM_LOG_BY_REG:
+      ASSERT_EQ("4 unwind Unsupported wRX register display\n", GetFakeLogPrint());
+      break;
   }
   ASSERT_EQ(0x10008U, exidx_->cfa());
 
-  ResetLogs();
+  ResetExidx();
   data_->push_back(0xc2);
   ASSERT_TRUE(exidx_->Decode());
   ASSERT_FALSE(exidx_->pc_set());
   ASSERT_EQ("", GetFakeLogBuf());
-  if (log_) {
-    ASSERT_EQ("4 unwind pop {wR10-wR12}\n", GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+      ASSERT_EQ("4 unwind pop {wR10-wR12}\n", GetFakeLogPrint());
+      break;
+    case ARM_LOG_BY_REG:
+      ASSERT_EQ("4 unwind Unsupported wRX register display\n", GetFakeLogPrint());
+      break;
   }
-  ASSERT_EQ(0x10020U, exidx_->cfa());
+  ASSERT_EQ(0x10018U, exidx_->cfa());
 
-  ResetLogs();
+  ResetExidx();
   data_->push_back(0xc5);
   ASSERT_TRUE(exidx_->Decode());
   ASSERT_FALSE(exidx_->pc_set());
   ASSERT_EQ("", GetFakeLogBuf());
-  if (log_) {
-    ASSERT_EQ("4 unwind pop {wR10-wR15}\n", GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+      ASSERT_EQ("4 unwind pop {wR10-wR15}\n", GetFakeLogPrint());
+      break;
+    case ARM_LOG_BY_REG:
+      ASSERT_EQ("4 unwind Unsupported wRX register display\n", GetFakeLogPrint());
+      break;
   }
-  ASSERT_EQ(0x10050U, exidx_->cfa());
+  ASSERT_EQ(0x10030U, exidx_->cfa());
 }
 
 TEST_P(ArmExidxDecodeTest, pop_mmx_wr) {
@@ -779,38 +1133,56 @@
   ASSERT_TRUE(exidx_->Decode());
   ASSERT_FALSE(exidx_->pc_set());
   ASSERT_EQ("", GetFakeLogBuf());
-  if (log_) {
-    ASSERT_EQ("4 unwind pop {wR0}\n", GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+      ASSERT_EQ("4 unwind pop {wR0}\n", GetFakeLogPrint());
+      break;
+    case ARM_LOG_BY_REG:
+      ASSERT_EQ("4 unwind Unsupported wRX register display\n", GetFakeLogPrint());
+      break;
   }
   ASSERT_EQ(0x10008U, exidx_->cfa());
 
-  ResetLogs();
+  ResetExidx();
   data_->push_back(0xc6);
   data_->push_back(0x25);
   ASSERT_TRUE(exidx_->Decode());
   ASSERT_FALSE(exidx_->pc_set());
   ASSERT_EQ("", GetFakeLogBuf());
-  if (log_) {
-    ASSERT_EQ("4 unwind pop {wR2-wR7}\n", GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+      ASSERT_EQ("4 unwind pop {wR2-wR7}\n", GetFakeLogPrint());
+      break;
+    case ARM_LOG_BY_REG:
+      ASSERT_EQ("4 unwind Unsupported wRX register display\n", GetFakeLogPrint());
+      break;
   }
-  ASSERT_EQ(0x10038U, exidx_->cfa());
+  ASSERT_EQ(0x10030U, exidx_->cfa());
 
-  ResetLogs();
+  ResetExidx();
   data_->push_back(0xc6);
   data_->push_back(0xff);
   ASSERT_TRUE(exidx_->Decode());
   ASSERT_FALSE(exidx_->pc_set());
   ASSERT_EQ("", GetFakeLogBuf());
-  if (log_) {
-    ASSERT_EQ("4 unwind pop {wR15-wR30}\n", GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+      ASSERT_EQ("4 unwind pop {wR15-wR30}\n", GetFakeLogPrint());
+      break;
+    case ARM_LOG_BY_REG:
+      ASSERT_EQ("4 unwind Unsupported wRX register display\n", GetFakeLogPrint());
+      break;
   }
-  ASSERT_EQ(0x100b8U, exidx_->cfa());
+  ASSERT_EQ(0x10080U, exidx_->cfa());
 }
 
 TEST_P(ArmExidxDecodeTest, pop_mmx_wcgr) {
@@ -820,38 +1192,56 @@
   ASSERT_TRUE(exidx_->Decode());
   ASSERT_FALSE(exidx_->pc_set());
   ASSERT_EQ("", GetFakeLogBuf());
-  if (log_) {
-    ASSERT_EQ("4 unwind pop {wCGR0}\n", GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+      ASSERT_EQ("4 unwind pop {wCGR0}\n", GetFakeLogPrint());
+      break;
+    case ARM_LOG_BY_REG:
+      ASSERT_EQ("4 unwind Unsupported wCGR register display\n", GetFakeLogPrint());
+      break;
   }
   ASSERT_EQ(0x10004U, exidx_->cfa());
 
-  ResetLogs();
+  ResetExidx();
   data_->push_back(0xc7);
   data_->push_back(0x0a);
   ASSERT_TRUE(exidx_->Decode());
   ASSERT_FALSE(exidx_->pc_set());
   ASSERT_EQ("", GetFakeLogBuf());
-  if (log_) {
-    ASSERT_EQ("4 unwind pop {wCGR1, wCGR3}\n", GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+      ASSERT_EQ("4 unwind pop {wCGR1, wCGR3}\n", GetFakeLogPrint());
+      break;
+    case ARM_LOG_BY_REG:
+      ASSERT_EQ("4 unwind Unsupported wCGR register display\n", GetFakeLogPrint());
+      break;
   }
-  ASSERT_EQ(0x1000cU, exidx_->cfa());
+  ASSERT_EQ(0x10008U, exidx_->cfa());
 
-  ResetLogs();
+  ResetExidx();
   data_->push_back(0xc7);
   data_->push_back(0x0f);
   ASSERT_TRUE(exidx_->Decode());
   ASSERT_FALSE(exidx_->pc_set());
   ASSERT_EQ("", GetFakeLogBuf());
-  if (log_) {
-    ASSERT_EQ("4 unwind pop {wCGR0, wCGR1, wCGR2, wCGR3}\n", GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+      ASSERT_EQ("4 unwind pop {wCGR0, wCGR1, wCGR2, wCGR3}\n", GetFakeLogPrint());
+      break;
+    case ARM_LOG_BY_REG:
+      ASSERT_EQ("4 unwind Unsupported wCGR register display\n", GetFakeLogPrint());
+      break;
   }
-  ASSERT_EQ(0x1001cU, exidx_->cfa());
+  ASSERT_EQ(0x10010U, exidx_->cfa());
 }
 
 TEST_P(ArmExidxDecodeTest, pop_vfp16_vpush) {
@@ -861,38 +1251,56 @@
   ASSERT_TRUE(exidx_->Decode());
   ASSERT_FALSE(exidx_->pc_set());
   ASSERT_EQ("", GetFakeLogBuf());
-  if (log_) {
-    ASSERT_EQ("4 unwind pop {d16}\n", GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+      ASSERT_EQ("4 unwind pop {d16}\n", GetFakeLogPrint());
+      break;
+    case ARM_LOG_BY_REG:
+      ASSERT_EQ("4 unwind Unsupported DX register display\n", GetFakeLogPrint());
+      break;
   }
   ASSERT_EQ(0x10008U, exidx_->cfa());
 
-  ResetLogs();
+  ResetExidx();
   data_->push_back(0xc8);
   data_->push_back(0x14);
   ASSERT_TRUE(exidx_->Decode());
   ASSERT_FALSE(exidx_->pc_set());
   ASSERT_EQ("", GetFakeLogBuf());
-  if (log_) {
-    ASSERT_EQ("4 unwind pop {d17-d21}\n", GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+      ASSERT_EQ("4 unwind pop {d17-d21}\n", GetFakeLogPrint());
+      break;
+    case ARM_LOG_BY_REG:
+      ASSERT_EQ("4 unwind Unsupported DX register display\n", GetFakeLogPrint());
+      break;
   }
-  ASSERT_EQ(0x10030U, exidx_->cfa());
+  ASSERT_EQ(0x10028U, exidx_->cfa());
 
-  ResetLogs();
+  ResetExidx();
   data_->push_back(0xc8);
   data_->push_back(0xff);
   ASSERT_TRUE(exidx_->Decode());
   ASSERT_FALSE(exidx_->pc_set());
   ASSERT_EQ("", GetFakeLogBuf());
-  if (log_) {
-    ASSERT_EQ("4 unwind pop {d31-d46}\n", GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+      ASSERT_EQ("4 unwind pop {d31-d46}\n", GetFakeLogPrint());
+      break;
+    case ARM_LOG_BY_REG:
+      ASSERT_EQ("4 unwind Unsupported DX register display\n", GetFakeLogPrint());
+      break;
   }
-  ASSERT_EQ(0x100b0U, exidx_->cfa());
+  ASSERT_EQ(0x10080U, exidx_->cfa());
 }
 
 TEST_P(ArmExidxDecodeTest, pop_vfp_vpush) {
@@ -902,38 +1310,56 @@
   ASSERT_TRUE(exidx_->Decode());
   ASSERT_FALSE(exidx_->pc_set());
   ASSERT_EQ("", GetFakeLogBuf());
-  if (log_) {
-    ASSERT_EQ("4 unwind pop {d0}\n", GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+      ASSERT_EQ("4 unwind pop {d0}\n", GetFakeLogPrint());
+      break;
+    case ARM_LOG_BY_REG:
+      ASSERT_EQ("4 unwind Unsupported DX register display\n", GetFakeLogPrint());
+      break;
   }
   ASSERT_EQ(0x10008U, exidx_->cfa());
 
-  ResetLogs();
+  ResetExidx();
   data_->push_back(0xc9);
   data_->push_back(0x23);
   ASSERT_TRUE(exidx_->Decode());
   ASSERT_FALSE(exidx_->pc_set());
   ASSERT_EQ("", GetFakeLogBuf());
-  if (log_) {
-    ASSERT_EQ("4 unwind pop {d2-d5}\n", GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+      ASSERT_EQ("4 unwind pop {d2-d5}\n", GetFakeLogPrint());
+      break;
+    case ARM_LOG_BY_REG:
+      ASSERT_EQ("4 unwind Unsupported DX register display\n", GetFakeLogPrint());
+      break;
   }
-  ASSERT_EQ(0x10028U, exidx_->cfa());
+  ASSERT_EQ(0x10020U, exidx_->cfa());
 
-  ResetLogs();
+  ResetExidx();
   data_->push_back(0xc9);
   data_->push_back(0xff);
   ASSERT_TRUE(exidx_->Decode());
   ASSERT_FALSE(exidx_->pc_set());
   ASSERT_EQ("", GetFakeLogBuf());
-  if (log_) {
-    ASSERT_EQ("4 unwind pop {d15-d30}\n", GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+      ASSERT_EQ("4 unwind pop {d15-d30}\n", GetFakeLogPrint());
+      break;
+    case ARM_LOG_BY_REG:
+      ASSERT_EQ("4 unwind Unsupported DX register display\n", GetFakeLogPrint());
+      break;
   }
-  ASSERT_EQ(0x100a8U, exidx_->cfa());
+  ASSERT_EQ(0x10080U, exidx_->cfa());
 }
 
 TEST_P(ArmExidxDecodeTest, pop_vfp8_vpush) {
@@ -942,36 +1368,54 @@
   ASSERT_TRUE(exidx_->Decode());
   ASSERT_FALSE(exidx_->pc_set());
   ASSERT_EQ("", GetFakeLogBuf());
-  if (log_) {
-    ASSERT_EQ("4 unwind pop {d8}\n", GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+      ASSERT_EQ("4 unwind pop {d8}\n", GetFakeLogPrint());
+      break;
+    case ARM_LOG_BY_REG:
+      ASSERT_EQ("4 unwind Unsupported DX register display\n", GetFakeLogPrint());
+      break;
   }
   ASSERT_EQ(0x10008U, exidx_->cfa());
 
-  ResetLogs();
+  ResetExidx();
   data_->push_back(0xd2);
   ASSERT_TRUE(exidx_->Decode());
   ASSERT_FALSE(exidx_->pc_set());
   ASSERT_EQ("", GetFakeLogBuf());
-  if (log_) {
-    ASSERT_EQ("4 unwind pop {d8-d10}\n", GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+      ASSERT_EQ("4 unwind pop {d8-d10}\n", GetFakeLogPrint());
+      break;
+    case ARM_LOG_BY_REG:
+      ASSERT_EQ("4 unwind Unsupported DX register display\n", GetFakeLogPrint());
+      break;
   }
-  ASSERT_EQ(0x10020U, exidx_->cfa());
+  ASSERT_EQ(0x10018U, exidx_->cfa());
 
-  ResetLogs();
+  ResetExidx();
   data_->push_back(0xd7);
   ASSERT_TRUE(exidx_->Decode());
   ASSERT_FALSE(exidx_->pc_set());
   ASSERT_EQ("", GetFakeLogBuf());
-  if (log_) {
-    ASSERT_EQ("4 unwind pop {d8-d15}\n", GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+      ASSERT_EQ("4 unwind pop {d8-d15}\n", GetFakeLogPrint());
+      break;
+    case ARM_LOG_BY_REG:
+      ASSERT_EQ("4 unwind Unsupported DX register display\n", GetFakeLogPrint());
+      break;
   }
-  ASSERT_EQ(0x10060U, exidx_->cfa());
+  ASSERT_EQ(0x10040U, exidx_->cfa());
 }
 
 TEST_P(ArmExidxDecodeTest, expect_truncated) {
@@ -1044,32 +1488,147 @@
 TEST_P(ArmExidxDecodeTest, eval_multiple_decodes) {
   // vsp = vsp + 4
   data_->push_back(0x00);
-  // vsp = vsp + 8
+  // vsp = vsp + 12
   data_->push_back(0x02);
   // Finish
   data_->push_back(0xb0);
 
   ASSERT_TRUE(exidx_->Eval());
-  if (log_) {
-    ASSERT_EQ("4 unwind vsp = vsp + 4\n"
-              "4 unwind vsp = vsp + 12\n"
-              "4 unwind finish\n", GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+      ASSERT_EQ(
+          "4 unwind vsp = vsp + 4\n"
+          "4 unwind vsp = vsp + 12\n"
+          "4 unwind finish\n",
+          GetFakeLogPrint());
+      break;
+    case ARM_LOG_BY_REG:
+      exidx_->LogByReg();
+      ASSERT_EQ("4 unwind cfa = r13 + 16\n", GetFakeLogPrint());
+      break;
   }
   ASSERT_EQ(0x10010U, exidx_->cfa());
   ASSERT_FALSE(exidx_->pc_set());
 }
 
+TEST_P(ArmExidxDecodeTest, eval_vsp_add_after_pop) {
+  // Pop {r15}
+  data_->push_back(0x88);
+  data_->push_back(0x00);
+  // vsp = vsp + 12
+  data_->push_back(0x02);
+  // Finish
+  data_->push_back(0xb0);
+  process_memory_.SetData32(0x10000, 0x10);
+
+  ASSERT_TRUE(exidx_->Eval());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+      ASSERT_EQ(
+          "4 unwind pop {r15}\n"
+          "4 unwind vsp = vsp + 12\n"
+          "4 unwind finish\n",
+          GetFakeLogPrint());
+      break;
+    case ARM_LOG_BY_REG:
+      exidx_->LogByReg();
+      ASSERT_EQ(
+          "4 unwind cfa = r13 + 16\n"
+          "4 unwind r15 = [cfa - 16]\n",
+          GetFakeLogPrint());
+      break;
+  }
+  ASSERT_EQ(0x10010U, exidx_->cfa());
+  ASSERT_TRUE(exidx_->pc_set());
+  ASSERT_EQ(0x10U, (*exidx_->regs())[15]);
+}
+
+TEST_P(ArmExidxDecodeTest, eval_vsp_add_large_after_pop) {
+  // Pop {r15}
+  data_->push_back(0x88);
+  data_->push_back(0x00);
+  // vsp = vsp + 1024
+  data_->push_back(0xb2);
+  data_->push_back(0x7f);
+  // Finish
+  data_->push_back(0xb0);
+  process_memory_.SetData32(0x10000, 0x10);
+
+  ASSERT_TRUE(exidx_->Eval());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+      ASSERT_EQ(
+          "4 unwind pop {r15}\n"
+          "4 unwind vsp = vsp + 1024\n"
+          "4 unwind finish\n",
+          GetFakeLogPrint());
+      break;
+    case ARM_LOG_BY_REG:
+      exidx_->LogByReg();
+      ASSERT_EQ(
+          "4 unwind cfa = r13 + 1028\n"
+          "4 unwind r15 = [cfa - 1028]\n",
+          GetFakeLogPrint());
+      break;
+  }
+  ASSERT_EQ(0x10404U, exidx_->cfa());
+  ASSERT_TRUE(exidx_->pc_set());
+  ASSERT_EQ(0x10U, (*exidx_->regs())[15]);
+}
+
+TEST_P(ArmExidxDecodeTest, eval_vsp_sub_after_pop) {
+  // Pop {r15}
+  data_->push_back(0x88);
+  data_->push_back(0x00);
+  // vsp = vsp - 4
+  data_->push_back(0x41);
+  // Finish
+  data_->push_back(0xb0);
+  process_memory_.SetData32(0x10000, 0x10);
+
+  ASSERT_TRUE(exidx_->Eval());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+      ASSERT_EQ(
+          "4 unwind pop {r15}\n"
+          "4 unwind vsp = vsp - 8\n"
+          "4 unwind finish\n",
+          GetFakeLogPrint());
+      break;
+    case ARM_LOG_BY_REG:
+      exidx_->LogByReg();
+      ASSERT_EQ(
+          "4 unwind cfa = r13 - 4\n"
+          "4 unwind r15 = [cfa + 4]\n",
+          GetFakeLogPrint());
+      break;
+  }
+  ASSERT_EQ(0xfffcU, exidx_->cfa());
+  ASSERT_TRUE(exidx_->pc_set());
+  ASSERT_EQ(0x10U, (*exidx_->regs())[15]);
+}
+
 TEST_P(ArmExidxDecodeTest, eval_pc_set) {
   // vsp = vsp + 4
   data_->push_back(0x00);
-  // vsp = vsp + 8
+  // vsp = vsp + 12
   data_->push_back(0x02);
   // Pop {r15}
   data_->push_back(0x88);
   data_->push_back(0x00);
-  // vsp = vsp + 8
+  // vsp = vsp + 12
   data_->push_back(0x02);
   // Finish
   data_->push_back(0xb0);
@@ -1077,18 +1636,33 @@
   process_memory_.SetData32(0x10010, 0x10);
 
   ASSERT_TRUE(exidx_->Eval());
-  if (log_) {
-    ASSERT_EQ("4 unwind vsp = vsp + 4\n"
-              "4 unwind vsp = vsp + 12\n"
-              "4 unwind pop {r15}\n"
-              "4 unwind vsp = vsp + 12\n"
-              "4 unwind finish\n", GetFakeLogPrint());
-  } else {
-    ASSERT_EQ("", GetFakeLogPrint());
+  switch (log_) {
+    case ARM_LOG_NONE:
+      ASSERT_EQ("", GetFakeLogPrint());
+      break;
+    case ARM_LOG_FULL:
+      ASSERT_EQ(
+          "4 unwind vsp = vsp + 4\n"
+          "4 unwind vsp = vsp + 12\n"
+          "4 unwind pop {r15}\n"
+          "4 unwind vsp = vsp + 12\n"
+          "4 unwind finish\n",
+          GetFakeLogPrint());
+      break;
+    case ARM_LOG_BY_REG:
+      exidx_->LogByReg();
+      ASSERT_EQ(
+          "4 unwind cfa = r13 + 32\n"
+          "4 unwind r15 = [cfa - 16]\n",
+          GetFakeLogPrint());
+      break;
   }
   ASSERT_EQ(0x10020U, exidx_->cfa());
   ASSERT_TRUE(exidx_->pc_set());
   ASSERT_EQ(0x10U, (*exidx_->regs())[15]);
 }
 
-INSTANTIATE_TEST_CASE_P(, ArmExidxDecodeTest, ::testing::Values("logging", "no_logging"));
+INSTANTIATE_TEST_CASE_P(, ArmExidxDecodeTest,
+                        ::testing::Values("logging", "register_logging", "no_logging"));
+
+}  // namespace unwindstack
diff --git a/libunwindstack/tests/ArmExidxExtractTest.cpp b/libunwindstack/tests/ArmExidxExtractTest.cpp
index aed75bf..79c799c 100644
--- a/libunwindstack/tests/ArmExidxExtractTest.cpp
+++ b/libunwindstack/tests/ArmExidxExtractTest.cpp
@@ -21,12 +21,15 @@
 
 #include <gtest/gtest.h>
 
+#include <unwindstack/Log.h>
+
 #include "ArmExidx.h"
-#include "Log.h"
 
 #include "LogFake.h"
 #include "MemoryFake.h"
 
+namespace unwindstack {
+
 class ArmExidxExtractTest : public ::testing::Test {
  protected:
   void SetUp() override {
@@ -254,22 +257,27 @@
 TEST_F(ArmExidxExtractTest, read_failures) {
   ASSERT_FALSE(exidx_->ExtractEntryData(0x5000));
   ASSERT_EQ(ARM_STATUS_READ_FAILED, exidx_->status());
+  EXPECT_EQ(0x5004U, exidx_->status_address());
 
   elf_memory_.SetData32(0x5000, 0x100);
   ASSERT_FALSE(exidx_->ExtractEntryData(0x5000));
   ASSERT_EQ(ARM_STATUS_READ_FAILED, exidx_->status());
+  EXPECT_EQ(0x5004U, exidx_->status_address());
 
   elf_memory_.SetData32(0x5004, 0x100);
   ASSERT_FALSE(exidx_->ExtractEntryData(0x5000));
   ASSERT_EQ(ARM_STATUS_READ_FAILED, exidx_->status());
+  EXPECT_EQ(0x5104U, exidx_->status_address());
 
   elf_memory_.SetData32(0x5104, 0x1);
   ASSERT_FALSE(exidx_->ExtractEntryData(0x5000));
   ASSERT_EQ(ARM_STATUS_READ_FAILED, exidx_->status());
+  EXPECT_EQ(0x5108U, exidx_->status_address());
 
   elf_memory_.SetData32(0x5108, 0x01010203);
   ASSERT_FALSE(exidx_->ExtractEntryData(0x5000));
   ASSERT_EQ(ARM_STATUS_READ_FAILED, exidx_->status());
+  EXPECT_EQ(0x510cU, exidx_->status_address());
 }
 
 TEST_F(ArmExidxExtractTest, malformed) {
@@ -293,7 +301,7 @@
   elf_memory_.SetData32(0x1000, 0x7fff2340);
   elf_memory_.SetData32(0x1004, 1);
 
-  exidx_->set_log(true);
+  exidx_->set_log(ARM_LOG_FULL);
   exidx_->set_log_indent(0);
   exidx_->set_log_skip_execution(false);
 
@@ -308,7 +316,7 @@
   elf_memory_.SetData32(0x4000, 0x7ffa3000);
   elf_memory_.SetData32(0x4004, 0x80a8b0b0);
 
-  exidx_->set_log(true);
+  exidx_->set_log(ARM_LOG_FULL);
   exidx_->set_log_indent(0);
   exidx_->set_log_skip_execution(false);
 
@@ -322,10 +330,12 @@
   elf_memory_.SetData32(0x6234, 0x2);
   elf_memory_.SetData32(0x6238, 0x00112233);
 
-  exidx_->set_log(true);
+  exidx_->set_log(ARM_LOG_FULL);
   exidx_->set_log_indent(0);
   exidx_->set_log_skip_execution(false);
 
   ASSERT_TRUE(exidx_->ExtractEntryData(0x5000));
   ASSERT_EQ("4 unwind Raw Data: 0x11 0x22 0x33 0xb0\n", GetFakeLogPrint());
 }
+
+}  // namespace unwindstack
diff --git a/libunwindstack/tests/DexFileData.h b/libunwindstack/tests/DexFileData.h
new file mode 100644
index 0000000..6975c68
--- /dev/null
+++ b/libunwindstack/tests/DexFileData.h
@@ -0,0 +1,45 @@
+/*
+ * 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 _LIBUNWINDSTACK_DEXFILESDATA_H
+#define _LIBUNWINDSTACK_DEXFILESDATA_H
+
+namespace unwindstack {
+
+// Borrowed from art/dex/dex_file_test.cc.
+static constexpr uint32_t kDexData[] = {
+    0x0a786564, 0x00383330, 0xc98b3ab8, 0xf3749d94, 0xaecca4d8, 0xffc7b09a, 0xdca9ca7f, 0x5be5deab,
+    0x00000220, 0x00000070, 0x12345678, 0x00000000, 0x00000000, 0x0000018c, 0x00000008, 0x00000070,
+    0x00000004, 0x00000090, 0x00000002, 0x000000a0, 0x00000000, 0x00000000, 0x00000003, 0x000000b8,
+    0x00000001, 0x000000d0, 0x00000130, 0x000000f0, 0x00000122, 0x0000012a, 0x00000132, 0x00000146,
+    0x00000151, 0x00000154, 0x00000158, 0x0000016d, 0x00000001, 0x00000002, 0x00000004, 0x00000006,
+    0x00000004, 0x00000002, 0x00000000, 0x00000005, 0x00000002, 0x0000011c, 0x00000000, 0x00000000,
+    0x00010000, 0x00000007, 0x00000001, 0x00000000, 0x00000000, 0x00000001, 0x00000001, 0x00000000,
+    0x00000003, 0x00000000, 0x0000017e, 0x00000000, 0x00010001, 0x00000001, 0x00000173, 0x00000004,
+    0x00021070, 0x000e0000, 0x00010001, 0x00000000, 0x00000178, 0x00000001, 0x0000000e, 0x00000001,
+    0x3c060003, 0x74696e69, 0x4c06003e, 0x6e69614d, 0x4c12003b, 0x6176616a, 0x6e616c2f, 0x624f2f67,
+    0x7463656a, 0x4d09003b, 0x2e6e6961, 0x6176616a, 0x00560100, 0x004c5602, 0x6a4c5b13, 0x2f617661,
+    0x676e616c, 0x7274532f, 0x3b676e69, 0x616d0400, 0x01006e69, 0x000e0700, 0x07000103, 0x0000000e,
+    0x81000002, 0x01f00480, 0x02880901, 0x0000000c, 0x00000000, 0x00000001, 0x00000000, 0x00000001,
+    0x00000008, 0x00000070, 0x00000002, 0x00000004, 0x00000090, 0x00000003, 0x00000002, 0x000000a0,
+    0x00000005, 0x00000003, 0x000000b8, 0x00000006, 0x00000001, 0x000000d0, 0x00002001, 0x00000002,
+    0x000000f0, 0x00001001, 0x00000001, 0x0000011c, 0x00002002, 0x00000008, 0x00000122, 0x00002003,
+    0x00000002, 0x00000173, 0x00002000, 0x00000001, 0x0000017e, 0x00001000, 0x00000001, 0x0000018c,
+};
+
+}  // namespace unwindstack
+
+#endif  // _LIBUNWINDSTACK_DEXFILESDATA_H
diff --git a/libunwindstack/tests/DexFileTest.cpp b/libunwindstack/tests/DexFileTest.cpp
new file mode 100644
index 0000000..4dd8cb0
--- /dev/null
+++ b/libunwindstack/tests/DexFileTest.cpp
@@ -0,0 +1,247 @@
+/*
+ * 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 <stdint.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <unordered_map>
+
+#include <android-base/test_utils.h>
+
+#include <unwindstack/MapInfo.h>
+#include <unwindstack/Memory.h>
+
+#include <dex/code_item_accessors-inl.h>
+#include <dex/standard_dex_file.h>
+
+#include <gtest/gtest.h>
+
+#include "DexFile.h"
+
+#include "DexFileData.h"
+#include "MemoryFake.h"
+
+namespace unwindstack {
+
+TEST(DexFileTest, from_file_open_non_exist) {
+  DexFileFromFile dex_file;
+  ASSERT_FALSE(dex_file.Open(0, "/file/does/not/exist"));
+}
+
+TEST(DexFileTest, from_file_open_too_small) {
+  TemporaryFile tf;
+  ASSERT_TRUE(tf.fd != -1);
+
+  ASSERT_EQ(sizeof(art::DexFile::Header) - 2,
+            static_cast<size_t>(
+                TEMP_FAILURE_RETRY(write(tf.fd, kDexData, sizeof(art::DexFile::Header)) - 2)));
+
+  // Header too small.
+  DexFileFromFile dex_file;
+  ASSERT_FALSE(dex_file.Open(0, tf.path));
+
+  // Header correct, file too small.
+  ASSERT_EQ(0, lseek(tf.fd, 0, SEEK_SET));
+  ASSERT_EQ(sizeof(art::DexFile::Header), static_cast<size_t>(TEMP_FAILURE_RETRY(write(
+                                              tf.fd, kDexData, sizeof(art::DexFile::Header)))));
+  ASSERT_FALSE(dex_file.Open(0, tf.path));
+}
+
+TEST(DexFileTest, from_file_open) {
+  TemporaryFile tf;
+  ASSERT_TRUE(tf.fd != -1);
+
+  ASSERT_EQ(sizeof(kDexData),
+            static_cast<size_t>(TEMP_FAILURE_RETRY(write(tf.fd, kDexData, sizeof(kDexData)))));
+
+  DexFileFromFile dex_file;
+  ASSERT_TRUE(dex_file.Open(0, tf.path));
+}
+
+TEST(DexFileTest, from_file_open_non_zero_offset) {
+  TemporaryFile tf;
+  ASSERT_TRUE(tf.fd != -1);
+
+  ASSERT_EQ(0x100, lseek(tf.fd, 0x100, SEEK_SET));
+  ASSERT_EQ(sizeof(kDexData),
+            static_cast<size_t>(TEMP_FAILURE_RETRY(write(tf.fd, kDexData, sizeof(kDexData)))));
+
+  DexFileFromFile dex_file;
+  ASSERT_TRUE(dex_file.Open(0x100, tf.path));
+}
+
+TEST(DexFileTest, from_memory_fail_too_small_for_header) {
+  MemoryFake memory;
+
+  memory.SetMemory(0x1000, kDexData, sizeof(art::DexFile::Header) - 1);
+  DexFileFromMemory dex_file;
+
+  ASSERT_FALSE(dex_file.Open(0x1000, &memory));
+}
+
+TEST(DexFileTest, from_memory_fail_too_small_for_data) {
+  MemoryFake memory;
+
+  memory.SetMemory(0x1000, kDexData, sizeof(kDexData) - 2);
+  DexFileFromMemory dex_file;
+
+  ASSERT_FALSE(dex_file.Open(0x1000, &memory));
+}
+
+TEST(DexFileTest, from_memory_open) {
+  MemoryFake memory;
+
+  memory.SetMemory(0x1000, kDexData, sizeof(kDexData));
+  DexFileFromMemory dex_file;
+
+  ASSERT_TRUE(dex_file.Open(0x1000, &memory));
+}
+
+TEST(DexFileTest, create_using_file) {
+  TemporaryFile tf;
+  ASSERT_TRUE(tf.fd != -1);
+
+  ASSERT_EQ(0x500, lseek(tf.fd, 0x500, SEEK_SET));
+  ASSERT_EQ(sizeof(kDexData),
+            static_cast<size_t>(TEMP_FAILURE_RETRY(write(tf.fd, kDexData, sizeof(kDexData)))));
+
+  MemoryFake memory;
+  MapInfo info(0, 0x10000, 0, 0x5, tf.path);
+  std::unique_ptr<DexFile> dex_file(DexFile::Create(0x500, &memory, &info));
+  ASSERT_TRUE(dex_file != nullptr);
+}
+
+TEST(DexFileTest, create_using_file_non_zero_start) {
+  TemporaryFile tf;
+  ASSERT_TRUE(tf.fd != -1);
+
+  ASSERT_EQ(0x500, lseek(tf.fd, 0x500, SEEK_SET));
+  ASSERT_EQ(sizeof(kDexData),
+            static_cast<size_t>(TEMP_FAILURE_RETRY(write(tf.fd, kDexData, sizeof(kDexData)))));
+
+  MemoryFake memory;
+  MapInfo info(0x100, 0x10000, 0, 0x5, tf.path);
+  std::unique_ptr<DexFile> dex_file(DexFile::Create(0x600, &memory, &info));
+  ASSERT_TRUE(dex_file != nullptr);
+}
+
+TEST(DexFileTest, create_using_file_non_zero_offset) {
+  TemporaryFile tf;
+  ASSERT_TRUE(tf.fd != -1);
+
+  ASSERT_EQ(0x500, lseek(tf.fd, 0x500, SEEK_SET));
+  ASSERT_EQ(sizeof(kDexData),
+            static_cast<size_t>(TEMP_FAILURE_RETRY(write(tf.fd, kDexData, sizeof(kDexData)))));
+
+  MemoryFake memory;
+  MapInfo info(0x100, 0x10000, 0x200, 0x5, tf.path);
+  std::unique_ptr<DexFile> dex_file(DexFile::Create(0x400, &memory, &info));
+  ASSERT_TRUE(dex_file != nullptr);
+}
+
+TEST(DexFileTest, create_using_memory_empty_file) {
+  MemoryFake memory;
+  memory.SetMemory(0x4000, kDexData, sizeof(kDexData));
+  MapInfo info(0x100, 0x10000, 0x200, 0x5, "");
+  std::unique_ptr<DexFile> dex_file(DexFile::Create(0x4000, &memory, &info));
+  ASSERT_TRUE(dex_file != nullptr);
+}
+
+TEST(DexFileTest, create_using_memory_file_does_not_exist) {
+  MemoryFake memory;
+  memory.SetMemory(0x4000, kDexData, sizeof(kDexData));
+  MapInfo info(0x100, 0x10000, 0x200, 0x5, "/does/not/exist");
+  std::unique_ptr<DexFile> dex_file(DexFile::Create(0x4000, &memory, &info));
+  ASSERT_TRUE(dex_file != nullptr);
+}
+
+TEST(DexFileTest, create_using_memory_file_is_malformed) {
+  TemporaryFile tf;
+  ASSERT_TRUE(tf.fd != -1);
+
+  ASSERT_EQ(sizeof(kDexData) - 10,
+            static_cast<size_t>(TEMP_FAILURE_RETRY(write(tf.fd, kDexData, sizeof(kDexData) - 10))));
+
+  MemoryFake memory;
+  memory.SetMemory(0x4000, kDexData, sizeof(kDexData));
+  MapInfo info(0x4000, 0x10000, 0x200, 0x5, "/does/not/exist");
+  std::unique_ptr<DexFile> dex_file(DexFile::Create(0x4000, &memory, &info));
+  ASSERT_TRUE(dex_file != nullptr);
+
+  // Check it came from memory by clearing memory and verifying it fails.
+  memory.Clear();
+  dex_file.reset(DexFile::Create(0x4000, &memory, &info));
+  ASSERT_TRUE(dex_file == nullptr);
+}
+
+TEST(DexFileTest, get_method_not_opened) {
+  std::string method("something");
+  uint64_t method_offset = 100;
+  DexFile dex_file;
+  dex_file.GetMethodInformation(0x100, &method, &method_offset);
+  EXPECT_EQ("something", method);
+  EXPECT_EQ(100U, method_offset);
+}
+
+TEST(DexFileTest, get_method) {
+  MemoryFake memory;
+  memory.SetMemory(0x4000, kDexData, sizeof(kDexData));
+  MapInfo info(0x100, 0x10000, 0x200, 0x5, "");
+  std::unique_ptr<DexFile> dex_file(DexFile::Create(0x4000, &memory, &info));
+  ASSERT_TRUE(dex_file != nullptr);
+
+  std::string method;
+  uint64_t method_offset;
+  ASSERT_TRUE(dex_file->GetMethodInformation(0x102, &method, &method_offset));
+  EXPECT_EQ("Main.<init>", method);
+  EXPECT_EQ(2U, method_offset);
+
+  ASSERT_TRUE(dex_file->GetMethodInformation(0x118, &method, &method_offset));
+  EXPECT_EQ("Main.main", method);
+  EXPECT_EQ(0U, method_offset);
+
+  // Make sure that any data that is cached is still retrievable.
+  ASSERT_TRUE(dex_file->GetMethodInformation(0x104, &method, &method_offset));
+  EXPECT_EQ("Main.<init>", method);
+  EXPECT_EQ(4U, method_offset);
+
+  ASSERT_TRUE(dex_file->GetMethodInformation(0x119, &method, &method_offset));
+  EXPECT_EQ("Main.main", method);
+  EXPECT_EQ(1U, method_offset);
+}
+
+TEST(DexFileTest, get_method_empty) {
+  MemoryFake memory;
+  memory.SetMemory(0x4000, kDexData, sizeof(kDexData));
+  MapInfo info(0x100, 0x10000, 0x200, 0x5, "");
+  std::unique_ptr<DexFile> dex_file(DexFile::Create(0x4000, &memory, &info));
+  ASSERT_TRUE(dex_file != nullptr);
+
+  std::string method;
+  uint64_t method_offset;
+  EXPECT_FALSE(dex_file->GetMethodInformation(0x100000, &method, &method_offset));
+
+  EXPECT_FALSE(dex_file->GetMethodInformation(0x98, &method, &method_offset));
+
+  // Make sure that once the whole dex file has been cached, no problems occur.
+  EXPECT_FALSE(dex_file->GetMethodInformation(0x98, &method, &method_offset));
+
+  // Choose a value that is in the cached map, but not in a valid method.
+  EXPECT_FALSE(dex_file->GetMethodInformation(0x110, &method, &method_offset));
+}
+
+}  // namespace unwindstack
diff --git a/libunwindstack/tests/DexFilesTest.cpp b/libunwindstack/tests/DexFilesTest.cpp
new file mode 100644
index 0000000..d029bb0
--- /dev/null
+++ b/libunwindstack/tests/DexFilesTest.cpp
@@ -0,0 +1,323 @@
+/*
+ * 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 <elf.h>
+#include <string.h>
+
+#include <memory>
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include <unwindstack/DexFiles.h>
+#include <unwindstack/Elf.h>
+#include <unwindstack/MapInfo.h>
+#include <unwindstack/Maps.h>
+#include <unwindstack/Memory.h>
+
+#include "DexFileData.h"
+#include "ElfFake.h"
+#include "MemoryFake.h"
+
+namespace unwindstack {
+
+class DexFilesTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    memory_ = new MemoryFake;
+    process_memory_.reset(memory_);
+
+    dex_files_.reset(new DexFiles(process_memory_));
+    dex_files_->SetArch(ARCH_ARM);
+
+    maps_.reset(
+        new BufferMaps("1000-4000 ---s 00000000 00:00 0\n"
+                       "4000-6000 r--s 00000000 00:00 0\n"
+                       "6000-8000 -w-s 00000000 00:00 0\n"
+                       "a000-c000 r-xp 00000000 00:00 0\n"
+                       "c000-f000 rwxp 00000000 00:00 0\n"
+                       "f000-11000 r-xp 00000000 00:00 0\n"
+                       "100000-110000 rw-p 0000000 00:00 0\n"
+                       "200000-210000 rw-p 0000000 00:00 0\n"
+                       "300000-400000 rw-p 0000000 00:00 0\n"));
+    ASSERT_TRUE(maps_->Parse());
+
+    // Global variable in a section that is not readable/executable.
+    MapInfo* map_info = maps_->Get(kMapGlobalNonReadableExectable);
+    ASSERT_TRUE(map_info != nullptr);
+    MemoryFake* memory = new MemoryFake;
+    ElfFake* elf = new ElfFake(memory);
+    elf->FakeSetValid(true);
+    ElfInterfaceFake* interface = new ElfInterfaceFake(memory);
+    elf->FakeSetInterface(interface);
+    interface->FakeSetGlobalVariable("__dex_debug_descriptor", 0x800);
+    map_info->elf.reset(elf);
+
+    // Global variable not set by default.
+    map_info = maps_->Get(kMapGlobalSetToZero);
+    ASSERT_TRUE(map_info != nullptr);
+    memory = new MemoryFake;
+    elf = new ElfFake(memory);
+    elf->FakeSetValid(true);
+    interface = new ElfInterfaceFake(memory);
+    elf->FakeSetInterface(interface);
+    interface->FakeSetGlobalVariable("__dex_debug_descriptor", 0x800);
+    map_info->elf.reset(elf);
+
+    // Global variable set in this map.
+    map_info = maps_->Get(kMapGlobal);
+    ASSERT_TRUE(map_info != nullptr);
+    memory = new MemoryFake;
+    elf = new ElfFake(memory);
+    elf->FakeSetValid(true);
+    interface = new ElfInterfaceFake(memory);
+    elf->FakeSetInterface(interface);
+    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);
+
+  static constexpr size_t kMapGlobalNonReadableExectable = 3;
+  static constexpr size_t kMapGlobalSetToZero = 4;
+  static constexpr size_t kMapGlobal = 5;
+  static constexpr size_t kMapDexFileEntries = 7;
+  static constexpr size_t kMapDexFiles = 8;
+
+  std::shared_ptr<Memory> process_memory_;
+  MemoryFake* memory_;
+  std::unique_ptr<DexFiles> dex_files_;
+  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:
+  //   uint32_t next
+  memory_->SetData32(entry_addr, next);
+  //   uint32_t prev
+  memory_->SetData32(entry_addr + 4, prev);
+  //   uint32_t dex_file
+  memory_->SetData32(entry_addr + 8, dex_file);
+}
+
+void DexFilesTest::WriteEntry64(uint64_t entry_addr, uint64_t next, uint64_t prev,
+                                uint64_t dex_file) {
+  // Format of the 64 bit DEXFileEntry structure:
+  //   uint64_t next
+  memory_->SetData64(entry_addr, next);
+  //   uint64_t prev
+  memory_->SetData64(entry_addr + 8, prev);
+  //   uint64_t dex_file
+  memory_->SetData64(entry_addr + 16, dex_file);
+}
+
+void DexFilesTest::WriteDex(uint64_t dex_file) {
+  memory_->SetMemory(dex_file, kDexData, sizeof(kDexData) * sizeof(uint32_t));
+}
+
+TEST_F(DexFilesTest, get_method_information_invalid) {
+  std::string method_name = "nothing";
+  uint64_t method_offset = 0x124;
+  MapInfo* info = maps_->Get(kMapDexFileEntries);
+
+  dex_files_->GetMethodInformation(maps_.get(), info, 0, &method_name, &method_offset);
+  EXPECT_EQ("nothing", method_name);
+  EXPECT_EQ(0x124U, method_offset);
+}
+
+TEST_F(DexFilesTest, get_method_information_32) {
+  std::string method_name = "nothing";
+  uint64_t method_offset = 0x124;
+  MapInfo* info = maps_->Get(kMapDexFiles);
+
+  WriteDescriptor32(0xf800, 0x200000);
+  WriteEntry32(0x200000, 0, 0, 0x300000);
+  WriteDex(0x300000);
+
+  dex_files_->GetMethodInformation(maps_.get(), info, 0x300100, &method_name, &method_offset);
+  EXPECT_EQ("Main.<init>", method_name);
+  EXPECT_EQ(0U, method_offset);
+}
+
+TEST_F(DexFilesTest, get_method_information_64) {
+  std::string method_name = "nothing";
+  uint64_t method_offset = 0x124;
+  MapInfo* info = maps_->Get(kMapDexFiles);
+
+  dex_files_->SetArch(ARCH_ARM64);
+  WriteDescriptor64(0xf800, 0x200000);
+  WriteEntry64(0x200000, 0, 0, 0x301000);
+  WriteDex(0x301000);
+
+  dex_files_->GetMethodInformation(maps_.get(), info, 0x301102, &method_name, &method_offset);
+  EXPECT_EQ("Main.<init>", method_name);
+  EXPECT_EQ(2U, method_offset);
+}
+
+TEST_F(DexFilesTest, get_method_information_not_first_entry_32) {
+  std::string method_name = "nothing";
+  uint64_t method_offset = 0x124;
+  MapInfo* info = maps_->Get(kMapDexFiles);
+
+  WriteDescriptor32(0xf800, 0x200000);
+  WriteEntry32(0x200000, 0x200100, 0, 0x100000);
+  WriteEntry32(0x200100, 0, 0x200000, 0x300000);
+  WriteDex(0x300000);
+
+  dex_files_->GetMethodInformation(maps_.get(), info, 0x300104, &method_name, &method_offset);
+  EXPECT_EQ("Main.<init>", method_name);
+  EXPECT_EQ(4U, method_offset);
+}
+
+TEST_F(DexFilesTest, get_method_information_not_first_entry_64) {
+  std::string method_name = "nothing";
+  uint64_t method_offset = 0x124;
+  MapInfo* info = maps_->Get(kMapDexFiles);
+
+  dex_files_->SetArch(ARCH_ARM64);
+  WriteDescriptor64(0xf800, 0x200000);
+  WriteEntry64(0x200000, 0x200100, 0, 0x100000);
+  WriteEntry64(0x200100, 0, 0x200000, 0x300000);
+  WriteDex(0x300000);
+
+  dex_files_->GetMethodInformation(maps_.get(), info, 0x300106, &method_name, &method_offset);
+  EXPECT_EQ("Main.<init>", method_name);
+  EXPECT_EQ(6U, method_offset);
+}
+
+TEST_F(DexFilesTest, get_method_information_cached) {
+  std::string method_name = "nothing";
+  uint64_t method_offset = 0x124;
+  MapInfo* info = maps_->Get(kMapDexFiles);
+
+  WriteDescriptor32(0xf800, 0x200000);
+  WriteEntry32(0x200000, 0, 0, 0x300000);
+  WriteDex(0x300000);
+
+  dex_files_->GetMethodInformation(maps_.get(), info, 0x300100, &method_name, &method_offset);
+  EXPECT_EQ("Main.<init>", method_name);
+  EXPECT_EQ(0U, method_offset);
+
+  // Clear all memory and make sure that data is acquired from the cache.
+  memory_->Clear();
+  dex_files_->GetMethodInformation(maps_.get(), info, 0x300100, &method_name, &method_offset);
+  EXPECT_EQ("Main.<init>", method_name);
+  EXPECT_EQ(0U, method_offset);
+}
+
+TEST_F(DexFilesTest, get_method_information_search_libs) {
+  std::string method_name = "nothing";
+  uint64_t method_offset = 0x124;
+  MapInfo* info = maps_->Get(kMapDexFiles);
+
+  WriteDescriptor32(0xf800, 0x200000);
+  WriteEntry32(0x200000, 0x200100, 0, 0x100000);
+  WriteEntry32(0x200100, 0, 0x200000, 0x300000);
+  WriteDex(0x300000);
+
+  // Only search a given named list of libs.
+  std::vector<std::string> libs{"libart.so"};
+  dex_files_.reset(new DexFiles(process_memory_, libs));
+  dex_files_->SetArch(ARCH_ARM);
+
+  dex_files_->GetMethodInformation(maps_.get(), info, 0x300104, &method_name, &method_offset);
+  EXPECT_EQ("nothing", method_name);
+  EXPECT_EQ(0x124U, method_offset);
+
+  MapInfo* map_info = maps_->Get(kMapGlobal);
+  map_info->name = "/system/lib/libart.so";
+  dex_files_.reset(new DexFiles(process_memory_, libs));
+  dex_files_->SetArch(ARCH_ARM);
+  // Make sure that clearing out copy of the libs doesn't affect the
+  // DexFiles object.
+  libs.clear();
+
+  dex_files_->GetMethodInformation(maps_.get(), info, 0x300104, &method_name, &method_offset);
+  EXPECT_EQ("Main.<init>", method_name);
+  EXPECT_EQ(4U, method_offset);
+}
+
+TEST_F(DexFilesTest, get_method_information_global_skip_zero_32) {
+  std::string method_name = "nothing";
+  uint64_t method_offset = 0x124;
+  MapInfo* info = maps_->Get(kMapDexFiles);
+
+  // First global variable found, but value is zero.
+  WriteDescriptor32(0xc800, 0);
+
+  WriteDescriptor32(0xf800, 0x200000);
+  WriteEntry32(0x200000, 0, 0, 0x300000);
+  WriteDex(0x300000);
+
+  dex_files_->GetMethodInformation(maps_.get(), info, 0x300100, &method_name, &method_offset);
+  EXPECT_EQ("Main.<init>", method_name);
+  EXPECT_EQ(0U, method_offset);
+
+  // Verify that second is ignored when first is set to non-zero
+  dex_files_.reset(new DexFiles(process_memory_));
+  dex_files_->SetArch(ARCH_ARM);
+  method_name = "fail";
+  method_offset = 0x123;
+  WriteDescriptor32(0xc800, 0x100000);
+  dex_files_->GetMethodInformation(maps_.get(), info, 0x300100, &method_name, &method_offset);
+  EXPECT_EQ("fail", method_name);
+  EXPECT_EQ(0x123U, method_offset);
+}
+
+TEST_F(DexFilesTest, get_method_information_global_skip_zero_64) {
+  std::string method_name = "nothing";
+  uint64_t method_offset = 0x124;
+  MapInfo* info = maps_->Get(kMapDexFiles);
+
+  // First global variable found, but value is zero.
+  WriteDescriptor64(0xc800, 0);
+
+  WriteDescriptor64(0xf800, 0x200000);
+  WriteEntry64(0x200000, 0, 0, 0x300000);
+  WriteDex(0x300000);
+
+  dex_files_->SetArch(ARCH_ARM64);
+  dex_files_->GetMethodInformation(maps_.get(), info, 0x300100, &method_name, &method_offset);
+  EXPECT_EQ("Main.<init>", method_name);
+  EXPECT_EQ(0U, method_offset);
+
+  // Verify that second is ignored when first is set to non-zero
+  dex_files_.reset(new DexFiles(process_memory_));
+  dex_files_->SetArch(ARCH_ARM64);
+  method_name = "fail";
+  method_offset = 0x123;
+  WriteDescriptor64(0xc800, 0x100000);
+  dex_files_->GetMethodInformation(maps_.get(), info, 0x300100, &method_name, &method_offset);
+  EXPECT_EQ("fail", method_name);
+  EXPECT_EQ(0x123U, method_offset);
+}
+
+}  // namespace unwindstack
diff --git a/libunwindstack/tests/DwarfCfaLogTest.cpp b/libunwindstack/tests/DwarfCfaLogTest.cpp
index 967c2c2..bb2e8f0 100644
--- a/libunwindstack/tests/DwarfCfaLogTest.cpp
+++ b/libunwindstack/tests/DwarfCfaLogTest.cpp
@@ -23,15 +23,18 @@
 #include <android-base/stringprintf.h>
 #include <gtest/gtest.h>
 
+#include <unwindstack/DwarfLocation.h>
+#include <unwindstack/DwarfMemory.h>
+#include <unwindstack/DwarfStructs.h>
+#include <unwindstack/Log.h>
+
 #include "DwarfCfa.h"
-#include "DwarfLocation.h"
-#include "DwarfMemory.h"
-#include "DwarfStructs.h"
-#include "Log.h"
 
 #include "LogFake.h"
 #include "MemoryFake.h"
 
+namespace unwindstack {
+
 template <typename TypeParam>
 class DwarfCfaLogTest : public ::testing::Test {
  protected:
@@ -76,7 +79,7 @@
     this->memory_.SetMemory(0x2000, std::vector<uint8_t>{i});
 
     ResetLogs();
-    ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x2000, 0x2001));
+    ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x2000, 0x2001));
     std::string expected = "4 unwind Illegal\n";
     expected += android::base::StringPrintf("4 unwind Raw Data: 0x%02x\n", i);
     ASSERT_EQ(expected, GetFakeLogPrint());
@@ -87,7 +90,7 @@
 TYPED_TEST_P(DwarfCfaLogTest, cfa_nop) {
   this->memory_.SetMemory(0x2000, std::vector<uint8_t>{0x00});
 
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x2000, 0x2001));
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x2000, 0x2001));
   std::string expected =
       "4 unwind DW_CFA_nop\n"
       "4 unwind Raw Data: 0x00\n";
@@ -98,7 +101,7 @@
 TYPED_TEST_P(DwarfCfaLogTest, cfa_offset) {
   this->memory_.SetMemory(0x2000, std::vector<uint8_t>{0x83, 0x04});
 
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x2000, 0x2002));
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x2000, 0x2002));
   std::string expected =
       "4 unwind DW_CFA_offset register(3) 4\n"
       "4 unwind Raw Data: 0x83 0x04\n";
@@ -108,7 +111,7 @@
   ResetLogs();
   this->memory_.SetMemory(0x2100, std::vector<uint8_t>{0x83, 0x84, 0x01});
 
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x2100, 0x2103));
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x2100, 0x2103));
   expected =
       "4 unwind DW_CFA_offset register(3) 132\n"
       "4 unwind Raw Data: 0x83 0x84 0x01\n";
@@ -119,7 +122,7 @@
 TYPED_TEST_P(DwarfCfaLogTest, cfa_offset_extended) {
   this->memory_.SetMemory(0x500, std::vector<uint8_t>{0x05, 0x03, 0x02});
 
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x500, 0x503));
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x500, 0x503));
   std::string expected =
       "4 unwind DW_CFA_offset_extended register(3) 2\n"
       "4 unwind Raw Data: 0x05 0x03 0x02\n";
@@ -129,7 +132,7 @@
   ResetLogs();
   this->memory_.SetMemory(0x1500, std::vector<uint8_t>{0x05, 0x81, 0x01, 0x82, 0x12});
 
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x1500, 0x1505));
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x1500, 0x1505));
   expected =
       "4 unwind DW_CFA_offset_extended register(129) 2306\n"
       "4 unwind Raw Data: 0x05 0x81 0x01 0x82 0x12\n";
@@ -140,7 +143,7 @@
 TYPED_TEST_P(DwarfCfaLogTest, cfa_offset_extended_sf) {
   this->memory_.SetMemory(0x500, std::vector<uint8_t>{0x11, 0x05, 0x10});
 
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x500, 0x503));
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x500, 0x503));
   std::string expected =
       "4 unwind DW_CFA_offset_extended_sf register(5) 16\n"
       "4 unwind Raw Data: 0x11 0x05 0x10\n";
@@ -151,7 +154,7 @@
   ResetLogs();
   this->memory_.SetMemory(0x1500, std::vector<uint8_t>{0x11, 0x86, 0x01, 0xff, 0x7f});
 
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x1500, 0x1505));
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x1500, 0x1505));
   expected =
       "4 unwind DW_CFA_offset_extended_sf register(134) -1\n"
       "4 unwind Raw Data: 0x11 0x86 0x01 0xff 0x7f\n";
@@ -162,7 +165,7 @@
 TYPED_TEST_P(DwarfCfaLogTest, cfa_restore) {
   this->memory_.SetMemory(0x2000, std::vector<uint8_t>{0xc2});
 
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x2000, 0x2001));
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x2000, 0x2001));
   std::string expected =
       "4 unwind DW_CFA_restore register(2)\n"
       "4 unwind Raw Data: 0xc2\n";
@@ -172,7 +175,7 @@
   ResetLogs();
   this->memory_.SetMemory(0x3000, std::vector<uint8_t>{0x82, 0x04, 0xc2});
 
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x3000, 0x3003));
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x3000, 0x3003));
   expected =
       "4 unwind DW_CFA_offset register(2) 4\n"
       "4 unwind Raw Data: 0x82 0x04\n"
@@ -185,7 +188,7 @@
 TYPED_TEST_P(DwarfCfaLogTest, cfa_restore_extended) {
   this->memory_.SetMemory(0x4000, std::vector<uint8_t>{0x06, 0x08});
 
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x4000, 0x4002));
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x4000, 0x4002));
   std::string expected =
       "4 unwind DW_CFA_restore_extended register(8)\n"
       "4 unwind Raw Data: 0x06 0x08\n";
@@ -195,7 +198,7 @@
   ResetLogs();
   this->memory_.SetMemory(0x5000, std::vector<uint8_t>{0x05, 0x82, 0x02, 0x04, 0x06, 0x82, 0x02});
 
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x5000, 0x5007));
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x5000, 0x5007));
   expected =
       "4 unwind DW_CFA_offset_extended register(258) 4\n"
       "4 unwind Raw Data: 0x05 0x82 0x02 0x04\n"
@@ -225,7 +228,7 @@
   this->memory_.SetMemory(0x50, buffer, sizeof(buffer));
   ResetLogs();
 
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x50, 0x51 + sizeof(TypeParam)));
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x50, 0x51 + sizeof(TypeParam)));
   std::string expected = "4 unwind DW_CFA_set_loc " + address_str + "\n";
   expected += "4 unwind " + raw_data + "\n";
   expected += "4 unwind \n";
@@ -237,7 +240,7 @@
   ResetLogs();
   this->fde_.pc_start = address + 0x10;
 
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x50, 0x51 + sizeof(TypeParam)));
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x50, 0x51 + sizeof(TypeParam)));
   expected = "4 unwind DW_CFA_set_loc " + address_str + "\n";
   expected += "4 unwind " + raw_data + "\n";
   expected += "4 unwind \n";
@@ -249,7 +252,7 @@
 TYPED_TEST_P(DwarfCfaLogTest, cfa_advance_loc) {
   this->memory_.SetMemory(0x200, std::vector<uint8_t>{0x44});
 
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x200, 0x201));
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x200, 0x201));
   std::string expected =
       "4 unwind DW_CFA_advance_loc 4\n"
       "4 unwind Raw Data: 0x44\n"
@@ -257,22 +260,12 @@
       "4 unwind PC 0x2010\n";
   ASSERT_EQ(expected, GetFakeLogPrint());
   ASSERT_EQ("", GetFakeLogBuf());
-
-  ResetLogs();
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x100, 0x200, 0x201));
-  expected =
-      "4 unwind DW_CFA_advance_loc 4\n"
-      "4 unwind Raw Data: 0x44\n"
-      "4 unwind \n"
-      "4 unwind PC 0x2110\n";
-  ASSERT_EQ(expected, GetFakeLogPrint());
-  ASSERT_EQ("", GetFakeLogBuf());
 }
 
 TYPED_TEST_P(DwarfCfaLogTest, cfa_advance_loc1) {
   this->memory_.SetMemory(0x200, std::vector<uint8_t>{0x02, 0x04});
 
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x200, 0x202));
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x200, 0x202));
   std::string expected =
       "4 unwind DW_CFA_advance_loc1 4\n"
       "4 unwind Raw Data: 0x02 0x04\n"
@@ -280,22 +273,12 @@
       "4 unwind PC 0x2004\n";
   ASSERT_EQ(expected, GetFakeLogPrint());
   ASSERT_EQ("", GetFakeLogBuf());
-
-  ResetLogs();
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x10, 0x200, 0x202));
-  expected =
-      "4 unwind DW_CFA_advance_loc1 4\n"
-      "4 unwind Raw Data: 0x02 0x04\n"
-      "4 unwind \n"
-      "4 unwind PC 0x2014\n";
-  ASSERT_EQ(expected, GetFakeLogPrint());
-  ASSERT_EQ("", GetFakeLogBuf());
 }
 
 TYPED_TEST_P(DwarfCfaLogTest, cfa_advance_loc2) {
   this->memory_.SetMemory(0x600, std::vector<uint8_t>{0x03, 0x04, 0x03});
 
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x600, 0x603));
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x600, 0x603));
   std::string expected =
       "4 unwind DW_CFA_advance_loc2 772\n"
       "4 unwind Raw Data: 0x03 0x04 0x03\n"
@@ -303,22 +286,12 @@
       "4 unwind PC 0x2304\n";
   ASSERT_EQ(expected, GetFakeLogPrint());
   ASSERT_EQ("", GetFakeLogBuf());
-
-  ResetLogs();
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x1000, 0x600, 0x603));
-  expected =
-      "4 unwind DW_CFA_advance_loc2 772\n"
-      "4 unwind Raw Data: 0x03 0x04 0x03\n"
-      "4 unwind \n"
-      "4 unwind PC 0x3304\n";
-  ASSERT_EQ(expected, GetFakeLogPrint());
-  ASSERT_EQ("", GetFakeLogBuf());
 }
 
 TYPED_TEST_P(DwarfCfaLogTest, cfa_advance_loc4) {
   this->memory_.SetMemory(0x500, std::vector<uint8_t>{0x04, 0x04, 0x03, 0x02, 0x01});
 
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x500, 0x505));
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x500, 0x505));
   std::string expected =
       "4 unwind DW_CFA_advance_loc4 16909060\n"
       "4 unwind Raw Data: 0x04 0x04 0x03 0x02 0x01\n"
@@ -326,22 +299,12 @@
       "4 unwind PC 0x1022304\n";
   ASSERT_EQ(expected, GetFakeLogPrint());
   ASSERT_EQ("", GetFakeLogBuf());
-
-  ResetLogs();
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x2000, 0x500, 0x505));
-  expected =
-      "4 unwind DW_CFA_advance_loc4 16909060\n"
-      "4 unwind Raw Data: 0x04 0x04 0x03 0x02 0x01\n"
-      "4 unwind \n"
-      "4 unwind PC 0x1024304\n";
-  ASSERT_EQ(expected, GetFakeLogPrint());
-  ASSERT_EQ("", GetFakeLogBuf());
 }
 
 TYPED_TEST_P(DwarfCfaLogTest, cfa_undefined) {
   this->memory_.SetMemory(0xa00, std::vector<uint8_t>{0x07, 0x09});
 
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0xa00, 0xa02));
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0xa00, 0xa02));
   std::string expected =
       "4 unwind DW_CFA_undefined register(9)\n"
       "4 unwind Raw Data: 0x07 0x09\n";
@@ -352,7 +315,7 @@
   dwarf_loc_regs_t cie_loc_regs;
   this->memory_.SetMemory(0x1a00, std::vector<uint8_t>{0x07, 0x81, 0x01});
 
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x1a00, 0x1a03));
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x1a00, 0x1a03));
   expected =
       "4 unwind DW_CFA_undefined register(129)\n"
       "4 unwind Raw Data: 0x07 0x81 0x01\n";
@@ -363,7 +326,7 @@
 TYPED_TEST_P(DwarfCfaLogTest, cfa_same) {
   this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x08, 0x7f});
 
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x100, 0x102));
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x100, 0x102));
   std::string expected =
       "4 unwind DW_CFA_same_value register(127)\n"
       "4 unwind Raw Data: 0x08 0x7f\n";
@@ -373,7 +336,7 @@
   ResetLogs();
   this->memory_.SetMemory(0x2100, std::vector<uint8_t>{0x08, 0xff, 0x01});
 
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x2100, 0x2103));
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x2100, 0x2103));
   expected =
       "4 unwind DW_CFA_same_value register(255)\n"
       "4 unwind Raw Data: 0x08 0xff 0x01\n";
@@ -384,7 +347,7 @@
 TYPED_TEST_P(DwarfCfaLogTest, cfa_register) {
   this->memory_.SetMemory(0x300, std::vector<uint8_t>{0x09, 0x02, 0x01});
 
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x300, 0x303));
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x300, 0x303));
   std::string expected =
       "4 unwind DW_CFA_register register(2) register(1)\n"
       "4 unwind Raw Data: 0x09 0x02 0x01\n";
@@ -394,7 +357,7 @@
   ResetLogs();
   this->memory_.SetMemory(0x4300, std::vector<uint8_t>{0x09, 0xff, 0x01, 0xff, 0x03});
 
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x4300, 0x4305));
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x4300, 0x4305));
   expected =
       "4 unwind DW_CFA_register register(255) register(511)\n"
       "4 unwind Raw Data: 0x09 0xff 0x01 0xff 0x03\n";
@@ -405,7 +368,7 @@
 TYPED_TEST_P(DwarfCfaLogTest, cfa_state) {
   this->memory_.SetMemory(0x300, std::vector<uint8_t>{0x0a});
 
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x300, 0x301));
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x300, 0x301));
 
   std::string expected =
       "4 unwind DW_CFA_remember_state\n"
@@ -416,7 +379,7 @@
   ResetLogs();
   this->memory_.SetMemory(0x4300, std::vector<uint8_t>{0x0b});
 
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x4300, 0x4301));
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x4300, 0x4301));
 
   expected =
       "4 unwind DW_CFA_restore_state\n"
@@ -428,7 +391,7 @@
 TYPED_TEST_P(DwarfCfaLogTest, cfa_state_cfa_offset_restore) {
   this->memory_.SetMemory(0x3000, std::vector<uint8_t>{0x0a, 0x0e, 0x40, 0x0b});
 
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x3000, 0x3004));
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x3000, 0x3004));
 
   std::string expected =
       "4 unwind DW_CFA_remember_state\n"
@@ -444,7 +407,7 @@
 TYPED_TEST_P(DwarfCfaLogTest, cfa_def_cfa) {
   this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x0c, 0x7f, 0x74});
 
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x100, 0x103));
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x100, 0x103));
 
   std::string expected =
       "4 unwind DW_CFA_def_cfa register(127) 116\n"
@@ -455,7 +418,7 @@
   ResetLogs();
   this->memory_.SetMemory(0x200, std::vector<uint8_t>{0x0c, 0xff, 0x02, 0xf4, 0x04});
 
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x200, 0x205));
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x200, 0x205));
 
   expected =
       "4 unwind DW_CFA_def_cfa register(383) 628\n"
@@ -467,7 +430,7 @@
 TYPED_TEST_P(DwarfCfaLogTest, cfa_def_cfa_sf) {
   this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x12, 0x30, 0x25});
 
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x100, 0x103));
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x100, 0x103));
 
   std::string expected =
       "4 unwind DW_CFA_def_cfa_sf register(48) 37\n"
@@ -479,7 +442,7 @@
   ResetLogs();
   this->memory_.SetMemory(0x200, std::vector<uint8_t>{0x12, 0xa3, 0x01, 0xfa, 0x7f});
 
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x200, 0x205));
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x200, 0x205));
 
   expected =
       "4 unwind DW_CFA_def_cfa_sf register(163) -6\n"
@@ -491,7 +454,7 @@
 TYPED_TEST_P(DwarfCfaLogTest, cfa_def_cfa_register) {
   this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x0d, 0x72});
 
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x100, 0x102));
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x100, 0x102));
 
   std::string expected =
       "4 unwind DW_CFA_def_cfa_register register(114)\n"
@@ -502,7 +465,7 @@
   ResetLogs();
   this->memory_.SetMemory(0x200, std::vector<uint8_t>{0x0d, 0xf9, 0x20});
 
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x200, 0x203));
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x200, 0x203));
 
   expected =
       "4 unwind DW_CFA_def_cfa_register register(4217)\n"
@@ -514,7 +477,7 @@
 TYPED_TEST_P(DwarfCfaLogTest, cfa_def_cfa_offset) {
   this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x0e, 0x59});
 
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x100, 0x102));
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x100, 0x102));
 
   std::string expected =
       "4 unwind DW_CFA_def_cfa_offset 89\n"
@@ -523,7 +486,7 @@
   ASSERT_EQ("", GetFakeLogBuf());
 
   ResetLogs();
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x100, 0x102));
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x100, 0x102));
 
   expected =
       "4 unwind DW_CFA_def_cfa_offset 89\n"
@@ -534,7 +497,7 @@
   ResetLogs();
   this->memory_.SetMemory(0x200, std::vector<uint8_t>{0x0e, 0xd4, 0x0a});
 
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x200, 0x203));
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x200, 0x203));
 
   expected =
       "4 unwind DW_CFA_def_cfa_offset 1364\n"
@@ -546,7 +509,7 @@
 TYPED_TEST_P(DwarfCfaLogTest, cfa_def_cfa_offset_sf) {
   this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x13, 0x23});
 
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x100, 0x102));
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x100, 0x102));
 
   std::string expected =
       "4 unwind DW_CFA_def_cfa_offset_sf 35\n"
@@ -555,7 +518,7 @@
   ASSERT_EQ("", GetFakeLogBuf());
 
   ResetLogs();
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x100, 0x102));
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x100, 0x102));
 
   expected =
       "4 unwind DW_CFA_def_cfa_offset_sf 35\n"
@@ -567,7 +530,7 @@
   ResetLogs();
   this->memory_.SetMemory(0x200, std::vector<uint8_t>{0x13, 0xf6, 0x7f});
 
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x200, 0x203));
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x200, 0x203));
 
   expected =
       "4 unwind DW_CFA_def_cfa_offset_sf -10\n"
@@ -579,7 +542,7 @@
 TYPED_TEST_P(DwarfCfaLogTest, cfa_def_cfa_expression) {
   this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x0f, 0x04, 0x01, 0x02, 0x04, 0x05});
 
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x100, 0x106));
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x100, 0x106));
 
   std::string expected =
       "4 unwind DW_CFA_def_cfa_expression 4\n"
@@ -611,7 +574,7 @@
   }
   expected += '\n';
   this->memory_.SetMemory(0x200, ops);
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x200, 0x284));
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x200, 0x284));
 
   expected = "4 unwind DW_CFA_def_cfa_expression 129\n" + expected;
   ASSERT_EQ(expected + op_string, GetFakeLogPrint());
@@ -621,7 +584,7 @@
 TYPED_TEST_P(DwarfCfaLogTest, cfa_expression) {
   this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x10, 0x04, 0x02, 0xc0, 0xc1});
 
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x100, 0x105));
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x100, 0x105));
 
   std::string expected =
       "4 unwind DW_CFA_expression register(4) 2\n"
@@ -649,7 +612,7 @@
   expected = "4 unwind DW_CFA_expression register(255) 130\n" + expected + "\n";
 
   this->memory_.SetMemory(0x200, ops);
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x200, 0x287));
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x200, 0x287));
 
   ASSERT_EQ(expected + op_string, GetFakeLogPrint());
   ASSERT_EQ("", GetFakeLogBuf());
@@ -658,7 +621,7 @@
 TYPED_TEST_P(DwarfCfaLogTest, cfa_val_offset) {
   this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x14, 0x45, 0x54});
 
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x100, 0x103));
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x100, 0x103));
 
   std::string expected =
       "4 unwind DW_CFA_val_offset register(69) 84\n"
@@ -669,7 +632,7 @@
   ResetLogs();
   this->memory_.SetMemory(0x400, std::vector<uint8_t>{0x14, 0xa2, 0x02, 0xb4, 0x05});
 
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x400, 0x405));
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x400, 0x405));
 
   expected =
       "4 unwind DW_CFA_val_offset register(290) 692\n"
@@ -681,7 +644,7 @@
 TYPED_TEST_P(DwarfCfaLogTest, cfa_val_offset_sf) {
   this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x15, 0x56, 0x12});
 
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x100, 0x103));
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x100, 0x103));
 
   std::string expected =
       "4 unwind DW_CFA_val_offset_sf register(86) 18\n"
@@ -693,7 +656,7 @@
   ResetLogs();
   this->memory_.SetMemory(0xa00, std::vector<uint8_t>{0x15, 0xff, 0x01, 0xc0, 0x7f});
 
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0xa00, 0xa05));
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0xa00, 0xa05));
 
   expected =
       "4 unwind DW_CFA_val_offset_sf register(255) -64\n"
@@ -705,7 +668,7 @@
 TYPED_TEST_P(DwarfCfaLogTest, cfa_val_expression) {
   this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x16, 0x05, 0x02, 0xb0, 0xb1});
 
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x100, 0x105));
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x100, 0x105));
 
   std::string expected =
       "4 unwind DW_CFA_val_expression register(5) 2\n"
@@ -734,7 +697,7 @@
 
   this->memory_.SetMemory(0xa00, ops);
 
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0xa00, 0xaad));
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0xa00, 0xaad));
 
   ASSERT_EQ(expected + op_string, GetFakeLogPrint());
   ASSERT_EQ("", GetFakeLogBuf());
@@ -743,7 +706,7 @@
 TYPED_TEST_P(DwarfCfaLogTest, cfa_gnu_args_size) {
   this->memory_.SetMemory(0x2000, std::vector<uint8_t>{0x2e, 0x04});
 
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x2000, 0x2002));
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x2000, 0x2002));
 
   std::string expected =
       "4 unwind DW_CFA_GNU_args_size 4\n"
@@ -754,7 +717,7 @@
   ResetLogs();
   this->memory_.SetMemory(0x5000, std::vector<uint8_t>{0x2e, 0xa4, 0x80, 0x04});
 
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x5000, 0x5004));
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x5000, 0x5004));
 
   expected =
       "4 unwind DW_CFA_GNU_args_size 65572\n"
@@ -766,7 +729,7 @@
 TYPED_TEST_P(DwarfCfaLogTest, cfa_gnu_negative_offset_extended) {
   this->memory_.SetMemory(0x500, std::vector<uint8_t>{0x2f, 0x08, 0x10});
 
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x500, 0x503));
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x500, 0x503));
 
   std::string expected =
       "4 unwind DW_CFA_GNU_negative_offset_extended register(8) 16\n"
@@ -777,7 +740,7 @@
   ResetLogs();
   this->memory_.SetMemory(0x1500, std::vector<uint8_t>{0x2f, 0x81, 0x02, 0xff, 0x01});
 
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x1500, 0x1505));
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x1500, 0x1505));
 
   expected =
       "4 unwind DW_CFA_GNU_negative_offset_extended register(257) 255\n"
@@ -789,7 +752,7 @@
 TYPED_TEST_P(DwarfCfaLogTest, cfa_register_override) {
   this->memory_.SetMemory(0x300, std::vector<uint8_t>{0x09, 0x02, 0x01, 0x09, 0x02, 0x04});
 
-  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x300, 0x306));
+  ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x300, 0x306));
 
   std::string expected =
       "4 unwind DW_CFA_register register(2) register(1)\n"
@@ -812,3 +775,5 @@
 
 typedef ::testing::Types<uint32_t, uint64_t> DwarfCfaLogTestTypes;
 INSTANTIATE_TYPED_TEST_CASE_P(, DwarfCfaLogTest, DwarfCfaLogTestTypes);
+
+}  // namespace unwindstack
diff --git a/libunwindstack/tests/DwarfCfaTest.cpp b/libunwindstack/tests/DwarfCfaTest.cpp
index 687af7d..7395b04 100644
--- a/libunwindstack/tests/DwarfCfaTest.cpp
+++ b/libunwindstack/tests/DwarfCfaTest.cpp
@@ -21,15 +21,19 @@
 
 #include <gtest/gtest.h>
 
+#include <unwindstack/DwarfError.h>
+#include <unwindstack/DwarfLocation.h>
+#include <unwindstack/DwarfMemory.h>
+#include <unwindstack/DwarfStructs.h>
+#include <unwindstack/Log.h>
+
 #include "DwarfCfa.h"
-#include "DwarfLocation.h"
-#include "DwarfMemory.h"
-#include "DwarfStructs.h"
-#include "Log.h"
 
 #include "LogFake.h"
 #include "MemoryFake.h"
 
+namespace unwindstack {
+
 template <typename TypeParam>
 class DwarfCfaTest : public ::testing::Test {
  protected:
@@ -74,7 +78,7 @@
     dwarf_loc_regs_t loc_regs;
 
     ASSERT_FALSE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x2000, 0x2001, &loc_regs));
-    ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->cfa_->last_error());
+    ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->cfa_->LastErrorCode());
     ASSERT_EQ(0x2001U, this->dmem_->cur_offset());
 
     ASSERT_EQ("", GetFakeLogPrint());
@@ -194,7 +198,7 @@
   dwarf_loc_regs_t loc_regs;
 
   ASSERT_FALSE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x2000, 0x2001, &loc_regs));
-  ASSERT_EQ(DWARF_ERROR_ILLEGAL_STATE, this->cfa_->last_error());
+  ASSERT_EQ(DWARF_ERROR_ILLEGAL_STATE, this->cfa_->LastErrorCode());
   ASSERT_EQ(0x2001U, this->dmem_->cur_offset());
   ASSERT_EQ(0U, loc_regs.size());
 
@@ -223,7 +227,7 @@
   dwarf_loc_regs_t loc_regs;
 
   ASSERT_FALSE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x4000, 0x4002, &loc_regs));
-  ASSERT_EQ(DWARF_ERROR_ILLEGAL_STATE, this->cfa_->last_error());
+  ASSERT_EQ(DWARF_ERROR_ILLEGAL_STATE, this->cfa_->LastErrorCode());
   ASSERT_EQ(0x4002U, this->dmem_->cur_offset());
   ASSERT_EQ(0U, loc_regs.size());
 
@@ -590,7 +594,7 @@
   // This fails because the cfa is not defined as a register.
   ASSERT_FALSE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x100, 0x102, &loc_regs));
   ASSERT_EQ(0U, loc_regs.size());
-  ASSERT_EQ(DWARF_ERROR_ILLEGAL_STATE, this->cfa_->last_error());
+  ASSERT_EQ(DWARF_ERROR_ILLEGAL_STATE, this->cfa_->LastErrorCode());
 
   ASSERT_EQ("4 unwind Attempt to set new register, but cfa is not already set to a register.\n",
             GetFakeLogPrint());
@@ -633,7 +637,7 @@
   // This fails because the cfa is not defined as a register.
   ASSERT_FALSE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x100, 0x102, &loc_regs));
   ASSERT_EQ(0U, loc_regs.size());
-  ASSERT_EQ(DWARF_ERROR_ILLEGAL_STATE, this->cfa_->last_error());
+  ASSERT_EQ(DWARF_ERROR_ILLEGAL_STATE, this->cfa_->LastErrorCode());
 
   ASSERT_EQ("4 unwind Attempt to set offset, but cfa is not set to a register.\n",
             GetFakeLogPrint());
@@ -675,7 +679,7 @@
 
   // This fails because the cfa is not defined as a register.
   ASSERT_FALSE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x100, 0x102, &loc_regs));
-  ASSERT_EQ(DWARF_ERROR_ILLEGAL_STATE, this->cfa_->last_error());
+  ASSERT_EQ(DWARF_ERROR_ILLEGAL_STATE, this->cfa_->LastErrorCode());
 
   ASSERT_EQ("4 unwind Attempt to set offset, but cfa is not set to a register.\n",
             GetFakeLogPrint());
@@ -733,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());
@@ -957,3 +963,5 @@
 
 typedef ::testing::Types<uint32_t, uint64_t> DwarfCfaTestTypes;
 INSTANTIATE_TYPED_TEST_CASE_P(, DwarfCfaTest, DwarfCfaTestTypes);
+
+}  // namespace unwindstack
diff --git a/libunwindstack/tests/DwarfDebugFrameTest.cpp b/libunwindstack/tests/DwarfDebugFrameTest.cpp
index 94a9974..d620934 100644
--- a/libunwindstack/tests/DwarfDebugFrameTest.cpp
+++ b/libunwindstack/tests/DwarfDebugFrameTest.cpp
@@ -16,369 +16,392 @@
 
 #include <stdint.h>
 
-#include <gmock/gmock.h>
+#include <vector>
+
 #include <gtest/gtest.h>
 
+#include <unwindstack/DwarfError.h>
+
 #include "DwarfDebugFrame.h"
 #include "DwarfEncoding.h"
 
 #include "LogFake.h"
 #include "MemoryFake.h"
-#include "RegsFake.h"
 
-template <typename TypeParam>
-class MockDwarfDebugFrame : public DwarfDebugFrame<TypeParam> {
- public:
-  MockDwarfDebugFrame(Memory* memory) : DwarfDebugFrame<TypeParam>(memory) {}
-  ~MockDwarfDebugFrame() = default;
-
-  void TestSetFdeCount(uint64_t count) { this->fde_count_ = count; }
-  void TestSetOffset(uint64_t offset) { this->offset_ = offset; }
-  void TestSetEndOffset(uint64_t offset) { this->end_offset_ = offset; }
-  void TestPushFdeInfo(const typename DwarfDebugFrame<TypeParam>::FdeInfo& info) {
-    this->fdes_.push_back(info);
-  }
-
-  uint64_t TestGetFdeCount() { return this->fde_count_; }
-  uint8_t TestGetOffset() { return this->offset_; }
-  uint8_t TestGetEndOffset() { return this->end_offset_; }
-  void TestGetFdeInfo(size_t index, typename DwarfDebugFrame<TypeParam>::FdeInfo* info) {
-    *info = this->fdes_[index];
-  }
-};
+namespace unwindstack {
 
 template <typename TypeParam>
 class DwarfDebugFrameTest : public ::testing::Test {
  protected:
   void SetUp() override {
     memory_.Clear();
-    debug_frame_ = new MockDwarfDebugFrame<TypeParam>(&memory_);
+    debug_frame_ = new DwarfDebugFrame<TypeParam>(&memory_);
     ResetLogs();
   }
 
   void TearDown() override { delete debug_frame_; }
 
   MemoryFake memory_;
-  MockDwarfDebugFrame<TypeParam>* debug_frame_ = nullptr;
+  DwarfDebugFrame<TypeParam>* debug_frame_ = nullptr;
 };
 TYPED_TEST_CASE_P(DwarfDebugFrameTest);
 
 // NOTE: All test class variables need to be referenced as this->.
 
-TYPED_TEST_P(DwarfDebugFrameTest, Init32) {
-  // CIE 32 information.
-  this->memory_.SetData32(0x5000, 0xfc);
-  this->memory_.SetData32(0x5004, 0xffffffff);
-  this->memory_.SetData8(0x5008, 1);
-  this->memory_.SetData8(0x5009, '\0');
-
-  // FDE 32 information.
-  this->memory_.SetData32(0x5100, 0xfc);
-  this->memory_.SetData32(0x5104, 0);
-  this->memory_.SetData32(0x5108, 0x1500);
-  this->memory_.SetData32(0x510c, 0x200);
-
-  this->memory_.SetData32(0x5200, 0xfc);
-  this->memory_.SetData32(0x5204, 0);
-  this->memory_.SetData32(0x5208, 0x2500);
-  this->memory_.SetData32(0x520c, 0x300);
-
-  // CIE 32 information.
-  this->memory_.SetData32(0x5300, 0xfc);
-  this->memory_.SetData32(0x5304, 0xffffffff);
-  this->memory_.SetData8(0x5308, 1);
-  this->memory_.SetData8(0x5309, '\0');
-
-  // FDE 32 information.
-  this->memory_.SetData32(0x5400, 0xfc);
-  this->memory_.SetData32(0x5404, 0x300);
-  this->memory_.SetData32(0x5408, 0x3500);
-  this->memory_.SetData32(0x540c, 0x400);
-
-  this->memory_.SetData32(0x5500, 0xfc);
-  this->memory_.SetData32(0x5504, 0x300);
-  this->memory_.SetData32(0x5508, 0x4500);
-  this->memory_.SetData32(0x550c, 0x500);
-
-  ASSERT_TRUE(this->debug_frame_->Init(0x5000, 0x600));
-  ASSERT_EQ(4U, this->debug_frame_->TestGetFdeCount());
-
-  typename DwarfDebugFrame<TypeParam>::FdeInfo info(0, 0, 0);
-
-  this->debug_frame_->TestGetFdeInfo(0, &info);
-  EXPECT_EQ(0x5100U, info.offset);
-  EXPECT_EQ(0x1500U, info.start);
-  EXPECT_EQ(0x1700U, info.end);
-
-  this->debug_frame_->TestGetFdeInfo(1, &info);
-  EXPECT_EQ(0x5200U, info.offset);
-  EXPECT_EQ(0x2500U, info.start);
-  EXPECT_EQ(0x2800U, info.end);
-
-  this->debug_frame_->TestGetFdeInfo(2, &info);
-  EXPECT_EQ(0x5400U, info.offset);
-  EXPECT_EQ(0x3500U, info.start);
-  EXPECT_EQ(0x3900U, info.end);
-
-  this->debug_frame_->TestGetFdeInfo(3, &info);
-  EXPECT_EQ(0x5500U, info.offset);
-  EXPECT_EQ(0x4500U, info.start);
-  EXPECT_EQ(0x4a00U, info.end);
+static void SetCie32(MemoryFake* memory, uint64_t offset, uint32_t length,
+                     std::vector<uint8_t> data) {
+  memory->SetData32(offset, length);
+  offset += 4;
+  // Indicates this is a cie.
+  memory->SetData32(offset, 0xffffffff);
+  offset += 4;
+  memory->SetMemory(offset, data);
 }
 
-TYPED_TEST_P(DwarfDebugFrameTest, Init32_fde_not_following_cie) {
-  // CIE 32 information.
-  this->memory_.SetData32(0x5000, 0xfc);
-  this->memory_.SetData32(0x5004, 0xffffffff);
-  this->memory_.SetData8(0x5008, 1);
-  this->memory_.SetData8(0x5009, '\0');
-
-  // FDE 32 information.
-  this->memory_.SetData32(0x5100, 0xfc);
-  this->memory_.SetData32(0x5104, 0x1000);
-  this->memory_.SetData32(0x5108, 0x1500);
-  this->memory_.SetData32(0x510c, 0x200);
-
-  ASSERT_FALSE(this->debug_frame_->Init(0x5000, 0x600));
-  ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->debug_frame_->last_error());
+static void SetCie64(MemoryFake* memory, uint64_t offset, uint64_t length,
+                     std::vector<uint8_t> data) {
+  memory->SetData32(offset, 0xffffffff);
+  offset += 4;
+  memory->SetData64(offset, length);
+  offset += 8;
+  // Indicates this is a cie.
+  memory->SetData64(offset, 0xffffffffffffffffUL);
+  offset += 8;
+  memory->SetMemory(offset, data);
 }
 
-TYPED_TEST_P(DwarfDebugFrameTest, Init64) {
+static void SetFde32(MemoryFake* memory, uint64_t offset, uint32_t length, uint64_t cie_offset,
+                     uint32_t pc_start, uint32_t pc_length, uint64_t segment_length = 0,
+                     std::vector<uint8_t>* data = nullptr) {
+  memory->SetData32(offset, length);
+  offset += 4;
+  memory->SetData32(offset, cie_offset);
+  offset += 4 + segment_length;
+  memory->SetData32(offset, pc_start);
+  offset += 4;
+  memory->SetData32(offset, pc_length);
+  if (data != nullptr) {
+    offset += 4;
+    memory->SetMemory(offset, *data);
+  }
+}
+
+static void SetFde64(MemoryFake* memory, uint64_t offset, uint64_t length, uint64_t cie_offset,
+                     uint64_t pc_start, uint64_t pc_length, uint64_t segment_length = 0,
+                     std::vector<uint8_t>* data = nullptr) {
+  memory->SetData32(offset, 0xffffffff);
+  offset += 4;
+  memory->SetData64(offset, length);
+  offset += 8;
+  memory->SetData64(offset, cie_offset);
+  offset += 8 + segment_length;
+  memory->SetData64(offset, pc_start);
+  offset += 8;
+  memory->SetData64(offset, pc_length);
+  if (data != nullptr) {
+    offset += 8;
+    memory->SetMemory(offset, *data);
+  }
+}
+
+static void SetFourFdes32(MemoryFake* memory) {
+  SetCie32(memory, 0x5000, 0xfc, std::vector<uint8_t>{1, '\0', 0, 0, 1});
+
+  // FDE 32 information.
+  SetFde32(memory, 0x5100, 0xfc, 0, 0x1500, 0x200);
+  SetFde32(memory, 0x5200, 0xfc, 0, 0x2500, 0x300);
+
+  // CIE 32 information.
+  SetCie32(memory, 0x5300, 0xfc, std::vector<uint8_t>{1, '\0', 0, 0, 1});
+
+  // FDE 32 information.
+  SetFde32(memory, 0x5400, 0xfc, 0x300, 0x3500, 0x400);
+  SetFde32(memory, 0x5500, 0xfc, 0x300, 0x4500, 0x500);
+}
+
+TYPED_TEST_P(DwarfDebugFrameTest, GetFdes32) {
+  SetFourFdes32(&this->memory_);
+  ASSERT_TRUE(this->debug_frame_->Init(0x5000, 0x600, 0));
+
+  std::vector<const DwarfFde*> fdes;
+  this->debug_frame_->GetFdes(&fdes);
+
+  ASSERT_EQ(4U, fdes.size());
+
+  EXPECT_EQ(0x5000U, fdes[0]->cie_offset);
+  EXPECT_EQ(0x5110U, fdes[0]->cfa_instructions_offset);
+  EXPECT_EQ(0x5200U, fdes[0]->cfa_instructions_end);
+  EXPECT_EQ(0x1500U, fdes[0]->pc_start);
+  EXPECT_EQ(0x1700U, fdes[0]->pc_end);
+  EXPECT_EQ(0U, fdes[0]->lsda_address);
+  EXPECT_TRUE(fdes[0]->cie != nullptr);
+
+  EXPECT_EQ(0x5000U, fdes[1]->cie_offset);
+  EXPECT_EQ(0x5210U, fdes[1]->cfa_instructions_offset);
+  EXPECT_EQ(0x5300U, fdes[1]->cfa_instructions_end);
+  EXPECT_EQ(0x2500U, fdes[1]->pc_start);
+  EXPECT_EQ(0x2800U, fdes[1]->pc_end);
+  EXPECT_EQ(0U, fdes[1]->lsda_address);
+  EXPECT_TRUE(fdes[1]->cie != nullptr);
+
+  EXPECT_EQ(0x5300U, fdes[2]->cie_offset);
+  EXPECT_EQ(0x5410U, fdes[2]->cfa_instructions_offset);
+  EXPECT_EQ(0x5500U, fdes[2]->cfa_instructions_end);
+  EXPECT_EQ(0x3500U, fdes[2]->pc_start);
+  EXPECT_EQ(0x3900U, fdes[2]->pc_end);
+  EXPECT_EQ(0U, fdes[2]->lsda_address);
+  EXPECT_TRUE(fdes[2]->cie != nullptr);
+
+  EXPECT_EQ(0x5300U, fdes[3]->cie_offset);
+  EXPECT_EQ(0x5510U, fdes[3]->cfa_instructions_offset);
+  EXPECT_EQ(0x5600U, fdes[3]->cfa_instructions_end);
+  EXPECT_EQ(0x4500U, fdes[3]->pc_start);
+  EXPECT_EQ(0x4a00U, fdes[3]->pc_end);
+  EXPECT_EQ(0U, fdes[3]->lsda_address);
+  EXPECT_TRUE(fdes[3]->cie != nullptr);
+}
+
+TYPED_TEST_P(DwarfDebugFrameTest, GetFdes32_after_GetFdeFromPc) {
+  SetFourFdes32(&this->memory_);
+  ASSERT_TRUE(this->debug_frame_->Init(0x5000, 0x600, 0));
+
+  const DwarfFde* fde = this->debug_frame_->GetFdeFromPc(0x3600);
+  ASSERT_TRUE(fde != nullptr);
+  EXPECT_EQ(0x3500U, fde->pc_start);
+  EXPECT_EQ(0x3900U, fde->pc_end);
+
+  std::vector<const DwarfFde*> fdes;
+  this->debug_frame_->GetFdes(&fdes);
+  ASSERT_EQ(4U, fdes.size());
+
+  // Verify that they got added in the correct order.
+  EXPECT_EQ(0x1500U, fdes[0]->pc_start);
+  EXPECT_EQ(0x1700U, fdes[0]->pc_end);
+  EXPECT_EQ(0x2500U, fdes[1]->pc_start);
+  EXPECT_EQ(0x2800U, fdes[1]->pc_end);
+  EXPECT_EQ(0x3500U, fdes[2]->pc_start);
+  EXPECT_EQ(0x3900U, fdes[2]->pc_end);
+  EXPECT_EQ(0x4500U, fdes[3]->pc_start);
+  EXPECT_EQ(0x4a00U, fdes[3]->pc_end);
+}
+
+TYPED_TEST_P(DwarfDebugFrameTest, GetFdes32_not_in_section) {
+  SetFourFdes32(&this->memory_);
+  ASSERT_TRUE(this->debug_frame_->Init(0x5000, 0x500, 0));
+
+  std::vector<const DwarfFde*> fdes;
+  this->debug_frame_->GetFdes(&fdes);
+
+  ASSERT_EQ(3U, fdes.size());
+}
+
+TYPED_TEST_P(DwarfDebugFrameTest, GetFdeFromPc32) {
+  SetFourFdes32(&this->memory_);
+  ASSERT_TRUE(this->debug_frame_->Init(0x5000, 0x600, 0));
+
+  const DwarfFde* fde = this->debug_frame_->GetFdeFromPc(0x1600);
+  ASSERT_TRUE(fde != nullptr);
+  EXPECT_EQ(0x1500U, fde->pc_start);
+
+  fde = this->debug_frame_->GetFdeFromPc(0x2600);
+  ASSERT_TRUE(fde != nullptr);
+  EXPECT_EQ(0x2500U, fde->pc_start);
+
+  fde = this->debug_frame_->GetFdeFromPc(0x3600);
+  ASSERT_TRUE(fde != nullptr);
+  EXPECT_EQ(0x3500U, fde->pc_start);
+
+  fde = this->debug_frame_->GetFdeFromPc(0x4600);
+  ASSERT_TRUE(fde != nullptr);
+  EXPECT_EQ(0x4500U, fde->pc_start);
+
+  fde = this->debug_frame_->GetFdeFromPc(0);
+  ASSERT_TRUE(fde == nullptr);
+}
+
+TYPED_TEST_P(DwarfDebugFrameTest, GetFdeFromPc32_reverse) {
+  SetFourFdes32(&this->memory_);
+  ASSERT_TRUE(this->debug_frame_->Init(0x5000, 0x600, 0));
+
+  const DwarfFde* fde = this->debug_frame_->GetFdeFromPc(0x4600);
+  ASSERT_TRUE(fde != nullptr);
+  EXPECT_EQ(0x4500U, fde->pc_start);
+
+  fde = this->debug_frame_->GetFdeFromPc(0x3600);
+  ASSERT_TRUE(fde != nullptr);
+  EXPECT_EQ(0x3500U, fde->pc_start);
+
+  fde = this->debug_frame_->GetFdeFromPc(0x2600);
+  ASSERT_TRUE(fde != nullptr);
+  EXPECT_EQ(0x2500U, fde->pc_start);
+
+  fde = this->debug_frame_->GetFdeFromPc(0x1600);
+  ASSERT_TRUE(fde != nullptr);
+  EXPECT_EQ(0x1500U, fde->pc_start);
+
+  fde = this->debug_frame_->GetFdeFromPc(0);
+  ASSERT_TRUE(fde == nullptr);
+}
+
+TYPED_TEST_P(DwarfDebugFrameTest, GetFdeFromPc32_not_in_section) {
+  SetFourFdes32(&this->memory_);
+  ASSERT_TRUE(this->debug_frame_->Init(0x5000, 0x500, 0));
+
+  const DwarfFde* fde = this->debug_frame_->GetFdeFromPc(0x4600);
+  ASSERT_TRUE(fde == nullptr);
+}
+
+static void SetFourFdes64(MemoryFake* memory) {
   // CIE 64 information.
-  this->memory_.SetData32(0x5000, 0xffffffff);
-  this->memory_.SetData64(0x5004, 0xf4);
-  this->memory_.SetData64(0x500c, 0xffffffffffffffffULL);
-  this->memory_.SetData8(0x5014, 1);
-  this->memory_.SetData8(0x5015, '\0');
+  SetCie64(memory, 0x5000, 0xf4, std::vector<uint8_t>{1, '\0', 0, 0, 1});
 
   // FDE 64 information.
-  this->memory_.SetData32(0x5100, 0xffffffff);
-  this->memory_.SetData64(0x5104, 0xf4);
-  this->memory_.SetData64(0x510c, 0);
-  this->memory_.SetData64(0x5114, 0x1500);
-  this->memory_.SetData64(0x511c, 0x200);
-
-  this->memory_.SetData32(0x5200, 0xffffffff);
-  this->memory_.SetData64(0x5204, 0xf4);
-  this->memory_.SetData64(0x520c, 0);
-  this->memory_.SetData64(0x5214, 0x2500);
-  this->memory_.SetData64(0x521c, 0x300);
+  SetFde64(memory, 0x5100, 0xf4, 0, 0x1500, 0x200);
+  SetFde64(memory, 0x5200, 0xf4, 0, 0x2500, 0x300);
 
   // CIE 64 information.
-  this->memory_.SetData32(0x5300, 0xffffffff);
-  this->memory_.SetData64(0x5304, 0xf4);
-  this->memory_.SetData64(0x530c, 0xffffffffffffffffULL);
-  this->memory_.SetData8(0x5314, 1);
-  this->memory_.SetData8(0x5315, '\0');
+  SetCie64(memory, 0x5300, 0xf4, std::vector<uint8_t>{1, '\0', 0, 0, 1});
 
   // FDE 64 information.
-  this->memory_.SetData32(0x5400, 0xffffffff);
-  this->memory_.SetData64(0x5404, 0xf4);
-  this->memory_.SetData64(0x540c, 0x300);
-  this->memory_.SetData64(0x5414, 0x3500);
-  this->memory_.SetData64(0x541c, 0x400);
-
-  this->memory_.SetData32(0x5500, 0xffffffff);
-  this->memory_.SetData64(0x5504, 0xf4);
-  this->memory_.SetData64(0x550c, 0x300);
-  this->memory_.SetData64(0x5514, 0x4500);
-  this->memory_.SetData64(0x551c, 0x500);
-
-  ASSERT_TRUE(this->debug_frame_->Init(0x5000, 0x600));
-  ASSERT_EQ(4U, this->debug_frame_->TestGetFdeCount());
-
-  typename DwarfDebugFrame<TypeParam>::FdeInfo info(0, 0, 0);
-
-  this->debug_frame_->TestGetFdeInfo(0, &info);
-  EXPECT_EQ(0x5100U, info.offset);
-  EXPECT_EQ(0x1500U, info.start);
-  EXPECT_EQ(0x1700U, info.end);
-
-  this->debug_frame_->TestGetFdeInfo(1, &info);
-  EXPECT_EQ(0x5200U, info.offset);
-  EXPECT_EQ(0x2500U, info.start);
-  EXPECT_EQ(0x2800U, info.end);
-
-  this->debug_frame_->TestGetFdeInfo(2, &info);
-  EXPECT_EQ(0x5400U, info.offset);
-  EXPECT_EQ(0x3500U, info.start);
-  EXPECT_EQ(0x3900U, info.end);
-
-  this->debug_frame_->TestGetFdeInfo(3, &info);
-  EXPECT_EQ(0x5500U, info.offset);
-  EXPECT_EQ(0x4500U, info.start);
-  EXPECT_EQ(0x4a00U, info.end);
+  SetFde64(memory, 0x5400, 0xf4, 0x300, 0x3500, 0x400);
+  SetFde64(memory, 0x5500, 0xf4, 0x300, 0x4500, 0x500);
 }
 
-TYPED_TEST_P(DwarfDebugFrameTest, Init64_fde_not_following_cie) {
-  // CIE 64 information.
-  this->memory_.SetData32(0x5000, 0xffffffff);
-  this->memory_.SetData64(0x5004, 0xf4);
-  this->memory_.SetData64(0x500c, 0xffffffffffffffffULL);
-  this->memory_.SetData8(0x5014, 1);
-  this->memory_.SetData8(0x5015, '\0');
+TYPED_TEST_P(DwarfDebugFrameTest, GetFdes64) {
+  SetFourFdes64(&this->memory_);
+  ASSERT_TRUE(this->debug_frame_->Init(0x5000, 0x600, 0));
 
-  // FDE 64 information.
-  this->memory_.SetData32(0x5100, 0xffffffff);
-  this->memory_.SetData64(0x5104, 0xf4);
-  this->memory_.SetData64(0x510c, 0x1000);
-  this->memory_.SetData64(0x5114, 0x1500);
-  this->memory_.SetData64(0x511c, 0x200);
+  std::vector<const DwarfFde*> fdes;
+  this->debug_frame_->GetFdes(&fdes);
 
-  ASSERT_FALSE(this->debug_frame_->Init(0x5000, 0x600));
-  ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->debug_frame_->last_error());
+  ASSERT_EQ(4U, fdes.size());
+
+  EXPECT_EQ(0x5000U, fdes[0]->cie_offset);
+  EXPECT_EQ(0x5124U, fdes[0]->cfa_instructions_offset);
+  EXPECT_EQ(0x5200U, fdes[0]->cfa_instructions_end);
+  EXPECT_EQ(0x1500U, fdes[0]->pc_start);
+  EXPECT_EQ(0x1700U, fdes[0]->pc_end);
+  EXPECT_EQ(0U, fdes[0]->lsda_address);
+  EXPECT_TRUE(fdes[0]->cie != nullptr);
+
+  EXPECT_EQ(0x5000U, fdes[1]->cie_offset);
+  EXPECT_EQ(0x5224U, fdes[1]->cfa_instructions_offset);
+  EXPECT_EQ(0x5300U, fdes[1]->cfa_instructions_end);
+  EXPECT_EQ(0x2500U, fdes[1]->pc_start);
+  EXPECT_EQ(0x2800U, fdes[1]->pc_end);
+  EXPECT_EQ(0U, fdes[1]->lsda_address);
+  EXPECT_TRUE(fdes[1]->cie != nullptr);
+
+  EXPECT_EQ(0x5300U, fdes[2]->cie_offset);
+  EXPECT_EQ(0x5424U, fdes[2]->cfa_instructions_offset);
+  EXPECT_EQ(0x5500U, fdes[2]->cfa_instructions_end);
+  EXPECT_EQ(0x3500U, fdes[2]->pc_start);
+  EXPECT_EQ(0x3900U, fdes[2]->pc_end);
+  EXPECT_EQ(0U, fdes[2]->lsda_address);
+  EXPECT_TRUE(fdes[2]->cie != nullptr);
+
+  EXPECT_EQ(0x5300U, fdes[3]->cie_offset);
+  EXPECT_EQ(0x5524U, fdes[3]->cfa_instructions_offset);
+  EXPECT_EQ(0x5600U, fdes[3]->cfa_instructions_end);
+  EXPECT_EQ(0x4500U, fdes[3]->pc_start);
+  EXPECT_EQ(0x4a00U, fdes[3]->pc_end);
+  EXPECT_EQ(0U, fdes[3]->lsda_address);
+  EXPECT_TRUE(fdes[3]->cie != nullptr);
 }
 
-TYPED_TEST_P(DwarfDebugFrameTest, Init_version1) {
-  // CIE 32 information.
-  this->memory_.SetData32(0x5000, 0xfc);
-  this->memory_.SetData32(0x5004, 0xffffffff);
-  this->memory_.SetData8(0x5008, 1);
-  // Augment string.
-  this->memory_.SetMemory(0x5009, std::vector<uint8_t>{'z', 'R', 'P', 'L', '\0'});
-  // Code alignment factor.
-  this->memory_.SetMemory(0x500e, std::vector<uint8_t>{0x80, 0x00});
-  // Data alignment factor.
-  this->memory_.SetMemory(0x5010, std::vector<uint8_t>{0x81, 0x80, 0x80, 0x00});
-  // Return address register
-  this->memory_.SetData8(0x5014, 0x84);
-  // Augmentation length
-  this->memory_.SetMemory(0x5015, std::vector<uint8_t>{0x84, 0x00});
-  // R data.
-  this->memory_.SetData8(0x5017, DW_EH_PE_pcrel | DW_EH_PE_udata2);
+TYPED_TEST_P(DwarfDebugFrameTest, GetFdes64_after_GetFdeFromPc) {
+  SetFourFdes64(&this->memory_);
+  ASSERT_TRUE(this->debug_frame_->Init(0x5000, 0x600, 0));
 
-  // FDE 32 information.
-  this->memory_.SetData32(0x5100, 0xfc);
-  this->memory_.SetData32(0x5104, 0);
-  this->memory_.SetData16(0x5108, 0x1500);
-  this->memory_.SetData16(0x510a, 0x200);
+  const DwarfFde* fde = this->debug_frame_->GetFdeFromPc(0x2600);
+  ASSERT_TRUE(fde != nullptr);
+  EXPECT_EQ(0x2500U, fde->pc_start);
+  EXPECT_EQ(0x2800U, fde->pc_end);
 
-  ASSERT_TRUE(this->debug_frame_->Init(0x5000, 0x200));
-  ASSERT_EQ(1U, this->debug_frame_->TestGetFdeCount());
+  std::vector<const DwarfFde*> fdes;
+  this->debug_frame_->GetFdes(&fdes);
+  ASSERT_EQ(4U, fdes.size());
 
-  typename DwarfDebugFrame<TypeParam>::FdeInfo info(0, 0, 0);
-  this->debug_frame_->TestGetFdeInfo(0, &info);
-  EXPECT_EQ(0x5100U, info.offset);
-  EXPECT_EQ(0x1500U, info.start);
-  EXPECT_EQ(0x1700U, info.end);
+  // Verify that they got added in the correct order.
+  EXPECT_EQ(0x1500U, fdes[0]->pc_start);
+  EXPECT_EQ(0x1700U, fdes[0]->pc_end);
+  EXPECT_EQ(0x2500U, fdes[1]->pc_start);
+  EXPECT_EQ(0x2800U, fdes[1]->pc_end);
+  EXPECT_EQ(0x3500U, fdes[2]->pc_start);
+  EXPECT_EQ(0x3900U, fdes[2]->pc_end);
+  EXPECT_EQ(0x4500U, fdes[3]->pc_start);
+  EXPECT_EQ(0x4a00U, fdes[3]->pc_end);
 }
 
-TYPED_TEST_P(DwarfDebugFrameTest, Init_version4) {
-  // CIE 32 information.
-  this->memory_.SetData32(0x5000, 0xfc);
-  this->memory_.SetData32(0x5004, 0xffffffff);
-  this->memory_.SetData8(0x5008, 4);
-  // Augment string.
-  this->memory_.SetMemory(0x5009, std::vector<uint8_t>{'z', 'L', 'P', 'R', '\0'});
-  // Address size.
-  this->memory_.SetData8(0x500e, 4);
-  // Segment size.
-  this->memory_.SetData8(0x500f, 0);
-  // Code alignment factor.
-  this->memory_.SetMemory(0x5010, std::vector<uint8_t>{0x80, 0x00});
-  // Data alignment factor.
-  this->memory_.SetMemory(0x5012, std::vector<uint8_t>{0x81, 0x80, 0x80, 0x00});
-  // Return address register
-  this->memory_.SetMemory(0x5016, std::vector<uint8_t>{0x85, 0x10});
-  // Augmentation length
-  this->memory_.SetMemory(0x5018, std::vector<uint8_t>{0x84, 0x00});
-  // L data.
-  this->memory_.SetData8(0x501a, 0x10);
-  // P data.
-  this->memory_.SetData8(0x501b, DW_EH_PE_udata4);
-  this->memory_.SetData32(0x501c, 0x100);
-  // R data.
-  this->memory_.SetData8(0x5020, DW_EH_PE_pcrel | DW_EH_PE_udata2);
+TYPED_TEST_P(DwarfDebugFrameTest, GetFdes64_not_in_section) {
+  SetFourFdes64(&this->memory_);
+  ASSERT_TRUE(this->debug_frame_->Init(0x5000, 0x500, 0));
 
-  // FDE 32 information.
-  this->memory_.SetData32(0x5100, 0xfc);
-  this->memory_.SetData32(0x5104, 0);
-  this->memory_.SetData16(0x5108, 0x1500);
-  this->memory_.SetData16(0x510a, 0x200);
+  std::vector<const DwarfFde*> fdes;
+  this->debug_frame_->GetFdes(&fdes);
 
-  ASSERT_TRUE(this->debug_frame_->Init(0x5000, 0x200));
-  ASSERT_EQ(1U, this->debug_frame_->TestGetFdeCount());
-
-  typename DwarfDebugFrame<TypeParam>::FdeInfo info(0, 0, 0);
-  this->debug_frame_->TestGetFdeInfo(0, &info);
-  EXPECT_EQ(0x5100U, info.offset);
-  EXPECT_EQ(0x1500U, info.start);
-  EXPECT_EQ(0x1700U, info.end);
+  ASSERT_EQ(3U, fdes.size());
 }
 
-TYPED_TEST_P(DwarfDebugFrameTest, GetFdeOffsetFromPc) {
-  typename DwarfDebugFrame<TypeParam>::FdeInfo info(0, 0, 0);
-  for (size_t i = 0; i < 9; i++) {
-    info.start = 0x1000 * (i + 1);
-    info.end = 0x1000 * (i + 2) - 0x10;
-    info.offset = 0x5000 + i * 0x20;
-    this->debug_frame_->TestPushFdeInfo(info);
-  }
+TYPED_TEST_P(DwarfDebugFrameTest, GetFdeFromPc64) {
+  SetFourFdes64(&this->memory_);
+  ASSERT_TRUE(this->debug_frame_->Init(0x5000, 0x600, 0));
 
-  this->debug_frame_->TestSetFdeCount(0);
-  uint64_t fde_offset;
-  ASSERT_FALSE(this->debug_frame_->GetFdeOffsetFromPc(0x1000, &fde_offset));
-  ASSERT_EQ(DWARF_ERROR_NONE, this->debug_frame_->last_error());
+  const DwarfFde* fde = this->debug_frame_->GetFdeFromPc(0x1600);
+  ASSERT_TRUE(fde != nullptr);
+  EXPECT_EQ(0x1500U, fde->pc_start);
 
-  this->debug_frame_->TestSetFdeCount(9);
-  ASSERT_FALSE(this->debug_frame_->GetFdeOffsetFromPc(0x100, &fde_offset));
-  ASSERT_EQ(DWARF_ERROR_NONE, this->debug_frame_->last_error());
-  // Odd number of elements.
-  for (size_t i = 0; i < 9; i++) {
-    TypeParam pc = 0x1000 * (i + 1);
-    ASSERT_TRUE(this->debug_frame_->GetFdeOffsetFromPc(pc, &fde_offset)) << "Failed at index " << i;
-    EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i;
-    ASSERT_TRUE(this->debug_frame_->GetFdeOffsetFromPc(pc + 1, &fde_offset)) << "Failed at index "
-                                                                             << i;
-    EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i;
-    ASSERT_TRUE(this->debug_frame_->GetFdeOffsetFromPc(pc + 0xeff, &fde_offset))
-        << "Failed at index " << i;
-    EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i;
-    ASSERT_FALSE(this->debug_frame_->GetFdeOffsetFromPc(pc + 0xfff, &fde_offset))
-        << "Failed at index " << i;
-    ASSERT_EQ(DWARF_ERROR_NONE, this->debug_frame_->last_error());
-  }
+  fde = this->debug_frame_->GetFdeFromPc(0x2600);
+  ASSERT_TRUE(fde != nullptr);
+  EXPECT_EQ(0x2500U, fde->pc_start);
 
-  // Even number of elements.
-  this->debug_frame_->TestSetFdeCount(10);
-  info.start = 0xa000;
-  info.end = 0xaff0;
-  info.offset = 0x5120;
-  this->debug_frame_->TestPushFdeInfo(info);
+  fde = this->debug_frame_->GetFdeFromPc(0x3600);
+  ASSERT_TRUE(fde != nullptr);
+  EXPECT_EQ(0x3500U, fde->pc_start);
 
-  for (size_t i = 0; i < 10; i++) {
-    TypeParam pc = 0x1000 * (i + 1);
-    ASSERT_TRUE(this->debug_frame_->GetFdeOffsetFromPc(pc, &fde_offset)) << "Failed at index " << i;
-    EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i;
-    ASSERT_TRUE(this->debug_frame_->GetFdeOffsetFromPc(pc + 1, &fde_offset)) << "Failed at index "
-                                                                             << i;
-    EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i;
-    ASSERT_TRUE(this->debug_frame_->GetFdeOffsetFromPc(pc + 0xeff, &fde_offset))
-        << "Failed at index " << i;
-    EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i;
-    ASSERT_FALSE(this->debug_frame_->GetFdeOffsetFromPc(pc + 0xfff, &fde_offset))
-        << "Failed at index " << i;
-    ASSERT_EQ(DWARF_ERROR_NONE, this->debug_frame_->last_error());
-  }
+  fde = this->debug_frame_->GetFdeFromPc(0x4600);
+  ASSERT_TRUE(fde != nullptr);
+  EXPECT_EQ(0x4500U, fde->pc_start);
+
+  fde = this->debug_frame_->GetFdeFromPc(0);
+  ASSERT_TRUE(fde == nullptr);
+}
+
+TYPED_TEST_P(DwarfDebugFrameTest, GetFdeFromPc64_reverse) {
+  SetFourFdes64(&this->memory_);
+  ASSERT_TRUE(this->debug_frame_->Init(0x5000, 0x600, 0));
+
+  const DwarfFde* fde = this->debug_frame_->GetFdeFromPc(0x4600);
+  ASSERT_TRUE(fde != nullptr);
+  EXPECT_EQ(0x4500U, fde->pc_start);
+
+  fde = this->debug_frame_->GetFdeFromPc(0x3600);
+  ASSERT_TRUE(fde != nullptr);
+  EXPECT_EQ(0x3500U, fde->pc_start);
+
+  fde = this->debug_frame_->GetFdeFromPc(0x2600);
+  ASSERT_TRUE(fde != nullptr);
+  EXPECT_EQ(0x2500U, fde->pc_start);
+
+  fde = this->debug_frame_->GetFdeFromPc(0x1600);
+  ASSERT_TRUE(fde != nullptr);
+  EXPECT_EQ(0x1500U, fde->pc_start);
+
+  fde = this->debug_frame_->GetFdeFromPc(0);
+  ASSERT_TRUE(fde == nullptr);
+}
+
+TYPED_TEST_P(DwarfDebugFrameTest, GetFdeFromPc64_not_in_section) {
+  SetFourFdes64(&this->memory_);
+  ASSERT_TRUE(this->debug_frame_->Init(0x5000, 0x500, 0));
+
+  const DwarfFde* fde = this->debug_frame_->GetFdeFromPc(0x4600);
+  ASSERT_TRUE(fde == nullptr);
 }
 
 TYPED_TEST_P(DwarfDebugFrameTest, GetCieFde32) {
-  this->debug_frame_->TestSetOffset(0x4000);
-
-  // CIE 32 information.
-  this->memory_.SetData32(0xf000, 0x100);
-  this->memory_.SetData32(0xf004, 0xffffffff);
-  this->memory_.SetData8(0xf008, 0x1);
-  this->memory_.SetData8(0xf009, '\0');
-  this->memory_.SetData8(0xf00a, 4);
-  this->memory_.SetData8(0xf00b, 8);
-  this->memory_.SetData8(0xf00c, 0x20);
-
-  // FDE 32 information.
-  this->memory_.SetData32(0x14000, 0x20);
-  this->memory_.SetData32(0x14004, 0xb000);
-  this->memory_.SetData32(0x14008, 0x9000);
-  this->memory_.SetData32(0x1400c, 0x100);
+  SetCie32(&this->memory_, 0xf000, 0x100, std::vector<uint8_t>{1, '\0', 4, 8, 0x20});
+  SetFde32(&this->memory_, 0x14000, 0x20, 0xf000, 0x9000, 0x100);
 
   const DwarfFde* fde = this->debug_frame_->GetFdeFromOffset(0x14000);
   ASSERT_TRUE(fde != nullptr);
@@ -405,24 +428,8 @@
 }
 
 TYPED_TEST_P(DwarfDebugFrameTest, GetCieFde64) {
-  this->debug_frame_->TestSetOffset(0x2000);
-
-  // CIE 64 information.
-  this->memory_.SetData32(0x6000, 0xffffffff);
-  this->memory_.SetData64(0x6004, 0x100);
-  this->memory_.SetData64(0x600c, 0xffffffffffffffffULL);
-  this->memory_.SetData8(0x6014, 0x1);
-  this->memory_.SetData8(0x6015, '\0');
-  this->memory_.SetData8(0x6016, 4);
-  this->memory_.SetData8(0x6017, 8);
-  this->memory_.SetData8(0x6018, 0x20);
-
-  // FDE 64 information.
-  this->memory_.SetData32(0x8000, 0xffffffff);
-  this->memory_.SetData64(0x8004, 0x200);
-  this->memory_.SetData64(0x800c, 0x4000);
-  this->memory_.SetData64(0x8014, 0x5000);
-  this->memory_.SetData64(0x801c, 0x300);
+  SetCie64(&this->memory_, 0x6000, 0x100, std::vector<uint8_t>{1, '\0', 4, 8, 0x20});
+  SetFde64(&this->memory_, 0x8000, 0x200, 0x6000, 0x5000, 0x300);
 
   const DwarfFde* fde = this->debug_frame_->GetFdeFromOffset(0x8000);
   ASSERT_TRUE(fde != nullptr);
@@ -448,9 +455,359 @@
   EXPECT_EQ(0x20U, fde->cie->return_address_register);
 }
 
-REGISTER_TYPED_TEST_CASE_P(DwarfDebugFrameTest, Init32, Init32_fde_not_following_cie, Init64,
-                           Init64_fde_not_following_cie, Init_version1, Init_version4,
-                           GetFdeOffsetFromPc, GetCieFde32, GetCieFde64);
+static void VerifyCieVersion(const DwarfCie* cie, uint8_t version, uint8_t segment_size,
+                             uint8_t fde_encoding, uint64_t return_address, uint64_t start_offset,
+                             uint64_t end_offset) {
+  EXPECT_EQ(version, cie->version);
+  EXPECT_EQ(fde_encoding, cie->fde_address_encoding);
+  EXPECT_EQ(DW_EH_PE_omit, cie->lsda_encoding);
+  EXPECT_EQ(segment_size, cie->segment_size);
+  EXPECT_EQ(1U, cie->augmentation_string.size());
+  EXPECT_EQ('\0', cie->augmentation_string[0]);
+  EXPECT_EQ(0U, cie->personality_handler);
+  EXPECT_EQ(4U, cie->code_alignment_factor);
+  EXPECT_EQ(8, cie->data_alignment_factor);
+  EXPECT_EQ(return_address, cie->return_address_register);
+  EXPECT_EQ(0x5000U + start_offset, cie->cfa_instructions_offset);
+  EXPECT_EQ(0x5000U + end_offset, cie->cfa_instructions_end);
+}
+
+TYPED_TEST_P(DwarfDebugFrameTest, GetCieFromOffset32_cie_cached) {
+  SetCie32(&this->memory_, 0x5000, 0x100, std::vector<uint8_t>{1, '\0', 4, 8, 0x20});
+  const DwarfCie* cie = this->debug_frame_->GetCieFromOffset(0x5000);
+  EXPECT_EQ(DWARF_ERROR_NONE, this->debug_frame_->LastErrorCode());
+  ASSERT_TRUE(cie != nullptr);
+  VerifyCieVersion(cie, 1, 0, DW_EH_PE_sdata4, 0x20, 0xd, 0x104);
+
+  std::vector<uint8_t> zero(0x100, 0);
+  this->memory_.SetMemory(0x5000, zero);
+  cie = this->debug_frame_->GetCieFromOffset(0x5000);
+  EXPECT_EQ(DWARF_ERROR_NONE, this->debug_frame_->LastErrorCode());
+  ASSERT_TRUE(cie != nullptr);
+  VerifyCieVersion(cie, 1, 0, DW_EH_PE_sdata4, 0x20, 0xd, 0x104);
+}
+
+TYPED_TEST_P(DwarfDebugFrameTest, GetCieFromOffset64_cie_cached) {
+  SetCie64(&this->memory_, 0x5000, 0x100, std::vector<uint8_t>{1, '\0', 4, 8, 0x20});
+  const DwarfCie* cie = this->debug_frame_->GetCieFromOffset(0x5000);
+  EXPECT_EQ(DWARF_ERROR_NONE, this->debug_frame_->LastErrorCode());
+  ASSERT_TRUE(cie != nullptr);
+  VerifyCieVersion(cie, 1, 0, DW_EH_PE_sdata8, 0x20, 0x19, 0x10c);
+
+  std::vector<uint8_t> zero(0x100, 0);
+  this->memory_.SetMemory(0x5000, zero);
+  cie = this->debug_frame_->GetCieFromOffset(0x5000);
+  EXPECT_EQ(DWARF_ERROR_NONE, this->debug_frame_->LastErrorCode());
+  ASSERT_TRUE(cie != nullptr);
+  VerifyCieVersion(cie, 1, 0, DW_EH_PE_sdata8, 0x20, 0x19, 0x10c);
+}
+
+TYPED_TEST_P(DwarfDebugFrameTest, GetCieFromOffset32_version1) {
+  SetCie32(&this->memory_, 0x5000, 0x100, std::vector<uint8_t>{1, '\0', 4, 8, 0x20});
+  const DwarfCie* cie = this->debug_frame_->GetCieFromOffset(0x5000);
+  EXPECT_EQ(DWARF_ERROR_NONE, this->debug_frame_->LastErrorCode());
+  ASSERT_TRUE(cie != nullptr);
+  VerifyCieVersion(cie, 1, 0, DW_EH_PE_sdata4, 0x20, 0xd, 0x104);
+}
+
+TYPED_TEST_P(DwarfDebugFrameTest, GetCieFromOffset64_version1) {
+  SetCie64(&this->memory_, 0x5000, 0x100, std::vector<uint8_t>{1, '\0', 4, 8, 0x20});
+  const DwarfCie* cie = this->debug_frame_->GetCieFromOffset(0x5000);
+  EXPECT_EQ(DWARF_ERROR_NONE, this->debug_frame_->LastErrorCode());
+  ASSERT_TRUE(cie != nullptr);
+  VerifyCieVersion(cie, 1, 0, DW_EH_PE_sdata8, 0x20, 0x19, 0x10c);
+}
+
+TYPED_TEST_P(DwarfDebugFrameTest, GetCieFromOffset32_version3) {
+  SetCie32(&this->memory_, 0x5000, 0x100, std::vector<uint8_t>{3, '\0', 4, 8, 0x81, 3});
+  const DwarfCie* cie = this->debug_frame_->GetCieFromOffset(0x5000);
+  EXPECT_EQ(DWARF_ERROR_NONE, this->debug_frame_->LastErrorCode());
+  ASSERT_TRUE(cie != nullptr);
+  VerifyCieVersion(cie, 3, 0, DW_EH_PE_sdata4, 0x181, 0xe, 0x104);
+}
+
+TYPED_TEST_P(DwarfDebugFrameTest, GetCieFromOffset64_version3) {
+  SetCie64(&this->memory_, 0x5000, 0x100, std::vector<uint8_t>{3, '\0', 4, 8, 0x81, 3});
+  const DwarfCie* cie = this->debug_frame_->GetCieFromOffset(0x5000);
+  EXPECT_EQ(DWARF_ERROR_NONE, this->debug_frame_->LastErrorCode());
+  ASSERT_TRUE(cie != nullptr);
+  VerifyCieVersion(cie, 3, 0, DW_EH_PE_sdata8, 0x181, 0x1a, 0x10c);
+}
+
+TYPED_TEST_P(DwarfDebugFrameTest, GetCieFromOffset32_version4) {
+  SetCie32(&this->memory_, 0x5000, 0x100, std::vector<uint8_t>{4, '\0', 0, 10, 4, 8, 0x81, 3});
+  const DwarfCie* cie = this->debug_frame_->GetCieFromOffset(0x5000);
+  EXPECT_EQ(DWARF_ERROR_NONE, this->debug_frame_->LastErrorCode());
+  ASSERT_TRUE(cie != nullptr);
+  VerifyCieVersion(cie, 4, 10, DW_EH_PE_sdata4, 0x181, 0x10, 0x104);
+}
+
+TYPED_TEST_P(DwarfDebugFrameTest, GetCieFromOffset64_version4) {
+  SetCie64(&this->memory_, 0x5000, 0x100, std::vector<uint8_t>{4, '\0', 0, 10, 4, 8, 0x81, 3});
+  const DwarfCie* cie = this->debug_frame_->GetCieFromOffset(0x5000);
+  EXPECT_EQ(DWARF_ERROR_NONE, this->debug_frame_->LastErrorCode());
+  ASSERT_TRUE(cie != nullptr);
+  VerifyCieVersion(cie, 4, 10, DW_EH_PE_sdata8, 0x181, 0x1c, 0x10c);
+}
+
+TYPED_TEST_P(DwarfDebugFrameTest, GetCieFromOffset_version_invalid) {
+  SetCie32(&this->memory_, 0x5000, 0x100, std::vector<uint8_t>{0, '\0', 1, 2, 3, 4, 5, 6, 7});
+  ASSERT_TRUE(this->debug_frame_->GetCieFromOffset(0x5000) == nullptr);
+  EXPECT_EQ(DWARF_ERROR_UNSUPPORTED_VERSION, this->debug_frame_->LastErrorCode());
+  SetCie64(&this->memory_, 0x6000, 0x100, std::vector<uint8_t>{0, '\0', 1, 2, 3, 4, 5, 6, 7});
+  ASSERT_TRUE(this->debug_frame_->GetCieFromOffset(0x6000) == nullptr);
+  EXPECT_EQ(DWARF_ERROR_UNSUPPORTED_VERSION, this->debug_frame_->LastErrorCode());
+
+  SetCie32(&this->memory_, 0x7000, 0x100, std::vector<uint8_t>{5, '\0', 1, 2, 3, 4, 5, 6, 7});
+  ASSERT_TRUE(this->debug_frame_->GetCieFromOffset(0x7000) == nullptr);
+  EXPECT_EQ(DWARF_ERROR_UNSUPPORTED_VERSION, this->debug_frame_->LastErrorCode());
+  SetCie64(&this->memory_, 0x8000, 0x100, std::vector<uint8_t>{5, '\0', 1, 2, 3, 4, 5, 6, 7});
+  ASSERT_TRUE(this->debug_frame_->GetCieFromOffset(0x8000) == nullptr);
+  EXPECT_EQ(DWARF_ERROR_UNSUPPORTED_VERSION, this->debug_frame_->LastErrorCode());
+}
+
+static void VerifyCieAugment(const DwarfCie* cie, uint64_t inst_offset, uint64_t inst_end) {
+  EXPECT_EQ(1U, cie->version);
+  EXPECT_EQ(DW_EH_PE_udata2, cie->fde_address_encoding);
+  EXPECT_EQ(DW_EH_PE_textrel | DW_EH_PE_udata2, cie->lsda_encoding);
+  EXPECT_EQ(0U, cie->segment_size);
+  EXPECT_EQ(5U, cie->augmentation_string.size());
+  EXPECT_EQ('z', cie->augmentation_string[0]);
+  EXPECT_EQ('L', cie->augmentation_string[1]);
+  EXPECT_EQ('P', cie->augmentation_string[2]);
+  EXPECT_EQ('R', cie->augmentation_string[3]);
+  EXPECT_EQ('\0', cie->augmentation_string[4]);
+  EXPECT_EQ(0x12345678U, cie->personality_handler);
+  EXPECT_EQ(4U, cie->code_alignment_factor);
+  EXPECT_EQ(8, cie->data_alignment_factor);
+  EXPECT_EQ(0x10U, cie->return_address_register);
+  EXPECT_EQ(inst_offset, cie->cfa_instructions_offset);
+  EXPECT_EQ(inst_end, cie->cfa_instructions_end);
+}
+
+TYPED_TEST_P(DwarfDebugFrameTest, GetCieFromOffset32_augment) {
+  SetCie32(&this->memory_, 0x5000, 0x100,
+           std::vector<uint8_t>{/* version */ 1,
+                                /* augment string */ 'z', 'L', 'P', 'R', '\0',
+                                /* code alignment factor */ 4,
+                                /* data alignment factor */ 8,
+                                /* return address register */ 0x10,
+                                /* augment length */ 0xf,
+                                /* L data */ DW_EH_PE_textrel | DW_EH_PE_udata2,
+                                /* P data */ DW_EH_PE_udata4, 0x78, 0x56, 0x34, 0x12,
+                                /* R data */ DW_EH_PE_udata2});
+
+  const DwarfCie* cie = this->debug_frame_->GetCieFromOffset(0x5000);
+  ASSERT_TRUE(cie != nullptr);
+  VerifyCieAugment(cie, 0x5021, 0x5104);
+}
+
+TYPED_TEST_P(DwarfDebugFrameTest, GetCieFromOffset64_augment) {
+  SetCie64(&this->memory_, 0x5000, 0x100,
+           std::vector<uint8_t>{/* version */ 1,
+                                /* augment string */ 'z', 'L', 'P', 'R', '\0',
+                                /* code alignment factor */ 4,
+                                /* data alignment factor */ 8,
+                                /* return address register */ 0x10,
+                                /* augment length */ 0xf,
+                                /* L data */ DW_EH_PE_textrel | DW_EH_PE_udata2,
+                                /* P data */ DW_EH_PE_udata4, 0x78, 0x56, 0x34, 0x12,
+                                /* R data */ DW_EH_PE_udata2});
+
+  const DwarfCie* cie = this->debug_frame_->GetCieFromOffset(0x5000);
+  ASSERT_TRUE(cie != nullptr);
+  VerifyCieAugment(cie, 0x502d, 0x510c);
+}
+
+TYPED_TEST_P(DwarfDebugFrameTest, GetFdeFromOffset32_augment) {
+  SetCie32(&this->memory_, 0x5000, 0xfc,
+           std::vector<uint8_t>{/* version */ 4,
+                                /* augment string */ 'z', '\0',
+                                /* address size */ 8,
+                                /* segment size */ 0x10,
+                                /* code alignment factor */ 16,
+                                /* data alignment factor */ 32,
+                                /* return address register */ 10,
+                                /* augment length */ 0x0});
+
+  std::vector<uint8_t> data{/* augment length */ 0x80, 0x3};
+  SetFde32(&this->memory_, 0x5200, 0x300, 0x5000, 0x4300, 0x300, 0x10, &data);
+
+  const DwarfFde* fde = this->debug_frame_->GetFdeFromOffset(0x5200);
+  ASSERT_TRUE(fde != nullptr);
+  ASSERT_TRUE(fde->cie != nullptr);
+  EXPECT_EQ(4U, fde->cie->version);
+  EXPECT_EQ(0x5000U, fde->cie_offset);
+  EXPECT_EQ(0x53a2U, fde->cfa_instructions_offset);
+  EXPECT_EQ(0x5504U, fde->cfa_instructions_end);
+  EXPECT_EQ(0x4300U, fde->pc_start);
+  EXPECT_EQ(0x4600U, fde->pc_end);
+  EXPECT_EQ(0U, fde->lsda_address);
+}
+
+TYPED_TEST_P(DwarfDebugFrameTest, GetFdeFromOffset64_augment) {
+  SetCie64(&this->memory_, 0x5000, 0xfc,
+           std::vector<uint8_t>{/* version */ 4,
+                                /* augment string */ 'z', '\0',
+                                /* address size */ 8,
+                                /* segment size */ 0x10,
+                                /* code alignment factor */ 16,
+                                /* data alignment factor */ 32,
+                                /* return address register */ 10,
+                                /* augment length */ 0x0});
+
+  std::vector<uint8_t> data{/* augment length */ 0x80, 0x3};
+  SetFde64(&this->memory_, 0x5200, 0x300, 0x5000, 0x4300, 0x300, 0x10, &data);
+
+  const DwarfFde* fde = this->debug_frame_->GetFdeFromOffset(0x5200);
+  ASSERT_TRUE(fde != nullptr);
+  ASSERT_TRUE(fde->cie != nullptr);
+  EXPECT_EQ(4U, fde->cie->version);
+  EXPECT_EQ(0x5000U, fde->cie_offset);
+  EXPECT_EQ(0x53b6U, fde->cfa_instructions_offset);
+  EXPECT_EQ(0x550cU, fde->cfa_instructions_end);
+  EXPECT_EQ(0x4300U, fde->pc_start);
+  EXPECT_EQ(0x4600U, fde->pc_end);
+  EXPECT_EQ(0U, fde->lsda_address);
+}
+
+TYPED_TEST_P(DwarfDebugFrameTest, GetFdeFromOffset32_lsda_address) {
+  SetCie32(&this->memory_, 0x5000, 0xfc,
+           std::vector<uint8_t>{/* version */ 1,
+                                /* augment string */ 'z', 'L', '\0',
+                                /* address size */ 8,
+                                /* code alignment factor */ 16,
+                                /* data alignment factor */ 32,
+                                /* return address register */ 10,
+                                /* augment length */ 0x2,
+                                /* L data */ DW_EH_PE_udata2});
+
+  std::vector<uint8_t> data{/* augment length */ 0x80, 0x3,
+                            /* lsda address */ 0x20, 0x45};
+  SetFde32(&this->memory_, 0x5200, 0x300, 0x5000, 0x4300, 0x300, 0, &data);
+
+  const DwarfFde* fde = this->debug_frame_->GetFdeFromOffset(0x5200);
+  ASSERT_TRUE(fde != nullptr);
+  ASSERT_TRUE(fde->cie != nullptr);
+  EXPECT_EQ(1U, fde->cie->version);
+  EXPECT_EQ(0x5000U, fde->cie_offset);
+  EXPECT_EQ(0x5392U, fde->cfa_instructions_offset);
+  EXPECT_EQ(0x5504U, fde->cfa_instructions_end);
+  EXPECT_EQ(0x4300U, fde->pc_start);
+  EXPECT_EQ(0x4600U, fde->pc_end);
+  EXPECT_EQ(0x4520U, fde->lsda_address);
+}
+
+TYPED_TEST_P(DwarfDebugFrameTest, GetFdeFromOffset64_lsda_address) {
+  SetCie64(&this->memory_, 0x5000, 0xfc,
+           std::vector<uint8_t>{/* version */ 1,
+                                /* augment string */ 'z', 'L', '\0',
+                                /* address size */ 8,
+                                /* code alignment factor */ 16,
+                                /* data alignment factor */ 32,
+                                /* return address register */ 10,
+                                /* augment length */ 0x2,
+                                /* L data */ DW_EH_PE_udata2});
+
+  std::vector<uint8_t> data{/* augment length */ 0x80, 0x3,
+                            /* lsda address */ 0x20, 0x45};
+  SetFde64(&this->memory_, 0x5200, 0x300, 0x5000, 0x4300, 0x300, 0, &data);
+
+  const DwarfFde* fde = this->debug_frame_->GetFdeFromOffset(0x5200);
+  ASSERT_TRUE(fde != nullptr);
+  ASSERT_TRUE(fde->cie != nullptr);
+  EXPECT_EQ(1U, fde->cie->version);
+  EXPECT_EQ(0x5000U, fde->cie_offset);
+  EXPECT_EQ(0x53a6U, fde->cfa_instructions_offset);
+  EXPECT_EQ(0x550cU, fde->cfa_instructions_end);
+  EXPECT_EQ(0x4300U, fde->pc_start);
+  EXPECT_EQ(0x4600U, fde->pc_end);
+  EXPECT_EQ(0x4520U, fde->lsda_address);
+}
+
+TYPED_TEST_P(DwarfDebugFrameTest, GetFdeFromPc_interleaved) {
+  SetCie32(&this->memory_, 0x5000, 0xfc, std::vector<uint8_t>{1, '\0', 0, 0, 1});
+
+  // FDE 0 (0x100 - 0x200)
+  SetFde32(&this->memory_, 0x5100, 0xfc, 0, 0x100, 0x100);
+  // FDE 1 (0x300 - 0x500)
+  SetFde32(&this->memory_, 0x5200, 0xfc, 0, 0x300, 0x200);
+  // FDE 2 (0x700 - 0x800)
+  SetFde32(&this->memory_, 0x5300, 0xfc, 0, 0x700, 0x100);
+  // FDE 3 (0xa00 - 0xb00)
+  SetFde32(&this->memory_, 0x5400, 0xfc, 0, 0xa00, 0x100);
+  // FDE 4 (0x100 - 0xb00)
+  SetFde32(&this->memory_, 0x5500, 0xfc, 0, 0x150, 0xa00);
+  // FDE 5 (0x0 - 0x50)
+  SetFde32(&this->memory_, 0x5600, 0xfc, 0, 0, 0x50);
+
+  this->debug_frame_->Init(0x5000, 0x700, 0);
+
+  // Force reading all entries so no entries are found.
+  const DwarfFde* fde = this->debug_frame_->GetFdeFromPc(0xfffff);
+  ASSERT_TRUE(fde == nullptr);
+
+  //   0x0   - 0x50   FDE 5
+  fde = this->debug_frame_->GetFdeFromPc(0x10);
+  ASSERT_TRUE(fde != nullptr);
+  EXPECT_EQ(0U, fde->pc_start);
+  EXPECT_EQ(0x50U, fde->pc_end);
+
+  //   0x100 - 0x200  FDE 0
+  fde = this->debug_frame_->GetFdeFromPc(0x170);
+  ASSERT_TRUE(fde != nullptr);
+  EXPECT_EQ(0x100U, fde->pc_start);
+  EXPECT_EQ(0x200U, fde->pc_end);
+
+  //   0x200 - 0x300  FDE 4
+  fde = this->debug_frame_->GetFdeFromPc(0x210);
+  ASSERT_TRUE(fde != nullptr);
+  EXPECT_EQ(0x150U, fde->pc_start);
+  EXPECT_EQ(0xb50U, fde->pc_end);
+
+  //   0x300 - 0x500  FDE 1
+  fde = this->debug_frame_->GetFdeFromPc(0x310);
+  ASSERT_TRUE(fde != nullptr);
+  EXPECT_EQ(0x300U, fde->pc_start);
+  EXPECT_EQ(0x500U, fde->pc_end);
+
+  //   0x700 - 0x800  FDE 2
+  fde = this->debug_frame_->GetFdeFromPc(0x790);
+  ASSERT_TRUE(fde != nullptr);
+  EXPECT_EQ(0x700U, fde->pc_start);
+  EXPECT_EQ(0x800U, fde->pc_end);
+
+  //   0x800 - 0x900  FDE 4
+  fde = this->debug_frame_->GetFdeFromPc(0x850);
+  ASSERT_TRUE(fde != nullptr);
+  EXPECT_EQ(0x150U, fde->pc_start);
+  EXPECT_EQ(0xb50U, fde->pc_end);
+
+  //   0xa00 - 0xb00  FDE 3
+  fde = this->debug_frame_->GetFdeFromPc(0xa35);
+  ASSERT_TRUE(fde != nullptr);
+  EXPECT_EQ(0xa00U, fde->pc_start);
+  EXPECT_EQ(0xb00U, fde->pc_end);
+
+  //   0xb00 - 0xb50  FDE 4
+  fde = this->debug_frame_->GetFdeFromPc(0xb20);
+  ASSERT_TRUE(fde != nullptr);
+  EXPECT_EQ(0x150U, fde->pc_start);
+  EXPECT_EQ(0xb50U, fde->pc_end);
+}
+
+REGISTER_TYPED_TEST_CASE_P(
+    DwarfDebugFrameTest, GetFdes32, GetFdes32_after_GetFdeFromPc, GetFdes32_not_in_section,
+    GetFdeFromPc32, GetFdeFromPc32_reverse, GetFdeFromPc32_not_in_section, GetFdes64,
+    GetFdes64_after_GetFdeFromPc, GetFdes64_not_in_section, GetFdeFromPc64, GetFdeFromPc64_reverse,
+    GetFdeFromPc64_not_in_section, GetCieFde32, GetCieFde64, GetCieFromOffset32_cie_cached,
+    GetCieFromOffset64_cie_cached, GetCieFromOffset32_version1, GetCieFromOffset64_version1,
+    GetCieFromOffset32_version3, GetCieFromOffset64_version3, GetCieFromOffset32_version4,
+    GetCieFromOffset64_version4, GetCieFromOffset_version_invalid, GetCieFromOffset32_augment,
+    GetCieFromOffset64_augment, GetFdeFromOffset32_augment, GetFdeFromOffset64_augment,
+    GetFdeFromOffset32_lsda_address, GetFdeFromOffset64_lsda_address, GetFdeFromPc_interleaved);
 
 typedef ::testing::Types<uint32_t, uint64_t> DwarfDebugFrameTestTypes;
 INSTANTIATE_TYPED_TEST_CASE_P(, DwarfDebugFrameTest, DwarfDebugFrameTestTypes);
+
+}  // namespace unwindstack
diff --git a/libunwindstack/tests/DwarfEhFrameTest.cpp b/libunwindstack/tests/DwarfEhFrameTest.cpp
index 4e538d4..9cac6e8 100644
--- a/libunwindstack/tests/DwarfEhFrameTest.cpp
+++ b/libunwindstack/tests/DwarfEhFrameTest.cpp
@@ -16,394 +16,118 @@
 
 #include <stdint.h>
 
-#include <gmock/gmock.h>
 #include <gtest/gtest.h>
 
+#include <unwindstack/DwarfError.h>
+
 #include "DwarfEhFrame.h"
 #include "DwarfEncoding.h"
 
 #include "LogFake.h"
 #include "MemoryFake.h"
-#include "RegsFake.h"
 
-template <typename TypeParam>
-class MockDwarfEhFrame : public DwarfEhFrame<TypeParam> {
- public:
-  MockDwarfEhFrame(Memory* memory) : DwarfEhFrame<TypeParam>(memory) {}
-  ~MockDwarfEhFrame() = default;
-
-  void TestSetTableEncoding(uint8_t encoding) { this->table_encoding_ = encoding; }
-  void TestSetEntriesOffset(uint64_t offset) { this->entries_offset_ = offset; }
-  void TestSetEntriesEnd(uint64_t end) { this->entries_end_ = end; }
-  void TestSetEntriesDataOffset(uint64_t offset) { this->entries_data_offset_ = offset; }
-  void TestSetCurEntriesOffset(uint64_t offset) { this->cur_entries_offset_ = offset; }
-  void TestSetTableEntrySize(size_t size) { this->table_entry_size_ = size; }
-
-  void TestSetFdeCount(uint64_t count) { this->fde_count_ = count; }
-  void TestSetFdeInfo(uint64_t index, const typename DwarfEhFrame<TypeParam>::FdeInfo& info) {
-    this->fde_info_[index] = info;
-  }
-
-  uint8_t TestGetVersion() { return this->version_; }
-  uint8_t TestGetPtrEncoding() { return this->ptr_encoding_; }
-  uint64_t TestGetPtrOffset() { return this->ptr_offset_; }
-  uint8_t TestGetTableEncoding() { return this->table_encoding_; }
-  uint64_t TestGetTableEntrySize() { return this->table_entry_size_; }
-  uint64_t TestGetFdeCount() { return this->fde_count_; }
-  uint64_t TestGetEntriesOffset() { return this->entries_offset_; }
-  uint64_t TestGetEntriesEnd() { return this->entries_end_; }
-  uint64_t TestGetEntriesDataOffset() { return this->entries_data_offset_; }
-  uint64_t TestGetCurEntriesOffset() { return this->cur_entries_offset_; }
-};
+namespace unwindstack {
 
 template <typename TypeParam>
 class DwarfEhFrameTest : public ::testing::Test {
  protected:
   void SetUp() override {
     memory_.Clear();
-    eh_frame_ = new MockDwarfEhFrame<TypeParam>(&memory_);
+    eh_frame_ = new DwarfEhFrame<TypeParam>(&memory_);
     ResetLogs();
   }
 
   void TearDown() override { delete eh_frame_; }
 
   MemoryFake memory_;
-  MockDwarfEhFrame<TypeParam>* eh_frame_ = nullptr;
+  DwarfEhFrame<TypeParam>* eh_frame_ = nullptr;
 };
 TYPED_TEST_CASE_P(DwarfEhFrameTest);
 
 // NOTE: All test class variables need to be referenced as this->.
 
-TYPED_TEST_P(DwarfEhFrameTest, Init) {
-  this->memory_.SetMemory(
-      0x1000, std::vector<uint8_t>{0x1, DW_EH_PE_udata2, DW_EH_PE_udata4, DW_EH_PE_sdata4});
-  this->memory_.SetData16(0x1004, 0x500);
-  this->memory_.SetData32(0x1006, 126);
+// Only verify different cie/fde format. All other DwarfSection corner
+// cases are tested in DwarfDebugFrameTest.cpp.
 
-  ASSERT_TRUE(this->eh_frame_->Init(0x1000, 0x100));
-  EXPECT_EQ(1U, this->eh_frame_->TestGetVersion());
-  EXPECT_EQ(DW_EH_PE_udata2, this->eh_frame_->TestGetPtrEncoding());
-  EXPECT_EQ(DW_EH_PE_sdata4, this->eh_frame_->TestGetTableEncoding());
-  EXPECT_EQ(4U, this->eh_frame_->TestGetTableEntrySize());
-  EXPECT_EQ(126U, this->eh_frame_->TestGetFdeCount());
-  EXPECT_EQ(0x500U, this->eh_frame_->TestGetPtrOffset());
-  EXPECT_EQ(0x100aU, this->eh_frame_->TestGetEntriesOffset());
-  EXPECT_EQ(0x1100U, this->eh_frame_->TestGetEntriesEnd());
-  EXPECT_EQ(0x1000U, this->eh_frame_->TestGetEntriesDataOffset());
-  EXPECT_EQ(0x100aU, this->eh_frame_->TestGetCurEntriesOffset());
-
-  // Verify an unexpected version will cause a fail.
-  this->memory_.SetData8(0x1000, 0);
-  ASSERT_FALSE(this->eh_frame_->Init(0x1000, 0x100));
-  ASSERT_EQ(DWARF_ERROR_UNSUPPORTED_VERSION, this->eh_frame_->last_error());
-  this->memory_.SetData8(0x1000, 2);
-  ASSERT_FALSE(this->eh_frame_->Init(0x1000, 0x100));
-  ASSERT_EQ(DWARF_ERROR_UNSUPPORTED_VERSION, this->eh_frame_->last_error());
-}
-
-TYPED_TEST_P(DwarfEhFrameTest, GetFdeInfoFromIndex_expect_cache_fail) {
-  this->eh_frame_->TestSetTableEntrySize(0x10);
-  this->eh_frame_->TestSetTableEncoding(DW_EH_PE_udata4);
-  ASSERT_TRUE(this->eh_frame_->GetFdeInfoFromIndex(0) == nullptr);
-  ASSERT_EQ(DWARF_ERROR_MEMORY_INVALID, this->eh_frame_->last_error());
-  ASSERT_TRUE(this->eh_frame_->GetFdeInfoFromIndex(0) == nullptr);
-  ASSERT_EQ(DWARF_ERROR_MEMORY_INVALID, this->eh_frame_->last_error());
-}
-
-TYPED_TEST_P(DwarfEhFrameTest, GetFdeInfoFromIndex_read_pcrel) {
-  this->eh_frame_->TestSetTableEncoding(DW_EH_PE_pcrel | DW_EH_PE_udata4);
-  this->eh_frame_->TestSetEntriesOffset(0x1000);
-  this->eh_frame_->TestSetEntriesDataOffset(0x3000);
-  this->eh_frame_->TestSetTableEntrySize(0x10);
-
-  this->memory_.SetData32(0x1040, 0x340);
-  this->memory_.SetData32(0x1044, 0x500);
-
-  auto info = this->eh_frame_->GetFdeInfoFromIndex(2);
-  ASSERT_TRUE(info != nullptr);
-  EXPECT_EQ(0x1380U, info->pc);
-  EXPECT_EQ(0x1540U, info->offset);
-}
-
-TYPED_TEST_P(DwarfEhFrameTest, GetFdeInfoFromIndex_read_datarel) {
-  this->eh_frame_->TestSetTableEncoding(DW_EH_PE_datarel | DW_EH_PE_udata4);
-  this->eh_frame_->TestSetEntriesOffset(0x1000);
-  this->eh_frame_->TestSetEntriesDataOffset(0x3000);
-  this->eh_frame_->TestSetTableEntrySize(0x10);
-
-  this->memory_.SetData32(0x1040, 0x340);
-  this->memory_.SetData32(0x1044, 0x500);
-
-  auto info = this->eh_frame_->GetFdeInfoFromIndex(2);
-  ASSERT_TRUE(info != nullptr);
-  EXPECT_EQ(0x3340U, info->pc);
-  EXPECT_EQ(0x3500U, info->offset);
-}
-
-TYPED_TEST_P(DwarfEhFrameTest, GetFdeInfoFromIndex_cached) {
-  this->eh_frame_->TestSetTableEncoding(DW_EH_PE_udata4);
-  this->eh_frame_->TestSetEntriesOffset(0x1000);
-  this->eh_frame_->TestSetTableEntrySize(0x10);
-
-  this->memory_.SetData32(0x1040, 0x340);
-  this->memory_.SetData32(0x1044, 0x500);
-
-  auto info = this->eh_frame_->GetFdeInfoFromIndex(2);
-  ASSERT_TRUE(info != nullptr);
-  EXPECT_EQ(0x340U, info->pc);
-  EXPECT_EQ(0x500U, info->offset);
-
-  // Clear the memory so that this will fail if it doesn't read cached data.
-  this->memory_.Clear();
-
-  info = this->eh_frame_->GetFdeInfoFromIndex(2);
-  ASSERT_TRUE(info != nullptr);
-  EXPECT_EQ(0x340U, info->pc);
-  EXPECT_EQ(0x500U, info->offset);
-}
-
-TYPED_TEST_P(DwarfEhFrameTest, GetFdeOffsetBinary_verify) {
-  this->eh_frame_->TestSetTableEntrySize(0x10);
-  this->eh_frame_->TestSetFdeCount(10);
-
-  typename DwarfEhFrame<TypeParam>::FdeInfo info;
-  for (size_t i = 0; i < 10; i++) {
-    info.pc = 0x1000 * (i + 1);
-    info.offset = 0x5000 + i * 0x20;
-    this->eh_frame_->TestSetFdeInfo(i, info);
-  }
-
-  uint64_t fde_offset;
-  EXPECT_FALSE(this->eh_frame_->GetFdeOffsetBinary(0x100, &fde_offset, 10));
-  // Not an error, just not found.
-  ASSERT_EQ(DWARF_ERROR_NONE, this->eh_frame_->last_error());
-  // Even number of elements.
-  for (size_t i = 0; i < 10; i++) {
-    TypeParam pc = 0x1000 * (i + 1);
-    EXPECT_TRUE(this->eh_frame_->GetFdeOffsetBinary(pc, &fde_offset, 10)) << "Failed at index " << i;
-    EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i;
-    EXPECT_TRUE(this->eh_frame_->GetFdeOffsetBinary(pc + 1, &fde_offset, 10)) << "Failed at index "
-                                                                              << i;
-    EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i;
-    EXPECT_TRUE(this->eh_frame_->GetFdeOffsetBinary(pc + 0xfff, &fde_offset, 10))
-        << "Failed at index " << i;
-    EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i;
-  }
-  // Odd number of elements.
-  for (size_t i = 0; i < 9; i++) {
-    TypeParam pc = 0x1000 * (i + 1);
-    EXPECT_TRUE(this->eh_frame_->GetFdeOffsetBinary(pc, &fde_offset, 9)) << "Failed at index " << i;
-    EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i;
-    EXPECT_TRUE(this->eh_frame_->GetFdeOffsetBinary(pc + 1, &fde_offset, 9)) << "Failed at index "
-                                                                             << i;
-    EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i;
-    EXPECT_TRUE(this->eh_frame_->GetFdeOffsetBinary(pc + 0xfff, &fde_offset, 9))
-        << "Failed at index " << i;
-    EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i;
-  }
-}
-
-TYPED_TEST_P(DwarfEhFrameTest, GetFdeOffsetSequential) {
-  this->eh_frame_->TestSetFdeCount(10);
-  this->eh_frame_->TestSetEntriesDataOffset(0x100);
-  this->eh_frame_->TestSetEntriesEnd(0x2000);
-  this->eh_frame_->TestSetTableEncoding(DW_EH_PE_udata4);
-
-  this->memory_.SetData32(0x1040, 0x340);
-  this->memory_.SetData32(0x1044, 0x500);
-
-  this->memory_.SetData32(0x1048, 0x440);
-  this->memory_.SetData32(0x104c, 0x600);
-
-  // Verify that if entries is zero, that it fails.
-  uint64_t fde_offset;
-  ASSERT_FALSE(this->eh_frame_->GetFdeOffsetSequential(0x340, &fde_offset));
-  this->eh_frame_->TestSetCurEntriesOffset(0x1040);
-
-  ASSERT_TRUE(this->eh_frame_->GetFdeOffsetSequential(0x340, &fde_offset));
-  EXPECT_EQ(0x500U, fde_offset);
-
-  ASSERT_TRUE(this->eh_frame_->GetFdeOffsetSequential(0x440, &fde_offset));
-  EXPECT_EQ(0x600U, fde_offset);
-
-  // Expect that the data is cached so no more memory reads will occur.
-  this->memory_.Clear();
-  ASSERT_TRUE(this->eh_frame_->GetFdeOffsetSequential(0x440, &fde_offset));
-  EXPECT_EQ(0x600U, fde_offset);
-}
-
-TYPED_TEST_P(DwarfEhFrameTest, GetFdeOffsetSequential_last_element) {
-  this->eh_frame_->TestSetFdeCount(2);
-  this->eh_frame_->TestSetEntriesDataOffset(0x100);
-  this->eh_frame_->TestSetEntriesEnd(0x2000);
-  this->eh_frame_->TestSetTableEncoding(DW_EH_PE_udata4);
-  this->eh_frame_->TestSetCurEntriesOffset(0x1040);
-
-  this->memory_.SetData32(0x1040, 0x340);
-  this->memory_.SetData32(0x1044, 0x500);
-
-  this->memory_.SetData32(0x1048, 0x440);
-  this->memory_.SetData32(0x104c, 0x600);
-
-  uint64_t fde_offset;
-  ASSERT_TRUE(this->eh_frame_->GetFdeOffsetSequential(0x540, &fde_offset));
-  EXPECT_EQ(0x600U, fde_offset);
-}
-
-TYPED_TEST_P(DwarfEhFrameTest, GetFdeOffsetSequential_end_check) {
-  this->eh_frame_->TestSetFdeCount(2);
-  this->eh_frame_->TestSetEntriesDataOffset(0x100);
-  this->eh_frame_->TestSetEntriesEnd(0x1048);
-  this->eh_frame_->TestSetTableEncoding(DW_EH_PE_udata4);
-
-  this->memory_.SetData32(0x1040, 0x340);
-  this->memory_.SetData32(0x1044, 0x500);
-
-  this->memory_.SetData32(0x1048, 0x440);
-  this->memory_.SetData32(0x104c, 0x600);
-
-  uint64_t fde_offset;
-  ASSERT_FALSE(this->eh_frame_->GetFdeOffsetSequential(0x540, &fde_offset));
-  ASSERT_EQ(DWARF_ERROR_NONE, this->eh_frame_->last_error());
-}
-
-TYPED_TEST_P(DwarfEhFrameTest, GetFdeOffsetFromPc_fail_fde_count) {
-  this->eh_frame_->TestSetFdeCount(0);
-
-  uint64_t fde_offset;
-  ASSERT_FALSE(this->eh_frame_->GetFdeOffsetFromPc(0x100, &fde_offset));
-  ASSERT_EQ(DWARF_ERROR_NONE, this->eh_frame_->last_error());
-}
-
-TYPED_TEST_P(DwarfEhFrameTest, GetFdeOffsetFromPc_binary_search) {
-  this->eh_frame_->TestSetTableEntrySize(16);
-  this->eh_frame_->TestSetFdeCount(10);
-
-  typename DwarfEhFrame<TypeParam>::FdeInfo info;
-  info.pc = 0x550;
-  info.offset = 0x10500;
-  this->eh_frame_->TestSetFdeInfo(5, info);
-  info.pc = 0x750;
-  info.offset = 0x10700;
-  this->eh_frame_->TestSetFdeInfo(7, info);
-  info.pc = 0x850;
-  info.offset = 0x10800;
-  this->eh_frame_->TestSetFdeInfo(8, info);
-
-  uint64_t fde_offset;
-  ASSERT_TRUE(this->eh_frame_->GetFdeOffsetFromPc(0x800, &fde_offset));
-  EXPECT_EQ(0x10700U, fde_offset);
-}
-
-TYPED_TEST_P(DwarfEhFrameTest, GetFdeOffsetFromPc_sequential_search) {
-  this->eh_frame_->TestSetFdeCount(10);
-  this->eh_frame_->TestSetTableEntrySize(0);
-
-  typename DwarfEhFrame<TypeParam>::FdeInfo info;
-  info.pc = 0x50;
-  info.offset = 0x10000;
-  this->eh_frame_->TestSetFdeInfo(0, info);
-  info.pc = 0x150;
-  info.offset = 0x10100;
-  this->eh_frame_->TestSetFdeInfo(1, info);
-  info.pc = 0x250;
-  info.offset = 0x10200;
-  this->eh_frame_->TestSetFdeInfo(2, info);
-
-  uint64_t fde_offset;
-  ASSERT_TRUE(this->eh_frame_->GetFdeOffsetFromPc(0x200, &fde_offset));
-  EXPECT_EQ(0x10100U, fde_offset);
-}
-
-TYPED_TEST_P(DwarfEhFrameTest, GetCieFde32) {
+TYPED_TEST_P(DwarfEhFrameTest, GetFdeCieFromOffset32) {
   // CIE 32 information.
-  this->memory_.SetData32(0xf000, 0x100);
-  this->memory_.SetData32(0xf004, 0);
-  this->memory_.SetData8(0xf008, 0x1);
-  this->memory_.SetData8(0xf009, '\0');
-  this->memory_.SetData8(0xf00a, 4);
-  this->memory_.SetData8(0xf00b, 8);
-  this->memory_.SetData8(0xf00c, 0x20);
+  this->memory_.SetData32(0x5000, 0xfc);
+  // Indicates this is a cie for eh_frame.
+  this->memory_.SetData32(0x5004, 0);
+  this->memory_.SetMemory(0x5008, std::vector<uint8_t>{1, '\0', 16, 32, 1});
 
   // FDE 32 information.
-  this->memory_.SetData32(0x14000, 0x20);
-  this->memory_.SetData32(0x14004, 0x5004);
-  this->memory_.SetData32(0x14008, 0x9000);
-  this->memory_.SetData32(0x1400c, 0x100);
+  this->memory_.SetData32(0x5100, 0xfc);
+  this->memory_.SetData32(0x5104, 0x104);
+  this->memory_.SetData32(0x5108, 0x1500);
+  this->memory_.SetData32(0x510c, 0x200);
 
-  const DwarfFde* fde = this->eh_frame_->GetFdeFromOffset(0x14000);
+  const DwarfFde* fde = this->eh_frame_->GetFdeFromOffset(0x5100);
   ASSERT_TRUE(fde != nullptr);
-  EXPECT_EQ(0x14010U, fde->cfa_instructions_offset);
-  EXPECT_EQ(0x14024U, fde->cfa_instructions_end);
-  EXPECT_EQ(0x1d00cU, fde->pc_start);
-  EXPECT_EQ(0x1d10cU, fde->pc_end);
-  EXPECT_EQ(0xf000U, fde->cie_offset);
+  EXPECT_EQ(0x5000U, fde->cie_offset);
+  EXPECT_EQ(0x5110U, fde->cfa_instructions_offset);
+  EXPECT_EQ(0x5200U, fde->cfa_instructions_end);
+  EXPECT_EQ(0x6608U, fde->pc_start);
+  EXPECT_EQ(0x6808U, fde->pc_end);
   EXPECT_EQ(0U, fde->lsda_address);
 
-  ASSERT_TRUE(fde->cie != nullptr);
-  EXPECT_EQ(1U, fde->cie->version);
-  EXPECT_EQ(DW_EH_PE_sdata4, fde->cie->fde_address_encoding);
-  EXPECT_EQ(DW_EH_PE_omit, fde->cie->lsda_encoding);
-  EXPECT_EQ(0U, fde->cie->segment_size);
-  EXPECT_EQ(1U, fde->cie->augmentation_string.size());
-  EXPECT_EQ('\0', fde->cie->augmentation_string[0]);
-  EXPECT_EQ(0U, fde->cie->personality_handler);
-  EXPECT_EQ(0xf00dU, fde->cie->cfa_instructions_offset);
-  EXPECT_EQ(0xf104U, fde->cie->cfa_instructions_end);
-  EXPECT_EQ(4U, fde->cie->code_alignment_factor);
-  EXPECT_EQ(8, fde->cie->data_alignment_factor);
-  EXPECT_EQ(0x20U, fde->cie->return_address_register);
+  const DwarfCie* cie = fde->cie;
+  ASSERT_TRUE(cie != nullptr);
+  EXPECT_EQ(1U, cie->version);
+  EXPECT_EQ(DW_EH_PE_sdata4, cie->fde_address_encoding);
+  EXPECT_EQ(DW_EH_PE_omit, cie->lsda_encoding);
+  EXPECT_EQ(0U, cie->segment_size);
+  EXPECT_EQ('\0', cie->augmentation_string[0]);
+  EXPECT_EQ(0U, cie->personality_handler);
+  EXPECT_EQ(0x500dU, cie->cfa_instructions_offset);
+  EXPECT_EQ(0x5100U, cie->cfa_instructions_end);
+  EXPECT_EQ(16U, cie->code_alignment_factor);
+  EXPECT_EQ(32U, cie->data_alignment_factor);
+  EXPECT_EQ(1U, cie->return_address_register);
 }
 
-TYPED_TEST_P(DwarfEhFrameTest, GetCieFde64) {
+TYPED_TEST_P(DwarfEhFrameTest, GetFdeCieFromOffset64) {
   // CIE 64 information.
-  this->memory_.SetData32(0x6000, 0xffffffff);
-  this->memory_.SetData64(0x6004, 0x100);
-  this->memory_.SetData64(0x600c, 0);
-  this->memory_.SetData8(0x6014, 0x1);
-  this->memory_.SetData8(0x6015, '\0');
-  this->memory_.SetData8(0x6016, 4);
-  this->memory_.SetData8(0x6017, 8);
-  this->memory_.SetData8(0x6018, 0x20);
+  this->memory_.SetData32(0x5000, 0xffffffff);
+  this->memory_.SetData64(0x5004, 0xfc);
+  // Indicates this is a cie for eh_frame.
+  this->memory_.SetData64(0x500c, 0);
+  this->memory_.SetMemory(0x5014, std::vector<uint8_t>{1, '\0', 16, 32, 1});
 
   // FDE 64 information.
-  this->memory_.SetData32(0x8000, 0xffffffff);
-  this->memory_.SetData64(0x8004, 0x200);
-  this->memory_.SetData64(0x800c, 0x200c);
-  this->memory_.SetData64(0x8014, 0x5000);
-  this->memory_.SetData64(0x801c, 0x300);
+  this->memory_.SetData32(0x5100, 0xffffffff);
+  this->memory_.SetData64(0x5104, 0xfc);
+  this->memory_.SetData64(0x510c, 0x10c);
+  this->memory_.SetData64(0x5114, 0x1500);
+  this->memory_.SetData64(0x511c, 0x200);
 
-  const DwarfFde* fde = this->eh_frame_->GetFdeFromOffset(0x8000);
+  const DwarfFde* fde = this->eh_frame_->GetFdeFromOffset(0x5100);
   ASSERT_TRUE(fde != nullptr);
-  EXPECT_EQ(0x8024U, fde->cfa_instructions_offset);
-  EXPECT_EQ(0x820cU, fde->cfa_instructions_end);
-  EXPECT_EQ(0xd01cU, fde->pc_start);
-  EXPECT_EQ(0xd31cU, fde->pc_end);
-  EXPECT_EQ(0x6000U, fde->cie_offset);
+  EXPECT_EQ(0x5000U, fde->cie_offset);
+  EXPECT_EQ(0x5124U, fde->cfa_instructions_offset);
+  EXPECT_EQ(0x5208U, fde->cfa_instructions_end);
+  EXPECT_EQ(0x6618U, fde->pc_start);
+  EXPECT_EQ(0x6818U, fde->pc_end);
   EXPECT_EQ(0U, fde->lsda_address);
 
-  ASSERT_TRUE(fde->cie != nullptr);
-  EXPECT_EQ(1U, fde->cie->version);
-  EXPECT_EQ(DW_EH_PE_sdata8, fde->cie->fde_address_encoding);
-  EXPECT_EQ(DW_EH_PE_omit, fde->cie->lsda_encoding);
-  EXPECT_EQ(0U, fde->cie->segment_size);
-  EXPECT_EQ(1U, fde->cie->augmentation_string.size());
-  EXPECT_EQ('\0', fde->cie->augmentation_string[0]);
-  EXPECT_EQ(0U, fde->cie->personality_handler);
-  EXPECT_EQ(0x6019U, fde->cie->cfa_instructions_offset);
-  EXPECT_EQ(0x610cU, fde->cie->cfa_instructions_end);
-  EXPECT_EQ(4U, fde->cie->code_alignment_factor);
-  EXPECT_EQ(8, fde->cie->data_alignment_factor);
-  EXPECT_EQ(0x20U, fde->cie->return_address_register);
+  const DwarfCie* cie = fde->cie;
+  ASSERT_TRUE(cie != nullptr);
+  EXPECT_EQ(1U, cie->version);
+  EXPECT_EQ(DW_EH_PE_sdata8, cie->fde_address_encoding);
+  EXPECT_EQ(DW_EH_PE_omit, cie->lsda_encoding);
+  EXPECT_EQ(0U, cie->segment_size);
+  EXPECT_EQ('\0', cie->augmentation_string[0]);
+  EXPECT_EQ(0U, cie->personality_handler);
+  EXPECT_EQ(0x5019U, cie->cfa_instructions_offset);
+  EXPECT_EQ(0x5108U, cie->cfa_instructions_end);
+  EXPECT_EQ(16U, cie->code_alignment_factor);
+  EXPECT_EQ(32U, cie->data_alignment_factor);
+  EXPECT_EQ(1U, cie->return_address_register);
 }
 
-REGISTER_TYPED_TEST_CASE_P(DwarfEhFrameTest, Init, GetFdeInfoFromIndex_expect_cache_fail,
-                           GetFdeInfoFromIndex_read_pcrel, GetFdeInfoFromIndex_read_datarel,
-                           GetFdeInfoFromIndex_cached, GetFdeOffsetBinary_verify,
-                           GetFdeOffsetSequential, GetFdeOffsetSequential_last_element,
-                           GetFdeOffsetSequential_end_check, GetFdeOffsetFromPc_fail_fde_count,
-                           GetFdeOffsetFromPc_binary_search, GetFdeOffsetFromPc_sequential_search,
-                           GetCieFde32, GetCieFde64);
+REGISTER_TYPED_TEST_CASE_P(DwarfEhFrameTest, GetFdeCieFromOffset32, GetFdeCieFromOffset64);
 
 typedef ::testing::Types<uint32_t, uint64_t> DwarfEhFrameTestTypes;
 INSTANTIATE_TYPED_TEST_CASE_P(, DwarfEhFrameTest, DwarfEhFrameTestTypes);
+
+}  // namespace unwindstack
diff --git a/libunwindstack/tests/DwarfEhFrameWithHdrTest.cpp b/libunwindstack/tests/DwarfEhFrameWithHdrTest.cpp
new file mode 100644
index 0000000..910ae36
--- /dev/null
+++ b/libunwindstack/tests/DwarfEhFrameWithHdrTest.cpp
@@ -0,0 +1,538 @@
+/*
+ * Copyright (C) 2016 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 <stdint.h>
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <unwindstack/DwarfError.h>
+
+#include "DwarfEhFrameWithHdr.h"
+#include "DwarfEncoding.h"
+
+#include "LogFake.h"
+#include "MemoryFake.h"
+
+namespace unwindstack {
+
+template <typename TypeParam>
+class TestDwarfEhFrameWithHdr : public DwarfEhFrameWithHdr<TypeParam> {
+ public:
+  TestDwarfEhFrameWithHdr(Memory* memory) : DwarfEhFrameWithHdr<TypeParam>(memory) {}
+  ~TestDwarfEhFrameWithHdr() = default;
+
+  void TestSetTableEncoding(uint8_t encoding) { this->table_encoding_ = encoding; }
+  void TestSetEntriesOffset(uint64_t offset) { this->entries_offset_ = offset; }
+  void TestSetEntriesEnd(uint64_t end) { this->entries_end_ = end; }
+  void TestSetEntriesDataOffset(uint64_t offset) { this->entries_data_offset_ = offset; }
+  void TestSetCurEntriesOffset(uint64_t offset) { this->cur_entries_offset_ = offset; }
+  void TestSetTableEntrySize(size_t size) { this->table_entry_size_ = size; }
+
+  void TestSetFdeCount(uint64_t count) { this->fde_count_ = count; }
+  void TestSetFdeInfo(uint64_t index, const typename DwarfEhFrameWithHdr<TypeParam>::FdeInfo& info) {
+    this->fde_info_[index] = info;
+  }
+
+  uint8_t TestGetVersion() { return this->version_; }
+  uint8_t TestGetPtrEncoding() { return this->ptr_encoding_; }
+  uint64_t TestGetPtrOffset() { return this->ptr_offset_; }
+  uint8_t TestGetTableEncoding() { return this->table_encoding_; }
+  uint64_t TestGetTableEntrySize() { return this->table_entry_size_; }
+  uint64_t TestGetFdeCount() { return this->fde_count_; }
+  uint64_t TestGetEntriesOffset() { return this->entries_offset_; }
+  uint64_t TestGetEntriesEnd() { return this->entries_end_; }
+  uint64_t TestGetEntriesDataOffset() { return this->entries_data_offset_; }
+  uint64_t TestGetCurEntriesOffset() { return this->cur_entries_offset_; }
+};
+
+template <typename TypeParam>
+class DwarfEhFrameWithHdrTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    memory_.Clear();
+    eh_frame_ = new TestDwarfEhFrameWithHdr<TypeParam>(&memory_);
+    ResetLogs();
+  }
+
+  void TearDown() override { delete eh_frame_; }
+
+  MemoryFake memory_;
+  TestDwarfEhFrameWithHdr<TypeParam>* eh_frame_ = nullptr;
+};
+TYPED_TEST_CASE_P(DwarfEhFrameWithHdrTest);
+
+// NOTE: All test class variables need to be referenced as this->.
+
+TYPED_TEST_P(DwarfEhFrameWithHdrTest, Init) {
+  this->memory_.SetMemory(
+      0x1000, std::vector<uint8_t>{0x1, DW_EH_PE_udata2, DW_EH_PE_udata4, DW_EH_PE_sdata4});
+  this->memory_.SetData16(0x1004, 0x500);
+  this->memory_.SetData32(0x1006, 126);
+
+  ASSERT_TRUE(this->eh_frame_->Init(0x1000, 0x100, 0));
+  EXPECT_EQ(1U, this->eh_frame_->TestGetVersion());
+  EXPECT_EQ(DW_EH_PE_udata2, this->eh_frame_->TestGetPtrEncoding());
+  EXPECT_EQ(DW_EH_PE_sdata4, this->eh_frame_->TestGetTableEncoding());
+  EXPECT_EQ(4U, this->eh_frame_->TestGetTableEntrySize());
+  EXPECT_EQ(126U, this->eh_frame_->TestGetFdeCount());
+  EXPECT_EQ(0x500U, this->eh_frame_->TestGetPtrOffset());
+  EXPECT_EQ(0x100aU, this->eh_frame_->TestGetEntriesOffset());
+  EXPECT_EQ(0x1100U, this->eh_frame_->TestGetEntriesEnd());
+  EXPECT_EQ(0x1000U, this->eh_frame_->TestGetEntriesDataOffset());
+  EXPECT_EQ(0x100aU, this->eh_frame_->TestGetCurEntriesOffset());
+
+  // Verify a zero fde count fails to init.
+  this->memory_.SetData32(0x1006, 0);
+  ASSERT_FALSE(this->eh_frame_->Init(0x1000, 0x100, 0));
+  ASSERT_EQ(DWARF_ERROR_NO_FDES, this->eh_frame_->LastErrorCode());
+
+  // Verify an unexpected version will cause a fail.
+  this->memory_.SetData32(0x1006, 126);
+  this->memory_.SetData8(0x1000, 0);
+  ASSERT_FALSE(this->eh_frame_->Init(0x1000, 0x100, 0));
+  ASSERT_EQ(DWARF_ERROR_UNSUPPORTED_VERSION, this->eh_frame_->LastErrorCode());
+  this->memory_.SetData8(0x1000, 2);
+  ASSERT_FALSE(this->eh_frame_->Init(0x1000, 0x100, 0));
+  ASSERT_EQ(DWARF_ERROR_UNSUPPORTED_VERSION, this->eh_frame_->LastErrorCode());
+}
+
+TYPED_TEST_P(DwarfEhFrameWithHdrTest, Init_non_zero_load_bias) {
+  this->memory_.SetMemory(0x1000, std::vector<uint8_t>{0x1, DW_EH_PE_udata2, DW_EH_PE_udata4,
+                                                       DW_EH_PE_pcrel | DW_EH_PE_sdata4});
+  this->memory_.SetData16(0x1004, 0x500);
+  this->memory_.SetData32(0x1006, 1);
+  this->memory_.SetData32(0x100a, 0x2500);
+  this->memory_.SetData32(0x100e, 0x1400);
+
+  // CIE 32 information.
+  this->memory_.SetData32(0x1300, 0xfc);
+  this->memory_.SetData32(0x1304, 0);
+  this->memory_.SetMemory(0x1308, std::vector<uint8_t>{1, 'z', 'R', '\0', 0, 0, 0, 0, 0x1b});
+
+  // FDE 32 information.
+  this->memory_.SetData32(0x1400, 0xfc);
+  this->memory_.SetData32(0x1404, 0x104);
+  this->memory_.SetData32(0x1408, 0x10f8);
+  this->memory_.SetData32(0x140c, 0x200);
+  this->memory_.SetData16(0x1410, 0);
+
+  ASSERT_TRUE(this->eh_frame_->Init(0x1000, 0x100, 0x2000));
+  EXPECT_EQ(1U, this->eh_frame_->TestGetVersion());
+  EXPECT_EQ(DW_EH_PE_udata2, this->eh_frame_->TestGetPtrEncoding());
+  EXPECT_EQ(0x1b, this->eh_frame_->TestGetTableEncoding());
+  EXPECT_EQ(4U, this->eh_frame_->TestGetTableEntrySize());
+  EXPECT_EQ(1U, this->eh_frame_->TestGetFdeCount());
+  EXPECT_EQ(0x500U, this->eh_frame_->TestGetPtrOffset());
+  EXPECT_EQ(0x100aU, this->eh_frame_->TestGetEntriesOffset());
+  EXPECT_EQ(0x1100U, this->eh_frame_->TestGetEntriesEnd());
+  EXPECT_EQ(0x1000U, this->eh_frame_->TestGetEntriesDataOffset());
+  EXPECT_EQ(0x100aU, this->eh_frame_->TestGetCurEntriesOffset());
+
+  const DwarfFde* fde = this->eh_frame_->GetFdeFromPc(0x4600);
+  ASSERT_TRUE(fde != nullptr);
+  EXPECT_EQ(0x4500U, fde->pc_start);
+  EXPECT_EQ(0x4700U, fde->pc_end);
+}
+
+TYPED_TEST_P(DwarfEhFrameWithHdrTest, GetFdes) {
+  this->memory_.SetMemory(
+      0x1000, std::vector<uint8_t>{1, DW_EH_PE_udata2, DW_EH_PE_udata4, DW_EH_PE_sdata4});
+  this->memory_.SetData16(0x1004, 0x500);
+  this->memory_.SetData32(0x1006, 4);
+
+  // Header information.
+  this->memory_.SetData32(0x100a, 0x4600);
+  this->memory_.SetData32(0x100e, 0x1500);
+  this->memory_.SetData32(0x1012, 0x5500);
+  this->memory_.SetData32(0x1016, 0x1400);
+  this->memory_.SetData32(0x101a, 0x6800);
+  this->memory_.SetData32(0x101e, 0x1700);
+  this->memory_.SetData32(0x1022, 0x7700);
+  this->memory_.SetData32(0x1026, 0x1600);
+
+  // CIE 32 information.
+  this->memory_.SetData32(0x1300, 0xfc);
+  this->memory_.SetData32(0x1304, 0);
+  this->memory_.SetMemory(0x1308, std::vector<uint8_t>{1, '\0', 0, 0, 0});
+
+  // FDE 32 information.
+  // pc 0x5500 - 0x5700
+  this->memory_.SetData32(0x1400, 0xfc);
+  this->memory_.SetData32(0x1404, 0x104);
+  this->memory_.SetData32(0x1408, 0x40f8);
+  this->memory_.SetData32(0x140c, 0x200);
+
+  // pc 0x4600 - 0x4800
+  this->memory_.SetData32(0x1500, 0xfc);
+  this->memory_.SetData32(0x1504, 0x204);
+  this->memory_.SetData32(0x1508, 0x30f8);
+  this->memory_.SetData32(0x150c, 0x200);
+
+  // pc 0x7700 - 0x7900
+  this->memory_.SetData32(0x1600, 0xfc);
+  this->memory_.SetData32(0x1604, 0x304);
+  this->memory_.SetData32(0x1608, 0x60f8);
+  this->memory_.SetData32(0x160c, 0x200);
+
+  // pc 0x6800 - 0x6a00
+  this->memory_.SetData32(0x1700, 0xfc);
+  this->memory_.SetData32(0x1704, 0x404);
+  this->memory_.SetData32(0x1708, 0x50f8);
+  this->memory_.SetData32(0x170c, 0x200);
+
+  ASSERT_TRUE(this->eh_frame_->Init(0x1000, 0x100, 0));
+
+  std::vector<const DwarfFde*> fdes;
+  this->eh_frame_->GetFdes(&fdes);
+  ASSERT_EQ(4U, fdes.size());
+
+  EXPECT_EQ(0x4600U, fdes[0]->pc_start);
+  EXPECT_EQ(0x4800U, fdes[0]->pc_end);
+  EXPECT_EQ(0x5500U, fdes[1]->pc_start);
+  EXPECT_EQ(0x5700U, fdes[1]->pc_end);
+  EXPECT_EQ(0x6800U, fdes[2]->pc_start);
+  EXPECT_EQ(0x6a00U, fdes[2]->pc_end);
+  EXPECT_EQ(0x7700U, fdes[3]->pc_start);
+  EXPECT_EQ(0x7900U, fdes[3]->pc_end);
+}
+
+TYPED_TEST_P(DwarfEhFrameWithHdrTest, GetFdeInfoFromIndex_expect_cache_fail) {
+  this->eh_frame_->TestSetTableEntrySize(0x10);
+  this->eh_frame_->TestSetTableEncoding(DW_EH_PE_udata4);
+  this->eh_frame_->TestSetEntriesOffset(0x1000);
+
+  ASSERT_TRUE(this->eh_frame_->GetFdeInfoFromIndex(0) == nullptr);
+  ASSERT_EQ(DWARF_ERROR_MEMORY_INVALID, this->eh_frame_->LastErrorCode());
+  EXPECT_EQ(0x1000U, this->eh_frame_->LastErrorAddress());
+  ASSERT_TRUE(this->eh_frame_->GetFdeInfoFromIndex(0) == nullptr);
+  ASSERT_EQ(DWARF_ERROR_MEMORY_INVALID, this->eh_frame_->LastErrorCode());
+  EXPECT_EQ(0x1000U, this->eh_frame_->LastErrorAddress());
+}
+
+// We are assuming that pc rel, is really relative to the load_bias.
+TYPED_TEST_P(DwarfEhFrameWithHdrTest, GetFdeInfoFromIndex_read_pcrel) {
+  this->eh_frame_->TestSetTableEncoding(DW_EH_PE_pcrel | DW_EH_PE_udata4);
+  this->eh_frame_->TestSetEntriesOffset(0x1000);
+  this->eh_frame_->TestSetEntriesDataOffset(0x3000);
+  this->eh_frame_->TestSetTableEntrySize(0x10);
+
+  this->memory_.SetData32(0x1040, 0x340);
+  this->memory_.SetData32(0x1044, 0x500);
+
+  auto info = this->eh_frame_->GetFdeInfoFromIndex(2);
+  ASSERT_TRUE(info != nullptr);
+  EXPECT_EQ(0x340U, info->pc);
+  EXPECT_EQ(0x500U, info->offset);
+}
+
+TYPED_TEST_P(DwarfEhFrameWithHdrTest, GetFdeInfoFromIndex_read_datarel) {
+  this->eh_frame_->TestSetTableEncoding(DW_EH_PE_datarel | DW_EH_PE_udata4);
+  this->eh_frame_->TestSetEntriesOffset(0x1000);
+  this->eh_frame_->TestSetEntriesDataOffset(0x3000);
+  this->eh_frame_->TestSetTableEntrySize(0x10);
+
+  this->memory_.SetData32(0x1040, 0x340);
+  this->memory_.SetData32(0x1044, 0x500);
+
+  auto info = this->eh_frame_->GetFdeInfoFromIndex(2);
+  ASSERT_TRUE(info != nullptr);
+  EXPECT_EQ(0x3340U, info->pc);
+  EXPECT_EQ(0x3500U, info->offset);
+}
+
+TYPED_TEST_P(DwarfEhFrameWithHdrTest, GetFdeInfoFromIndex_cached) {
+  this->eh_frame_->TestSetTableEncoding(DW_EH_PE_udata4);
+  this->eh_frame_->TestSetEntriesOffset(0x1000);
+  this->eh_frame_->TestSetTableEntrySize(0x10);
+
+  this->memory_.SetData32(0x1040, 0x340);
+  this->memory_.SetData32(0x1044, 0x500);
+
+  auto info = this->eh_frame_->GetFdeInfoFromIndex(2);
+  ASSERT_TRUE(info != nullptr);
+  EXPECT_EQ(0x340U, info->pc);
+  EXPECT_EQ(0x500U, info->offset);
+
+  // Clear the memory so that this will fail if it doesn't read cached data.
+  this->memory_.Clear();
+
+  info = this->eh_frame_->GetFdeInfoFromIndex(2);
+  ASSERT_TRUE(info != nullptr);
+  EXPECT_EQ(0x340U, info->pc);
+  EXPECT_EQ(0x500U, info->offset);
+}
+
+TYPED_TEST_P(DwarfEhFrameWithHdrTest, GetFdeOffsetBinary_verify) {
+  this->eh_frame_->TestSetTableEntrySize(0x10);
+  this->eh_frame_->TestSetFdeCount(10);
+
+  typename DwarfEhFrameWithHdr<TypeParam>::FdeInfo info;
+  for (size_t i = 0; i < 10; i++) {
+    info.pc = 0x1000 * (i + 1);
+    info.offset = 0x5000 + i * 0x20;
+    this->eh_frame_->TestSetFdeInfo(i, info);
+  }
+
+  uint64_t fde_offset;
+  EXPECT_FALSE(this->eh_frame_->GetFdeOffsetBinary(0x100, &fde_offset, 10));
+  // Not an error, just not found.
+  ASSERT_EQ(DWARF_ERROR_NONE, this->eh_frame_->LastErrorCode());
+  // Even number of elements.
+  for (size_t i = 0; i < 10; i++) {
+    TypeParam pc = 0x1000 * (i + 1);
+    EXPECT_TRUE(this->eh_frame_->GetFdeOffsetBinary(pc, &fde_offset, 10)) << "Failed at index " << i;
+    EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i;
+    EXPECT_TRUE(this->eh_frame_->GetFdeOffsetBinary(pc + 1, &fde_offset, 10))
+        << "Failed at index " << i;
+    EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i;
+    EXPECT_TRUE(this->eh_frame_->GetFdeOffsetBinary(pc + 0xfff, &fde_offset, 10))
+        << "Failed at index " << i;
+    EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i;
+  }
+  // Odd number of elements.
+  for (size_t i = 0; i < 9; i++) {
+    TypeParam pc = 0x1000 * (i + 1);
+    EXPECT_TRUE(this->eh_frame_->GetFdeOffsetBinary(pc, &fde_offset, 9)) << "Failed at index " << i;
+    EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i;
+    EXPECT_TRUE(this->eh_frame_->GetFdeOffsetBinary(pc + 1, &fde_offset, 9))
+        << "Failed at index " << i;
+    EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i;
+    EXPECT_TRUE(this->eh_frame_->GetFdeOffsetBinary(pc + 0xfff, &fde_offset, 9))
+        << "Failed at index " << i;
+    EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i;
+  }
+}
+
+TYPED_TEST_P(DwarfEhFrameWithHdrTest, GetFdeOffsetBinary_index_fail) {
+  this->eh_frame_->TestSetTableEntrySize(0x10);
+  this->eh_frame_->TestSetFdeCount(10);
+
+  uint64_t fde_offset;
+  EXPECT_FALSE(this->eh_frame_->GetFdeOffsetBinary(0x1000, &fde_offset, 10));
+}
+
+TYPED_TEST_P(DwarfEhFrameWithHdrTest, GetFdeOffsetSequential) {
+  this->eh_frame_->TestSetFdeCount(10);
+  this->eh_frame_->TestSetEntriesDataOffset(0x100);
+  this->eh_frame_->TestSetEntriesEnd(0x2000);
+  this->eh_frame_->TestSetTableEncoding(DW_EH_PE_udata4);
+
+  this->memory_.SetData32(0x1040, 0x340);
+  this->memory_.SetData32(0x1044, 0x500);
+
+  this->memory_.SetData32(0x1048, 0x440);
+  this->memory_.SetData32(0x104c, 0x600);
+
+  // Verify that if entries is zero, that it fails.
+  uint64_t fde_offset;
+  ASSERT_FALSE(this->eh_frame_->GetFdeOffsetSequential(0x344, &fde_offset));
+  this->eh_frame_->TestSetCurEntriesOffset(0x1040);
+
+  ASSERT_TRUE(this->eh_frame_->GetFdeOffsetSequential(0x344, &fde_offset));
+  EXPECT_EQ(0x500U, fde_offset);
+
+  ASSERT_TRUE(this->eh_frame_->GetFdeOffsetSequential(0x444, &fde_offset));
+  EXPECT_EQ(0x600U, fde_offset);
+
+  // Expect that the data is cached so no more memory reads will occur.
+  this->memory_.Clear();
+  ASSERT_TRUE(this->eh_frame_->GetFdeOffsetSequential(0x444, &fde_offset));
+  EXPECT_EQ(0x600U, fde_offset);
+}
+
+TYPED_TEST_P(DwarfEhFrameWithHdrTest, GetFdeOffsetSequential_last_element) {
+  this->eh_frame_->TestSetFdeCount(2);
+  this->eh_frame_->TestSetEntriesDataOffset(0x100);
+  this->eh_frame_->TestSetEntriesEnd(0x2000);
+  this->eh_frame_->TestSetTableEncoding(DW_EH_PE_udata4);
+  this->eh_frame_->TestSetCurEntriesOffset(0x1040);
+
+  this->memory_.SetData32(0x1040, 0x340);
+  this->memory_.SetData32(0x1044, 0x500);
+
+  this->memory_.SetData32(0x1048, 0x440);
+  this->memory_.SetData32(0x104c, 0x600);
+
+  uint64_t fde_offset;
+  ASSERT_TRUE(this->eh_frame_->GetFdeOffsetSequential(0x540, &fde_offset));
+  EXPECT_EQ(0x600U, fde_offset);
+}
+
+TYPED_TEST_P(DwarfEhFrameWithHdrTest, GetFdeOffsetSequential_end_check) {
+  this->eh_frame_->TestSetFdeCount(2);
+  this->eh_frame_->TestSetEntriesDataOffset(0x100);
+  this->eh_frame_->TestSetEntriesEnd(0x1048);
+  this->eh_frame_->TestSetTableEncoding(DW_EH_PE_udata4);
+
+  this->memory_.SetData32(0x1040, 0x340);
+  this->memory_.SetData32(0x1044, 0x500);
+
+  this->memory_.SetData32(0x1048, 0x440);
+  this->memory_.SetData32(0x104c, 0x600);
+
+  uint64_t fde_offset;
+  ASSERT_FALSE(this->eh_frame_->GetFdeOffsetSequential(0x540, &fde_offset));
+  ASSERT_EQ(DWARF_ERROR_NONE, this->eh_frame_->LastErrorCode());
+}
+
+TYPED_TEST_P(DwarfEhFrameWithHdrTest, GetFdeOffsetFromPc_fail_fde_count) {
+  this->eh_frame_->TestSetFdeCount(0);
+
+  uint64_t fde_offset;
+  ASSERT_FALSE(this->eh_frame_->GetFdeOffsetFromPc(0x100, &fde_offset));
+  ASSERT_EQ(DWARF_ERROR_NONE, this->eh_frame_->LastErrorCode());
+}
+
+TYPED_TEST_P(DwarfEhFrameWithHdrTest, GetFdeOffsetFromPc_binary_search) {
+  this->eh_frame_->TestSetTableEntrySize(16);
+  this->eh_frame_->TestSetFdeCount(10);
+
+  typename DwarfEhFrameWithHdr<TypeParam>::FdeInfo info;
+  info.pc = 0x550;
+  info.offset = 0x10500;
+  this->eh_frame_->TestSetFdeInfo(5, info);
+  info.pc = 0x750;
+  info.offset = 0x10700;
+  this->eh_frame_->TestSetFdeInfo(7, info);
+  info.pc = 0x850;
+  info.offset = 0x10800;
+  this->eh_frame_->TestSetFdeInfo(8, info);
+
+  uint64_t fde_offset;
+  ASSERT_TRUE(this->eh_frame_->GetFdeOffsetFromPc(0x800, &fde_offset));
+  EXPECT_EQ(0x10700U, fde_offset);
+}
+
+TYPED_TEST_P(DwarfEhFrameWithHdrTest, GetFdeOffsetFromPc_sequential_search) {
+  this->eh_frame_->TestSetFdeCount(10);
+  this->eh_frame_->TestSetTableEntrySize(0);
+
+  typename DwarfEhFrameWithHdr<TypeParam>::FdeInfo info;
+  info.pc = 0x50;
+  info.offset = 0x10000;
+  this->eh_frame_->TestSetFdeInfo(0, info);
+  info.pc = 0x150;
+  info.offset = 0x10100;
+  this->eh_frame_->TestSetFdeInfo(1, info);
+  info.pc = 0x250;
+  info.offset = 0x10200;
+  this->eh_frame_->TestSetFdeInfo(2, info);
+
+  uint64_t fde_offset;
+  ASSERT_TRUE(this->eh_frame_->GetFdeOffsetFromPc(0x200, &fde_offset));
+  EXPECT_EQ(0x10100U, fde_offset);
+}
+
+TYPED_TEST_P(DwarfEhFrameWithHdrTest, GetCieFde32) {
+  // CIE 32 information.
+  this->memory_.SetData32(0xf000, 0x100);
+  this->memory_.SetData32(0xf004, 0);
+  this->memory_.SetMemory(0xf008, std::vector<uint8_t>{1, '\0', 4, 8, 0x20});
+
+  // FDE 32 information.
+  this->memory_.SetData32(0x14000, 0x20);
+  this->memory_.SetData32(0x14004, 0x5004);
+  this->memory_.SetData32(0x14008, 0x9000);
+  this->memory_.SetData32(0x1400c, 0x100);
+
+  const DwarfFde* fde = this->eh_frame_->GetFdeFromOffset(0x14000);
+  ASSERT_TRUE(fde != nullptr);
+  EXPECT_EQ(0x14010U, fde->cfa_instructions_offset);
+  EXPECT_EQ(0x14024U, fde->cfa_instructions_end);
+  EXPECT_EQ(0x1d008U, fde->pc_start);
+  EXPECT_EQ(0x1d108U, fde->pc_end);
+  EXPECT_EQ(0xf000U, fde->cie_offset);
+  EXPECT_EQ(0U, fde->lsda_address);
+
+  ASSERT_TRUE(fde->cie != nullptr);
+  EXPECT_EQ(1U, fde->cie->version);
+  EXPECT_EQ(DW_EH_PE_sdata4, fde->cie->fde_address_encoding);
+  EXPECT_EQ(DW_EH_PE_omit, fde->cie->lsda_encoding);
+  EXPECT_EQ(0U, fde->cie->segment_size);
+  EXPECT_EQ(1U, fde->cie->augmentation_string.size());
+  EXPECT_EQ('\0', fde->cie->augmentation_string[0]);
+  EXPECT_EQ(0U, fde->cie->personality_handler);
+  EXPECT_EQ(0xf00dU, fde->cie->cfa_instructions_offset);
+  EXPECT_EQ(0xf104U, fde->cie->cfa_instructions_end);
+  EXPECT_EQ(4U, fde->cie->code_alignment_factor);
+  EXPECT_EQ(8, fde->cie->data_alignment_factor);
+  EXPECT_EQ(0x20U, fde->cie->return_address_register);
+}
+
+TYPED_TEST_P(DwarfEhFrameWithHdrTest, GetCieFde64) {
+  // CIE 64 information.
+  this->memory_.SetData32(0x6000, 0xffffffff);
+  this->memory_.SetData64(0x6004, 0x100);
+  this->memory_.SetData64(0x600c, 0);
+  this->memory_.SetMemory(0x6014, std::vector<uint8_t>{1, '\0', 4, 8, 0x20});
+
+  // FDE 64 information.
+  this->memory_.SetData32(0x8000, 0xffffffff);
+  this->memory_.SetData64(0x8004, 0x200);
+  this->memory_.SetData64(0x800c, 0x200c);
+  this->memory_.SetData64(0x8014, 0x5000);
+  this->memory_.SetData64(0x801c, 0x300);
+
+  const DwarfFde* fde = this->eh_frame_->GetFdeFromOffset(0x8000);
+  ASSERT_TRUE(fde != nullptr);
+  EXPECT_EQ(0x8024U, fde->cfa_instructions_offset);
+  EXPECT_EQ(0x820cU, fde->cfa_instructions_end);
+  EXPECT_EQ(0xd018U, fde->pc_start);
+  EXPECT_EQ(0xd318U, fde->pc_end);
+  EXPECT_EQ(0x6000U, fde->cie_offset);
+  EXPECT_EQ(0U, fde->lsda_address);
+
+  ASSERT_TRUE(fde->cie != nullptr);
+  EXPECT_EQ(1U, fde->cie->version);
+  EXPECT_EQ(DW_EH_PE_sdata8, fde->cie->fde_address_encoding);
+  EXPECT_EQ(DW_EH_PE_omit, fde->cie->lsda_encoding);
+  EXPECT_EQ(0U, fde->cie->segment_size);
+  EXPECT_EQ(1U, fde->cie->augmentation_string.size());
+  EXPECT_EQ('\0', fde->cie->augmentation_string[0]);
+  EXPECT_EQ(0U, fde->cie->personality_handler);
+  EXPECT_EQ(0x6019U, fde->cie->cfa_instructions_offset);
+  EXPECT_EQ(0x610cU, fde->cie->cfa_instructions_end);
+  EXPECT_EQ(4U, fde->cie->code_alignment_factor);
+  EXPECT_EQ(8, fde->cie->data_alignment_factor);
+  EXPECT_EQ(0x20U, fde->cie->return_address_register);
+}
+
+TYPED_TEST_P(DwarfEhFrameWithHdrTest, GetFdeFromPc_fde_not_found) {
+  this->eh_frame_->TestSetTableEntrySize(16);
+  this->eh_frame_->TestSetFdeCount(1);
+
+  typename DwarfEhFrameWithHdr<TypeParam>::FdeInfo info;
+  info.pc = 0x550;
+  info.offset = 0x10500;
+  this->eh_frame_->TestSetFdeInfo(0, info);
+
+  ASSERT_EQ(nullptr, this->eh_frame_->GetFdeFromPc(0x800));
+}
+
+REGISTER_TYPED_TEST_CASE_P(DwarfEhFrameWithHdrTest, Init, Init_non_zero_load_bias, GetFdes,
+                           GetFdeInfoFromIndex_expect_cache_fail, GetFdeInfoFromIndex_read_pcrel,
+                           GetFdeInfoFromIndex_read_datarel, GetFdeInfoFromIndex_cached,
+                           GetFdeOffsetBinary_verify, GetFdeOffsetBinary_index_fail,
+                           GetFdeOffsetSequential, GetFdeOffsetSequential_last_element,
+                           GetFdeOffsetSequential_end_check, GetFdeOffsetFromPc_fail_fde_count,
+                           GetFdeOffsetFromPc_binary_search, GetFdeOffsetFromPc_sequential_search,
+                           GetCieFde32, GetCieFde64, GetFdeFromPc_fde_not_found);
+
+typedef ::testing::Types<uint32_t, uint64_t> DwarfEhFrameWithHdrTestTypes;
+INSTANTIATE_TYPED_TEST_CASE_P(, DwarfEhFrameWithHdrTest, DwarfEhFrameWithHdrTestTypes);
+
+}  // namespace unwindstack
diff --git a/libunwindstack/tests/DwarfMemoryTest.cpp b/libunwindstack/tests/DwarfMemoryTest.cpp
index 4877f36..f12d2fe 100644
--- a/libunwindstack/tests/DwarfMemoryTest.cpp
+++ b/libunwindstack/tests/DwarfMemoryTest.cpp
@@ -21,10 +21,12 @@
 
 #include <gtest/gtest.h>
 
-#include "DwarfMemory.h"
+#include <unwindstack/DwarfMemory.h>
 
 #include "MemoryFake.h"
 
+namespace unwindstack {
+
 class DwarfMemoryTest : public ::testing::Test {
  protected:
   void SetUp() override {
@@ -50,6 +52,8 @@
   void ReadEncodedValue_non_zero_adjust();
   template <typename AddressType>
   void ReadEncodedValue_overflow();
+  template <typename AddressType>
+  void ReadEncodedValue_high_bit_set();
 
   MemoryFake memory_;
   std::unique_ptr<DwarfMemory> dwarf_mem_;
@@ -237,9 +241,13 @@
   ASSERT_EQ(0U, value);
 }
 
-TEST_F(DwarfMemoryTest, ReadEncodedValue_omit_uint32_t) { ReadEncodedValue_omit<uint32_t>(); }
+TEST_F(DwarfMemoryTest, ReadEncodedValue_omit_uint32_t) {
+  ReadEncodedValue_omit<uint32_t>();
+}
 
-TEST_F(DwarfMemoryTest, ReadEncodedValue_omit_uint64_t) { ReadEncodedValue_omit<uint64_t>(); }
+TEST_F(DwarfMemoryTest, ReadEncodedValue_omit_uint64_t) {
+  ReadEncodedValue_omit<uint64_t>();
+}
 
 TEST_F(DwarfMemoryTest, ReadEncodedValue_absptr_uint32_t) {
   uint64_t value = 100;
@@ -302,9 +310,13 @@
   ASSERT_EQ(0xffffffffffffe100ULL, value);
 }
 
-TEST_F(DwarfMemoryTest, ReadEncodedValue_leb128_uint32_t) { ReadEncodedValue_leb128<uint32_t>(); }
+TEST_F(DwarfMemoryTest, ReadEncodedValue_leb128_uint32_t) {
+  ReadEncodedValue_leb128<uint32_t>();
+}
 
-TEST_F(DwarfMemoryTest, ReadEncodedValue_leb128_uint64_t) { ReadEncodedValue_leb128<uint64_t>(); }
+TEST_F(DwarfMemoryTest, ReadEncodedValue_leb128_uint64_t) {
+  ReadEncodedValue_leb128<uint64_t>();
+}
 
 template <typename AddressType>
 void DwarfMemoryTest::ReadEncodedValue_data1() {
@@ -319,9 +331,13 @@
   ASSERT_EQ(0xffffffffffffffe0ULL, value);
 }
 
-TEST_F(DwarfMemoryTest, ReadEncodedValue_data1_uint32_t) { ReadEncodedValue_data1<uint32_t>(); }
+TEST_F(DwarfMemoryTest, ReadEncodedValue_data1_uint32_t) {
+  ReadEncodedValue_data1<uint32_t>();
+}
 
-TEST_F(DwarfMemoryTest, ReadEncodedValue_data1_uint64_t) { ReadEncodedValue_data1<uint64_t>(); }
+TEST_F(DwarfMemoryTest, ReadEncodedValue_data1_uint64_t) {
+  ReadEncodedValue_data1<uint64_t>();
+}
 
 template <typename AddressType>
 void DwarfMemoryTest::ReadEncodedValue_data2() {
@@ -336,9 +352,13 @@
   ASSERT_EQ(0xffffffffffffe000ULL, value);
 }
 
-TEST_F(DwarfMemoryTest, ReadEncodedValue_data2_uint32_t) { ReadEncodedValue_data2<uint32_t>(); }
+TEST_F(DwarfMemoryTest, ReadEncodedValue_data2_uint32_t) {
+  ReadEncodedValue_data2<uint32_t>();
+}
 
-TEST_F(DwarfMemoryTest, ReadEncodedValue_data2_uint64_t) { ReadEncodedValue_data2<uint64_t>(); }
+TEST_F(DwarfMemoryTest, ReadEncodedValue_data2_uint64_t) {
+  ReadEncodedValue_data2<uint64_t>();
+}
 
 template <typename AddressType>
 void DwarfMemoryTest::ReadEncodedValue_data4() {
@@ -353,9 +373,13 @@
   ASSERT_EQ(0xffffffffe0000000ULL, value);
 }
 
-TEST_F(DwarfMemoryTest, ReadEncodedValue_data4_uint32_t) { ReadEncodedValue_data4<uint32_t>(); }
+TEST_F(DwarfMemoryTest, ReadEncodedValue_data4_uint32_t) {
+  ReadEncodedValue_data4<uint32_t>();
+}
 
-TEST_F(DwarfMemoryTest, ReadEncodedValue_data4_uint64_t) { ReadEncodedValue_data4<uint64_t>(); }
+TEST_F(DwarfMemoryTest, ReadEncodedValue_data4_uint64_t) {
+  ReadEncodedValue_data4<uint64_t>();
+}
 
 template <typename AddressType>
 void DwarfMemoryTest::ReadEncodedValue_data8() {
@@ -370,9 +394,13 @@
   ASSERT_EQ(0xe000000000000000ULL, value);
 }
 
-TEST_F(DwarfMemoryTest, ReadEncodedValue_data8_uint32_t) { ReadEncodedValue_data8<uint32_t>(); }
+TEST_F(DwarfMemoryTest, ReadEncodedValue_data8_uint32_t) {
+  ReadEncodedValue_data8<uint32_t>();
+}
 
-TEST_F(DwarfMemoryTest, ReadEncodedValue_data8_uint64_t) { ReadEncodedValue_data8<uint64_t>(); }
+TEST_F(DwarfMemoryTest, ReadEncodedValue_data8_uint64_t) {
+  ReadEncodedValue_data8<uint64_t>();
+}
 
 template <typename AddressType>
 void DwarfMemoryTest::ReadEncodedValue_non_zero_adjust() {
@@ -409,6 +437,26 @@
   ReadEncodedValue_overflow<uint64_t>();
 }
 
+template <typename AddressType>
+void DwarfMemoryTest::ReadEncodedValue_high_bit_set() {
+  uint64_t value;
+  memory_.SetData32(0, 0x15234);
+  ASSERT_FALSE(dwarf_mem_->ReadEncodedValue<AddressType>(0xc3, &value));
+
+  dwarf_mem_->set_func_offset(0x60000);
+  dwarf_mem_->set_cur_offset(0);
+  ASSERT_TRUE(dwarf_mem_->ReadEncodedValue<AddressType>(0xc3, &value));
+  ASSERT_EQ(0x75234U, value);
+}
+
+TEST_F(DwarfMemoryTest, ReadEncodedValue_high_bit_set_uint32_t) {
+  ReadEncodedValue_high_bit_set<uint32_t>();
+}
+
+TEST_F(DwarfMemoryTest, ReadEncodedValue_high_bit_set_uint64_t) {
+  ReadEncodedValue_high_bit_set<uint64_t>();
+}
+
 TEST_F(DwarfMemoryTest, AdjustEncodedValue_absptr) {
   uint64_t value = 0x1234;
   ASSERT_TRUE(dwarf_mem_->AdjustEncodedValue(0x00, &value));
@@ -470,3 +518,5 @@
   ASSERT_TRUE(dwarf_mem_->AdjustEncodedValue(0x40, &value));
   ASSERT_EQ(0x14234U, value);
 }
+
+}  // namespace unwindstack
diff --git a/libunwindstack/tests/DwarfOpLogTest.cpp b/libunwindstack/tests/DwarfOpLogTest.cpp
index d18aad0..3f09dd8 100644
--- a/libunwindstack/tests/DwarfOpLogTest.cpp
+++ b/libunwindstack/tests/DwarfOpLogTest.cpp
@@ -21,14 +21,17 @@
 
 #include <gtest/gtest.h>
 
-#include "DwarfError.h"
-#include "DwarfMemory.h"
+#include <unwindstack/DwarfError.h>
+#include <unwindstack/DwarfMemory.h>
+#include <unwindstack/Log.h>
+#include <unwindstack/Regs.h>
+
 #include "DwarfOp.h"
-#include "Log.h"
-#include "Regs.h"
 
 #include "MemoryFake.h"
 
+namespace unwindstack {
+
 template <typename TypeParam>
 class DwarfOpLogTest : public ::testing::Test {
  protected:
@@ -66,3 +69,5 @@
 
 typedef ::testing::Types<uint32_t, uint64_t> DwarfOpLogTestTypes;
 INSTANTIATE_TYPED_TEST_CASE_P(, DwarfOpLogTest, DwarfOpLogTestTypes);
+
+}  // namespace unwindstack
diff --git a/libunwindstack/tests/DwarfOpTest.cpp b/libunwindstack/tests/DwarfOpTest.cpp
index 20c488a..d424d5f 100644
--- a/libunwindstack/tests/DwarfOpTest.cpp
+++ b/libunwindstack/tests/DwarfOpTest.cpp
@@ -21,14 +21,17 @@
 
 #include <gtest/gtest.h>
 
-#include "DwarfError.h"
-#include "DwarfMemory.h"
+#include <unwindstack/DwarfError.h>
+#include <unwindstack/DwarfMemory.h>
+#include <unwindstack/Log.h>
+
 #include "DwarfOp.h"
-#include "Log.h"
 
 #include "MemoryFake.h"
 #include "RegsFake.h"
 
+namespace unwindstack {
+
 template <typename TypeParam>
 class DwarfOpTest : public ::testing::Test {
  protected:
@@ -49,27 +52,29 @@
 
 TYPED_TEST_P(DwarfOpTest, decode) {
   // Memory error.
-  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
-  ASSERT_EQ(DWARF_ERROR_MEMORY_INVALID, this->op_->last_error());
+  ASSERT_FALSE(this->op_->Decode());
+  ASSERT_EQ(DWARF_ERROR_MEMORY_INVALID, this->op_->LastErrorCode());
+  EXPECT_EQ(0U, this->op_->LastErrorAddress());
 
   // No error.
   this->op_memory_.SetMemory(0, std::vector<uint8_t>{0x96});
   this->mem_->set_cur_offset(0);
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
-  ASSERT_EQ(DWARF_ERROR_NONE, this->op_->last_error());
+  ASSERT_TRUE(this->op_->Decode());
+  ASSERT_EQ(DWARF_ERROR_NONE, this->op_->LastErrorCode());
   ASSERT_EQ(0x96U, this->op_->cur_op());
   ASSERT_EQ(1U, this->mem_->cur_offset());
 }
 
 TYPED_TEST_P(DwarfOpTest, eval) {
   // Memory error.
-  ASSERT_FALSE(this->op_->Eval(0, 2, DWARF_VERSION_MAX));
-  ASSERT_EQ(DWARF_ERROR_MEMORY_INVALID, this->op_->last_error());
+  ASSERT_FALSE(this->op_->Eval(0, 2));
+  ASSERT_EQ(DWARF_ERROR_MEMORY_INVALID, this->op_->LastErrorCode());
+  EXPECT_EQ(0U, this->op_->LastErrorAddress());
 
   // Register set.
   // Do this first, to verify that subsequent calls reset the value.
   this->op_memory_.SetMemory(0, std::vector<uint8_t>{0x50});
-  ASSERT_TRUE(this->op_->Eval(0, 1, DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Eval(0, 1));
   ASSERT_TRUE(this->op_->is_register());
   ASSERT_EQ(1U, this->mem_->cur_offset());
   ASSERT_EQ(1U, this->op_->StackSize());
@@ -80,8 +85,8 @@
   };
   this->op_memory_.SetMemory(0, opcode_buffer);
 
-  ASSERT_TRUE(this->op_->Eval(0, 8, DWARF_VERSION_MAX));
-  ASSERT_EQ(DWARF_ERROR_NONE, this->op_->last_error());
+  ASSERT_TRUE(this->op_->Eval(0, 8));
+  ASSERT_EQ(DWARF_ERROR_NONE, this->op_->LastErrorCode());
   ASSERT_FALSE(this->op_->is_register());
   ASSERT_EQ(8U, this->mem_->cur_offset());
   ASSERT_EQ(4U, this->op_->StackSize());
@@ -92,8 +97,8 @@
 
   // Infinite loop.
   this->op_memory_.SetMemory(0, std::vector<uint8_t>{0x2f, 0xfd, 0xff});
-  ASSERT_FALSE(this->op_->Eval(0, 4, DWARF_VERSION_MAX));
-  ASSERT_EQ(DWARF_ERROR_TOO_MANY_ITERATIONS, this->op_->last_error());
+  ASSERT_FALSE(this->op_->Eval(0, 4));
+  ASSERT_EQ(DWARF_ERROR_TOO_MANY_ITERATIONS, this->op_->LastErrorCode());
   ASSERT_FALSE(this->op_->is_register());
   ASSERT_EQ(0U, this->op_->StackSize());
 }
@@ -107,30 +112,8 @@
   this->op_memory_.SetMemory(0, opcode_buffer);
 
   for (size_t i = 0; i < opcode_buffer.size(); i++) {
-    ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
-    ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->op_->last_error());
-    ASSERT_EQ(opcode_buffer[i], this->op_->cur_op());
-  }
-}
-
-TYPED_TEST_P(DwarfOpTest, illegal_in_version3) {
-  std::vector<uint8_t> opcode_buffer = {0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d};
-  this->op_memory_.SetMemory(0, opcode_buffer);
-
-  for (size_t i = 0; i < opcode_buffer.size(); i++) {
-    ASSERT_FALSE(this->op_->Decode(2));
-    ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->op_->last_error());
-    ASSERT_EQ(opcode_buffer[i], this->op_->cur_op());
-  }
-}
-
-TYPED_TEST_P(DwarfOpTest, illegal_in_version4) {
-  std::vector<uint8_t> opcode_buffer = {0x9e, 0x9f};
-  this->op_memory_.SetMemory(0, opcode_buffer);
-
-  for (size_t i = 0; i < opcode_buffer.size(); i++) {
-    ASSERT_FALSE(this->op_->Decode(3));
-    ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->op_->last_error());
+    ASSERT_FALSE(this->op_->Decode());
+    ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->op_->LastErrorCode());
     ASSERT_EQ(opcode_buffer[i], this->op_->cur_op());
   }
 }
@@ -169,13 +152,13 @@
   this->op_memory_.SetMemory(0, opcode_buffer);
 
   // Push the stack values.
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
+  ASSERT_TRUE(this->op_->Decode());
+  ASSERT_TRUE(this->op_->Decode());
 
   while (this->mem_->cur_offset() < opcode_buffer.size()) {
-    ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
-    ASSERT_EQ(DWARF_ERROR_NOT_IMPLEMENTED, this->op_->last_error());
+    ASSERT_FALSE(this->op_->Decode());
+    ASSERT_EQ(DWARF_ERROR_NOT_IMPLEMENTED, this->op_->LastErrorCode());
   }
 }
 
@@ -189,7 +172,7 @@
   }
   this->op_memory_.SetMemory(0, opcode_buffer);
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x03, this->op_->cur_op());
   ASSERT_EQ(1U, this->op_->StackSize());
   if (sizeof(TypeParam) == 4) {
@@ -212,18 +195,19 @@
   TypeParam value = 0x12345678;
   this->regular_memory_.SetMemory(0x2010, &value, sizeof(value));
 
-  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
-  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+  ASSERT_FALSE(this->op_->Decode());
+  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(1U, this->op_->StackSize());
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x06, this->op_->cur_op());
   ASSERT_EQ(1U, this->op_->StackSize());
   ASSERT_EQ(value, this->op_->StackAt(0));
 
-  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
-  ASSERT_EQ(DWARF_ERROR_MEMORY_INVALID, this->op_->last_error());
+  ASSERT_FALSE(this->op_->Decode());
+  ASSERT_EQ(DWARF_ERROR_MEMORY_INVALID, this->op_->LastErrorCode());
+  ASSERT_EQ(0x12345678U, this->op_->LastErrorAddress());
 }
 
 TYPED_TEST_P(DwarfOpTest, op_deref_size) {
@@ -231,14 +215,14 @@
   TypeParam value = 0x12345678;
   this->regular_memory_.SetMemory(0x2010, &value, sizeof(value));
 
-  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
-  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+  ASSERT_FALSE(this->op_->Decode());
+  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
 
   // Read all byte sizes up to the sizeof the type.
   for (size_t i = 1; i < sizeof(TypeParam); i++) {
     this->op_memory_.SetMemory(
         0, std::vector<uint8_t>{0x0a, 0x10, 0x20, 0x94, static_cast<uint8_t>(i)});
-    ASSERT_TRUE(this->op_->Eval(0, 5, DWARF_VERSION_MAX)) << "Failed at size " << i;
+    ASSERT_TRUE(this->op_->Eval(0, 5)) << "Failed at size " << i;
     ASSERT_EQ(1U, this->op_->StackSize()) << "Failed at size " << i;
     ASSERT_EQ(0x94, this->op_->cur_op()) << "Failed at size " << i;
     TypeParam expected_value = 0;
@@ -248,18 +232,19 @@
 
   // Zero byte read.
   this->op_memory_.SetMemory(0, std::vector<uint8_t>{0x0a, 0x10, 0x20, 0x94, 0x00});
-  ASSERT_FALSE(this->op_->Eval(0, 5, DWARF_VERSION_MAX));
-  ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->op_->last_error());
+  ASSERT_FALSE(this->op_->Eval(0, 5));
+  ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->op_->LastErrorCode());
 
   // Read too many bytes.
   this->op_memory_.SetMemory(0, std::vector<uint8_t>{0x0a, 0x10, 0x20, 0x94, sizeof(TypeParam) + 1});
-  ASSERT_FALSE(this->op_->Eval(0, 5, DWARF_VERSION_MAX));
-  ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->op_->last_error());
+  ASSERT_FALSE(this->op_->Eval(0, 5));
+  ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->op_->LastErrorCode());
 
   // Force bad memory read.
   this->op_memory_.SetMemory(0, std::vector<uint8_t>{0x0a, 0x10, 0x40, 0x94, 0x01});
-  ASSERT_FALSE(this->op_->Eval(0, 5, DWARF_VERSION_MAX));
-  ASSERT_EQ(DWARF_ERROR_MEMORY_INVALID, this->op_->last_error());
+  ASSERT_FALSE(this->op_->Eval(0, 5));
+  ASSERT_EQ(DWARF_ERROR_MEMORY_INVALID, this->op_->LastErrorCode());
+  EXPECT_EQ(0x4010U, this->op_->LastErrorAddress());
 }
 
 TYPED_TEST_P(DwarfOpTest, const_unsigned) {
@@ -277,40 +262,40 @@
   this->op_memory_.SetMemory(0, opcode_buffer);
 
   // const1u
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x08, this->op_->cur_op());
   ASSERT_EQ(1U, this->op_->StackSize());
   ASSERT_EQ(0x12U, this->op_->StackAt(0));
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x08, this->op_->cur_op());
   ASSERT_EQ(2U, this->op_->StackSize());
   ASSERT_EQ(0xffU, this->op_->StackAt(0));
 
   // const2u
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x0a, this->op_->cur_op());
   ASSERT_EQ(3U, this->op_->StackSize());
   ASSERT_EQ(0x1245U, this->op_->StackAt(0));
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x0a, this->op_->cur_op());
   ASSERT_EQ(4U, this->op_->StackSize());
   ASSERT_EQ(0xff00U, this->op_->StackAt(0));
 
   // const4u
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x0c, this->op_->cur_op());
   ASSERT_EQ(5U, this->op_->StackSize());
   ASSERT_EQ(0x45342312U, this->op_->StackAt(0));
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x0c, this->op_->cur_op());
   ASSERT_EQ(6U, this->op_->StackSize());
   ASSERT_EQ(0xff010203U, this->op_->StackAt(0));
 
   // const8u
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x0e, this->op_->cur_op());
   ASSERT_EQ(7U, this->op_->StackSize());
   if (sizeof(TypeParam) == 4) {
@@ -319,7 +304,7 @@
     ASSERT_EQ(0x0102030405060708ULL, this->op_->StackAt(0));
   }
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x0e, this->op_->cur_op());
   ASSERT_EQ(8U, this->op_->StackSize());
   if (sizeof(TypeParam) == 4) {
@@ -344,40 +329,40 @@
   this->op_memory_.SetMemory(0, opcode_buffer);
 
   // const1s
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x09, this->op_->cur_op());
   ASSERT_EQ(1U, this->op_->StackSize());
   ASSERT_EQ(0x12U, this->op_->StackAt(0));
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x09, this->op_->cur_op());
   ASSERT_EQ(2U, this->op_->StackSize());
   ASSERT_EQ(static_cast<TypeParam>(-1), this->op_->StackAt(0));
 
   // const2s
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x0b, this->op_->cur_op());
   ASSERT_EQ(3U, this->op_->StackSize());
   ASSERT_EQ(0x3221U, this->op_->StackAt(0));
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x0b, this->op_->cur_op());
   ASSERT_EQ(4U, this->op_->StackSize());
   ASSERT_EQ(static_cast<TypeParam>(-248), this->op_->StackAt(0));
 
   // const4s
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x0d, this->op_->cur_op());
   ASSERT_EQ(5U, this->op_->StackSize());
   ASSERT_EQ(0x12233445U, this->op_->StackAt(0));
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x0d, this->op_->cur_op());
   ASSERT_EQ(6U, this->op_->StackSize());
   ASSERT_EQ(static_cast<TypeParam>(-16580095), this->op_->StackAt(0));
 
   // const8s
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x0f, this->op_->cur_op());
   ASSERT_EQ(7U, this->op_->StackSize());
   if (sizeof(TypeParam) == 4) {
@@ -386,7 +371,7 @@
     ASSERT_EQ(0x1223344556677889ULL, this->op_->StackAt(0));
   }
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x0f, this->op_->cur_op());
   ASSERT_EQ(8U, this->op_->StackSize());
   if (sizeof(TypeParam) == 4) {
@@ -407,28 +392,28 @@
   this->op_memory_.SetMemory(0, opcode_buffer);
 
   // Single byte ULEB128
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x10, this->op_->cur_op());
   ASSERT_EQ(1U, this->op_->StackSize());
   ASSERT_EQ(0x22U, this->op_->StackAt(0));
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x10, this->op_->cur_op());
   ASSERT_EQ(2U, this->op_->StackSize());
   ASSERT_EQ(0x7fU, this->op_->StackAt(0));
 
   // Multi byte ULEB128
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x10, this->op_->cur_op());
   ASSERT_EQ(3U, this->op_->StackSize());
   ASSERT_EQ(0x1122U, this->op_->StackAt(0));
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x10, this->op_->cur_op());
   ASSERT_EQ(4U, this->op_->StackSize());
   ASSERT_EQ(0x3a22U, this->op_->StackAt(0));
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x10, this->op_->cur_op());
   ASSERT_EQ(5U, this->op_->StackSize());
   if (sizeof(TypeParam) == 4) {
@@ -437,7 +422,7 @@
     ASSERT_EQ(0x9101c305080c101ULL, this->op_->StackAt(0));
   }
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x10, this->op_->cur_op());
   ASSERT_EQ(6U, this->op_->StackSize());
   if (sizeof(TypeParam) == 4) {
@@ -473,28 +458,28 @@
   this->op_memory_.SetMemory(0, opcode_buffer);
 
   // Single byte SLEB128
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x11, this->op_->cur_op());
   ASSERT_EQ(1U, this->op_->StackSize());
   ASSERT_EQ(0x22U, this->op_->StackAt(0));
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x11, this->op_->cur_op());
   ASSERT_EQ(2U, this->op_->StackSize());
   ASSERT_EQ(static_cast<TypeParam>(-1), this->op_->StackAt(0));
 
   // Multi byte SLEB128
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x11, this->op_->cur_op());
   ASSERT_EQ(3U, this->op_->StackSize());
   ASSERT_EQ(0x1122U, this->op_->StackAt(0));
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x11, this->op_->cur_op());
   ASSERT_EQ(4U, this->op_->StackSize());
   ASSERT_EQ(static_cast<TypeParam>(-1502), this->op_->StackAt(0));
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x11, this->op_->cur_op());
   ASSERT_EQ(5U, this->op_->StackSize());
   if (sizeof(TypeParam) == 4) {
@@ -503,7 +488,7 @@
     ASSERT_EQ(0x9101c305080c101ULL, this->op_->StackAt(0));
   }
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x11, this->op_->cur_op());
   ASSERT_EQ(6U, this->op_->StackSize());
   if (sizeof(TypeParam) == 4) {
@@ -524,21 +509,21 @@
   };
   this->op_memory_.SetMemory(0, opcode_buffer);
 
-  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_FALSE(this->op_->Decode());
   ASSERT_EQ(0x12, this->op_->cur_op());
-  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(1U, this->op_->StackSize());
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x12, this->op_->cur_op());
   ASSERT_EQ(2U, this->op_->StackSize());
   ASSERT_EQ(0x15U, this->op_->StackAt(0));
   ASSERT_EQ(0x15U, this->op_->StackAt(1));
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(3U, this->op_->StackSize());
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x12, this->op_->cur_op());
   ASSERT_EQ(4U, this->op_->StackSize());
   ASSERT_EQ(0x23U, this->op_->StackAt(0));
@@ -558,23 +543,23 @@
   };
   this->op_memory_.SetMemory(0, opcode_buffer);
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(1U, this->op_->StackSize());
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(2U, this->op_->StackSize());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x13, this->op_->cur_op());
   ASSERT_EQ(1U, this->op_->StackSize());
   ASSERT_EQ(0x10U, this->op_->StackAt(0));
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x13, this->op_->cur_op());
   ASSERT_EQ(0U, this->op_->StackSize());
 
-  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_FALSE(this->op_->Decode());
   ASSERT_EQ(0x13, this->op_->cur_op());
-  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
 }
 
 TYPED_TEST_P(DwarfOpTest, op_over) {
@@ -590,26 +575,26 @@
   };
   this->op_memory_.SetMemory(0, opcode_buffer);
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(1U, this->op_->StackSize());
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(2U, this->op_->StackSize());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x14, this->op_->cur_op());
   ASSERT_EQ(3U, this->op_->StackSize());
   ASSERT_EQ(0x1aU, this->op_->StackAt(0));
   ASSERT_EQ(0xedU, this->op_->StackAt(1));
   ASSERT_EQ(0x1aU, this->op_->StackAt(2));
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(2U, this->op_->StackSize());
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(1U, this->op_->StackSize());
 
-  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_FALSE(this->op_->Decode());
   ASSERT_EQ(0x14, this->op_->cur_op());
-  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
 }
 
 TYPED_TEST_P(DwarfOpTest, op_pick) {
@@ -625,14 +610,14 @@
   };
   this->op_memory_.SetMemory(0, opcode_buffer);
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(1U, this->op_->StackSize());
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(2U, this->op_->StackSize());
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(3U, this->op_->StackSize());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x15, this->op_->cur_op());
   ASSERT_EQ(4U, this->op_->StackSize());
   ASSERT_EQ(0xedU, this->op_->StackAt(0));
@@ -640,7 +625,7 @@
   ASSERT_EQ(0xedU, this->op_->StackAt(2));
   ASSERT_EQ(0x1aU, this->op_->StackAt(3));
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x15, this->op_->cur_op());
   ASSERT_EQ(5U, this->op_->StackSize());
   ASSERT_EQ(0x1aU, this->op_->StackAt(0));
@@ -649,9 +634,9 @@
   ASSERT_EQ(0xedU, this->op_->StackAt(3));
   ASSERT_EQ(0x1aU, this->op_->StackAt(4));
 
-  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_FALSE(this->op_->Decode());
   ASSERT_EQ(0x15, this->op_->cur_op());
-  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
 }
 
 TYPED_TEST_P(DwarfOpTest, op_swap) {
@@ -665,25 +650,25 @@
   };
   this->op_memory_.SetMemory(0, opcode_buffer);
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(1U, this->op_->StackSize());
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(2U, this->op_->StackSize());
   ASSERT_EQ(0xabU, this->op_->StackAt(0));
   ASSERT_EQ(0x26U, this->op_->StackAt(1));
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x16, this->op_->cur_op());
   ASSERT_EQ(2U, this->op_->StackSize());
   ASSERT_EQ(0x26U, this->op_->StackAt(0));
   ASSERT_EQ(0xabU, this->op_->StackAt(1));
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(1U, this->op_->StackSize());
 
-  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_FALSE(this->op_->Decode());
   ASSERT_EQ(0x16, this->op_->cur_op());
-  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
 }
 
 TYPED_TEST_P(DwarfOpTest, op_rot) {
@@ -699,28 +684,28 @@
   };
   this->op_memory_.SetMemory(0, opcode_buffer);
 
-  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
-  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+  ASSERT_FALSE(this->op_->Decode());
+  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(1U, this->op_->StackSize());
 
-  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
-  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+  ASSERT_FALSE(this->op_->Decode());
+  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(2U, this->op_->StackSize());
 
-  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
-  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+  ASSERT_FALSE(this->op_->Decode());
+  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(3U, this->op_->StackSize());
   ASSERT_EQ(0x30U, this->op_->StackAt(0));
   ASSERT_EQ(0x20U, this->op_->StackAt(1));
   ASSERT_EQ(0x10U, this->op_->StackAt(2));
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x17, this->op_->cur_op());
   ASSERT_EQ(3U, this->op_->StackSize());
   ASSERT_EQ(0x20U, this->op_->StackAt(0));
@@ -749,30 +734,30 @@
   opcode_buffer.push_back(0x19);
   this->op_memory_.SetMemory(0, opcode_buffer);
 
-  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
-  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+  ASSERT_FALSE(this->op_->Decode());
+  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(1U, this->op_->StackSize());
   ASSERT_EQ(0x10U, this->op_->StackAt(0));
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x19, this->op_->cur_op());
   ASSERT_EQ(1U, this->op_->StackSize());
   ASSERT_EQ(0x10U, this->op_->StackAt(0));
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(2U, this->op_->StackSize());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x19, this->op_->cur_op());
   ASSERT_EQ(2U, this->op_->StackSize());
   ASSERT_EQ(0x1U, this->op_->StackAt(0));
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(3U, this->op_->StackSize());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x19, this->op_->cur_op());
   ASSERT_EQ(3U, this->op_->StackSize());
   if (sizeof(TypeParam) == 4) {
@@ -801,57 +786,57 @@
   };
   this->op_memory_.SetMemory(0, opcode_buffer);
 
-  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
-  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+  ASSERT_FALSE(this->op_->Decode());
+  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(1U, this->op_->StackSize());
 
-  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
-  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+  ASSERT_FALSE(this->op_->Decode());
+  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
 
   // Two positive values.
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(2U, this->op_->StackSize());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x1b, this->op_->cur_op());
   ASSERT_EQ(1U, this->op_->StackSize());
   ASSERT_EQ(0x10U, this->op_->StackAt(0));
 
   // Two negative values.
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(2U, this->op_->StackSize());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(3U, this->op_->StackSize());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x1b, this->op_->cur_op());
   ASSERT_EQ(2U, this->op_->StackSize());
   ASSERT_EQ(0x04U, this->op_->StackAt(0));
 
   // One negative value, one positive value.
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(3U, this->op_->StackSize());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(4U, this->op_->StackSize());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x1b, this->op_->cur_op());
   ASSERT_EQ(3U, this->op_->StackSize());
   ASSERT_EQ(static_cast<TypeParam>(-4), this->op_->StackAt(0));
 
   // Divide by zero.
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(4U, this->op_->StackSize());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(5U, this->op_->StackSize());
 
-  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
-  ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->op_->last_error());
+  ASSERT_FALSE(this->op_->Decode());
+  ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->op_->LastErrorCode());
 }
 
 TYPED_TEST_P(DwarfOpTest, op_div) {
@@ -867,19 +852,19 @@
   };
   this->op_memory_.SetMemory(0, opcode_buffer);
 
-  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
-  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+  ASSERT_FALSE(this->op_->Decode());
+  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(1U, this->op_->StackSize());
 
-  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
-  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+  ASSERT_FALSE(this->op_->Decode());
+  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(2U, this->op_->StackSize());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x1a, this->op_->cur_op());
   ASSERT_EQ(1U, this->op_->StackSize());
   ASSERT_EQ(0x40U, this->op_->StackAt(0));
@@ -898,19 +883,19 @@
   };
   this->op_memory_.SetMemory(0, opcode_buffer);
 
-  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
-  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+  ASSERT_FALSE(this->op_->Decode());
+  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(1U, this->op_->StackSize());
 
-  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
-  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+  ASSERT_FALSE(this->op_->Decode());
+  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(2U, this->op_->StackSize());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x1c, this->op_->cur_op());
   ASSERT_EQ(1U, this->op_->StackSize());
   ASSERT_EQ(0x44U, this->op_->StackAt(0));
@@ -931,30 +916,30 @@
   };
   this->op_memory_.SetMemory(0, opcode_buffer);
 
-  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
-  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+  ASSERT_FALSE(this->op_->Decode());
+  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(1U, this->op_->StackSize());
 
-  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
-  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+  ASSERT_FALSE(this->op_->Decode());
+  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(2U, this->op_->StackSize());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x1d, this->op_->cur_op());
   ASSERT_EQ(1U, this->op_->StackSize());
   ASSERT_EQ(0x03U, this->op_->StackAt(0));
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(2U, this->op_->StackSize());
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(3U, this->op_->StackSize());
 
-  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
-  ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->op_->last_error());
+  ASSERT_FALSE(this->op_->Decode());
+  ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->op_->LastErrorCode());
 }
 
 TYPED_TEST_P(DwarfOpTest, op_mul) {
@@ -970,19 +955,19 @@
   };
   this->op_memory_.SetMemory(0, opcode_buffer);
 
-  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
-  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+  ASSERT_FALSE(this->op_->Decode());
+  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(1U, this->op_->StackSize());
 
-  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
-  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+  ASSERT_FALSE(this->op_->Decode());
+  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(2U, this->op_->StackSize());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x1e, this->op_->cur_op());
   ASSERT_EQ(1U, this->op_->StackSize());
   ASSERT_EQ(0x120U, this->op_->StackAt(0));
@@ -999,21 +984,21 @@
   };
   this->op_memory_.SetMemory(0, opcode_buffer);
 
-  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
-  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+  ASSERT_FALSE(this->op_->Decode());
+  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(1U, this->op_->StackSize());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x1f, this->op_->cur_op());
   ASSERT_EQ(1U, this->op_->StackSize());
   ASSERT_EQ(static_cast<TypeParam>(-72), this->op_->StackAt(0));
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(2U, this->op_->StackSize());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x1f, this->op_->cur_op());
   ASSERT_EQ(2U, this->op_->StackSize());
   ASSERT_EQ(0x01U, this->op_->StackAt(0));
@@ -1030,21 +1015,21 @@
   };
   this->op_memory_.SetMemory(0, opcode_buffer);
 
-  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
-  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+  ASSERT_FALSE(this->op_->Decode());
+  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(1U, this->op_->StackSize());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x20, this->op_->cur_op());
   ASSERT_EQ(1U, this->op_->StackSize());
   ASSERT_EQ(static_cast<TypeParam>(-5), this->op_->StackAt(0));
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(2U, this->op_->StackSize());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x20, this->op_->cur_op());
   ASSERT_EQ(2U, this->op_->StackSize());
   ASSERT_EQ(0x03U, this->op_->StackAt(0));
@@ -1063,19 +1048,19 @@
   };
   this->op_memory_.SetMemory(0, opcode_buffer);
 
-  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
-  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+  ASSERT_FALSE(this->op_->Decode());
+  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(1U, this->op_->StackSize());
 
-  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
-  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+  ASSERT_FALSE(this->op_->Decode());
+  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(2U, this->op_->StackSize());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x21, this->op_->cur_op());
   ASSERT_EQ(1U, this->op_->StackSize());
   ASSERT_EQ(0xfcU, this->op_->StackAt(0));
@@ -1094,19 +1079,19 @@
   };
   this->op_memory_.SetMemory(0, opcode_buffer);
 
-  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
-  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+  ASSERT_FALSE(this->op_->Decode());
+  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(1U, this->op_->StackSize());
 
-  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
-  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+  ASSERT_FALSE(this->op_->Decode());
+  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(2U, this->op_->StackSize());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x22, this->op_->cur_op());
   ASSERT_EQ(1U, this->op_->StackSize());
   ASSERT_EQ(0x1f1U, this->op_->StackAt(0));
@@ -1121,13 +1106,13 @@
   };
   this->op_memory_.SetMemory(0, opcode_buffer);
 
-  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
-  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+  ASSERT_FALSE(this->op_->Decode());
+  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(1U, this->op_->StackSize());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x23, this->op_->cur_op());
   ASSERT_EQ(1U, this->op_->StackSize());
   ASSERT_EQ(0x28d0U, this->op_->StackAt(0));
@@ -1146,19 +1131,19 @@
   };
   this->op_memory_.SetMemory(0, opcode_buffer);
 
-  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
-  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+  ASSERT_FALSE(this->op_->Decode());
+  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(1U, this->op_->StackSize());
 
-  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
-  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+  ASSERT_FALSE(this->op_->Decode());
+  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(2U, this->op_->StackSize());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x24, this->op_->cur_op());
   ASSERT_EQ(1U, this->op_->StackSize());
   ASSERT_EQ(0x338U, this->op_->StackAt(0));
@@ -1177,19 +1162,19 @@
   };
   this->op_memory_.SetMemory(0, opcode_buffer);
 
-  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
-  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+  ASSERT_FALSE(this->op_->Decode());
+  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(1U, this->op_->StackSize());
 
-  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
-  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+  ASSERT_FALSE(this->op_->Decode());
+  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(2U, this->op_->StackSize());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x25, this->op_->cur_op());
   ASSERT_EQ(1U, this->op_->StackSize());
   if (sizeof(TypeParam) == 4) {
@@ -1212,19 +1197,19 @@
   };
   this->op_memory_.SetMemory(0, opcode_buffer);
 
-  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
-  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+  ASSERT_FALSE(this->op_->Decode());
+  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(1U, this->op_->StackSize());
 
-  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
-  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+  ASSERT_FALSE(this->op_->Decode());
+  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(2U, this->op_->StackSize());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x26, this->op_->cur_op());
   ASSERT_EQ(1U, this->op_->StackSize());
   ASSERT_EQ(static_cast<TypeParam>(-2), this->op_->StackAt(0));
@@ -1243,19 +1228,19 @@
   };
   this->op_memory_.SetMemory(0, opcode_buffer);
 
-  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
-  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+  ASSERT_FALSE(this->op_->Decode());
+  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(1U, this->op_->StackSize());
 
-  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
-  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+  ASSERT_FALSE(this->op_->Decode());
+  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(2U, this->op_->StackSize());
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x27, this->op_->cur_op());
   ASSERT_EQ(1U, this->op_->StackSize());
   ASSERT_EQ(0x50U, this->op_->StackAt(0));
@@ -1276,48 +1261,48 @@
   };
   this->op_memory_.SetMemory(0, opcode_buffer);
 
-  ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
-  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+  ASSERT_FALSE(this->op_->Decode());
+  ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
 
   // Push on a non-zero value with a positive branch.
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(1U, this->op_->StackSize());
 
   uint64_t offset = this->mem_->cur_offset() + 3;
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x28, this->op_->cur_op());
   ASSERT_EQ(0U, this->op_->StackSize());
   ASSERT_EQ(offset + 0x102, this->mem_->cur_offset());
 
   // Push on a zero value with a positive branch.
   this->mem_->set_cur_offset(offset);
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(1U, this->op_->StackSize());
 
   offset = this->mem_->cur_offset() + 3;
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x28, this->op_->cur_op());
   ASSERT_EQ(0U, this->op_->StackSize());
   ASSERT_EQ(offset - 5, this->mem_->cur_offset());
 
   // Push on a non-zero value with a negative branch.
   this->mem_->set_cur_offset(offset);
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(1U, this->op_->StackSize());
 
   offset = this->mem_->cur_offset() + 3;
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x28, this->op_->cur_op());
   ASSERT_EQ(0U, this->op_->StackSize());
   ASSERT_EQ(offset - 4, this->mem_->cur_offset());
 
   // Push on a zero value with a negative branch.
   this->mem_->set_cur_offset(offset);
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(1U, this->op_->StackSize());
 
   offset = this->mem_->cur_offset() + 3;
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x28, this->op_->cur_op());
   ASSERT_EQ(0U, this->op_->StackSize());
   ASSERT_EQ(offset + 16, this->mem_->cur_offset());
@@ -1337,14 +1322,14 @@
     opcode_buffer[3] = opcode;
     this->op_memory_.SetMemory(0, opcode_buffer);
 
-    ASSERT_FALSE(this->op_->Eval(0, 1, DWARF_VERSION_MAX));
+    ASSERT_FALSE(this->op_->Eval(0, 1));
     ASSERT_EQ(opcode, this->op_->cur_op());
-    ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+    ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
 
-    ASSERT_FALSE(this->op_->Eval(1, 4, DWARF_VERSION_MAX));
+    ASSERT_FALSE(this->op_->Eval(1, 4));
     ASSERT_EQ(opcode, this->op_->cur_op());
     ASSERT_EQ(1U, this->op_->StackSize());
-    ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+    ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->LastErrorCode());
   }
 }
 
@@ -1380,7 +1365,7 @@
     opcode_buffer[14] = expected[i];
     this->op_memory_.SetMemory(0, opcode_buffer);
 
-    ASSERT_TRUE(this->op_->Eval(0, 15, DWARF_VERSION_MAX))
+    ASSERT_TRUE(this->op_->Eval(0, 15))
         << "Op: 0x" << std::hex << static_cast<uint32_t>(expected[i]) << " failed";
 
     ASSERT_EQ(3U, this->op_->StackSize());
@@ -1400,14 +1385,14 @@
   this->op_memory_.SetMemory(0, opcode_buffer);
 
   uint64_t offset = this->mem_->cur_offset() + 3;
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x2f, this->op_->cur_op());
   ASSERT_EQ(0U, this->op_->StackSize());
   ASSERT_EQ(offset + 0x2010, this->mem_->cur_offset());
 
   this->mem_->set_cur_offset(offset);
   offset = this->mem_->cur_offset() + 3;
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x2f, this->op_->cur_op());
   ASSERT_EQ(0U, this->op_->StackSize());
   ASSERT_EQ(offset - 3, this->mem_->cur_offset());
@@ -1424,7 +1409,7 @@
 
   for (size_t i = 0; i < opcode_buffer.size(); i++) {
     uint32_t op = opcode_buffer[i];
-    ASSERT_TRUE(this->op_->Eval(i, i + 1, DWARF_VERSION_MAX)) << "Failed op: 0x" << std::hex << op;
+    ASSERT_TRUE(this->op_->Eval(i, i + 1)) << "Failed op: 0x" << std::hex << op;
     ASSERT_EQ(op, this->op_->cur_op());
     ASSERT_EQ(1U, this->op_->StackSize()) << "Failed op: 0x" << std::hex << op;
     ASSERT_EQ(op - 0x30U, this->op_->StackAt(0)) << "Failed op: 0x" << std::hex << op;
@@ -1442,7 +1427,7 @@
 
   for (size_t i = 0; i < opcode_buffer.size(); i++) {
     uint32_t op = opcode_buffer[i];
-    ASSERT_TRUE(this->op_->Eval(i, i + 1, DWARF_VERSION_MAX)) << "Failed op: 0x" << std::hex << op;
+    ASSERT_TRUE(this->op_->Eval(i, i + 1)) << "Failed op: 0x" << std::hex << op;
     ASSERT_EQ(op, this->op_->cur_op());
     ASSERT_TRUE(this->op_->is_register()) << "Failed op: 0x" << std::hex << op;
     ASSERT_EQ(1U, this->op_->StackSize()) << "Failed op: 0x" << std::hex << op;
@@ -1456,13 +1441,13 @@
   };
   this->op_memory_.SetMemory(0, opcode_buffer);
 
-  ASSERT_TRUE(this->op_->Eval(0, 2, DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Eval(0, 2));
   ASSERT_EQ(0x90, this->op_->cur_op());
   ASSERT_TRUE(this->op_->is_register());
   ASSERT_EQ(1U, this->op_->StackSize());
   ASSERT_EQ(0x02U, this->op_->StackAt(0));
 
-  ASSERT_TRUE(this->op_->Eval(2, 5, DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Eval(2, 5));
   ASSERT_EQ(0x90, this->op_->cur_op());
   ASSERT_TRUE(this->op_->is_register());
   ASSERT_EQ(1U, this->op_->StackSize());
@@ -1483,25 +1468,24 @@
   }
   this->op_memory_.SetMemory(0, opcode_buffer);
 
-  RegsFake<TypeParam> regs(32, 10);
+  RegsImplFake<TypeParam> regs(32);
   for (size_t i = 0; i < 32; i++) {
     regs[i] = i + 10;
   }
-  this->op_->set_regs(&regs);
+  RegsInfo<TypeParam> regs_info(&regs);
+  this->op_->set_regs_info(&regs_info);
 
   uint64_t offset = 0;
   for (uint32_t op = 0x70; op <= 0x8f; op++) {
     // Positive value added to register.
-    ASSERT_TRUE(this->op_->Eval(offset, offset + 2, DWARF_VERSION_MAX)) << "Failed op: 0x"
-                                                                        << std::hex << op;
+    ASSERT_TRUE(this->op_->Eval(offset, offset + 2)) << "Failed op: 0x" << std::hex << op;
     ASSERT_EQ(op, this->op_->cur_op());
     ASSERT_EQ(1U, this->op_->StackSize()) << "Failed op: 0x" << std::hex << op;
     ASSERT_EQ(op - 0x70 + 10 + 0x12, this->op_->StackAt(0)) << "Failed op: 0x" << std::hex << op;
     offset += 2;
 
     // Negative value added to register.
-    ASSERT_TRUE(this->op_->Eval(offset, offset + 2, DWARF_VERSION_MAX)) << "Failed op: 0x"
-                                                                        << std::hex << op;
+    ASSERT_TRUE(this->op_->Eval(offset, offset + 2)) << "Failed op: 0x" << std::hex << op;
     ASSERT_EQ(op, this->op_->cur_op());
     ASSERT_EQ(1U, this->op_->StackSize()) << "Failed op: 0x" << std::hex << op;
     ASSERT_EQ(op - 0x70 + 10 - 2, this->op_->StackAt(0)) << "Failed op: 0x" << std::hex << op;
@@ -1515,21 +1499,22 @@
   };
   this->op_memory_.SetMemory(0, opcode_buffer);
 
-  RegsFake<TypeParam> regs(16, 10);
+  RegsImplFake<TypeParam> regs(16);
   for (size_t i = 0; i < 16; i++) {
     regs[i] = i + 10;
   }
-  this->op_->set_regs(&regs);
+  RegsInfo<TypeParam> regs_info(&regs);
+  this->op_->set_regs_info(&regs_info);
 
   // Should pass since this references the last regsister.
-  ASSERT_TRUE(this->op_->Eval(0, 2, DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Eval(0, 2));
   ASSERT_EQ(0x7fU, this->op_->cur_op());
   ASSERT_EQ(1U, this->op_->StackSize());
   ASSERT_EQ(0x2bU, this->op_->StackAt(0));
 
   // Should fail since this references a non-existent register.
-  ASSERT_FALSE(this->op_->Eval(2, 4, DWARF_VERSION_MAX));
-  ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->op_->last_error());
+  ASSERT_FALSE(this->op_->Eval(2, 4));
+  ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->op_->LastErrorCode());
 }
 
 TYPED_TEST_P(DwarfOpTest, op_bregx) {
@@ -1541,41 +1526,60 @@
                                         0x92, 0x80, 0x15, 0x80, 0x02};
   this->op_memory_.SetMemory(0, opcode_buffer);
 
-  RegsFake<TypeParam> regs(10, 10);
+  RegsImplFake<TypeParam> regs(10);
   regs[5] = 0x45;
   regs[6] = 0x190;
-  this->op_->set_regs(&regs);
+  RegsInfo<TypeParam> regs_info(&regs);
+  this->op_->set_regs_info(&regs_info);
 
-  ASSERT_TRUE(this->op_->Eval(0, 3, DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Eval(0, 3));
   ASSERT_EQ(0x92, this->op_->cur_op());
   ASSERT_EQ(1U, this->op_->StackSize());
   ASSERT_EQ(0x65U, this->op_->StackAt(0));
 
-  ASSERT_TRUE(this->op_->Eval(3, 7, DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Eval(3, 7));
   ASSERT_EQ(0x92, this->op_->cur_op());
   ASSERT_EQ(1U, this->op_->StackSize());
   ASSERT_EQ(0x90U, this->op_->StackAt(0));
 
-  ASSERT_FALSE(this->op_->Eval(7, 12, DWARF_VERSION_MAX));
-  ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->op_->last_error());
+  ASSERT_FALSE(this->op_->Eval(7, 12));
+  ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->op_->LastErrorCode());
 }
 
 TYPED_TEST_P(DwarfOpTest, op_nop) {
   this->op_memory_.SetMemory(0, std::vector<uint8_t>{0x96});
 
-  ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+  ASSERT_TRUE(this->op_->Decode());
   ASSERT_EQ(0x96, this->op_->cur_op());
   ASSERT_EQ(0U, this->op_->StackSize());
 }
 
-REGISTER_TYPED_TEST_CASE_P(DwarfOpTest, decode, eval, illegal_opcode, illegal_in_version3,
-                           illegal_in_version4, not_implemented, op_addr, op_deref, op_deref_size,
-                           const_unsigned, const_signed, const_uleb, const_sleb, op_dup, op_drop,
-                           op_over, op_pick, op_swap, op_rot, op_abs, op_and, op_div, op_minus,
-                           op_mod, op_mul, op_neg, op_not, op_or, op_plus, op_plus_uconst, op_shl,
-                           op_shr, op_shra, op_xor, op_bra, compare_opcode_stack_error,
-                           compare_opcodes, op_skip, op_lit, op_reg, op_regx, op_breg,
-                           op_breg_invalid_register, op_bregx, op_nop);
+TYPED_TEST_P(DwarfOpTest, is_dex_pc) {
+  // Special sequence that indicates this is a dex pc.
+  this->op_memory_.SetMemory(0, std::vector<uint8_t>{0x0c, 'D', 'E', 'X', '1', 0x13});
+
+  ASSERT_TRUE(this->op_->Eval(0, 6));
+  EXPECT_TRUE(this->op_->dex_pc_set());
+
+  // Try without the last op.
+  ASSERT_TRUE(this->op_->Eval(0, 5));
+  EXPECT_FALSE(this->op_->dex_pc_set());
+
+  // Change the constant.
+  this->op_memory_.SetMemory(0, std::vector<uint8_t>{0x0c, 'D', 'E', 'X', '2', 0x13});
+  ASSERT_TRUE(this->op_->Eval(0, 6));
+  EXPECT_FALSE(this->op_->dex_pc_set());
+}
+
+REGISTER_TYPED_TEST_CASE_P(DwarfOpTest, decode, eval, illegal_opcode, not_implemented, op_addr,
+                           op_deref, op_deref_size, const_unsigned, const_signed, const_uleb,
+                           const_sleb, op_dup, op_drop, op_over, op_pick, op_swap, op_rot, op_abs,
+                           op_and, op_div, op_minus, op_mod, op_mul, op_neg, op_not, op_or, op_plus,
+                           op_plus_uconst, op_shl, op_shr, op_shra, op_xor, op_bra,
+                           compare_opcode_stack_error, compare_opcodes, op_skip, op_lit, op_reg,
+                           op_regx, op_breg, op_breg_invalid_register, op_bregx, op_nop, is_dex_pc);
 
 typedef ::testing::Types<uint32_t, uint64_t> DwarfOpTestTypes;
 INSTANTIATE_TYPED_TEST_CASE_P(, DwarfOpTest, DwarfOpTestTypes);
+
+}  // namespace unwindstack
diff --git a/libunwindstack/tests/DwarfSectionImplTest.cpp b/libunwindstack/tests/DwarfSectionImplTest.cpp
index 71b114a..46f555a 100644
--- a/libunwindstack/tests/DwarfSectionImplTest.cpp
+++ b/libunwindstack/tests/DwarfSectionImplTest.cpp
@@ -16,53 +16,42 @@
 
 #include <stdint.h>
 
-#include <gmock/gmock.h>
 #include <gtest/gtest.h>
 
-#include "DwarfSection.h"
+#include <unwindstack/DwarfError.h>
+#include <unwindstack/DwarfSection.h>
+
+#include "DwarfEncoding.h"
 
 #include "LogFake.h"
 #include "MemoryFake.h"
 #include "RegsFake.h"
 
+namespace unwindstack {
+
 template <typename TypeParam>
-class MockDwarfSectionImpl : public DwarfSectionImpl<TypeParam> {
+class TestDwarfSectionImpl : public DwarfSectionImpl<TypeParam> {
  public:
-  MockDwarfSectionImpl(Memory* memory) : DwarfSectionImpl<TypeParam>(memory) {}
-  virtual ~MockDwarfSectionImpl() = default;
+  TestDwarfSectionImpl(Memory* memory) : DwarfSectionImpl<TypeParam>(memory) {}
+  virtual ~TestDwarfSectionImpl() = default;
 
-  MOCK_METHOD2(Init, bool(uint64_t, uint64_t));
+  bool Init(uint64_t, uint64_t, uint64_t) override { return false; }
 
-  MOCK_METHOD2(GetFdeOffsetFromPc, bool(uint64_t, uint64_t*));
+  void GetFdes(std::vector<const DwarfFde*>*) override {}
 
-  MOCK_METHOD1(GetFdeFromIndex, const DwarfFde*(size_t));
+  const DwarfFde* GetFdeFromPc(uint64_t) override { return nullptr; }
 
-  MOCK_METHOD1(IsCie32, bool(uint32_t));
+  uint64_t GetCieOffsetFromFde32(uint32_t) { return 0; }
 
-  MOCK_METHOD1(IsCie64, bool(uint64_t));
+  uint64_t GetCieOffsetFromFde64(uint64_t) { return 0; }
 
-  MOCK_METHOD1(GetCieOffsetFromFde32, uint64_t(uint32_t));
-
-  MOCK_METHOD1(GetCieOffsetFromFde64, uint64_t(uint64_t));
-
-  MOCK_METHOD1(AdjustPcFromFde, uint64_t(uint64_t));
-
-  void TestSetCachedCieEntry(uint64_t offset, const DwarfCie& cie) {
-    this->cie_entries_[offset] = cie;
-  }
-  void TestClearCachedCieEntry() { this->cie_entries_.clear(); }
-
-  void TestSetCachedFdeEntry(uint64_t offset, const DwarfFde& fde) {
-    this->fde_entries_[offset] = fde;
-  }
-  void TestClearCachedFdeEntry() { this->fde_entries_.clear(); }
+  uint64_t AdjustPcFromFde(uint64_t) override { return 0; }
 
   void TestSetCachedCieLocRegs(uint64_t offset, const dwarf_loc_regs_t& loc_regs) {
     this->cie_loc_regs_[offset] = loc_regs;
   }
   void TestClearCachedCieLocRegs() { this->cie_loc_regs_.clear(); }
-
-  void TestClearError() { this->last_error_ = DWARF_ERROR_NONE; }
+  void TestClearError() { this->last_error_.code = DWARF_ERROR_NONE; }
 };
 
 template <typename TypeParam>
@@ -70,36 +59,60 @@
  protected:
   void SetUp() override {
     memory_.Clear();
-    section_ = new MockDwarfSectionImpl<TypeParam>(&memory_);
+    section_ = new TestDwarfSectionImpl<TypeParam>(&memory_);
     ResetLogs();
   }
 
   void TearDown() override { delete section_; }
 
   MemoryFake memory_;
-  MockDwarfSectionImpl<TypeParam>* section_ = nullptr;
+  TestDwarfSectionImpl<TypeParam>* section_ = nullptr;
 };
 TYPED_TEST_CASE_P(DwarfSectionImplTest);
 
 // NOTE: All test class variables need to be referenced as this->.
 
+TYPED_TEST_P(DwarfSectionImplTest, GetCieFromOffset_fail_should_not_cache) {
+  ASSERT_TRUE(this->section_->GetCieFromOffset(0x4000) == nullptr);
+  EXPECT_EQ(DWARF_ERROR_MEMORY_INVALID, this->section_->LastErrorCode());
+  EXPECT_EQ(0x4000U, this->section_->LastErrorAddress());
+
+  this->section_->TestClearError();
+  ASSERT_TRUE(this->section_->GetCieFromOffset(0x4000) == nullptr);
+  EXPECT_EQ(DWARF_ERROR_MEMORY_INVALID, this->section_->LastErrorCode());
+  EXPECT_EQ(0x4000U, this->section_->LastErrorAddress());
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, GetFdeFromOffset_fail_should_not_cache) {
+  ASSERT_TRUE(this->section_->GetFdeFromOffset(0x4000) == nullptr);
+  EXPECT_EQ(DWARF_ERROR_MEMORY_INVALID, this->section_->LastErrorCode());
+  EXPECT_EQ(0x4000U, this->section_->LastErrorAddress());
+
+  this->section_->TestClearError();
+  ASSERT_TRUE(this->section_->GetFdeFromOffset(0x4000) == nullptr);
+  EXPECT_EQ(DWARF_ERROR_MEMORY_INVALID, this->section_->LastErrorCode());
+  EXPECT_EQ(0x4000U, this->section_->LastErrorAddress());
+}
+
 TYPED_TEST_P(DwarfSectionImplTest, Eval_cfa_expr_eval_fail) {
   DwarfCie cie{.version = 3, .return_address_register = 5};
-  RegsFake<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, 0x5000}};
-  ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs));
-  EXPECT_EQ(DWARF_ERROR_MEMORY_INVALID, this->section_->last_error());
+  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());
+  EXPECT_EQ(0x5000U, this->section_->LastErrorAddress());
 }
 
 TYPED_TEST_P(DwarfSectionImplTest, Eval_cfa_expr_no_stack) {
   DwarfCie cie{.version = 3, .return_address_register = 5};
-  RegsFake<TypeParam> regs(10, 9);
+  RegsImplFake<TypeParam> regs(10);
   dwarf_loc_regs_t loc_regs;
 
   regs.set_pc(0x100);
@@ -107,14 +120,15 @@
   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, 0x5000}};
-  ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs));
-  EXPECT_EQ(DWARF_ERROR_ILLEGAL_STATE, this->section_->last_error());
+  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());
 }
 
 TYPED_TEST_P(DwarfSectionImplTest, Eval_cfa_expr) {
   DwarfCie cie{.version = 3, .return_address_register = 5};
-  RegsFake<TypeParam> regs(10, 9);
+  RegsImplFake<TypeParam> regs(10);
   dwarf_loc_regs_t loc_regs;
 
   regs.set_pc(0x100);
@@ -124,15 +138,15 @@
   this->memory_.SetMemory(0x5000, std::vector<uint8_t>{0x0c, 0x00, 0x00, 0x00, 0x80});
   TypeParam cfa_value = 0x12345;
   this->memory_.SetMemory(0x80000000, &cfa_value, sizeof(cfa_value));
-  loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_EXPRESSION, {0x4, 0x5000}};
-  ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs));
-  EXPECT_EQ(0x12345U, regs.sp());
-  EXPECT_EQ(0x20U, regs.pc());
+  loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_EXPRESSION, {0x4, 0x5004}};
+  bool finished;
+  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};
-  RegsFake<TypeParam> regs(10, 9);
+  RegsImplFake<TypeParam> regs(10);
   dwarf_loc_regs_t loc_regs;
 
   regs.set_pc(0x100);
@@ -140,15 +154,17 @@
   regs[5] = 0x20;
   regs[9] = 0x3000;
   this->memory_.SetMemory(0x5000, std::vector<uint8_t>{0x0c, 0x00, 0x00, 0x00, 0x80});
-  loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_VAL_EXPRESSION, {0x4, 0x5000}};
-  ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs));
+  loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_VAL_EXPRESSION, {0x4, 0x5004}};
+  bool finished;
+  ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs, &finished));
+  ASSERT_FALSE(finished);
   EXPECT_EQ(0x80000000U, regs.sp());
   EXPECT_EQ(0x20U, regs.pc());
 }
 
 TYPED_TEST_P(DwarfSectionImplTest, Eval_cfa_expr_is_register) {
   DwarfCie cie{.version = 3, .return_address_register = 5};
-  RegsFake<TypeParam> regs(10, 9);
+  RegsImplFake<TypeParam> regs(10);
   dwarf_loc_regs_t loc_regs;
 
   regs.set_pc(0x100);
@@ -156,60 +172,64 @@
   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, 0x5000}};
-  ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs));
-  EXPECT_EQ(DWARF_ERROR_NOT_IMPLEMENTED, this->section_->last_error());
+  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());
 }
 
 TYPED_TEST_P(DwarfSectionImplTest, Eval_bad_regs) {
   DwarfCie cie{.return_address_register = 60};
-  RegsFake<TypeParam> regs(10, 9);
+  RegsImplFake<TypeParam> regs(10);
   dwarf_loc_regs_t loc_regs;
 
-  ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs));
-  EXPECT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->section_->last_error());
+  bool finished;
+  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_no_cfa) {
   DwarfCie cie{.return_address_register = 5};
-  RegsFake<TypeParam> regs(10, 9);
+  RegsImplFake<TypeParam> regs(10);
   dwarf_loc_regs_t loc_regs;
 
-  ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs));
-  EXPECT_EQ(DWARF_ERROR_CFA_NOT_DEFINED, this->section_->last_error());
+  bool finished;
+  ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs, &finished));
+  EXPECT_EQ(DWARF_ERROR_CFA_NOT_DEFINED, this->section_->LastErrorCode());
 }
 
 TYPED_TEST_P(DwarfSectionImplTest, Eval_cfa_bad) {
   DwarfCie cie{.return_address_register = 5};
-  RegsFake<TypeParam> regs(10, 9);
+  RegsImplFake<TypeParam> regs(10);
   dwarf_loc_regs_t loc_regs;
 
   loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {20, 0}};
-  ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs));
-  EXPECT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->section_->last_error());
+  bool finished;
+  ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs, &finished));
+  EXPECT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->section_->LastErrorCode());
 
   this->section_->TestClearError();
   loc_regs.erase(CFA_REG);
   loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_INVALID, {0, 0}};
-  ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs));
-  EXPECT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->section_->last_error());
+  ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs, &finished));
+  EXPECT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->section_->LastErrorCode());
 
   this->section_->TestClearError();
   loc_regs.erase(CFA_REG);
   loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_OFFSET, {0, 0}};
-  ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs));
-  EXPECT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->section_->last_error());
+  ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs, &finished));
+  EXPECT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->section_->LastErrorCode());
 
   this->section_->TestClearError();
   loc_regs.erase(CFA_REG);
   loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_VAL_OFFSET, {0, 0}};
-  ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs));
-  EXPECT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->section_->last_error());
+  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_register_prev) {
   DwarfCie cie{.return_address_register = 5};
-  RegsFake<TypeParam> regs(10, 9);
+  RegsImplFake<TypeParam> regs(10);
   dwarf_loc_regs_t loc_regs;
 
   regs.set_pc(0x100);
@@ -217,14 +237,16 @@
   regs[5] = 0x20;
   regs[9] = 0x3000;
   loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {9, 0}};
-  ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs));
+  bool finished;
+  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};
-  RegsFake<TypeParam> regs(10, 9);
+  RegsImplFake<TypeParam> regs(10);
   dwarf_loc_regs_t loc_regs;
 
   regs.set_pc(0x100);
@@ -233,29 +255,87 @@
   regs[6] = 0x4000;
   regs[9] = 0x3000;
   loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {6, 0}};
-  ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs));
+  bool finished;
+  ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs, &finished));
+  EXPECT_FALSE(finished);
   EXPECT_EQ(0x20U, regs.pc());
   EXPECT_EQ(0x4000U, regs.sp());
 }
 
 TYPED_TEST_P(DwarfSectionImplTest, Eval_double_indirection) {
   DwarfCie cie{.return_address_register = 5};
-  RegsFake<TypeParam> regs(10, 9);
+  RegsImplFake<TypeParam> regs(10);
   dwarf_loc_regs_t loc_regs;
 
   regs.set_pc(0x100);
   regs.set_sp(0x2000);
+  regs[1] = 0x100;
+  regs[3] = 0x300;
   regs[8] = 0x10;
   loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {8, 0}};
-  loc_regs[1] = DwarfLocation{DWARF_LOCATION_REGISTER, {3, 0}};
-  loc_regs[9] = DwarfLocation{DWARF_LOCATION_REGISTER, {1, 0}};
-  ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs));
-  EXPECT_EQ(DWARF_ERROR_ILLEGAL_STATE, this->section_->last_error());
+  loc_regs[1] = DwarfLocation{DWARF_LOCATION_REGISTER, {3, 1}};
+  loc_regs[9] = DwarfLocation{DWARF_LOCATION_REGISTER, {1, 2}};
+  bool finished;
+  ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs, &finished));
+  EXPECT_EQ(0x301U, regs[1]);
+  EXPECT_EQ(0x300U, regs[3]);
+  EXPECT_EQ(0x10U, regs[8]);
+  EXPECT_EQ(0x102U, regs[9]);
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, Eval_register_reference_chain) {
+  DwarfCie cie{.return_address_register = 5};
+  RegsImplFake<TypeParam> regs(10);
+  dwarf_loc_regs_t loc_regs;
+
+  regs.set_pc(0x100);
+  regs.set_sp(0x2000);
+  regs[0] = 0x10;
+  regs[1] = 0x20;
+  regs[2] = 0x30;
+  regs[3] = 0x40;
+  regs[4] = 0x50;
+  regs[5] = 0x60;
+  regs[8] = 0x20;
+  loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {8, 0}};
+  loc_regs[1] = DwarfLocation{DWARF_LOCATION_REGISTER, {0, 1}};
+  loc_regs[2] = DwarfLocation{DWARF_LOCATION_REGISTER, {1, 2}};
+  loc_regs[3] = DwarfLocation{DWARF_LOCATION_REGISTER, {2, 3}};
+  loc_regs[4] = DwarfLocation{DWARF_LOCATION_REGISTER, {3, 4}};
+  loc_regs[5] = DwarfLocation{DWARF_LOCATION_REGISTER, {4, 5}};
+  bool finished;
+  ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs, &finished));
+  EXPECT_EQ(0x10U, regs[0]);
+  EXPECT_EQ(0x11U, regs[1]);
+  EXPECT_EQ(0x22U, regs[2]);
+  EXPECT_EQ(0x33U, regs[3]);
+  EXPECT_EQ(0x44U, regs[4]);
+  EXPECT_EQ(0x55U, regs[5]);
+  EXPECT_EQ(0x20U, regs[8]);
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, Eval_dex_pc) {
+  DwarfCie cie{.return_address_register = 5};
+  RegsImplFake<TypeParam> regs(10);
+  dwarf_loc_regs_t loc_regs;
+
+  regs.set_pc(0x100);
+  regs.set_sp(0x2000);
+  regs[0] = 0x10;
+  regs[8] = 0x20;
+  loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {8, 0}};
+  loc_regs[1] = DwarfLocation{DWARF_LOCATION_VAL_EXPRESSION, {0x8, 0x5008}};
+  this->memory_.SetMemory(0x5000, std::vector<uint8_t>{0x0c, 'D', 'E', 'X', '1', 0x13, 0x08, 0x11});
+  bool finished;
+  ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs, &finished));
+  EXPECT_EQ(0x10U, regs[0]);
+  EXPECT_EQ(0x20U, regs[8]);
+  EXPECT_EQ(0x11U, regs.dex_pc());
 }
 
 TYPED_TEST_P(DwarfSectionImplTest, Eval_invalid_register) {
   DwarfCie cie{.return_address_register = 5};
-  RegsFake<TypeParam> regs(10, 9);
+  RegsImplFake<TypeParam> regs(10);
   dwarf_loc_regs_t loc_regs;
 
   regs.set_pc(0x100);
@@ -263,13 +343,14 @@
   regs[8] = 0x10;
   loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {8, 0}};
   loc_regs[1] = DwarfLocation{DWARF_LOCATION_REGISTER, {10, 0}};
-  ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs));
-  EXPECT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->section_->last_error());
+  bool finished;
+  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_different_reg_locations) {
   DwarfCie cie{.return_address_register = 5};
-  RegsFake<TypeParam> regs(10, 9);
+  RegsImplFake<TypeParam> regs(10);
   dwarf_loc_regs_t loc_regs;
 
   if (sizeof(TypeParam) == sizeof(uint64_t)) {
@@ -287,7 +368,9 @@
   loc_regs[1] = DwarfLocation{DWARF_LOCATION_VAL_OFFSET, {0x100, 0}};
   loc_regs[2] = DwarfLocation{DWARF_LOCATION_OFFSET, {0x50, 0}};
   loc_regs[3] = DwarfLocation{DWARF_LOCATION_UNDEFINED, {0, 0}};
-  ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs));
+  bool finished;
+  ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs, &finished));
+  EXPECT_FALSE(finished);
   EXPECT_EQ(0x10U, regs.pc());
   EXPECT_EQ(0x2100U, regs.sp());
   EXPECT_EQ(0x2200U, regs[1]);
@@ -301,7 +384,7 @@
 
 TYPED_TEST_P(DwarfSectionImplTest, Eval_return_address_undefined) {
   DwarfCie cie{.return_address_register = 5};
-  RegsFake<TypeParam> regs(10, 9);
+  RegsImplFake<TypeParam> regs(10);
   dwarf_loc_regs_t loc_regs;
 
   regs.set_pc(0x100);
@@ -310,14 +393,33 @@
   regs[8] = 0x10;
   loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {8, 0}};
   loc_regs[5] = DwarfLocation{DWARF_LOCATION_UNDEFINED, {0, 0}};
-  ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs));
+  bool finished;
+  ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs, &finished));
+  EXPECT_TRUE(finished);
+  EXPECT_EQ(0U, regs.pc());
+  EXPECT_EQ(0x10U, regs.sp());
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, Eval_pc_zero) {
+  DwarfCie cie{.return_address_register = 5};
+  RegsImplFake<TypeParam> regs(10);
+  dwarf_loc_regs_t loc_regs;
+
+  regs.set_pc(0x100);
+  regs.set_sp(0x2000);
+  regs[5] = 0;
+  regs[8] = 0x10;
+  loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {8, 0}};
+  bool finished;
+  ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs, &finished));
+  EXPECT_TRUE(finished);
   EXPECT_EQ(0U, regs.pc());
   EXPECT_EQ(0x10U, regs.sp());
 }
 
 TYPED_TEST_P(DwarfSectionImplTest, Eval_return_address) {
   DwarfCie cie{.return_address_register = 5};
-  RegsFake<TypeParam> regs(10, 9);
+  RegsImplFake<TypeParam> regs(10);
   dwarf_loc_regs_t loc_regs;
 
   regs.set_pc(0x100);
@@ -325,14 +427,16 @@
   regs[5] = 0x20;
   regs[8] = 0x10;
   loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {8, 0}};
-  ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs));
+  bool finished;
+  ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs, &finished));
+  EXPECT_FALSE(finished);
   EXPECT_EQ(0x20U, regs.pc());
   EXPECT_EQ(0x10U, regs.sp());
 }
 
 TYPED_TEST_P(DwarfSectionImplTest, Eval_ignore_large_reg_loc) {
   DwarfCie cie{.return_address_register = 5};
-  RegsFake<TypeParam> regs(10, 9);
+  RegsImplFake<TypeParam> regs(10);
   dwarf_loc_regs_t loc_regs;
 
   regs.set_pc(0x100);
@@ -342,14 +446,16 @@
   loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {8, 0}};
   // This should not result in any errors.
   loc_regs[20] = DwarfLocation{DWARF_LOCATION_REGISTER, {8, 0}};
-  ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs));
+  bool finished;
+  ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs, &finished));
+  EXPECT_FALSE(finished);
   EXPECT_EQ(0x20U, regs.pc());
   EXPECT_EQ(0x10U, regs.sp());
 }
 
 TYPED_TEST_P(DwarfSectionImplTest, Eval_reg_expr) {
   DwarfCie cie{.version = 3, .return_address_register = 5};
-  RegsFake<TypeParam> regs(10, 9);
+  RegsImplFake<TypeParam> regs(10);
   dwarf_loc_regs_t loc_regs;
 
   regs.set_pc(0x100);
@@ -359,15 +465,17 @@
   TypeParam cfa_value = 0x12345;
   this->memory_.SetMemory(0x80000000, &cfa_value, sizeof(cfa_value));
   loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {8, 0}};
-  loc_regs[5] = DwarfLocation{DWARF_LOCATION_EXPRESSION, {0x4, 0x5000}};
-  ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs));
+  loc_regs[5] = 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(0x3000U, regs.sp());
   EXPECT_EQ(0x12345U, regs.pc());
 }
 
 TYPED_TEST_P(DwarfSectionImplTest, Eval_reg_val_expr) {
   DwarfCie cie{.version = 3, .return_address_register = 5};
-  RegsFake<TypeParam> regs(10, 9);
+  RegsImplFake<TypeParam> regs(10);
   dwarf_loc_regs_t loc_regs;
 
   regs.set_pc(0x100);
@@ -375,367 +483,14 @@
   regs[8] = 0x3000;
   this->memory_.SetMemory(0x5000, std::vector<uint8_t>{0x0c, 0x00, 0x00, 0x00, 0x80});
   loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {8, 0}};
-  loc_regs[5] = DwarfLocation{DWARF_LOCATION_VAL_EXPRESSION, {0x4, 0x5000}};
-  ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs));
+  loc_regs[5] = DwarfLocation{DWARF_LOCATION_VAL_EXPRESSION, {0x4, 0x5004}};
+  bool finished;
+  ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs, &finished));
+  EXPECT_FALSE(finished);
   EXPECT_EQ(0x3000U, regs.sp());
   EXPECT_EQ(0x80000000U, regs.pc());
 }
 
-TYPED_TEST_P(DwarfSectionImplTest, Eval_same_cfa_same_pc) {
-  DwarfCie cie{.version = 3, .return_address_register = 5};
-  RegsFake<TypeParam> regs(10, 9);
-  dwarf_loc_regs_t loc_regs;
-
-  regs.set_pc(0x100);
-  regs.set_sp(0x2000);
-  regs[5] = 0x100;
-  regs[8] = 0x2000;
-  loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {8, 0}};
-  ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs));
-  EXPECT_EQ(0x2000U, regs.sp());
-  EXPECT_EQ(0x100U, regs.pc());
-}
-
-TYPED_TEST_P(DwarfSectionImplTest, GetCie_fail_should_not_cache) {
-  ASSERT_TRUE(this->section_->GetCie(0x4000) == nullptr);
-  EXPECT_EQ(DWARF_ERROR_MEMORY_INVALID, this->section_->last_error());
-  this->section_->TestClearError();
-  ASSERT_TRUE(this->section_->GetCie(0x4000) == nullptr);
-  EXPECT_EQ(DWARF_ERROR_MEMORY_INVALID, this->section_->last_error());
-}
-
-TYPED_TEST_P(DwarfSectionImplTest, GetCie_32_version_check) {
-  this->memory_.SetData32(0x5000, 0x100);
-  this->memory_.SetData32(0x5004, 0xffffffff);
-  this->memory_.SetData8(0x5008, 0x1);
-  this->memory_.SetData8(0x5009, '\0');
-  this->memory_.SetData8(0x500a, 4);
-  this->memory_.SetData8(0x500b, 8);
-  this->memory_.SetData8(0x500c, 0x20);
-
-  EXPECT_CALL(*this->section_, IsCie32(0xffffffff)).WillRepeatedly(::testing::Return(true));
-
-  const DwarfCie* cie = this->section_->GetCie(0x5000);
-  ASSERT_TRUE(cie != nullptr);
-  EXPECT_EQ(1U, cie->version);
-  EXPECT_EQ(DW_EH_PE_sdata4, cie->fde_address_encoding);
-  EXPECT_EQ(DW_EH_PE_omit, cie->lsda_encoding);
-  EXPECT_EQ(0U, cie->segment_size);
-  EXPECT_EQ(1U, cie->augmentation_string.size());
-  EXPECT_EQ('\0', cie->augmentation_string[0]);
-  EXPECT_EQ(0U, cie->personality_handler);
-  EXPECT_EQ(0x500dU, cie->cfa_instructions_offset);
-  EXPECT_EQ(0x5104U, cie->cfa_instructions_end);
-  EXPECT_EQ(4U, cie->code_alignment_factor);
-  EXPECT_EQ(8, cie->data_alignment_factor);
-  EXPECT_EQ(0x20U, cie->return_address_register);
-  EXPECT_EQ(DWARF_ERROR_NONE, this->section_->last_error());
-
-  this->section_->TestClearCachedCieEntry();
-  // Set version to 0, 2, 5 and verify we fail.
-  this->memory_.SetData8(0x5008, 0x0);
-  this->section_->TestClearError();
-  ASSERT_TRUE(this->section_->GetCie(0x5000) == nullptr);
-  EXPECT_EQ(DWARF_ERROR_UNSUPPORTED_VERSION, this->section_->last_error());
-
-  this->memory_.SetData8(0x5008, 0x2);
-  this->section_->TestClearError();
-  ASSERT_TRUE(this->section_->GetCie(0x5000) == nullptr);
-  EXPECT_EQ(DWARF_ERROR_UNSUPPORTED_VERSION, this->section_->last_error());
-
-  this->memory_.SetData8(0x5008, 0x5);
-  this->section_->TestClearError();
-  ASSERT_TRUE(this->section_->GetCie(0x5000) == nullptr);
-  EXPECT_EQ(DWARF_ERROR_UNSUPPORTED_VERSION, this->section_->last_error());
-}
-
-TYPED_TEST_P(DwarfSectionImplTest, GetCie_negative_data_alignment_factor) {
-  this->memory_.SetData32(0x5000, 0x100);
-  this->memory_.SetData32(0x5004, 0xffffffff);
-  this->memory_.SetData8(0x5008, 0x1);
-  this->memory_.SetData8(0x5009, '\0');
-  this->memory_.SetData8(0x500a, 4);
-  this->memory_.SetMemory(0x500b, std::vector<uint8_t>{0xfc, 0xff, 0xff, 0xff, 0x7f});
-  this->memory_.SetData8(0x5010, 0x20);
-
-  EXPECT_CALL(*this->section_, IsCie32(0xffffffff)).WillRepeatedly(::testing::Return(true));
-
-  const DwarfCie* cie = this->section_->GetCie(0x5000);
-  ASSERT_TRUE(cie != nullptr);
-  EXPECT_EQ(1U, cie->version);
-  EXPECT_EQ(DW_EH_PE_sdata4, cie->fde_address_encoding);
-  EXPECT_EQ(DW_EH_PE_omit, cie->lsda_encoding);
-  EXPECT_EQ(0U, cie->segment_size);
-  EXPECT_EQ(1U, cie->augmentation_string.size());
-  EXPECT_EQ('\0', cie->augmentation_string[0]);
-  EXPECT_EQ(0U, cie->personality_handler);
-  EXPECT_EQ(0x5011U, cie->cfa_instructions_offset);
-  EXPECT_EQ(0x5104U, cie->cfa_instructions_end);
-  EXPECT_EQ(4U, cie->code_alignment_factor);
-  EXPECT_EQ(-4, cie->data_alignment_factor);
-  EXPECT_EQ(0x20U, cie->return_address_register);
-}
-
-TYPED_TEST_P(DwarfSectionImplTest, GetCie_64_no_augment) {
-  this->memory_.SetData32(0x8000, 0xffffffff);
-  this->memory_.SetData64(0x8004, 0x200);
-  this->memory_.SetData64(0x800c, 0xffffffff);
-  this->memory_.SetData8(0x8014, 0x1);
-  this->memory_.SetData8(0x8015, '\0');
-  this->memory_.SetData8(0x8016, 4);
-  this->memory_.SetData8(0x8017, 8);
-  this->memory_.SetData8(0x8018, 0x20);
-
-  EXPECT_CALL(*this->section_, IsCie64(0xffffffff)).WillRepeatedly(::testing::Return(true));
-
-  const DwarfCie* cie = this->section_->GetCie(0x8000);
-  ASSERT_TRUE(cie != nullptr);
-  EXPECT_EQ(1U, cie->version);
-  EXPECT_EQ(DW_EH_PE_sdata8, cie->fde_address_encoding);
-  EXPECT_EQ(DW_EH_PE_omit, cie->lsda_encoding);
-  EXPECT_EQ(0U, cie->segment_size);
-  EXPECT_EQ(1U, cie->augmentation_string.size());
-  EXPECT_EQ('\0', cie->augmentation_string[0]);
-  EXPECT_EQ(0U, cie->personality_handler);
-  EXPECT_EQ(0x8019U, cie->cfa_instructions_offset);
-  EXPECT_EQ(0x820cU, cie->cfa_instructions_end);
-  EXPECT_EQ(4U, cie->code_alignment_factor);
-  EXPECT_EQ(8, cie->data_alignment_factor);
-  EXPECT_EQ(0x20U, cie->return_address_register);
-}
-
-TYPED_TEST_P(DwarfSectionImplTest, GetCie_augment) {
-  this->memory_.SetData32(0x5000, 0x100);
-  this->memory_.SetData32(0x5004, 0xffffffff);
-  this->memory_.SetData8(0x5008, 0x1);
-  this->memory_.SetMemory(0x5009, std::vector<uint8_t>{'z', 'L', 'P', 'R', '\0'});
-  this->memory_.SetData8(0x500e, 4);
-  this->memory_.SetData8(0x500f, 8);
-  this->memory_.SetData8(0x5010, 0x10);
-  // Augment length.
-  this->memory_.SetData8(0x5011, 0xf);
-  // L data.
-  this->memory_.SetData8(0x5012, DW_EH_PE_textrel | DW_EH_PE_udata2);
-  // P data.
-  this->memory_.SetData8(0x5013, DW_EH_PE_udata4);
-  this->memory_.SetData32(0x5014, 0x12345678);
-  // R data.
-  this->memory_.SetData8(0x5018, DW_EH_PE_udata2);
-
-  EXPECT_CALL(*this->section_, IsCie32(0xffffffff)).WillRepeatedly(::testing::Return(true));
-
-  const DwarfCie* cie = this->section_->GetCie(0x5000);
-  ASSERT_TRUE(cie != nullptr);
-  EXPECT_EQ(1U, cie->version);
-  EXPECT_EQ(DW_EH_PE_udata2, cie->fde_address_encoding);
-  EXPECT_EQ(DW_EH_PE_textrel | DW_EH_PE_udata2, cie->lsda_encoding);
-  EXPECT_EQ(0U, cie->segment_size);
-  EXPECT_EQ(5U, cie->augmentation_string.size());
-  EXPECT_EQ('z', cie->augmentation_string[0]);
-  EXPECT_EQ('L', cie->augmentation_string[1]);
-  EXPECT_EQ('P', cie->augmentation_string[2]);
-  EXPECT_EQ('R', cie->augmentation_string[3]);
-  EXPECT_EQ('\0', cie->augmentation_string[4]);
-  EXPECT_EQ(0x12345678U, cie->personality_handler);
-  EXPECT_EQ(0x5021U, cie->cfa_instructions_offset);
-  EXPECT_EQ(0x5104U, cie->cfa_instructions_end);
-  EXPECT_EQ(4U, cie->code_alignment_factor);
-  EXPECT_EQ(8, cie->data_alignment_factor);
-  EXPECT_EQ(0x10U, cie->return_address_register);
-}
-
-TYPED_TEST_P(DwarfSectionImplTest, GetCie_version_3) {
-  this->memory_.SetData32(0x5000, 0x100);
-  this->memory_.SetData32(0x5004, 0xffffffff);
-  this->memory_.SetData8(0x5008, 0x3);
-  this->memory_.SetData8(0x5009, '\0');
-  this->memory_.SetData8(0x500a, 4);
-  this->memory_.SetData8(0x500b, 8);
-  this->memory_.SetMemory(0x500c, std::vector<uint8_t>{0x81, 0x03});
-
-  EXPECT_CALL(*this->section_, IsCie32(0xffffffff)).WillRepeatedly(::testing::Return(true));
-
-  const DwarfCie* cie = this->section_->GetCie(0x5000);
-  ASSERT_TRUE(cie != nullptr);
-  EXPECT_EQ(3U, cie->version);
-  EXPECT_EQ(DW_EH_PE_sdata4, cie->fde_address_encoding);
-  EXPECT_EQ(DW_EH_PE_omit, cie->lsda_encoding);
-  EXPECT_EQ(0U, cie->segment_size);
-  EXPECT_EQ(1U, cie->augmentation_string.size());
-  EXPECT_EQ('\0', cie->augmentation_string[0]);
-  EXPECT_EQ(0U, cie->personality_handler);
-  EXPECT_EQ(0x500eU, cie->cfa_instructions_offset);
-  EXPECT_EQ(0x5104U, cie->cfa_instructions_end);
-  EXPECT_EQ(4U, cie->code_alignment_factor);
-  EXPECT_EQ(8, cie->data_alignment_factor);
-  EXPECT_EQ(0x181U, cie->return_address_register);
-}
-
-TYPED_TEST_P(DwarfSectionImplTest, GetCie_version_4) {
-  this->memory_.SetData32(0x5000, 0x100);
-  this->memory_.SetData32(0x5004, 0xffffffff);
-  this->memory_.SetData8(0x5008, 0x4);
-  this->memory_.SetData8(0x5009, '\0');
-  this->memory_.SetData8(0x500a, 4);
-  this->memory_.SetData8(0x500b, 0x13);
-  this->memory_.SetData8(0x500c, 4);
-  this->memory_.SetData8(0x500d, 8);
-  this->memory_.SetMemory(0x500e, std::vector<uint8_t>{0x81, 0x03});
-
-  EXPECT_CALL(*this->section_, IsCie32(0xffffffff)).WillRepeatedly(::testing::Return(true));
-
-  const DwarfCie* cie = this->section_->GetCie(0x5000);
-  ASSERT_TRUE(cie != nullptr);
-  EXPECT_EQ(4U, cie->version);
-  EXPECT_EQ(DW_EH_PE_sdata4, cie->fde_address_encoding);
-  EXPECT_EQ(DW_EH_PE_omit, cie->lsda_encoding);
-  EXPECT_EQ(0x13U, cie->segment_size);
-  EXPECT_EQ(1U, cie->augmentation_string.size());
-  EXPECT_EQ('\0', cie->augmentation_string[0]);
-  EXPECT_EQ(0U, cie->personality_handler);
-  EXPECT_EQ(0x5010U, cie->cfa_instructions_offset);
-  EXPECT_EQ(0x5104U, cie->cfa_instructions_end);
-  EXPECT_EQ(4U, cie->code_alignment_factor);
-  EXPECT_EQ(8, cie->data_alignment_factor);
-  EXPECT_EQ(0x181U, cie->return_address_register);
-}
-
-TYPED_TEST_P(DwarfSectionImplTest, GetFdeFromOffset_fail_should_not_cache) {
-  ASSERT_TRUE(this->section_->GetFdeFromOffset(0x4000) == nullptr);
-  EXPECT_EQ(DWARF_ERROR_MEMORY_INVALID, this->section_->last_error());
-  this->section_->TestClearError();
-  ASSERT_TRUE(this->section_->GetFdeFromOffset(0x4000) == nullptr);
-  EXPECT_EQ(DWARF_ERROR_MEMORY_INVALID, this->section_->last_error());
-}
-
-TYPED_TEST_P(DwarfSectionImplTest, GetFdeFromOffset_32_no_augment) {
-  this->memory_.SetData32(0x4000, 0x20);
-  this->memory_.SetData32(0x4004, 0x8000);
-  this->memory_.SetData32(0x4008, 0x5000);
-  this->memory_.SetData32(0x400c, 0x100);
-
-  EXPECT_CALL(*this->section_, IsCie32(0x8000)).WillOnce(::testing::Return(false));
-  EXPECT_CALL(*this->section_, GetCieOffsetFromFde32(0x8000)).WillOnce(::testing::Return(0x8000));
-  DwarfCie cie{};
-  cie.fde_address_encoding = DW_EH_PE_udata4;
-  this->section_->TestSetCachedCieEntry(0x8000, cie);
-  EXPECT_CALL(*this->section_, AdjustPcFromFde(0x5000)).WillOnce(::testing::Return(0x5000));
-
-  const DwarfFde* fde = this->section_->GetFdeFromOffset(0x4000);
-  ASSERT_TRUE(fde != nullptr);
-  ASSERT_TRUE(fde->cie != nullptr);
-  EXPECT_EQ(0x4010U, fde->cfa_instructions_offset);
-  EXPECT_EQ(0x4024U, fde->cfa_instructions_end);
-  EXPECT_EQ(0x5000U, fde->pc_start);
-  EXPECT_EQ(0x5100U, fde->pc_end);
-  EXPECT_EQ(0x8000U, fde->cie_offset);
-  EXPECT_EQ(0U, fde->lsda_address);
-}
-
-TYPED_TEST_P(DwarfSectionImplTest, GetFdeFromOffset_32_no_augment_non_zero_segment_size) {
-  this->memory_.SetData32(0x4000, 0x30);
-  this->memory_.SetData32(0x4004, 0x8000);
-  this->memory_.SetData32(0x4018, 0x5000);
-  this->memory_.SetData32(0x401c, 0x100);
-
-  EXPECT_CALL(*this->section_, IsCie32(0x8000)).WillOnce(::testing::Return(false));
-  EXPECT_CALL(*this->section_, GetCieOffsetFromFde32(0x8000)).WillOnce(::testing::Return(0x8000));
-  DwarfCie cie{};
-  cie.fde_address_encoding = DW_EH_PE_udata4;
-  cie.segment_size = 0x10;
-  this->section_->TestSetCachedCieEntry(0x8000, cie);
-  EXPECT_CALL(*this->section_, AdjustPcFromFde(0x5000)).WillOnce(::testing::Return(0x5000));
-
-  const DwarfFde* fde = this->section_->GetFdeFromOffset(0x4000);
-  ASSERT_TRUE(fde != nullptr);
-  ASSERT_TRUE(fde->cie != nullptr);
-  EXPECT_EQ(0x4020U, fde->cfa_instructions_offset);
-  EXPECT_EQ(0x4034U, fde->cfa_instructions_end);
-  EXPECT_EQ(0x5000U, fde->pc_start);
-  EXPECT_EQ(0x5100U, fde->pc_end);
-  EXPECT_EQ(0x8000U, fde->cie_offset);
-  EXPECT_EQ(0U, fde->lsda_address);
-}
-
-TYPED_TEST_P(DwarfSectionImplTest, GetFdeFromOffset_32_augment) {
-  this->memory_.SetData32(0x4000, 0x100);
-  this->memory_.SetData32(0x4004, 0x8000);
-  this->memory_.SetData32(0x4008, 0x5000);
-  this->memory_.SetData32(0x400c, 0x100);
-  this->memory_.SetMemory(0x4010, std::vector<uint8_t>{0x82, 0x01});
-  this->memory_.SetData16(0x4012, 0x1234);
-
-  EXPECT_CALL(*this->section_, IsCie32(0x8000)).WillOnce(::testing::Return(false));
-  EXPECT_CALL(*this->section_, GetCieOffsetFromFde32(0x8000)).WillOnce(::testing::Return(0x8000));
-  DwarfCie cie{};
-  cie.fde_address_encoding = DW_EH_PE_udata4;
-  cie.augmentation_string.push_back('z');
-  cie.lsda_encoding = DW_EH_PE_udata2;
-  this->section_->TestSetCachedCieEntry(0x8000, cie);
-  EXPECT_CALL(*this->section_, AdjustPcFromFde(0x5000)).WillOnce(::testing::Return(0x5000));
-
-  const DwarfFde* fde = this->section_->GetFdeFromOffset(0x4000);
-  ASSERT_TRUE(fde != nullptr);
-  ASSERT_TRUE(fde->cie != nullptr);
-  EXPECT_EQ(0x4094U, fde->cfa_instructions_offset);
-  EXPECT_EQ(0x4104U, fde->cfa_instructions_end);
-  EXPECT_EQ(0x5000U, fde->pc_start);
-  EXPECT_EQ(0x5100U, fde->pc_end);
-  EXPECT_EQ(0x8000U, fde->cie_offset);
-  EXPECT_EQ(0x1234U, fde->lsda_address);
-}
-
-TYPED_TEST_P(DwarfSectionImplTest, GetFdeFromOffset_64_no_augment) {
-  this->memory_.SetData32(0x4000, 0xffffffff);
-  this->memory_.SetData64(0x4004, 0x100);
-  this->memory_.SetData64(0x400c, 0x12345678);
-  this->memory_.SetData32(0x4014, 0x5000);
-  this->memory_.SetData32(0x4018, 0x100);
-
-  EXPECT_CALL(*this->section_, IsCie64(0x12345678)).WillOnce(::testing::Return(false));
-  EXPECT_CALL(*this->section_, GetCieOffsetFromFde64(0x12345678))
-      .WillOnce(::testing::Return(0x12345678));
-  DwarfCie cie{};
-  cie.fde_address_encoding = DW_EH_PE_udata4;
-  this->section_->TestSetCachedCieEntry(0x12345678, cie);
-  EXPECT_CALL(*this->section_, AdjustPcFromFde(0x5000)).WillOnce(::testing::Return(0x5000));
-
-  const DwarfFde* fde = this->section_->GetFdeFromOffset(0x4000);
-  ASSERT_TRUE(fde != nullptr);
-  ASSERT_TRUE(fde->cie != nullptr);
-  EXPECT_EQ(0x401cU, fde->cfa_instructions_offset);
-  EXPECT_EQ(0x410cU, fde->cfa_instructions_end);
-  EXPECT_EQ(0x5000U, fde->pc_start);
-  EXPECT_EQ(0x5100U, fde->pc_end);
-  EXPECT_EQ(0x12345678U, fde->cie_offset);
-  EXPECT_EQ(0U, fde->lsda_address);
-}
-
-TYPED_TEST_P(DwarfSectionImplTest, GetFdeFromOffset_cached) {
-  DwarfCie cie{};
-  cie.fde_address_encoding = DW_EH_PE_udata4;
-  cie.augmentation_string.push_back('z');
-  cie.lsda_encoding = DW_EH_PE_udata2;
-
-  DwarfFde fde_cached{};
-  fde_cached.cfa_instructions_offset = 0x1000;
-  fde_cached.cfa_instructions_end = 0x1100;
-  fde_cached.pc_start = 0x9000;
-  fde_cached.pc_end = 0x9400;
-  fde_cached.cie_offset = 0x30000;
-  fde_cached.cie = &cie;
-  this->section_->TestSetCachedFdeEntry(0x6000, fde_cached);
-
-  const DwarfFde* fde = this->section_->GetFdeFromOffset(0x6000);
-  ASSERT_TRUE(fde != nullptr);
-  ASSERT_EQ(&cie, fde->cie);
-  EXPECT_EQ(0x1000U, fde->cfa_instructions_offset);
-  EXPECT_EQ(0x1100U, fde->cfa_instructions_end);
-  EXPECT_EQ(0x9000U, fde->pc_start);
-  EXPECT_EQ(0x9400U, fde->pc_end);
-  EXPECT_EQ(0x30000U, fde->cie_offset);
-}
-
 TYPED_TEST_P(DwarfSectionImplTest, GetCfaLocationInfo_cie_not_cached) {
   DwarfCie cie{};
   cie.cfa_instructions_offset = 0x3000;
@@ -774,7 +529,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});
 
@@ -804,7 +560,7 @@
 
   this->memory_.SetMemory(0x5000, std::vector<uint8_t>{0x00});
   this->memory_.SetMemory(0x6000, std::vector<uint8_t>{0xc2});
-  ASSERT_TRUE(this->section_->Log(2, 0x1000, 0x1000, &fde));
+  ASSERT_TRUE(this->section_->Log(2, 0x1000, &fde));
 
   ASSERT_EQ(
       "4 unwind     DW_CFA_nop\n"
@@ -815,18 +571,18 @@
   ASSERT_EQ("", GetFakeLogBuf());
 }
 
-REGISTER_TYPED_TEST_CASE_P(
-    DwarfSectionImplTest, Eval_cfa_expr_eval_fail, Eval_cfa_expr_no_stack,
-    Eval_cfa_expr_is_register, Eval_cfa_expr, Eval_cfa_val_expr, Eval_bad_regs, Eval_no_cfa,
-    Eval_cfa_bad, Eval_cfa_register_prev, Eval_cfa_register_from_value, Eval_double_indirection,
-    Eval_invalid_register, Eval_different_reg_locations, Eval_return_address_undefined,
-    Eval_return_address, Eval_ignore_large_reg_loc, Eval_reg_expr, Eval_reg_val_expr,
-    Eval_same_cfa_same_pc, GetCie_fail_should_not_cache, GetCie_32_version_check,
-    GetCie_negative_data_alignment_factor, GetCie_64_no_augment, GetCie_augment, GetCie_version_3,
-    GetCie_version_4, GetFdeFromOffset_fail_should_not_cache, GetFdeFromOffset_32_no_augment,
-    GetFdeFromOffset_32_no_augment_non_zero_segment_size, GetFdeFromOffset_32_augment,
-    GetFdeFromOffset_64_no_augment, GetFdeFromOffset_cached, GetCfaLocationInfo_cie_not_cached,
-    GetCfaLocationInfo_cie_cached, Log);
+REGISTER_TYPED_TEST_CASE_P(DwarfSectionImplTest, GetCieFromOffset_fail_should_not_cache,
+                           GetFdeFromOffset_fail_should_not_cache, Eval_cfa_expr_eval_fail,
+                           Eval_cfa_expr_no_stack, Eval_cfa_expr_is_register, Eval_cfa_expr,
+                           Eval_cfa_val_expr, Eval_bad_regs, Eval_no_cfa, Eval_cfa_bad,
+                           Eval_cfa_register_prev, Eval_cfa_register_from_value,
+                           Eval_double_indirection, Eval_register_reference_chain, Eval_dex_pc,
+                           Eval_invalid_register, Eval_different_reg_locations,
+                           Eval_return_address_undefined, Eval_pc_zero, Eval_return_address,
+                           Eval_ignore_large_reg_loc, Eval_reg_expr, Eval_reg_val_expr,
+                           GetCfaLocationInfo_cie_not_cached, GetCfaLocationInfo_cie_cached, Log);
 
 typedef ::testing::Types<uint32_t, uint64_t> DwarfSectionImplTestTypes;
 INSTANTIATE_TYPED_TEST_CASE_P(, DwarfSectionImplTest, DwarfSectionImplTestTypes);
+
+}  // namespace unwindstack
diff --git a/libunwindstack/tests/DwarfSectionTest.cpp b/libunwindstack/tests/DwarfSectionTest.cpp
index 20f0e2a..d754fcc 100644
--- a/libunwindstack/tests/DwarfSectionTest.cpp
+++ b/libunwindstack/tests/DwarfSectionTest.cpp
@@ -19,33 +19,29 @@
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
 
-#include "DwarfSection.h"
+#include <unwindstack/DwarfSection.h>
 
 #include "MemoryFake.h"
 
+namespace unwindstack {
+
 class MockDwarfSection : public DwarfSection {
  public:
   MockDwarfSection(Memory* memory) : DwarfSection(memory) {}
   virtual ~MockDwarfSection() = default;
 
-  MOCK_METHOD4(Log, bool(uint8_t, uint64_t, uint64_t, const DwarfFde*));
+  MOCK_METHOD3(Init, bool(uint64_t, uint64_t, uint64_t));
 
-  MOCK_METHOD4(Eval, bool(const DwarfCie*, Memory*, const dwarf_loc_regs_t&, Regs*));
+  MOCK_METHOD5(Eval, bool(const DwarfCie*, Memory*, const dwarf_loc_regs_t&, Regs*, bool*));
+
+  MOCK_METHOD3(Log, bool(uint8_t, uint64_t, const DwarfFde*));
+
+  MOCK_METHOD1(GetFdes, void(std::vector<const DwarfFde*>*));
+
+  MOCK_METHOD1(GetFdeFromPc, const DwarfFde*(uint64_t));
 
   MOCK_METHOD3(GetCfaLocationInfo, bool(uint64_t, const DwarfFde*, dwarf_loc_regs_t*));
 
-  MOCK_METHOD2(Init, bool(uint64_t, uint64_t));
-
-  MOCK_METHOD2(GetFdeOffsetFromPc, bool(uint64_t, uint64_t*));
-
-  MOCK_METHOD1(GetFdeFromOffset, const DwarfFde*(uint64_t));
-
-  MOCK_METHOD1(GetFdeFromIndex, const DwarfFde*(size_t));
-
-  MOCK_METHOD1(IsCie32, bool(uint32_t));
-
-  MOCK_METHOD1(IsCie64, bool(uint64_t));
-
   MOCK_METHOD1(GetCieOffsetFromFde32, uint64_t(uint32_t));
 
   MOCK_METHOD1(GetCieOffsetFromFde64, uint64_t(uint64_t));
@@ -55,106 +51,117 @@
 
 class DwarfSectionTest : public ::testing::Test {
  protected:
+  void SetUp() override { section_.reset(new MockDwarfSection(&memory_)); }
+
   MemoryFake memory_;
+  std::unique_ptr<MockDwarfSection> section_;
 };
 
-TEST_F(DwarfSectionTest, GetFdeOffsetFromPc_fail_from_pc) {
-  MockDwarfSection mock_section(&memory_);
-
-  EXPECT_CALL(mock_section, GetFdeOffsetFromPc(0x1000, ::testing::_))
-      .WillOnce(::testing::Return(false));
-
-  // Verify nullptr when GetFdeOffsetFromPc fails.
-  ASSERT_TRUE(mock_section.GetFdeFromPc(0x1000) == nullptr);
-}
-
-TEST_F(DwarfSectionTest, GetFdeOffsetFromPc_fail_fde_pc_end) {
-  MockDwarfSection mock_section(&memory_);
-
-  DwarfFde fde{};
-  fde.pc_end = 0x500;
-
-  EXPECT_CALL(mock_section, GetFdeOffsetFromPc(0x1000, ::testing::_))
-      .WillOnce(::testing::Return(true));
-  EXPECT_CALL(mock_section, GetFdeFromOffset(::testing::_)).WillOnce(::testing::Return(&fde));
-
-  // Verify nullptr when GetFdeOffsetFromPc fails.
-  ASSERT_TRUE(mock_section.GetFdeFromPc(0x1000) == nullptr);
-}
-
-TEST_F(DwarfSectionTest, GetFdeOffsetFromPc_pass) {
-  MockDwarfSection mock_section(&memory_);
-
-  DwarfFde fde{};
-  fde.pc_end = 0x2000;
-
-  EXPECT_CALL(mock_section, GetFdeOffsetFromPc(0x1000, ::testing::_))
-      .WillOnce(::testing::Return(true));
-  EXPECT_CALL(mock_section, GetFdeFromOffset(::testing::_)).WillOnce(::testing::Return(&fde));
-
-  // Verify nullptr when GetFdeOffsetFromPc fails.
-  ASSERT_EQ(&fde, mock_section.GetFdeFromPc(0x1000));
-}
-
 TEST_F(DwarfSectionTest, Step_fail_fde) {
-  MockDwarfSection mock_section(&memory_);
+  EXPECT_CALL(*section_, GetFdeFromPc(0x1000)).WillOnce(::testing::Return(nullptr));
 
-  EXPECT_CALL(mock_section, GetFdeOffsetFromPc(0x1000, ::testing::_))
-      .WillOnce(::testing::Return(false));
-
-  ASSERT_FALSE(mock_section.Step(0x1000, nullptr, nullptr));
+  bool finished;
+  ASSERT_FALSE(section_->Step(0x1000, nullptr, nullptr, &finished));
 }
 
 TEST_F(DwarfSectionTest, Step_fail_cie_null) {
-  MockDwarfSection mock_section(&memory_);
-
   DwarfFde fde{};
   fde.pc_end = 0x2000;
   fde.cie = nullptr;
 
-  EXPECT_CALL(mock_section, GetFdeOffsetFromPc(0x1000, ::testing::_))
-      .WillOnce(::testing::Return(true));
-  EXPECT_CALL(mock_section, GetFdeFromOffset(::testing::_)).WillOnce(::testing::Return(&fde));
+  EXPECT_CALL(*section_, GetFdeFromPc(0x1000)).WillOnce(::testing::Return(&fde));
 
-  ASSERT_FALSE(mock_section.Step(0x1000, nullptr, nullptr));
+  bool finished;
+  ASSERT_FALSE(section_->Step(0x1000, nullptr, nullptr, &finished));
 }
 
 TEST_F(DwarfSectionTest, Step_fail_cfa_location) {
-  MockDwarfSection mock_section(&memory_);
-
   DwarfCie cie{};
   DwarfFde fde{};
   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::_))
+  EXPECT_CALL(*section_, GetFdeFromPc(0x1000)).WillOnce(::testing::Return(&fde));
+  EXPECT_CALL(*section_, GetCfaLocationInfo(0x1000, &fde, ::testing::_))
       .WillOnce(::testing::Return(false));
 
-  ASSERT_FALSE(mock_section.Step(0x1000, nullptr, nullptr));
+  bool finished;
+  ASSERT_FALSE(section_->Step(0x1000, nullptr, nullptr, &finished));
 }
 
 TEST_F(DwarfSectionTest, Step_pass) {
-  MockDwarfSection mock_section(&memory_);
-
   DwarfCie cie{};
   DwarfFde fde{};
   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::_))
+  EXPECT_CALL(*section_, GetFdeFromPc(0x1000)).WillOnce(::testing::Return(&fde));
+  EXPECT_CALL(*section_, GetCfaLocationInfo(0x1000, &fde, ::testing::_))
       .WillOnce(::testing::Return(true));
 
   MemoryFake process;
-  EXPECT_CALL(mock_section, Eval(&cie, &process, ::testing::_, nullptr))
+  EXPECT_CALL(*section_, Eval(&cie, &process, ::testing::_, nullptr, ::testing::_))
       .WillOnce(::testing::Return(true));
 
-  ASSERT_TRUE(mock_section.Step(0x1000, nullptr, &process));
+  bool finished;
+  ASSERT_TRUE(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) {
+  DwarfCie cie{};
+  DwarfFde fde{};
+  fde.pc_start = 0x500;
+  fde.pc_end = 0x2000;
+  fde.cie = &cie;
+
+  EXPECT_CALL(*section_, GetFdeFromPc(0x1000)).WillOnce(::testing::Return(&fde));
+  EXPECT_CALL(*section_, GetCfaLocationInfo(0x1000, &fde, ::testing::_))
+      .WillOnce(::testing::Invoke(MockGetCfaLocationInfo));
+
+  MemoryFake process;
+  EXPECT_CALL(*section_, Eval(&cie, &process, ::testing::_, nullptr, ::testing::_))
+      .WillRepeatedly(::testing::Return(true));
+
+  bool finished;
+  ASSERT_TRUE(section_->Step(0x1000, nullptr, &process, &finished));
+  ASSERT_TRUE(section_->Step(0x1000, nullptr, &process, &finished));
+  ASSERT_TRUE(section_->Step(0x1500, nullptr, &process, &finished));
+}
+
+TEST_F(DwarfSectionTest, Step_cache_not_in_pc) {
+  DwarfCie cie{};
+  DwarfFde fde0{};
+  fde0.pc_start = 0x1000;
+  fde0.pc_end = 0x2000;
+  fde0.cie = &cie;
+  EXPECT_CALL(*section_, GetFdeFromPc(0x1000)).WillOnce(::testing::Return(&fde0));
+  EXPECT_CALL(*section_, GetCfaLocationInfo(0x1000, &fde0, ::testing::_))
+      .WillOnce(::testing::Invoke(MockGetCfaLocationInfo));
+
+  MemoryFake process;
+  EXPECT_CALL(*section_, Eval(&cie, &process, ::testing::_, nullptr, ::testing::_))
+      .WillRepeatedly(::testing::Return(true));
+
+  bool finished;
+  ASSERT_TRUE(section_->Step(0x1000, nullptr, &process, &finished));
+
+  DwarfFde fde1{};
+  fde1.pc_start = 0x500;
+  fde1.pc_end = 0x800;
+  fde1.cie = &cie;
+  EXPECT_CALL(*section_, GetFdeFromPc(0x600)).WillOnce(::testing::Return(&fde1));
+  EXPECT_CALL(*section_, GetCfaLocationInfo(0x600, &fde1, ::testing::_))
+      .WillOnce(::testing::Invoke(MockGetCfaLocationInfo));
+
+  ASSERT_TRUE(section_->Step(0x600, nullptr, &process, &finished));
+  ASSERT_TRUE(section_->Step(0x700, nullptr, &process, &finished));
+}
+
+}  // namespace unwindstack
diff --git a/libunwindstack/tests/ElfCacheTest.cpp b/libunwindstack/tests/ElfCacheTest.cpp
new file mode 100644
index 0000000..89331ea
--- /dev/null
+++ b/libunwindstack/tests/ElfCacheTest.cpp
@@ -0,0 +1,264 @@
+/*
+ * 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 <elf.h>
+#include <unistd.h>
+
+#include <android-base/file.h>
+#include <android-base/test_utils.h>
+
+#include <gtest/gtest.h>
+
+#include <unwindstack/Elf.h>
+#include <unwindstack/MapInfo.h>
+
+#include "ElfTestUtils.h"
+#include "MemoryFake.h"
+
+namespace unwindstack {
+
+class ElfCacheTest : public ::testing::Test {
+ protected:
+  static void SetUpTestCase() { memory_.reset(new MemoryFake); }
+
+  void SetUp() override { Elf::SetCachingEnabled(true); }
+
+  void TearDown() override { Elf::SetCachingEnabled(false); }
+
+  void WriteElfFile(uint64_t offset, TemporaryFile* tf, uint32_t type) {
+    ASSERT_TRUE(type == EM_ARM || type == EM_386 || type == EM_X86_64);
+    size_t ehdr_size;
+    Elf32_Ehdr ehdr32;
+    Elf64_Ehdr ehdr64;
+    void* ptr;
+    if (type == EM_ARM || type == EM_386) {
+      ehdr_size = sizeof(ehdr32);
+      ptr = &ehdr32;
+      TestInitEhdr(&ehdr32, ELFCLASS32, type);
+    } else {
+      ehdr_size = sizeof(ehdr64);
+      ptr = &ehdr64;
+      TestInitEhdr(&ehdr64, ELFCLASS64, type);
+    }
+
+    ASSERT_EQ(offset, static_cast<uint64_t>(lseek(tf->fd, offset, SEEK_SET)));
+    ASSERT_TRUE(android::base::WriteFully(tf->fd, ptr, ehdr_size));
+  }
+
+  void VerifyWithinSameMap(bool cache_enabled);
+  void VerifySameMap(bool cache_enabled);
+  void VerifyWithinSameMapNeverReadAtZero(bool cache_enabled);
+
+  static std::shared_ptr<Memory> memory_;
+};
+
+std::shared_ptr<Memory> ElfCacheTest::memory_;
+
+void ElfCacheTest::VerifySameMap(bool cache_enabled) {
+  if (!cache_enabled) {
+    Elf::SetCachingEnabled(false);
+  }
+
+  TemporaryFile tf;
+  ASSERT_TRUE(tf.fd != -1);
+  WriteElfFile(0, &tf, EM_ARM);
+  close(tf.fd);
+
+  uint64_t start = 0x1000;
+  uint64_t end = 0x20000;
+  MapInfo info1(start, end, 0, 0x5, tf.path);
+  MapInfo info2(start, end, 0, 0x5, tf.path);
+
+  Elf* elf1 = info1.GetElf(memory_, true);
+  ASSERT_TRUE(elf1->valid());
+  Elf* elf2 = info2.GetElf(memory_, true);
+  ASSERT_TRUE(elf2->valid());
+
+  if (cache_enabled) {
+    EXPECT_EQ(elf1, elf2);
+  } else {
+    EXPECT_NE(elf1, elf2);
+  }
+}
+
+TEST_F(ElfCacheTest, no_caching) {
+  VerifySameMap(false);
+}
+
+TEST_F(ElfCacheTest, caching_invalid_elf) {
+  VerifySameMap(true);
+}
+
+void ElfCacheTest::VerifyWithinSameMap(bool cache_enabled) {
+  if (!cache_enabled) {
+    Elf::SetCachingEnabled(false);
+  }
+
+  TemporaryFile tf;
+  ASSERT_TRUE(tf.fd != -1);
+  WriteElfFile(0, &tf, EM_ARM);
+  WriteElfFile(0x100, &tf, EM_386);
+  WriteElfFile(0x200, &tf, EM_X86_64);
+  lseek(tf.fd, 0x500, SEEK_SET);
+  uint8_t value = 0;
+  write(tf.fd, &value, 1);
+  close(tf.fd);
+
+  uint64_t start = 0x1000;
+  uint64_t end = 0x20000;
+  // Will have an elf at offset 0 in file.
+  MapInfo info0_1(start, end, 0, 0x5, tf.path);
+  MapInfo info0_2(start, end, 0, 0x5, tf.path);
+  // Will have an elf at offset 0x100 in file.
+  MapInfo info100_1(start, end, 0x100, 0x5, tf.path);
+  MapInfo info100_2(start, end, 0x100, 0x5, tf.path);
+  // Will have an elf at offset 0x200 in file.
+  MapInfo info200_1(start, end, 0x200, 0x5, tf.path);
+  MapInfo info200_2(start, end, 0x200, 0x5, tf.path);
+  // Will have an elf at offset 0 in file.
+  MapInfo info300_1(start, end, 0x300, 0x5, tf.path);
+  MapInfo info300_2(start, end, 0x300, 0x5, tf.path);
+
+  Elf* elf0_1 = info0_1.GetElf(memory_, true);
+  ASSERT_TRUE(elf0_1->valid());
+  EXPECT_EQ(ARCH_ARM, elf0_1->arch());
+  Elf* elf0_2 = info0_2.GetElf(memory_, true);
+  ASSERT_TRUE(elf0_2->valid());
+  EXPECT_EQ(ARCH_ARM, elf0_2->arch());
+  EXPECT_EQ(0U, info0_1.elf_offset);
+  EXPECT_EQ(0U, info0_2.elf_offset);
+  if (cache_enabled) {
+    EXPECT_EQ(elf0_1, elf0_2);
+  } else {
+    EXPECT_NE(elf0_1, elf0_2);
+  }
+
+  Elf* elf100_1 = info100_1.GetElf(memory_, true);
+  ASSERT_TRUE(elf100_1->valid());
+  EXPECT_EQ(ARCH_X86, elf100_1->arch());
+  Elf* elf100_2 = info100_2.GetElf(memory_, true);
+  ASSERT_TRUE(elf100_2->valid());
+  EXPECT_EQ(ARCH_X86, elf100_2->arch());
+  EXPECT_EQ(0U, info100_1.elf_offset);
+  EXPECT_EQ(0U, info100_2.elf_offset);
+  if (cache_enabled) {
+    EXPECT_EQ(elf100_1, elf100_2);
+  } else {
+    EXPECT_NE(elf100_1, elf100_2);
+  }
+
+  Elf* elf200_1 = info200_1.GetElf(memory_, true);
+  ASSERT_TRUE(elf200_1->valid());
+  EXPECT_EQ(ARCH_X86_64, elf200_1->arch());
+  Elf* elf200_2 = info200_2.GetElf(memory_, true);
+  ASSERT_TRUE(elf200_2->valid());
+  EXPECT_EQ(ARCH_X86_64, elf200_2->arch());
+  EXPECT_EQ(0U, info200_1.elf_offset);
+  EXPECT_EQ(0U, info200_2.elf_offset);
+  if (cache_enabled) {
+    EXPECT_EQ(elf200_1, elf200_2);
+  } else {
+    EXPECT_NE(elf200_1, elf200_2);
+  }
+
+  Elf* elf300_1 = info300_1.GetElf(memory_, true);
+  ASSERT_TRUE(elf300_1->valid());
+  EXPECT_EQ(ARCH_ARM, elf300_1->arch());
+  Elf* elf300_2 = info300_2.GetElf(memory_, true);
+  ASSERT_TRUE(elf300_2->valid());
+  EXPECT_EQ(ARCH_ARM, elf300_2->arch());
+  EXPECT_EQ(0x300U, info300_1.elf_offset);
+  EXPECT_EQ(0x300U, info300_2.elf_offset);
+  if (cache_enabled) {
+    EXPECT_EQ(elf300_1, elf300_2);
+    EXPECT_EQ(elf0_1, elf300_1);
+  } else {
+    EXPECT_NE(elf300_1, elf300_2);
+    EXPECT_NE(elf0_1, elf300_1);
+  }
+}
+
+TEST_F(ElfCacheTest, no_caching_valid_elf_offset_non_zero) {
+  VerifyWithinSameMap(false);
+}
+
+TEST_F(ElfCacheTest, caching_valid_elf_offset_non_zero) {
+  VerifyWithinSameMap(true);
+}
+
+// Verify that when reading from multiple non-zero offsets in the same map
+// that when cached, all of the elf objects are the same.
+void ElfCacheTest::VerifyWithinSameMapNeverReadAtZero(bool cache_enabled) {
+  if (!cache_enabled) {
+    Elf::SetCachingEnabled(false);
+  }
+
+  TemporaryFile tf;
+  ASSERT_TRUE(tf.fd != -1);
+  WriteElfFile(0, &tf, EM_ARM);
+  lseek(tf.fd, 0x500, SEEK_SET);
+  uint8_t value = 0;
+  write(tf.fd, &value, 1);
+  close(tf.fd);
+
+  uint64_t start = 0x1000;
+  uint64_t end = 0x20000;
+  // Multiple info sections at different offsets will have non-zero elf offsets.
+  MapInfo info300_1(start, end, 0x300, 0x5, tf.path);
+  MapInfo info300_2(start, end, 0x300, 0x5, tf.path);
+  MapInfo info400_1(start, end, 0x400, 0x5, tf.path);
+  MapInfo info400_2(start, end, 0x400, 0x5, tf.path);
+
+  Elf* elf300_1 = info300_1.GetElf(memory_, true);
+  ASSERT_TRUE(elf300_1->valid());
+  EXPECT_EQ(ARCH_ARM, elf300_1->arch());
+  Elf* elf300_2 = info300_2.GetElf(memory_, true);
+  ASSERT_TRUE(elf300_2->valid());
+  EXPECT_EQ(ARCH_ARM, elf300_2->arch());
+  EXPECT_EQ(0x300U, info300_1.elf_offset);
+  EXPECT_EQ(0x300U, info300_2.elf_offset);
+  if (cache_enabled) {
+    EXPECT_EQ(elf300_1, elf300_2);
+  } else {
+    EXPECT_NE(elf300_1, elf300_2);
+  }
+
+  Elf* elf400_1 = info400_1.GetElf(memory_, true);
+  ASSERT_TRUE(elf400_1->valid());
+  EXPECT_EQ(ARCH_ARM, elf400_1->arch());
+  Elf* elf400_2 = info400_2.GetElf(memory_, true);
+  ASSERT_TRUE(elf400_2->valid());
+  EXPECT_EQ(ARCH_ARM, elf400_2->arch());
+  EXPECT_EQ(0x400U, info400_1.elf_offset);
+  EXPECT_EQ(0x400U, info400_2.elf_offset);
+  if (cache_enabled) {
+    EXPECT_EQ(elf400_1, elf400_2);
+    EXPECT_EQ(elf300_1, elf400_1);
+  } else {
+    EXPECT_NE(elf400_1, elf400_2);
+    EXPECT_NE(elf300_1, elf400_1);
+  }
+}
+
+TEST_F(ElfCacheTest, no_caching_valid_elf_offset_non_zero_never_read_at_zero) {
+  VerifyWithinSameMapNeverReadAtZero(false);
+}
+
+TEST_F(ElfCacheTest, caching_valid_elf_offset_non_zero_never_read_at_zero) {
+  VerifyWithinSameMapNeverReadAtZero(true);
+}
+
+}  // namespace unwindstack
diff --git a/libunwindstack/tests/ElfFake.cpp b/libunwindstack/tests/ElfFake.cpp
new file mode 100644
index 0000000..3d5ddd6
--- /dev/null
+++ b/libunwindstack/tests/ElfFake.cpp
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2017 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 <stdint.h>
+
+#include <deque>
+#include <string>
+
+#include <unwindstack/Elf.h>
+#include <unwindstack/ElfInterface.h>
+#include <unwindstack/Memory.h>
+#include <unwindstack/Regs.h>
+
+#include "ElfFake.h"
+#include "RegsFake.h"
+
+namespace unwindstack {
+
+std::deque<FunctionData> ElfInterfaceFake::functions_;
+std::deque<StepData> ElfInterfaceFake::steps_;
+
+bool ElfInterfaceFake::GetFunctionName(uint64_t, std::string* name, uint64_t* offset) {
+  if (functions_.empty()) {
+    return false;
+  }
+  auto entry = functions_.front();
+  functions_.pop_front();
+  *name = entry.name;
+  *offset = entry.offset;
+  return true;
+}
+
+bool ElfInterfaceFake::GetGlobalVariable(const std::string& global, uint64_t* offset) {
+  auto entry = globals_.find(global);
+  if (entry == globals_.end()) {
+    return false;
+  }
+  *offset = entry->second;
+  return true;
+}
+
+bool ElfInterfaceFake::Step(uint64_t, Regs* regs, Memory*, bool* finished) {
+  if (steps_.empty()) {
+    return false;
+  }
+  auto entry = steps_.front();
+  steps_.pop_front();
+
+  if (entry.pc == 0 && entry.sp == 0 && !entry.finished) {
+    // Pretend as though there is no frame.
+    return false;
+  }
+
+  RegsFake* fake_regs = reinterpret_cast<RegsFake*>(regs);
+  fake_regs->set_pc(entry.pc);
+  fake_regs->set_sp(entry.sp);
+  *finished = entry.finished;
+  return true;
+}
+
+}  // namespace unwindstack
diff --git a/libunwindstack/tests/ElfFake.h b/libunwindstack/tests/ElfFake.h
new file mode 100644
index 0000000..a3bf5ce
--- /dev/null
+++ b/libunwindstack/tests/ElfFake.h
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2017 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 _LIBUNWINDSTACK_TESTS_ELF_FAKE_H
+#define _LIBUNWINDSTACK_TESTS_ELF_FAKE_H
+
+#include <stdint.h>
+
+#include <deque>
+#include <string>
+#include <unordered_map>
+
+#include <unwindstack/Elf.h>
+#include <unwindstack/ElfInterface.h>
+#include <unwindstack/Memory.h>
+#include <unwindstack/Regs.h>
+
+#include "ElfInterfaceArm.h"
+
+namespace unwindstack {
+
+struct StepData {
+  StepData(uint64_t pc, uint64_t sp, bool finished) : pc(pc), sp(sp), finished(finished) {}
+  uint64_t pc;
+  uint64_t sp;
+  bool finished;
+};
+
+struct FunctionData {
+  FunctionData(std::string name, uint64_t offset) : name(name), offset(offset) {}
+
+  std::string name;
+  uint64_t offset;
+};
+
+class ElfFake : public Elf {
+ public:
+  ElfFake(Memory* memory) : Elf(memory) { valid_ = true; }
+  virtual ~ElfFake() = default;
+
+  void FakeSetValid(bool valid) { valid_ = valid; }
+
+  void FakeSetLoadBias(uint64_t load_bias) { load_bias_ = load_bias; }
+
+  void FakeSetInterface(ElfInterface* interface) { interface_.reset(interface); }
+  void FakeSetGnuDebugdataInterface(ElfInterface* interface) {
+    gnu_debugdata_interface_.reset(interface);
+  }
+};
+
+class ElfInterfaceFake : public ElfInterface {
+ public:
+  ElfInterfaceFake(Memory* memory) : ElfInterface(memory) {}
+  virtual ~ElfInterfaceFake() = default;
+
+  bool Init(uint64_t*) override { return false; }
+  void InitHeaders(uint64_t) override {}
+  bool GetSoname(std::string*) override { return false; }
+
+  bool GetFunctionName(uint64_t, std::string*, uint64_t*) override;
+  bool GetGlobalVariable(const std::string&, uint64_t*) override;
+
+  bool Step(uint64_t, Regs*, Memory*, bool*) override;
+
+  void FakeSetGlobalVariable(const std::string& global, uint64_t offset) {
+    globals_[global] = offset;
+  }
+
+  static void FakePushFunctionData(const FunctionData data) { functions_.push_back(data); }
+  static void FakePushStepData(const StepData data) { steps_.push_back(data); }
+
+  static void FakeClear() {
+    functions_.clear();
+    steps_.clear();
+  }
+
+  void FakeSetErrorCode(ErrorCode code) { last_error_.code = code; }
+
+  void FakeSetErrorAddress(uint64_t address) { last_error_.address = address; }
+
+ private:
+  std::unordered_map<std::string, uint64_t> globals_;
+
+  static std::deque<FunctionData> functions_;
+  static std::deque<StepData> steps_;
+};
+
+class ElfInterface32Fake : public ElfInterface32 {
+ public:
+  ElfInterface32Fake(Memory* memory) : ElfInterface32(memory) {}
+  virtual ~ElfInterface32Fake() = default;
+
+  void FakeSetEhFrameOffset(uint64_t offset) { eh_frame_offset_ = offset; }
+  void FakeSetEhFrameSize(uint64_t size) { eh_frame_size_ = size; }
+  void FakeSetDebugFrameOffset(uint64_t offset) { debug_frame_offset_ = offset; }
+  void FakeSetDebugFrameSize(uint64_t size) { debug_frame_size_ = size; }
+};
+
+class ElfInterface64Fake : public ElfInterface64 {
+ public:
+  ElfInterface64Fake(Memory* memory) : ElfInterface64(memory) {}
+  virtual ~ElfInterface64Fake() = default;
+
+  void FakeSetEhFrameOffset(uint64_t offset) { eh_frame_offset_ = offset; }
+  void FakeSetEhFrameSize(uint64_t size) { eh_frame_size_ = size; }
+  void FakeSetDebugFrameOffset(uint64_t offset) { debug_frame_offset_ = offset; }
+  void FakeSetDebugFrameSize(uint64_t size) { debug_frame_size_ = size; }
+};
+
+class ElfInterfaceArmFake : public ElfInterfaceArm {
+ public:
+  ElfInterfaceArmFake(Memory* memory) : ElfInterfaceArm(memory) {}
+  virtual ~ElfInterfaceArmFake() = default;
+
+  void FakeSetStartOffset(uint64_t offset) { start_offset_ = offset; }
+  void FakeSetTotalEntries(size_t entries) { total_entries_ = entries; }
+};
+
+}  // namespace unwindstack
+
+#endif  // _LIBUNWINDSTACK_TESTS_ELF_FAKE_H
diff --git a/libunwindstack/tests/ElfInterfaceArmTest.cpp b/libunwindstack/tests/ElfInterfaceArmTest.cpp
index 67c9a6b..43c6a97 100644
--- a/libunwindstack/tests/ElfInterfaceArmTest.cpp
+++ b/libunwindstack/tests/ElfInterfaceArmTest.cpp
@@ -20,12 +20,16 @@
 
 #include <vector>
 
-#include "ElfInterfaceArm.h"
-#include "Machine.h"
-#include "Regs.h"
+#include <unwindstack/MachineArm.h>
+#include <unwindstack/RegsArm.h>
 
+#include "ElfInterfaceArm.h"
+
+#include "ElfFake.h"
 #include "MemoryFake.h"
 
+namespace unwindstack {
+
 class ElfInterfaceArmTest : public ::testing::Test {
  protected:
   void SetUp() override {
@@ -38,7 +42,7 @@
 };
 
 TEST_F(ElfInterfaceArmTest, GetPrel32Addr) {
-  ElfInterfaceArm interface(&memory_);
+  ElfInterfaceArmFake interface(&memory_);
   memory_.SetData32(0x1000, 0x230000);
 
   uint32_t value;
@@ -55,36 +59,36 @@
 }
 
 TEST_F(ElfInterfaceArmTest, FindEntry_start_zero) {
-  ElfInterfaceArm interface(&memory_);
-  interface.set_start_offset(0);
-  interface.set_total_entries(10);
+  ElfInterfaceArmFake interface(&memory_);
+  interface.FakeSetStartOffset(0);
+  interface.FakeSetTotalEntries(10);
 
   uint64_t entry_offset;
   ASSERT_FALSE(interface.FindEntry(0x1000, &entry_offset));
 }
 
 TEST_F(ElfInterfaceArmTest, FindEntry_no_entries) {
-  ElfInterfaceArm interface(&memory_);
-  interface.set_start_offset(0x100);
-  interface.set_total_entries(0);
+  ElfInterfaceArmFake interface(&memory_);
+  interface.FakeSetStartOffset(0x100);
+  interface.FakeSetTotalEntries(0);
 
   uint64_t entry_offset;
   ASSERT_FALSE(interface.FindEntry(0x1000, &entry_offset));
 }
 
 TEST_F(ElfInterfaceArmTest, FindEntry_no_valid_memory) {
-  ElfInterfaceArm interface(&memory_);
-  interface.set_start_offset(0x100);
-  interface.set_total_entries(2);
+  ElfInterfaceArmFake interface(&memory_);
+  interface.FakeSetStartOffset(0x100);
+  interface.FakeSetTotalEntries(2);
 
   uint64_t entry_offset;
   ASSERT_FALSE(interface.FindEntry(0x1000, &entry_offset));
 }
 
 TEST_F(ElfInterfaceArmTest, FindEntry_ip_before_first) {
-  ElfInterfaceArm interface(&memory_);
-  interface.set_start_offset(0x1000);
-  interface.set_total_entries(1);
+  ElfInterfaceArmFake interface(&memory_);
+  interface.FakeSetStartOffset(0x1000);
+  interface.FakeSetTotalEntries(1);
   memory_.SetData32(0x1000, 0x6000);
 
   uint64_t entry_offset;
@@ -92,9 +96,9 @@
 }
 
 TEST_F(ElfInterfaceArmTest, FindEntry_single_entry_negative_value) {
-  ElfInterfaceArm interface(&memory_);
-  interface.set_start_offset(0x8000);
-  interface.set_total_entries(1);
+  ElfInterfaceArmFake interface(&memory_);
+  interface.FakeSetStartOffset(0x8000);
+  interface.FakeSetTotalEntries(1);
   memory_.SetData32(0x8000, 0x7fffff00);
 
   uint64_t entry_offset;
@@ -103,9 +107,9 @@
 }
 
 TEST_F(ElfInterfaceArmTest, FindEntry_two_entries) {
-  ElfInterfaceArm interface(&memory_);
-  interface.set_start_offset(0x1000);
-  interface.set_total_entries(2);
+  ElfInterfaceArmFake interface(&memory_);
+  interface.FakeSetStartOffset(0x1000);
+  interface.FakeSetTotalEntries(2);
   memory_.SetData32(0x1000, 0x6000);
   memory_.SetData32(0x1008, 0x7000);
 
@@ -114,11 +118,10 @@
   ASSERT_EQ(0x1000U, entry_offset);
 }
 
-
 TEST_F(ElfInterfaceArmTest, FindEntry_last_check_single_entry) {
-  ElfInterfaceArm interface(&memory_);
-  interface.set_start_offset(0x1000);
-  interface.set_total_entries(1);
+  ElfInterfaceArmFake interface(&memory_);
+  interface.FakeSetStartOffset(0x1000);
+  interface.FakeSetTotalEntries(1);
   memory_.SetData32(0x1000, 0x6000);
 
   uint64_t entry_offset;
@@ -133,9 +136,9 @@
 }
 
 TEST_F(ElfInterfaceArmTest, FindEntry_last_check_multiple_entries) {
-  ElfInterfaceArm interface(&memory_);
-  interface.set_start_offset(0x1000);
-  interface.set_total_entries(2);
+  ElfInterfaceArmFake interface(&memory_);
+  interface.FakeSetStartOffset(0x1000);
+  interface.FakeSetTotalEntries(2);
   memory_.SetData32(0x1000, 0x6000);
   memory_.SetData32(0x1008, 0x8000);
 
@@ -152,9 +155,9 @@
 }
 
 TEST_F(ElfInterfaceArmTest, FindEntry_multiple_entries_even) {
-  ElfInterfaceArm interface(&memory_);
-  interface.set_start_offset(0x1000);
-  interface.set_total_entries(4);
+  ElfInterfaceArmFake interface(&memory_);
+  interface.FakeSetStartOffset(0x1000);
+  interface.FakeSetTotalEntries(4);
   memory_.SetData32(0x1000, 0x6000);
   memory_.SetData32(0x1008, 0x7000);
   memory_.SetData32(0x1010, 0x8000);
@@ -175,9 +178,9 @@
 }
 
 TEST_F(ElfInterfaceArmTest, FindEntry_multiple_entries_odd) {
-  ElfInterfaceArm interface(&memory_);
-  interface.set_start_offset(0x1000);
-  interface.set_total_entries(5);
+  ElfInterfaceArmFake interface(&memory_);
+  interface.FakeSetStartOffset(0x1000);
+  interface.FakeSetTotalEntries(5);
   memory_.SetData32(0x1000, 0x5000);
   memory_.SetData32(0x1008, 0x6000);
   memory_.SetData32(0x1010, 0x7000);
@@ -200,9 +203,9 @@
 }
 
 TEST_F(ElfInterfaceArmTest, iterate) {
-  ElfInterfaceArm interface(&memory_);
-  interface.set_start_offset(0x1000);
-  interface.set_total_entries(5);
+  ElfInterfaceArmFake interface(&memory_);
+  interface.FakeSetStartOffset(0x1000);
+  interface.FakeSetTotalEntries(5);
   memory_.SetData32(0x1000, 0x5000);
   memory_.SetData32(0x1008, 0x6000);
   memory_.SetData32(0x1010, 0x7000);
@@ -239,91 +242,34 @@
   ASSERT_EQ(0xa020U, entries[4]);
 }
 
-TEST_F(ElfInterfaceArmTest, FindEntry_load_bias) {
-  ElfInterfaceArm interface(&memory_);
-  interface.set_start_offset(0x1000);
-  interface.set_total_entries(2);
-  memory_.SetData32(0x1000, 0x6000);
-  memory_.SetData32(0x1008, 0x8000);
+TEST_F(ElfInterfaceArmTest, HandleUnknownType_arm_exidx) {
+  ElfInterfaceArmFake interface(&memory_);
 
-  uint64_t entry_offset;
-  interface.set_load_bias(0x2000);
-  ASSERT_FALSE(interface.FindEntry(0x1000, &entry_offset));
-  ASSERT_FALSE(interface.FindEntry(0x8000, &entry_offset));
-  ASSERT_FALSE(interface.FindEntry(0x8fff, &entry_offset));
-  ASSERT_TRUE(interface.FindEntry(0x9000, &entry_offset));
-  ASSERT_EQ(0x1000U, entry_offset);
-  ASSERT_TRUE(interface.FindEntry(0xb007, &entry_offset));
-  ASSERT_EQ(0x1000U, entry_offset);
-  ASSERT_TRUE(interface.FindEntry(0xb008, &entry_offset));
-  ASSERT_EQ(0x1008U, entry_offset);
-}
+  interface.FakeSetStartOffset(0x1000);
+  interface.FakeSetTotalEntries(100);
 
-TEST_F(ElfInterfaceArmTest, HandleType_not_arm_exidx) {
-  ElfInterfaceArm interface(&memory_);
-
-  ASSERT_FALSE(interface.HandleType(0x1000, PT_NULL));
-  ASSERT_FALSE(interface.HandleType(0x1000, PT_LOAD));
-  ASSERT_FALSE(interface.HandleType(0x1000, PT_DYNAMIC));
-  ASSERT_FALSE(interface.HandleType(0x1000, PT_INTERP));
-  ASSERT_FALSE(interface.HandleType(0x1000, PT_NOTE));
-  ASSERT_FALSE(interface.HandleType(0x1000, PT_SHLIB));
-  ASSERT_FALSE(interface.HandleType(0x1000, PT_PHDR));
-  ASSERT_FALSE(interface.HandleType(0x1000, PT_TLS));
-  ASSERT_FALSE(interface.HandleType(0x1000, PT_LOOS));
-  ASSERT_FALSE(interface.HandleType(0x1000, PT_HIOS));
-  ASSERT_FALSE(interface.HandleType(0x1000, PT_LOPROC));
-  ASSERT_FALSE(interface.HandleType(0x1000, PT_HIPROC));
-  ASSERT_FALSE(interface.HandleType(0x1000, PT_GNU_EH_FRAME));
-  ASSERT_FALSE(interface.HandleType(0x1000, PT_GNU_STACK));
-}
-
-TEST_F(ElfInterfaceArmTest, HandleType_arm_exidx) {
-  ElfInterfaceArm interface(&memory_);
-
-  Elf32_Phdr phdr;
-  interface.set_start_offset(0x1000);
-  interface.set_total_entries(100);
-  phdr.p_vaddr = 0x2000;
-  phdr.p_memsz = 0xa00;
-
-  // Verify that if reads fail, we don't set the values but still get true.
-  ASSERT_TRUE(interface.HandleType(0x1000, 0x70000001));
-  ASSERT_EQ(0x1000U, interface.start_offset());
-  ASSERT_EQ(100U, interface.total_entries());
-
-  // Verify that if the second read fails, we still don't set the values.
-  memory_.SetData32(
-      0x1000 + reinterpret_cast<uint64_t>(&phdr.p_vaddr) - reinterpret_cast<uint64_t>(&phdr),
-      phdr.p_vaddr);
-  ASSERT_TRUE(interface.HandleType(0x1000, 0x70000001));
+  // Verify that if the type is not the one we want, we don't set the values.
+  interface.HandleUnknownType(0x70000000, 0x2000, 320);
   ASSERT_EQ(0x1000U, interface.start_offset());
   ASSERT_EQ(100U, interface.total_entries());
 
   // Everything is correct and present.
-  memory_.SetData32(
-      0x1000 + reinterpret_cast<uint64_t>(&phdr.p_memsz) - reinterpret_cast<uint64_t>(&phdr),
-      phdr.p_memsz);
-  ASSERT_TRUE(interface.HandleType(0x1000, 0x70000001));
+  interface.HandleUnknownType(0x70000001, 0x2000, 320);
   ASSERT_EQ(0x2000U, interface.start_offset());
-  ASSERT_EQ(320U, interface.total_entries());
-
-  // Non-zero load bias.
-  interface.set_load_bias(0x1000);
-  ASSERT_TRUE(interface.HandleType(0x1000, 0x70000001));
-  ASSERT_EQ(0x1000U, interface.start_offset());
-  ASSERT_EQ(320U, interface.total_entries());
+  ASSERT_EQ(40U, interface.total_entries());
 }
 
 TEST_F(ElfInterfaceArmTest, StepExidx) {
-  ElfInterfaceArm interface(&memory_);
+  ElfInterfaceArmFake interface(&memory_);
 
   // FindEntry fails.
-  ASSERT_FALSE(interface.StepExidx(0x7000, nullptr, nullptr));
+  bool finished;
+  ASSERT_FALSE(interface.StepExidx(0x7000, nullptr, nullptr, &finished));
+  EXPECT_EQ(ERROR_UNWIND_INFO, interface.LastErrorCode());
 
   // ExtractEntry should fail.
-  interface.set_start_offset(0x1000);
-  interface.set_total_entries(2);
+  interface.FakeSetStartOffset(0x1000);
+  interface.FakeSetTotalEntries(2);
   memory_.SetData32(0x1000, 0x6000);
   memory_.SetData32(0x1008, 0x8000);
 
@@ -332,26 +278,41 @@
   regs[ARM_REG_LR] = 0x20000;
   regs.set_sp(regs[ARM_REG_SP]);
   regs.set_pc(0x1234);
-  ASSERT_FALSE(interface.StepExidx(0x7000, &regs, &process_memory_));
+  ASSERT_FALSE(interface.StepExidx(0x7000, &regs, &process_memory_, &finished));
+  EXPECT_EQ(ERROR_MEMORY_INVALID, interface.LastErrorCode());
+  EXPECT_EQ(0x1004U, interface.LastErrorAddress());
 
   // Eval should fail.
   memory_.SetData32(0x1004, 0x81000000);
-  ASSERT_FALSE(interface.StepExidx(0x7000, &regs, &process_memory_));
+  ASSERT_FALSE(interface.StepExidx(0x7000, &regs, &process_memory_, &finished));
+  EXPECT_EQ(ERROR_UNWIND_INFO, interface.LastErrorCode());
 
   // Everything should pass.
   memory_.SetData32(0x1004, 0x80b0b0b0);
-  ASSERT_TRUE(interface.StepExidx(0x7000, &regs, &process_memory_));
+  ASSERT_TRUE(interface.StepExidx(0x7000, &regs, &process_memory_, &finished));
+  EXPECT_EQ(ERROR_UNWIND_INFO, interface.LastErrorCode());
+  ASSERT_FALSE(finished);
   ASSERT_EQ(0x1000U, regs.sp());
   ASSERT_EQ(0x1000U, regs[ARM_REG_SP]);
   ASSERT_EQ(0x20000U, regs.pc());
   ASSERT_EQ(0x20000U, regs[ARM_REG_PC]);
+
+  // Load bias is non-zero.
+  interface.set_load_bias(0x1000);
+  ASSERT_TRUE(interface.StepExidx(0x8000, &regs, &process_memory_, &finished));
+  EXPECT_EQ(ERROR_UNWIND_INFO, interface.LastErrorCode());
+
+  // Pc too small.
+  interface.set_load_bias(0x9000);
+  ASSERT_FALSE(interface.StepExidx(0x8000, &regs, &process_memory_, &finished));
+  EXPECT_EQ(ERROR_UNWIND_INFO, interface.LastErrorCode());
 }
 
 TEST_F(ElfInterfaceArmTest, StepExidx_pc_set) {
-  ElfInterfaceArm interface(&memory_);
+  ElfInterfaceArmFake interface(&memory_);
 
-  interface.set_start_offset(0x1000);
-  interface.set_total_entries(2);
+  interface.FakeSetStartOffset(0x1000);
+  interface.FakeSetTotalEntries(2);
   memory_.SetData32(0x1000, 0x6000);
   memory_.SetData32(0x1004, 0x808800b0);
   memory_.SetData32(0x1008, 0x8000);
@@ -364,9 +325,98 @@
   regs.set_pc(0x1234);
 
   // Everything should pass.
-  ASSERT_TRUE(interface.StepExidx(0x7000, &regs, &process_memory_));
+  bool finished;
+  ASSERT_TRUE(interface.StepExidx(0x7000, &regs, &process_memory_, &finished));
+  EXPECT_EQ(ERROR_NONE, interface.LastErrorCode());
+  ASSERT_FALSE(finished);
   ASSERT_EQ(0x10004U, regs.sp());
   ASSERT_EQ(0x10004U, regs[ARM_REG_SP]);
   ASSERT_EQ(0x10U, regs.pc());
   ASSERT_EQ(0x10U, regs[ARM_REG_PC]);
 }
+
+TEST_F(ElfInterfaceArmTest, StepExidx_cant_unwind) {
+  ElfInterfaceArmFake interface(&memory_);
+
+  interface.FakeSetStartOffset(0x1000);
+  interface.FakeSetTotalEntries(1);
+  memory_.SetData32(0x1000, 0x6000);
+  memory_.SetData32(0x1004, 1);
+
+  RegsArm regs;
+  regs[ARM_REG_SP] = 0x10000;
+  regs[ARM_REG_LR] = 0x20000;
+  regs.set_sp(regs[ARM_REG_SP]);
+  regs.set_pc(0x1234);
+
+  bool finished;
+  ASSERT_TRUE(interface.StepExidx(0x7000, &regs, &process_memory_, &finished));
+  EXPECT_EQ(ERROR_NONE, interface.LastErrorCode());
+  ASSERT_TRUE(finished);
+  ASSERT_EQ(0x10000U, regs.sp());
+  ASSERT_EQ(0x10000U, regs[ARM_REG_SP]);
+  ASSERT_EQ(0x1234U, regs.pc());
+}
+
+TEST_F(ElfInterfaceArmTest, StepExidx_refuse_unwind) {
+  ElfInterfaceArmFake interface(&memory_);
+
+  interface.FakeSetStartOffset(0x1000);
+  interface.FakeSetTotalEntries(1);
+  memory_.SetData32(0x1000, 0x6000);
+  memory_.SetData32(0x1004, 0x808000b0);
+
+  RegsArm regs;
+  regs[ARM_REG_SP] = 0x10000;
+  regs[ARM_REG_LR] = 0x20000;
+  regs.set_sp(regs[ARM_REG_SP]);
+  regs.set_pc(0x1234);
+
+  bool finished;
+  ASSERT_TRUE(interface.StepExidx(0x7000, &regs, &process_memory_, &finished));
+  EXPECT_EQ(ERROR_NONE, interface.LastErrorCode());
+  ASSERT_TRUE(finished);
+  ASSERT_EQ(0x10000U, regs.sp());
+  ASSERT_EQ(0x10000U, regs[ARM_REG_SP]);
+  ASSERT_EQ(0x1234U, regs.pc());
+}
+
+TEST_F(ElfInterfaceArmTest, StepExidx_pc_zero) {
+  ElfInterfaceArmFake interface(&memory_);
+
+  interface.FakeSetStartOffset(0x1000);
+  interface.FakeSetTotalEntries(1);
+  memory_.SetData32(0x1000, 0x6000);
+  // Set the pc using a pop r15 command.
+  memory_.SetData32(0x1004, 0x808800b0);
+
+  // pc value of zero.
+  process_memory_.SetData32(0x10000, 0);
+
+  RegsArm regs;
+  regs[ARM_REG_SP] = 0x10000;
+  regs[ARM_REG_LR] = 0x20000;
+  regs.set_sp(regs[ARM_REG_SP]);
+  regs.set_pc(0x1234);
+
+  bool finished;
+  ASSERT_TRUE(interface.StepExidx(0x7000, &regs, &process_memory_, &finished));
+  EXPECT_EQ(ERROR_NONE, interface.LastErrorCode());
+  ASSERT_TRUE(finished);
+  ASSERT_EQ(0U, regs.pc());
+
+  // Now set the pc from the lr register (pop r14).
+  memory_.SetData32(0x1004, 0x808400b0);
+
+  regs[ARM_REG_SP] = 0x10000;
+  regs[ARM_REG_LR] = 0x20000;
+  regs.set_sp(regs[ARM_REG_SP]);
+  regs.set_pc(0x1234);
+
+  ASSERT_TRUE(interface.StepExidx(0x7000, &regs, &process_memory_, &finished));
+  EXPECT_EQ(ERROR_NONE, interface.LastErrorCode());
+  ASSERT_TRUE(finished);
+  ASSERT_EQ(0U, regs.pc());
+}
+
+}  // namespace unwindstack
diff --git a/libunwindstack/tests/ElfInterfaceTest.cpp b/libunwindstack/tests/ElfInterfaceTest.cpp
index 0f56ba8..aa6df84 100644
--- a/libunwindstack/tests/ElfInterfaceTest.cpp
+++ b/libunwindstack/tests/ElfInterfaceTest.cpp
@@ -20,9 +20,12 @@
 
 #include <gtest/gtest.h>
 
-#include "ElfInterface.h"
+#include <unwindstack/ElfInterface.h>
+
+#include "DwarfEncoding.h"
 #include "ElfInterfaceArm.h"
 
+#include "ElfFake.h"
 #include "MemoryFake.h"
 
 #if !defined(PT_ARM_EXIDX)
@@ -33,6 +36,8 @@
 #define EM_AARCH64 183
 #endif
 
+namespace unwindstack {
+
 class ElfInterfaceTest : public ::testing::Test {
  protected:
   void SetUp() override {
@@ -58,15 +63,28 @@
   template <typename Ehdr, typename Phdr, typename Dyn, typename ElfInterfaceType>
   void ManyPhdrs();
 
-  template <typename Ehdr, typename Phdr, typename Dyn, typename ElfInterfaceType>
+  enum SonameTestEnum : uint8_t {
+    SONAME_NORMAL,
+    SONAME_DTNULL_AFTER,
+    SONAME_DTSIZE_SMALL,
+    SONAME_MISSING_MAP,
+  };
+
+  template <typename Ehdr, typename Phdr, typename Shdr, typename Dyn>
+  void SonameInit(SonameTestEnum test_type = SONAME_NORMAL);
+
+  template <typename ElfInterfaceType>
   void Soname();
 
-  template <typename Ehdr, typename Phdr, typename Dyn, typename ElfInterfaceType>
+  template <typename ElfInterfaceType>
   void SonameAfterDtNull();
 
-  template <typename Ehdr, typename Phdr, typename Dyn, typename ElfInterfaceType>
+  template <typename ElfInterfaceType>
   void SonameSize();
 
+  template <typename ElfInterfaceType>
+  void SonameMissingMap();
+
   template <typename ElfType>
   void InitHeadersEhFrameTest();
 
@@ -98,8 +116,7 @@
 template <typename Sym>
 void ElfInterfaceTest::InitSym(uint64_t offset, uint32_t value, uint32_t size, uint32_t name_offset,
                                uint64_t sym_offset, const char* name) {
-  Sym sym;
-  memset(&sym, 0, sizeof(sym));
+  Sym sym = {};
   sym.st_info = STT_FUNC;
   sym.st_value = value;
   sym.st_size = size;
@@ -114,15 +131,13 @@
 void ElfInterfaceTest::SinglePtLoad() {
   std::unique_ptr<ElfInterface> elf(new ElfInterfaceType(&memory_));
 
-  Ehdr ehdr;
-  memset(&ehdr, 0, sizeof(ehdr));
+  Ehdr ehdr = {};
   ehdr.e_phoff = 0x100;
   ehdr.e_phnum = 1;
   ehdr.e_phentsize = sizeof(Phdr);
   memory_.SetMemory(0, &ehdr, sizeof(ehdr));
 
-  Phdr phdr;
-  memset(&phdr, 0, sizeof(phdr));
+  Phdr phdr = {};
   phdr.p_type = PT_LOAD;
   phdr.p_vaddr = 0x2000;
   phdr.p_memsz = 0x10000;
@@ -130,7 +145,9 @@
   phdr.p_align = 0x1000;
   memory_.SetMemory(0x100, &phdr, sizeof(phdr));
 
-  ASSERT_TRUE(elf->Init());
+  uint64_t load_bias = 0;
+  ASSERT_TRUE(elf->Init(&load_bias));
+  EXPECT_EQ(0x2000U, load_bias);
 
   const std::unordered_map<uint64_t, LoadInfo>& pt_loads = elf->pt_loads();
   ASSERT_EQ(1U, pt_loads.size());
@@ -152,15 +169,13 @@
 void ElfInterfaceTest::MultipleExecutablePtLoads() {
   std::unique_ptr<ElfInterface> elf(new ElfInterfaceType(&memory_));
 
-  Ehdr ehdr;
-  memset(&ehdr, 0, sizeof(ehdr));
+  Ehdr ehdr = {};
   ehdr.e_phoff = 0x100;
   ehdr.e_phnum = 3;
   ehdr.e_phentsize = sizeof(Phdr);
   memory_.SetMemory(0, &ehdr, sizeof(ehdr));
 
-  Phdr phdr;
-  memset(&phdr, 0, sizeof(phdr));
+  Phdr phdr = {};
   phdr.p_type = PT_LOAD;
   phdr.p_vaddr = 0x2000;
   phdr.p_memsz = 0x10000;
@@ -186,7 +201,9 @@
   phdr.p_align = 0x1002;
   memory_.SetMemory(0x100 + 2 * sizeof(phdr), &phdr, sizeof(phdr));
 
-  ASSERT_TRUE(elf->Init());
+  uint64_t load_bias = 0;
+  ASSERT_TRUE(elf->Init(&load_bias));
+  EXPECT_EQ(0x2000U, load_bias);
 
   const std::unordered_map<uint64_t, LoadInfo>& pt_loads = elf->pt_loads();
   ASSERT_EQ(3U, pt_loads.size());
@@ -219,15 +236,13 @@
 void ElfInterfaceTest::MultipleExecutablePtLoadsIncrementsNotSizeOfPhdr() {
   std::unique_ptr<ElfInterface> elf(new ElfInterfaceType(&memory_));
 
-  Ehdr ehdr;
-  memset(&ehdr, 0, sizeof(ehdr));
+  Ehdr ehdr = {};
   ehdr.e_phoff = 0x100;
   ehdr.e_phnum = 3;
   ehdr.e_phentsize = sizeof(Phdr) + 100;
   memory_.SetMemory(0, &ehdr, sizeof(ehdr));
 
-  Phdr phdr;
-  memset(&phdr, 0, sizeof(phdr));
+  Phdr phdr = {};
   phdr.p_type = PT_LOAD;
   phdr.p_vaddr = 0x2000;
   phdr.p_memsz = 0x10000;
@@ -253,7 +268,9 @@
   phdr.p_align = 0x1002;
   memory_.SetMemory(0x100 + 2 * (sizeof(phdr) + 100), &phdr, sizeof(phdr));
 
-  ASSERT_TRUE(elf->Init());
+  uint64_t load_bias = 0;
+  ASSERT_TRUE(elf->Init(&load_bias));
+  EXPECT_EQ(0x2000U, load_bias);
 
   const std::unordered_map<uint64_t, LoadInfo>& pt_loads = elf->pt_loads();
   ASSERT_EQ(3U, pt_loads.size());
@@ -288,15 +305,13 @@
 void ElfInterfaceTest::NonExecutablePtLoads() {
   std::unique_ptr<ElfInterface> elf(new ElfInterfaceType(&memory_));
 
-  Ehdr ehdr;
-  memset(&ehdr, 0, sizeof(ehdr));
+  Ehdr ehdr = {};
   ehdr.e_phoff = 0x100;
   ehdr.e_phnum = 3;
   ehdr.e_phentsize = sizeof(Phdr);
   memory_.SetMemory(0, &ehdr, sizeof(ehdr));
 
-  Phdr phdr;
-  memset(&phdr, 0, sizeof(phdr));
+  Phdr phdr = {};
   phdr.p_type = PT_LOAD;
   phdr.p_vaddr = 0x2000;
   phdr.p_memsz = 0x10000;
@@ -322,7 +337,9 @@
   phdr.p_align = 0x1002;
   memory_.SetMemory(0x100 + 2 * sizeof(phdr), &phdr, sizeof(phdr));
 
-  ASSERT_TRUE(elf->Init());
+  uint64_t load_bias = 0;
+  ASSERT_TRUE(elf->Init(&load_bias));
+  EXPECT_EQ(0U, load_bias);
 
   const std::unordered_map<uint64_t, LoadInfo>& pt_loads = elf->pt_loads();
   ASSERT_EQ(1U, pt_loads.size());
@@ -345,17 +362,15 @@
 void ElfInterfaceTest::ManyPhdrs() {
   std::unique_ptr<ElfInterface> elf(new ElfInterfaceType(&memory_));
 
-  Ehdr ehdr;
-  memset(&ehdr, 0, sizeof(ehdr));
+  Ehdr ehdr = {};
   ehdr.e_phoff = 0x100;
   ehdr.e_phnum = 7;
   ehdr.e_phentsize = sizeof(Phdr);
   memory_.SetMemory(0, &ehdr, sizeof(ehdr));
 
-  Phdr phdr;
   uint64_t phdr_offset = 0x100;
 
-  memset(&phdr, 0, sizeof(phdr));
+  Phdr phdr = {};
   phdr.p_type = PT_LOAD;
   phdr.p_vaddr = 0x2000;
   phdr.p_memsz = 0x10000;
@@ -394,7 +409,9 @@
   memory_.SetMemory(phdr_offset, &phdr, sizeof(phdr));
   phdr_offset += sizeof(phdr);
 
-  ASSERT_TRUE(elf->Init());
+  uint64_t load_bias = 0;
+  ASSERT_TRUE(elf->Init(&load_bias));
+  EXPECT_EQ(0x2000U, load_bias);
 
   const std::unordered_map<uint64_t, LoadInfo>& pt_loads = elf->pt_loads();
   ASSERT_EQ(1U, pt_loads.size());
@@ -416,25 +433,25 @@
 TEST_F(ElfInterfaceTest, elf32_arm) {
   ElfInterfaceArm elf_arm(&memory_);
 
-  Elf32_Ehdr ehdr;
-  memset(&ehdr, 0, sizeof(ehdr));
+  Elf32_Ehdr ehdr = {};
   ehdr.e_phoff = 0x100;
   ehdr.e_phnum = 1;
   ehdr.e_phentsize = sizeof(Elf32_Phdr);
   memory_.SetMemory(0, &ehdr, sizeof(ehdr));
 
-  Elf32_Phdr phdr;
-  memset(&phdr, 0, sizeof(phdr));
+  Elf32_Phdr phdr = {};
   phdr.p_type = PT_ARM_EXIDX;
-  phdr.p_vaddr = 0x2000;
-  phdr.p_memsz = 16;
+  phdr.p_offset = 0x2000;
+  phdr.p_filesz = 16;
   memory_.SetMemory(0x100, &phdr, sizeof(phdr));
 
   // Add arm exidx entries.
   memory_.SetData32(0x2000, 0x1000);
   memory_.SetData32(0x2008, 0x1000);
 
-  ASSERT_TRUE(elf_arm.Init());
+  uint64_t load_bias = 0;
+  ASSERT_TRUE(elf_arm.Init(&load_bias));
+  EXPECT_EQ(0U, load_bias);
 
   std::vector<uint32_t> entries;
   for (auto addr : elf_arm) {
@@ -448,19 +465,28 @@
   ASSERT_EQ(2U, elf_arm.total_entries());
 }
 
-template <typename Ehdr, typename Phdr, typename Dyn, typename ElfInterfaceType>
-void ElfInterfaceTest::Soname() {
-  std::unique_ptr<ElfInterface> elf(new ElfInterfaceType(&memory_));
-
-  Ehdr ehdr;
-  memset(&ehdr, 0, sizeof(ehdr));
+template <typename Ehdr, typename Phdr, typename Shdr, typename Dyn>
+void ElfInterfaceTest::SonameInit(SonameTestEnum test_type) {
+  Ehdr ehdr = {};
+  ehdr.e_shoff = 0x200;
+  ehdr.e_shnum = 2;
+  ehdr.e_shentsize = sizeof(Shdr);
   ehdr.e_phoff = 0x100;
   ehdr.e_phnum = 1;
   ehdr.e_phentsize = sizeof(Phdr);
   memory_.SetMemory(0, &ehdr, sizeof(ehdr));
 
-  Phdr phdr;
-  memset(&phdr, 0, sizeof(phdr));
+  Shdr shdr = {};
+  shdr.sh_type = SHT_STRTAB;
+  if (test_type == SONAME_MISSING_MAP) {
+    shdr.sh_addr = 0x20100;
+  } else {
+    shdr.sh_addr = 0x10100;
+  }
+  shdr.sh_offset = 0x10000;
+  memory_.SetMemory(0x200 + sizeof(shdr), &shdr, sizeof(shdr));
+
+  Phdr phdr = {};
   phdr.p_type = PT_DYNAMIC;
   phdr.p_offset = 0x2000;
   phdr.p_memsz = sizeof(Dyn) * 3;
@@ -470,15 +496,25 @@
   Dyn dyn;
 
   dyn.d_tag = DT_STRTAB;
-  dyn.d_un.d_ptr = 0x10000;
+  dyn.d_un.d_ptr = 0x10100;
   memory_.SetMemory(offset, &dyn, sizeof(dyn));
   offset += sizeof(dyn);
 
   dyn.d_tag = DT_STRSZ;
-  dyn.d_un.d_val = 0x1000;
+  if (test_type == SONAME_DTSIZE_SMALL) {
+    dyn.d_un.d_val = 0x10;
+  } else {
+    dyn.d_un.d_val = 0x1000;
+  }
   memory_.SetMemory(offset, &dyn, sizeof(dyn));
   offset += sizeof(dyn);
 
+  if (test_type == SONAME_DTNULL_AFTER) {
+    dyn.d_tag = DT_NULL;
+    memory_.SetMemory(offset, &dyn, sizeof(dyn));
+    offset += sizeof(dyn);
+  }
+
   dyn.d_tag = DT_SONAME;
   dyn.d_un.d_val = 0x10;
   memory_.SetMemory(offset, &dyn, sizeof(dyn));
@@ -488,277 +524,172 @@
   memory_.SetMemory(offset, &dyn, sizeof(dyn));
 
   SetStringMemory(0x10010, "fake_soname.so");
+}
 
-  ASSERT_TRUE(elf->Init());
+template <typename ElfInterfaceType>
+void ElfInterfaceTest::Soname() {
+  std::unique_ptr<ElfInterface> elf(new ElfInterfaceType(&memory_));
+
+  uint64_t load_bias = 0;
+  ASSERT_TRUE(elf->Init(&load_bias));
+  EXPECT_EQ(0U, load_bias);
+
   std::string name;
   ASSERT_TRUE(elf->GetSoname(&name));
   ASSERT_STREQ("fake_soname.so", name.c_str());
 }
 
 TEST_F(ElfInterfaceTest, elf32_soname) {
-  Soname<Elf32_Ehdr, Elf32_Phdr, Elf32_Dyn, ElfInterface32>();
+  SonameInit<Elf32_Ehdr, Elf32_Phdr, Elf32_Shdr, Elf32_Dyn>();
+  Soname<ElfInterface32>();
 }
 
 TEST_F(ElfInterfaceTest, elf64_soname) {
-  Soname<Elf64_Ehdr, Elf64_Phdr, Elf64_Dyn, ElfInterface64>();
+  SonameInit<Elf64_Ehdr, Elf64_Phdr, Elf64_Shdr, Elf64_Dyn>();
+  Soname<ElfInterface64>();
 }
 
-template <typename Ehdr, typename Phdr, typename Dyn, typename ElfInterfaceType>
+template <typename ElfInterfaceType>
 void ElfInterfaceTest::SonameAfterDtNull() {
   std::unique_ptr<ElfInterface> elf(new ElfInterfaceType(&memory_));
 
-  Ehdr ehdr;
-  memset(&ehdr, 0, sizeof(ehdr));
-  ehdr.e_phoff = 0x100;
-  ehdr.e_phnum = 1;
-  ehdr.e_phentsize = sizeof(Phdr);
-  memory_.SetMemory(0, &ehdr, sizeof(ehdr));
+  uint64_t load_bias = 0;
+  ASSERT_TRUE(elf->Init(&load_bias));
+  EXPECT_EQ(0U, load_bias);
 
-  Phdr phdr;
-  memset(&phdr, 0, sizeof(phdr));
-  phdr.p_type = PT_DYNAMIC;
-  phdr.p_offset = 0x2000;
-  phdr.p_memsz = sizeof(Dyn) * 3;
-  memory_.SetMemory(0x100, &phdr, sizeof(phdr));
-
-  Dyn dyn;
-  uint64_t offset = 0x2000;
-
-  dyn.d_tag = DT_STRTAB;
-  dyn.d_un.d_ptr = 0x10000;
-  memory_.SetMemory(offset, &dyn, sizeof(dyn));
-  offset += sizeof(dyn);
-
-  dyn.d_tag = DT_STRSZ;
-  dyn.d_un.d_val = 0x1000;
-  memory_.SetMemory(offset, &dyn, sizeof(dyn));
-  offset += sizeof(dyn);
-
-  dyn.d_tag = DT_NULL;
-  memory_.SetMemory(offset, &dyn, sizeof(dyn));
-  offset += sizeof(dyn);
-
-  dyn.d_tag = DT_SONAME;
-  dyn.d_un.d_val = 0x10;
-  memory_.SetMemory(offset, &dyn, sizeof(dyn));
-  offset += sizeof(dyn);
-
-  SetStringMemory(0x10010, "fake_soname.so");
-
-  ASSERT_TRUE(elf->Init());
   std::string name;
   ASSERT_FALSE(elf->GetSoname(&name));
 }
 
 TEST_F(ElfInterfaceTest, elf32_soname_after_dt_null) {
-  SonameAfterDtNull<Elf32_Ehdr, Elf32_Phdr, Elf32_Dyn, ElfInterface32>();
+  SonameInit<Elf32_Ehdr, Elf32_Phdr, Elf32_Shdr, Elf32_Dyn>(SONAME_DTNULL_AFTER);
+  SonameAfterDtNull<ElfInterface32>();
 }
 
 TEST_F(ElfInterfaceTest, elf64_soname_after_dt_null) {
-  SonameAfterDtNull<Elf64_Ehdr, Elf64_Phdr, Elf64_Dyn, ElfInterface64>();
+  SonameInit<Elf64_Ehdr, Elf64_Phdr, Elf64_Shdr, Elf64_Dyn>(SONAME_DTNULL_AFTER);
+  SonameAfterDtNull<ElfInterface64>();
 }
 
-template <typename Ehdr, typename Phdr, typename Dyn, typename ElfInterfaceType>
+template <typename ElfInterfaceType>
 void ElfInterfaceTest::SonameSize() {
   std::unique_ptr<ElfInterface> elf(new ElfInterfaceType(&memory_));
 
-  Ehdr ehdr;
-  memset(&ehdr, 0, sizeof(ehdr));
-  ehdr.e_phoff = 0x100;
-  ehdr.e_phnum = 1;
-  ehdr.e_phentsize = sizeof(Phdr);
-  memory_.SetMemory(0, &ehdr, sizeof(ehdr));
+  uint64_t load_bias = 0;
+  ASSERT_TRUE(elf->Init(&load_bias));
+  EXPECT_EQ(0U, load_bias);
 
-  Phdr phdr;
-  memset(&phdr, 0, sizeof(phdr));
-  phdr.p_type = PT_DYNAMIC;
-  phdr.p_offset = 0x2000;
-  phdr.p_memsz = sizeof(Dyn);
-  memory_.SetMemory(0x100, &phdr, sizeof(phdr));
-
-  Dyn dyn;
-  uint64_t offset = 0x2000;
-
-  dyn.d_tag = DT_STRTAB;
-  dyn.d_un.d_ptr = 0x10000;
-  memory_.SetMemory(offset, &dyn, sizeof(dyn));
-  offset += sizeof(dyn);
-
-  dyn.d_tag = DT_STRSZ;
-  dyn.d_un.d_val = 0x10;
-  memory_.SetMemory(offset, &dyn, sizeof(dyn));
-  offset += sizeof(dyn);
-
-  dyn.d_tag = DT_SONAME;
-  dyn.d_un.d_val = 0x10;
-  memory_.SetMemory(offset, &dyn, sizeof(dyn));
-  offset += sizeof(dyn);
-
-  dyn.d_tag = DT_NULL;
-  memory_.SetMemory(offset, &dyn, sizeof(dyn));
-
-  SetStringMemory(0x10010, "fake_soname.so");
-
-  ASSERT_TRUE(elf->Init());
   std::string name;
   ASSERT_FALSE(elf->GetSoname(&name));
 }
 
 TEST_F(ElfInterfaceTest, elf32_soname_size) {
-  SonameSize<Elf32_Ehdr, Elf32_Phdr, Elf32_Dyn, ElfInterface32>();
+  SonameInit<Elf32_Ehdr, Elf32_Phdr, Elf32_Shdr, Elf32_Dyn>(SONAME_DTSIZE_SMALL);
+  SonameSize<ElfInterface32>();
 }
 
 TEST_F(ElfInterfaceTest, elf64_soname_size) {
-  SonameSize<Elf64_Ehdr, Elf64_Phdr, Elf64_Dyn, ElfInterface64>();
+  SonameInit<Elf64_Ehdr, Elf64_Phdr, Elf64_Shdr, Elf64_Dyn>(SONAME_DTSIZE_SMALL);
+  SonameSize<ElfInterface64>();
 }
 
-class MockElfInterface32 : public ElfInterface32 {
- public:
-  MockElfInterface32(Memory* memory) : ElfInterface32(memory) {}
-  virtual ~MockElfInterface32() = default;
+// Verify that there is no map from STRTAB in the dynamic section to a
+// STRTAB entry in the section headers.
+template <typename ElfInterfaceType>
+void ElfInterfaceTest::SonameMissingMap() {
+  std::unique_ptr<ElfInterface> elf(new ElfInterfaceType(&memory_));
 
-  void TestSetEhFrameOffset(uint64_t offset) { eh_frame_offset_ = offset; }
-  void TestSetEhFrameSize(uint64_t size) { eh_frame_size_ = size; }
+  uint64_t load_bias = 0;
+  ASSERT_TRUE(elf->Init(&load_bias));
+  EXPECT_EQ(0U, load_bias);
 
-  void TestSetDebugFrameOffset(uint64_t offset) { debug_frame_offset_ = offset; }
-  void TestSetDebugFrameSize(uint64_t size) { debug_frame_size_ = size; }
-};
+  std::string name;
+  ASSERT_FALSE(elf->GetSoname(&name));
+}
 
-class MockElfInterface64 : public ElfInterface64 {
- public:
-  MockElfInterface64(Memory* memory) : ElfInterface64(memory) {}
-  virtual ~MockElfInterface64() = default;
+TEST_F(ElfInterfaceTest, elf32_soname_missing_map) {
+  SonameInit<Elf32_Ehdr, Elf32_Phdr, Elf32_Shdr, Elf32_Dyn>(SONAME_MISSING_MAP);
+  SonameMissingMap<ElfInterface32>();
+}
 
-  void TestSetEhFrameOffset(uint64_t offset) { eh_frame_offset_ = offset; }
-  void TestSetEhFrameSize(uint64_t size) { eh_frame_size_ = size; }
-
-  void TestSetDebugFrameOffset(uint64_t offset) { debug_frame_offset_ = offset; }
-  void TestSetDebugFrameSize(uint64_t size) { debug_frame_size_ = size; }
-};
+TEST_F(ElfInterfaceTest, elf64_soname_missing_map) {
+  SonameInit<Elf64_Ehdr, Elf64_Phdr, Elf64_Shdr, Elf64_Dyn>(SONAME_MISSING_MAP);
+  SonameMissingMap<ElfInterface64>();
+}
 
 template <typename ElfType>
 void ElfInterfaceTest::InitHeadersEhFrameTest() {
   ElfType elf(&memory_);
 
-  elf.TestSetEhFrameOffset(0x10000);
-  elf.TestSetEhFrameSize(0);
-  elf.TestSetDebugFrameOffset(0);
-  elf.TestSetDebugFrameSize(0);
+  elf.FakeSetEhFrameOffset(0x10000);
+  elf.FakeSetEhFrameSize(0);
+  elf.FakeSetDebugFrameOffset(0);
+  elf.FakeSetDebugFrameSize(0);
 
   memory_.SetMemory(0x10000,
                     std::vector<uint8_t>{0x1, DW_EH_PE_udata2, DW_EH_PE_udata2, DW_EH_PE_udata2});
   memory_.SetData32(0x10004, 0x500);
   memory_.SetData32(0x10008, 250);
 
-  elf.InitHeaders();
+  elf.InitHeaders(0);
 
   EXPECT_FALSE(elf.eh_frame() == nullptr);
   EXPECT_TRUE(elf.debug_frame() == nullptr);
 }
 
 TEST_F(ElfInterfaceTest, init_headers_eh_frame32) {
-  InitHeadersEhFrameTest<MockElfInterface32>();
+  InitHeadersEhFrameTest<ElfInterface32Fake>();
 }
 
 TEST_F(ElfInterfaceTest, init_headers_eh_frame64) {
-  InitHeadersEhFrameTest<MockElfInterface64>();
+  InitHeadersEhFrameTest<ElfInterface64Fake>();
 }
 
 template <typename ElfType>
 void ElfInterfaceTest::InitHeadersDebugFrame() {
   ElfType elf(&memory_);
 
-  elf.TestSetEhFrameOffset(0);
-  elf.TestSetEhFrameSize(0);
-  elf.TestSetDebugFrameOffset(0x5000);
-  elf.TestSetDebugFrameSize(0x200);
+  elf.FakeSetEhFrameOffset(0);
+  elf.FakeSetEhFrameSize(0);
+  elf.FakeSetDebugFrameOffset(0x5000);
+  elf.FakeSetDebugFrameSize(0x200);
 
   memory_.SetData32(0x5000, 0xfc);
   memory_.SetData32(0x5004, 0xffffffff);
-  memory_.SetData8(0x5008, 1);
-  memory_.SetData8(0x5009, '\0');
+  memory_.SetMemory(0x5008, std::vector<uint8_t>{1, '\0', 4, 8, 2});
 
   memory_.SetData32(0x5100, 0xfc);
   memory_.SetData32(0x5104, 0);
   memory_.SetData32(0x5108, 0x1500);
   memory_.SetData32(0x510c, 0x200);
 
-  elf.InitHeaders();
+  elf.InitHeaders(0);
 
   EXPECT_TRUE(elf.eh_frame() == nullptr);
   EXPECT_FALSE(elf.debug_frame() == nullptr);
 }
 
 TEST_F(ElfInterfaceTest, init_headers_debug_frame32) {
-  InitHeadersDebugFrame<MockElfInterface32>();
+  InitHeadersDebugFrame<ElfInterface32Fake>();
 }
 
 TEST_F(ElfInterfaceTest, init_headers_debug_frame64) {
-  InitHeadersDebugFrame<MockElfInterface64>();
-}
-
-template <typename ElfType>
-void ElfInterfaceTest::InitHeadersEhFrameFail() {
-  ElfType elf(&memory_);
-
-  elf.TestSetEhFrameOffset(0x1000);
-  elf.TestSetEhFrameSize(0x100);
-  elf.TestSetDebugFrameOffset(0);
-  elf.TestSetDebugFrameSize(0);
-
-  elf.InitHeaders();
-
-  EXPECT_TRUE(elf.eh_frame() == nullptr);
-  EXPECT_EQ(0U, elf.eh_frame_offset());
-  EXPECT_EQ(static_cast<uint64_t>(-1), elf.eh_frame_size());
-  EXPECT_TRUE(elf.debug_frame() == nullptr);
-}
-
-TEST_F(ElfInterfaceTest, init_headers_eh_frame32_fail) {
-  InitHeadersEhFrameFail<MockElfInterface32>();
-}
-
-TEST_F(ElfInterfaceTest, init_headers_eh_frame64_fail) {
-  InitHeadersEhFrameFail<MockElfInterface64>();
-}
-
-template <typename ElfType>
-void ElfInterfaceTest::InitHeadersDebugFrameFail() {
-  ElfType elf(&memory_);
-
-  elf.TestSetEhFrameOffset(0);
-  elf.TestSetEhFrameSize(0);
-  elf.TestSetDebugFrameOffset(0x1000);
-  elf.TestSetDebugFrameSize(0x100);
-
-  elf.InitHeaders();
-
-  EXPECT_TRUE(elf.eh_frame() == nullptr);
-  EXPECT_TRUE(elf.debug_frame() == nullptr);
-  EXPECT_EQ(0U, elf.debug_frame_offset());
-  EXPECT_EQ(static_cast<uint64_t>(-1), elf.debug_frame_size());
-}
-
-TEST_F(ElfInterfaceTest, init_headers_debug_frame32_fail) {
-  InitHeadersDebugFrameFail<MockElfInterface32>();
-}
-
-TEST_F(ElfInterfaceTest, init_headers_debug_frame64_fail) {
-  InitHeadersDebugFrameFail<MockElfInterface64>();
+  InitHeadersDebugFrame<ElfInterface64Fake>();
 }
 
 template <typename Ehdr, typename Shdr, typename ElfInterfaceType>
 void ElfInterfaceTest::InitSectionHeadersMalformed() {
   std::unique_ptr<ElfInterfaceType> elf(new ElfInterfaceType(&memory_));
 
-  Ehdr ehdr;
-  memset(&ehdr, 0, sizeof(ehdr));
+  Ehdr ehdr = {};
   ehdr.e_shoff = 0x1000;
   ehdr.e_shnum = 10;
   ehdr.e_shentsize = sizeof(Shdr);
   memory_.SetMemory(0, &ehdr, sizeof(ehdr));
 
-  ASSERT_TRUE(elf->Init());
+  uint64_t load_bias = 0;
+  ASSERT_TRUE(elf->Init(&load_bias));
+  EXPECT_EQ(0U, load_bias);
 }
 
 TEST_F(ElfInterfaceTest, init_section_headers_malformed32) {
@@ -775,8 +706,7 @@
 
   uint64_t offset = 0x1000;
 
-  Ehdr ehdr;
-  memset(&ehdr, 0, sizeof(ehdr));
+  Ehdr ehdr = {};
   ehdr.e_shoff = offset;
   ehdr.e_shnum = 10;
   ehdr.e_shentsize = entry_size;
@@ -784,8 +714,7 @@
 
   offset += ehdr.e_shentsize;
 
-  Shdr shdr;
-  memset(&shdr, 0, sizeof(shdr));
+  Shdr shdr = {};
   shdr.sh_type = SHT_SYMTAB;
   shdr.sh_link = 4;
   shdr.sh_addr = 0x5000;
@@ -823,7 +752,9 @@
   InitSym<Sym>(0x5000, 0x90000, 0x1000, 0x100, 0xf000, "function_one");
   InitSym<Sym>(0x6000, 0xd0000, 0x1000, 0x300, 0xf000, "function_two");
 
-  ASSERT_TRUE(elf->Init());
+  uint64_t load_bias = 0;
+  ASSERT_TRUE(elf->Init(&load_bias));
+  EXPECT_EQ(0U, load_bias);
   EXPECT_EQ(0U, elf->debug_frame_offset());
   EXPECT_EQ(0U, elf->debug_frame_size());
   EXPECT_EQ(0U, elf->gnu_debugdata_offset());
@@ -862,8 +793,7 @@
 
   uint64_t offset = 0x2000;
 
-  Ehdr ehdr;
-  memset(&ehdr, 0, sizeof(ehdr));
+  Ehdr ehdr = {};
   ehdr.e_shoff = offset;
   ehdr.e_shnum = 10;
   ehdr.e_shentsize = sizeof(Shdr);
@@ -872,8 +802,7 @@
 
   offset += ehdr.e_shentsize;
 
-  Shdr shdr;
-  memset(&shdr, 0, sizeof(shdr));
+  Shdr shdr = {};
   shdr.sh_type = SHT_PROGBITS;
   shdr.sh_link = 2;
   shdr.sh_name = 0x200;
@@ -904,14 +833,44 @@
   memory_.SetMemory(offset, &shdr, sizeof(shdr));
   offset += ehdr.e_shentsize;
 
+  memset(&shdr, 0, sizeof(shdr));
+  shdr.sh_type = SHT_PROGBITS;
+  shdr.sh_link = 2;
+  shdr.sh_name = 0x300;
+  shdr.sh_addr = 0x7000;
+  shdr.sh_offset = 0x7000;
+  shdr.sh_entsize = 0x100;
+  shdr.sh_size = 0x800;
+  memory_.SetMemory(offset, &shdr, sizeof(shdr));
+  offset += ehdr.e_shentsize;
+
+  memset(&shdr, 0, sizeof(shdr));
+  shdr.sh_type = SHT_PROGBITS;
+  shdr.sh_link = 2;
+  shdr.sh_name = 0x400;
+  shdr.sh_addr = 0x6000;
+  shdr.sh_offset = 0xa000;
+  shdr.sh_entsize = 0x100;
+  shdr.sh_size = 0xf00;
+  memory_.SetMemory(offset, &shdr, sizeof(shdr));
+  offset += ehdr.e_shentsize;
+
   memory_.SetMemory(0xf100, ".debug_frame", sizeof(".debug_frame"));
   memory_.SetMemory(0xf200, ".gnu_debugdata", sizeof(".gnu_debugdata"));
+  memory_.SetMemory(0xf300, ".eh_frame", sizeof(".eh_frame"));
+  memory_.SetMemory(0xf400, ".eh_frame_hdr", sizeof(".eh_frame_hdr"));
 
-  ASSERT_TRUE(elf->Init());
+  uint64_t load_bias = 0;
+  ASSERT_TRUE(elf->Init(&load_bias));
+  EXPECT_EQ(0U, load_bias);
   EXPECT_EQ(0x6000U, elf->debug_frame_offset());
   EXPECT_EQ(0x500U, elf->debug_frame_size());
   EXPECT_EQ(0x5000U, elf->gnu_debugdata_offset());
   EXPECT_EQ(0x800U, elf->gnu_debugdata_size());
+  EXPECT_EQ(0x7000U, elf->eh_frame_offset());
+  EXPECT_EQ(0x800U, elf->eh_frame_size());
+  EXPECT_EQ(0xa000U, elf->eh_frame_hdr_offset());
+  EXPECT_EQ(0xf00U, elf->eh_frame_hdr_size());
 }
 
 TEST_F(ElfInterfaceTest, init_section_headers_offsets32) {
@@ -921,3 +880,174 @@
 TEST_F(ElfInterfaceTest, init_section_headers_offsets64) {
   InitSectionHeadersOffsets<Elf64_Ehdr, Elf64_Shdr, ElfInterface64>();
 }
+
+TEST_F(ElfInterfaceTest, is_valid_pc_from_pt_load) {
+  std::unique_ptr<ElfInterface> elf(new ElfInterface32(&memory_));
+
+  Elf32_Ehdr ehdr = {};
+  ehdr.e_phoff = 0x100;
+  ehdr.e_phnum = 1;
+  ehdr.e_phentsize = sizeof(Elf32_Phdr);
+  memory_.SetMemory(0, &ehdr, sizeof(ehdr));
+
+  Elf32_Phdr phdr = {};
+  phdr.p_type = PT_LOAD;
+  phdr.p_vaddr = 0;
+  phdr.p_memsz = 0x10000;
+  phdr.p_flags = PF_R | PF_X;
+  phdr.p_align = 0x1000;
+  memory_.SetMemory(0x100, &phdr, sizeof(phdr));
+
+  uint64_t load_bias = 0;
+  ASSERT_TRUE(elf->Init(&load_bias));
+  EXPECT_EQ(0U, load_bias);
+  EXPECT_TRUE(elf->IsValidPc(0));
+  EXPECT_TRUE(elf->IsValidPc(0x5000));
+  EXPECT_TRUE(elf->IsValidPc(0xffff));
+  EXPECT_FALSE(elf->IsValidPc(0x10000));
+}
+
+TEST_F(ElfInterfaceTest, is_valid_pc_from_pt_load_non_zero_load_bias) {
+  std::unique_ptr<ElfInterface> elf(new ElfInterface32(&memory_));
+
+  Elf32_Ehdr ehdr = {};
+  ehdr.e_phoff = 0x100;
+  ehdr.e_phnum = 1;
+  ehdr.e_phentsize = sizeof(Elf32_Phdr);
+  memory_.SetMemory(0, &ehdr, sizeof(ehdr));
+
+  Elf32_Phdr phdr = {};
+  phdr.p_type = PT_LOAD;
+  phdr.p_vaddr = 0x2000;
+  phdr.p_memsz = 0x10000;
+  phdr.p_flags = PF_R | PF_X;
+  phdr.p_align = 0x1000;
+  memory_.SetMemory(0x100, &phdr, sizeof(phdr));
+
+  uint64_t load_bias = 0;
+  ASSERT_TRUE(elf->Init(&load_bias));
+  EXPECT_EQ(0x2000U, load_bias);
+  EXPECT_FALSE(elf->IsValidPc(0));
+  EXPECT_FALSE(elf->IsValidPc(0x1000));
+  EXPECT_FALSE(elf->IsValidPc(0x1fff));
+  EXPECT_TRUE(elf->IsValidPc(0x2000));
+  EXPECT_TRUE(elf->IsValidPc(0x5000));
+  EXPECT_TRUE(elf->IsValidPc(0x11fff));
+  EXPECT_FALSE(elf->IsValidPc(0x12000));
+}
+
+TEST_F(ElfInterfaceTest, is_valid_pc_from_debug_frame) {
+  std::unique_ptr<ElfInterface> elf(new ElfInterface32(&memory_));
+
+  uint64_t sh_offset = 0x100;
+
+  Elf32_Ehdr ehdr = {};
+  ehdr.e_shstrndx = 1;
+  ehdr.e_shoff = sh_offset;
+  ehdr.e_shentsize = sizeof(Elf32_Shdr);
+  ehdr.e_shnum = 3;
+  memory_.SetMemory(0, &ehdr, sizeof(ehdr));
+
+  Elf32_Shdr shdr = {};
+  shdr.sh_type = SHT_NULL;
+  memory_.SetMemory(sh_offset, &shdr, sizeof(shdr));
+
+  sh_offset += sizeof(shdr);
+  memset(&shdr, 0, sizeof(shdr));
+  shdr.sh_type = SHT_STRTAB;
+  shdr.sh_name = 1;
+  shdr.sh_offset = 0x500;
+  shdr.sh_size = 0x100;
+  memory_.SetMemory(sh_offset, &shdr, sizeof(shdr));
+  memory_.SetMemory(0x500, ".debug_frame");
+
+  sh_offset += sizeof(shdr);
+  memset(&shdr, 0, sizeof(shdr));
+  shdr.sh_type = SHT_PROGBITS;
+  shdr.sh_name = 0;
+  shdr.sh_addr = 0x600;
+  shdr.sh_offset = 0x600;
+  shdr.sh_size = 0x200;
+  memory_.SetMemory(sh_offset, &shdr, sizeof(shdr));
+
+  // CIE 32.
+  memory_.SetData32(0x600, 0xfc);
+  memory_.SetData32(0x604, 0xffffffff);
+  memory_.SetMemory(0x608, std::vector<uint8_t>{1, '\0', 4, 4, 1});
+
+  // FDE 32.
+  memory_.SetData32(0x700, 0xfc);
+  memory_.SetData32(0x704, 0);
+  memory_.SetData32(0x708, 0x2100);
+  memory_.SetData32(0x70c, 0x200);
+
+  uint64_t load_bias = 0;
+  ASSERT_TRUE(elf->Init(&load_bias));
+  elf->InitHeaders(0);
+  EXPECT_EQ(0U, load_bias);
+  EXPECT_FALSE(elf->IsValidPc(0));
+  EXPECT_FALSE(elf->IsValidPc(0x20ff));
+  EXPECT_TRUE(elf->IsValidPc(0x2100));
+  EXPECT_TRUE(elf->IsValidPc(0x2200));
+  EXPECT_TRUE(elf->IsValidPc(0x22ff));
+  EXPECT_FALSE(elf->IsValidPc(0x2300));
+}
+
+TEST_F(ElfInterfaceTest, is_valid_pc_from_eh_frame) {
+  std::unique_ptr<ElfInterface> elf(new ElfInterface32(&memory_));
+
+  uint64_t sh_offset = 0x100;
+
+  Elf32_Ehdr ehdr = {};
+  ehdr.e_shstrndx = 1;
+  ehdr.e_shoff = sh_offset;
+  ehdr.e_shentsize = sizeof(Elf32_Shdr);
+  ehdr.e_shnum = 3;
+  memory_.SetMemory(0, &ehdr, sizeof(ehdr));
+
+  Elf32_Shdr shdr = {};
+  shdr.sh_type = SHT_NULL;
+  memory_.SetMemory(sh_offset, &shdr, sizeof(shdr));
+
+  sh_offset += sizeof(shdr);
+  memset(&shdr, 0, sizeof(shdr));
+  shdr.sh_type = SHT_STRTAB;
+  shdr.sh_name = 1;
+  shdr.sh_offset = 0x500;
+  shdr.sh_size = 0x100;
+  memory_.SetMemory(sh_offset, &shdr, sizeof(shdr));
+  memory_.SetMemory(0x500, ".eh_frame");
+
+  sh_offset += sizeof(shdr);
+  memset(&shdr, 0, sizeof(shdr));
+  shdr.sh_type = SHT_PROGBITS;
+  shdr.sh_name = 0;
+  shdr.sh_addr = 0x600;
+  shdr.sh_offset = 0x600;
+  shdr.sh_size = 0x200;
+  memory_.SetMemory(sh_offset, &shdr, sizeof(shdr));
+
+  // CIE 32.
+  memory_.SetData32(0x600, 0xfc);
+  memory_.SetData32(0x604, 0);
+  memory_.SetMemory(0x608, std::vector<uint8_t>{1, '\0', 4, 4, 1});
+
+  // FDE 32.
+  memory_.SetData32(0x700, 0xfc);
+  memory_.SetData32(0x704, 0x104);
+  memory_.SetData32(0x708, 0x20f8);
+  memory_.SetData32(0x70c, 0x200);
+
+  uint64_t load_bias = 0;
+  ASSERT_TRUE(elf->Init(&load_bias));
+  elf->InitHeaders(0);
+  EXPECT_EQ(0U, load_bias);
+  EXPECT_FALSE(elf->IsValidPc(0));
+  EXPECT_FALSE(elf->IsValidPc(0x27ff));
+  EXPECT_TRUE(elf->IsValidPc(0x2800));
+  EXPECT_TRUE(elf->IsValidPc(0x2900));
+  EXPECT_TRUE(elf->IsValidPc(0x29ff));
+  EXPECT_FALSE(elf->IsValidPc(0x2a00));
+}
+
+}  // namespace unwindstack
diff --git a/libunwindstack/tests/ElfTest.cpp b/libunwindstack/tests/ElfTest.cpp
index bf0823b..55fe16f 100644
--- a/libunwindstack/tests/ElfTest.cpp
+++ b/libunwindstack/tests/ElfTest.cpp
@@ -20,17 +20,24 @@
 #include <sys/types.h>
 #include <unistd.h>
 
+#include <gmock/gmock.h>
 #include <gtest/gtest.h>
 
-#include "Elf.h"
+#include <unwindstack/Elf.h>
+#include <unwindstack/MapInfo.h>
+#include <unwindstack/RegsArm.h>
 
+#include "ElfFake.h"
 #include "ElfTestUtils.h"
+#include "LogFake.h"
 #include "MemoryFake.h"
 
 #if !defined(PT_ARM_EXIDX)
 #define PT_ARM_EXIDX 0x70000001
 #endif
 
+namespace unwindstack {
+
 class ElfTest : public ::testing::Test {
  protected:
   void SetUp() override {
@@ -103,7 +110,7 @@
 TEST_F(ElfTest, invalid_memory) {
   Elf elf(memory_);
 
-  ASSERT_FALSE(elf.Init());
+  ASSERT_FALSE(elf.Init(false));
   ASSERT_FALSE(elf.valid());
 }
 
@@ -115,7 +122,7 @@
   // Corrupt the ELF signature.
   memory_->SetData32(0, 0x7f000000);
 
-  ASSERT_FALSE(elf.Init());
+  ASSERT_FALSE(elf.Init(false));
   ASSERT_FALSE(elf.valid());
   ASSERT_TRUE(elf.interface() == nullptr);
 
@@ -125,7 +132,34 @@
   uint64_t func_offset;
   ASSERT_FALSE(elf.GetFunctionName(0, &name, &func_offset));
 
-  ASSERT_FALSE(elf.Step(0, nullptr, nullptr));
+  bool finished;
+  ASSERT_FALSE(elf.Step(0, 0, nullptr, nullptr, &finished));
+}
+
+TEST_F(ElfTest, elf32_invalid_machine) {
+  Elf elf(memory_);
+
+  InitElf32(EM_PPC);
+
+  ResetLogs();
+  ASSERT_FALSE(elf.Init(false));
+
+  ASSERT_EQ("", GetFakeLogBuf());
+  ASSERT_EQ("4 unwind 32 bit elf that is neither arm nor x86 nor mips: e_machine = 20\n\n",
+            GetFakeLogPrint());
+}
+
+TEST_F(ElfTest, elf64_invalid_machine) {
+  Elf elf(memory_);
+
+  InitElf64(EM_PPC64);
+
+  ResetLogs();
+  ASSERT_FALSE(elf.Init(false));
+
+  ASSERT_EQ("", GetFakeLogBuf());
+  ASSERT_EQ("4 unwind 64 bit elf that is neither aarch64 nor x86_64 nor mips64: e_machine = 21\n\n",
+            GetFakeLogPrint());
 }
 
 TEST_F(ElfTest, elf_arm) {
@@ -133,19 +167,31 @@
 
   InitElf32(EM_ARM);
 
-  ASSERT_TRUE(elf.Init());
+  ASSERT_TRUE(elf.Init(false));
   ASSERT_TRUE(elf.valid());
   ASSERT_EQ(static_cast<uint32_t>(EM_ARM), elf.machine_type());
   ASSERT_EQ(ELFCLASS32, elf.class_type());
   ASSERT_TRUE(elf.interface() != nullptr);
 }
 
+TEST_F(ElfTest, elf_mips) {
+  Elf elf(memory_);
+
+  InitElf32(EM_MIPS);
+
+  ASSERT_TRUE(elf.Init(false));
+  ASSERT_TRUE(elf.valid());
+  ASSERT_EQ(static_cast<uint32_t>(EM_MIPS), elf.machine_type());
+  ASSERT_EQ(ELFCLASS32, elf.class_type());
+  ASSERT_TRUE(elf.interface() != nullptr);
+}
+
 TEST_F(ElfTest, elf_x86) {
   Elf elf(memory_);
 
   InitElf32(EM_386);
 
-  ASSERT_TRUE(elf.Init());
+  ASSERT_TRUE(elf.Init(false));
   ASSERT_TRUE(elf.valid());
   ASSERT_EQ(static_cast<uint32_t>(EM_386), elf.machine_type());
   ASSERT_EQ(ELFCLASS32, elf.class_type());
@@ -157,7 +203,7 @@
 
   InitElf64(EM_AARCH64);
 
-  ASSERT_TRUE(elf.Init());
+  ASSERT_TRUE(elf.Init(false));
   ASSERT_TRUE(elf.valid());
   ASSERT_EQ(static_cast<uint32_t>(EM_AARCH64), elf.machine_type());
   ASSERT_EQ(ELFCLASS64, elf.class_type());
@@ -169,13 +215,25 @@
 
   InitElf64(EM_X86_64);
 
-  ASSERT_TRUE(elf.Init());
+  ASSERT_TRUE(elf.Init(false));
   ASSERT_TRUE(elf.valid());
   ASSERT_EQ(static_cast<uint32_t>(EM_X86_64), elf.machine_type());
   ASSERT_EQ(ELFCLASS64, elf.class_type());
   ASSERT_TRUE(elf.interface() != nullptr);
 }
 
+TEST_F(ElfTest, elf_mips64) {
+  Elf elf(memory_);
+
+  InitElf64(EM_MIPS);
+
+  ASSERT_TRUE(elf.Init(false));
+  ASSERT_TRUE(elf.valid());
+  ASSERT_EQ(static_cast<uint32_t>(EM_MIPS), elf.machine_type());
+  ASSERT_EQ(ELFCLASS64, elf.class_type());
+  ASSERT_TRUE(elf.interface() != nullptr);
+}
+
 TEST_F(ElfTest, gnu_debugdata_init_fail32) {
   TestInitGnuDebugdata<Elf32_Ehdr, Elf32_Shdr>(ELFCLASS32, EM_ARM, false,
                                                [&](uint64_t offset, const void* ptr, size_t size) {
@@ -183,7 +241,7 @@
                                                });
 
   Elf elf(memory_);
-  ASSERT_TRUE(elf.Init());
+  ASSERT_TRUE(elf.Init(false));
   ASSERT_TRUE(elf.interface() != nullptr);
   ASSERT_TRUE(elf.gnu_debugdata_interface() == nullptr);
   EXPECT_EQ(0x1acU, elf.interface()->gnu_debugdata_offset());
@@ -197,7 +255,7 @@
                                                });
 
   Elf elf(memory_);
-  ASSERT_TRUE(elf.Init());
+  ASSERT_TRUE(elf.Init(false));
   ASSERT_TRUE(elf.interface() != nullptr);
   ASSERT_TRUE(elf.gnu_debugdata_interface() == nullptr);
   EXPECT_EQ(0x200U, elf.interface()->gnu_debugdata_offset());
@@ -211,14 +269,11 @@
                                                });
 
   Elf elf(memory_);
-  ASSERT_TRUE(elf.Init());
+  ASSERT_TRUE(elf.Init(true));
   ASSERT_TRUE(elf.interface() != nullptr);
-  ASSERT_TRUE(elf.gnu_debugdata_interface() == nullptr);
+  ASSERT_TRUE(elf.gnu_debugdata_interface() != nullptr);
   EXPECT_EQ(0x1acU, elf.interface()->gnu_debugdata_offset());
   EXPECT_EQ(0x8cU, elf.interface()->gnu_debugdata_size());
-
-  elf.InitGnuDebugdata();
-  ASSERT_TRUE(elf.gnu_debugdata_interface() != nullptr);
 }
 
 TEST_F(ElfTest, gnu_debugdata_init64) {
@@ -228,12 +283,279 @@
                                                });
 
   Elf elf(memory_);
-  ASSERT_TRUE(elf.Init());
+  ASSERT_TRUE(elf.Init(true));
   ASSERT_TRUE(elf.interface() != nullptr);
-  ASSERT_TRUE(elf.gnu_debugdata_interface() == nullptr);
+  ASSERT_TRUE(elf.gnu_debugdata_interface() != nullptr);
   EXPECT_EQ(0x200U, elf.interface()->gnu_debugdata_offset());
   EXPECT_EQ(0x90U, elf.interface()->gnu_debugdata_size());
-
-  elf.InitGnuDebugdata();
-  ASSERT_TRUE(elf.gnu_debugdata_interface() != nullptr);
 }
+
+TEST_F(ElfTest, rel_pc) {
+  ElfFake elf(memory_);
+
+  ElfInterfaceFake* interface = new ElfInterfaceFake(memory_);
+  elf.FakeSetInterface(interface);
+
+  elf.FakeSetValid(true);
+  MapInfo map_info(0x1000, 0x2000);
+
+  ASSERT_EQ(0x101U, elf.GetRelPc(0x1101, &map_info));
+
+  elf.FakeSetValid(false);
+  ASSERT_EQ(0x101U, elf.GetRelPc(0x1101, &map_info));
+}
+
+TEST_F(ElfTest, step_in_signal_map) {
+  ElfFake elf(memory_);
+
+  RegsArm regs;
+  regs[13] = 0x50000;
+  regs[15] = 0x8000;
+
+  ElfInterfaceFake* interface = new ElfInterfaceFake(memory_);
+  elf.FakeSetInterface(interface);
+
+  memory_->SetData32(0x3000, 0xdf0027ad);
+  MemoryFake process_memory;
+  process_memory.SetData32(0x50000, 0);
+  for (size_t i = 0; i < 16; i++) {
+    process_memory.SetData32(0x500a0 + i * sizeof(uint32_t), i);
+  }
+
+  elf.FakeSetValid(true);
+  bool finished;
+  ASSERT_TRUE(elf.Step(0x3000, 0x1000, &regs, &process_memory, &finished));
+  EXPECT_FALSE(finished);
+  EXPECT_EQ(15U, regs.pc());
+  EXPECT_EQ(13U, regs.sp());
+}
+
+class ElfInterfaceMock : public ElfInterface {
+ public:
+  ElfInterfaceMock(Memory* memory) : ElfInterface(memory) {}
+  virtual ~ElfInterfaceMock() = default;
+
+  bool Init(uint64_t*) override { return false; }
+  void InitHeaders(uint64_t) override {}
+  bool GetSoname(std::string*) override { return false; }
+  bool GetFunctionName(uint64_t, std::string*, uint64_t*) override { return false; }
+
+  MOCK_METHOD4(Step, bool(uint64_t, Regs*, Memory*, bool*));
+  MOCK_METHOD2(GetGlobalVariable, bool(const std::string&, uint64_t*));
+  MOCK_METHOD1(IsValidPc, bool(uint64_t));
+
+  void MockSetDynamicOffset(uint64_t offset) { dynamic_offset_ = offset; }
+  void MockSetDynamicVaddr(uint64_t vaddr) { dynamic_vaddr_ = vaddr; }
+  void MockSetDynamicSize(uint64_t size) { dynamic_size_ = size; }
+};
+
+TEST_F(ElfTest, step_in_interface) {
+  ElfFake elf(memory_);
+  elf.FakeSetValid(true);
+
+  RegsArm regs;
+
+  ElfInterfaceMock* interface = new ElfInterfaceMock(memory_);
+  elf.FakeSetInterface(interface);
+  MemoryFake process_memory;
+
+  bool finished;
+  EXPECT_CALL(*interface, Step(0x1000, &regs, &process_memory, &finished))
+      .WillOnce(::testing::Return(true));
+
+  ASSERT_TRUE(elf.Step(0x1004, 0x1000, &regs, &process_memory, &finished));
+}
+
+TEST_F(ElfTest, get_global_invalid_elf) {
+  ElfFake elf(memory_);
+  elf.FakeSetValid(false);
+
+  std::string global("something");
+  uint64_t offset;
+  ASSERT_FALSE(elf.GetGlobalVariable(global, &offset));
+}
+
+TEST_F(ElfTest, get_global_valid_not_in_interface) {
+  ElfFake elf(memory_);
+  elf.FakeSetValid(true);
+
+  ElfInterfaceMock* interface = new ElfInterfaceMock(memory_);
+  elf.FakeSetInterface(interface);
+
+  uint64_t offset;
+  std::string global("something");
+  EXPECT_CALL(*interface, GetGlobalVariable(global, &offset)).WillOnce(::testing::Return(false));
+
+  ASSERT_FALSE(elf.GetGlobalVariable(global, &offset));
+}
+
+TEST_F(ElfTest, get_global_valid_below_load_bias) {
+  ElfFake elf(memory_);
+  elf.FakeSetValid(true);
+  elf.FakeSetLoadBias(0x1000);
+
+  ElfInterfaceMock* interface = new ElfInterfaceMock(memory_);
+  elf.FakeSetInterface(interface);
+
+  uint64_t offset;
+  std::string global("something");
+  EXPECT_CALL(*interface, GetGlobalVariable(global, &offset))
+      .WillOnce(::testing::DoAll(::testing::SetArgPointee<1>(0x300), ::testing::Return(true)));
+
+  ASSERT_FALSE(elf.GetGlobalVariable(global, &offset));
+}
+
+TEST_F(ElfTest, get_global_valid_dynamic_zero_non_zero_load_bias) {
+  ElfFake elf(memory_);
+  elf.FakeSetValid(true);
+  elf.FakeSetLoadBias(0x100);
+
+  ElfInterfaceMock* interface = new ElfInterfaceMock(memory_);
+  elf.FakeSetInterface(interface);
+
+  uint64_t offset;
+  std::string global("something");
+  EXPECT_CALL(*interface, GetGlobalVariable(global, &offset))
+      .WillOnce(::testing::DoAll(::testing::SetArgPointee<1>(0x300), ::testing::Return(true)));
+
+  ASSERT_TRUE(elf.GetGlobalVariable(global, &offset));
+  EXPECT_EQ(0x200U, offset);
+}
+
+TEST_F(ElfTest, get_global_valid_dynamic_zero) {
+  ElfFake elf(memory_);
+  elf.FakeSetValid(true);
+
+  ElfInterfaceMock* interface = new ElfInterfaceMock(memory_);
+  elf.FakeSetInterface(interface);
+
+  ElfInterfaceMock* gnu_interface = new ElfInterfaceMock(memory_);
+  elf.FakeSetGnuDebugdataInterface(gnu_interface);
+
+  uint64_t offset;
+  std::string global("something");
+  EXPECT_CALL(*interface, GetGlobalVariable(global, &offset)).WillOnce(::testing::Return(false));
+
+  EXPECT_CALL(*gnu_interface, GetGlobalVariable(global, &offset))
+      .WillOnce(::testing::DoAll(::testing::SetArgPointee<1>(0x500), ::testing::Return(true)));
+
+  ASSERT_TRUE(elf.GetGlobalVariable(global, &offset));
+  EXPECT_EQ(0x500U, offset);
+}
+
+TEST_F(ElfTest, get_global_valid_in_gnu_debugdata_dynamic_zero) {
+  ElfFake elf(memory_);
+  elf.FakeSetValid(true);
+
+  ElfInterfaceMock* interface = new ElfInterfaceMock(memory_);
+  elf.FakeSetInterface(interface);
+
+  uint64_t offset;
+  std::string global("something");
+  EXPECT_CALL(*interface, GetGlobalVariable(global, &offset))
+      .WillOnce(::testing::DoAll(::testing::SetArgPointee<1>(0x300), ::testing::Return(true)));
+
+  ASSERT_TRUE(elf.GetGlobalVariable(global, &offset));
+  EXPECT_EQ(0x300U, offset);
+}
+
+TEST_F(ElfTest, get_global_valid_dynamic_adjust_negative) {
+  ElfFake elf(memory_);
+  elf.FakeSetValid(true);
+
+  ElfInterfaceMock* interface = new ElfInterfaceMock(memory_);
+  interface->MockSetDynamicOffset(0x400);
+  interface->MockSetDynamicVaddr(0x800);
+  interface->MockSetDynamicSize(0x100);
+  elf.FakeSetInterface(interface);
+
+  uint64_t offset;
+  std::string global("something");
+  EXPECT_CALL(*interface, GetGlobalVariable(global, &offset))
+      .WillOnce(::testing::DoAll(::testing::SetArgPointee<1>(0x850), ::testing::Return(true)));
+
+  ASSERT_TRUE(elf.GetGlobalVariable(global, &offset));
+  EXPECT_EQ(0x450U, offset);
+}
+
+TEST_F(ElfTest, get_global_valid_dynamic_adjust_positive) {
+  ElfFake elf(memory_);
+  elf.FakeSetValid(true);
+
+  ElfInterfaceMock* interface = new ElfInterfaceMock(memory_);
+  interface->MockSetDynamicOffset(0x1000);
+  interface->MockSetDynamicVaddr(0x800);
+  interface->MockSetDynamicSize(0x100);
+  elf.FakeSetInterface(interface);
+
+  uint64_t offset;
+  std::string global("something");
+  EXPECT_CALL(*interface, GetGlobalVariable(global, &offset))
+      .WillOnce(::testing::DoAll(::testing::SetArgPointee<1>(0x850), ::testing::Return(true)));
+
+  ASSERT_TRUE(elf.GetGlobalVariable(global, &offset));
+  EXPECT_EQ(0x1050U, offset);
+}
+
+TEST_F(ElfTest, is_valid_pc_elf_invalid) {
+  ElfFake elf(memory_);
+  elf.FakeSetValid(false);
+
+  EXPECT_FALSE(elf.IsValidPc(0x100));
+  EXPECT_FALSE(elf.IsValidPc(0x200));
+}
+
+TEST_F(ElfTest, is_valid_pc_interface) {
+  ElfFake elf(memory_);
+  elf.FakeSetValid(true);
+
+  ElfInterfaceMock* interface = new ElfInterfaceMock(memory_);
+  elf.FakeSetInterface(interface);
+
+  EXPECT_CALL(*interface, IsValidPc(0x1500)).WillOnce(::testing::Return(true));
+
+  EXPECT_TRUE(elf.IsValidPc(0x1500));
+}
+
+TEST_F(ElfTest, is_valid_pc_from_gnu_debugdata) {
+  ElfFake elf(memory_);
+  elf.FakeSetValid(true);
+
+  ElfInterfaceMock* interface = new ElfInterfaceMock(memory_);
+  elf.FakeSetInterface(interface);
+  ElfInterfaceMock* gnu_interface = new ElfInterfaceMock(memory_);
+  elf.FakeSetGnuDebugdataInterface(gnu_interface);
+
+  EXPECT_CALL(*interface, IsValidPc(0x1500)).WillOnce(::testing::Return(false));
+  EXPECT_CALL(*gnu_interface, IsValidPc(0x1500)).WillOnce(::testing::Return(true));
+
+  EXPECT_TRUE(elf.IsValidPc(0x1500));
+}
+
+TEST_F(ElfTest, error_code_not_valid) {
+  ElfFake elf(memory_);
+  elf.FakeSetValid(false);
+
+  ErrorData error{ERROR_MEMORY_INVALID, 0x100};
+  elf.GetLastError(&error);
+  EXPECT_EQ(ERROR_MEMORY_INVALID, error.code);
+  EXPECT_EQ(0x100U, error.address);
+}
+
+TEST_F(ElfTest, error_code_valid) {
+  ElfFake elf(memory_);
+  elf.FakeSetValid(true);
+  ElfInterfaceFake* interface = new ElfInterfaceFake(memory_);
+  elf.FakeSetInterface(interface);
+  interface->FakeSetErrorCode(ERROR_MEMORY_INVALID);
+  interface->FakeSetErrorAddress(0x1000);
+
+  ErrorData error{ERROR_NONE, 0};
+  elf.GetLastError(&error);
+  EXPECT_EQ(ERROR_MEMORY_INVALID, error.code);
+  EXPECT_EQ(0x1000U, error.address);
+  EXPECT_EQ(ERROR_MEMORY_INVALID, elf.GetLastErrorCode());
+  EXPECT_EQ(0x1000U, elf.GetLastErrorAddress());
+}
+
+}  // namespace unwindstack
diff --git a/libunwindstack/tests/ElfTestUtils.cpp b/libunwindstack/tests/ElfTestUtils.cpp
index 8755f16..69163ac 100644
--- a/libunwindstack/tests/ElfTestUtils.cpp
+++ b/libunwindstack/tests/ElfTestUtils.cpp
@@ -31,6 +31,8 @@
 
 #include "ElfTestUtils.h"
 
+namespace unwindstack {
+
 template <typename Ehdr>
 void TestInitEhdr(Ehdr* ehdr, uint32_t elf_class, uint32_t machine_type) {
   memset(ehdr, 0, sizeof(Ehdr));
@@ -45,6 +47,15 @@
   ehdr->e_ehsize = sizeof(Ehdr);
 }
 
+std::string TestGetFileDirectory() {
+  std::string exec(testing::internal::GetArgvs()[0]);
+  auto const value = exec.find_last_of('/');
+  if (value == std::string::npos) {
+    return "tests/files/";
+  }
+  return exec.substr(0, value + 1) + "tests/files/";
+}
+
 template <typename Ehdr, typename Shdr>
 void TestInitGnuDebugdata(uint32_t elf_class, uint32_t machine, bool init_gnu_debugdata,
                           TestCopyFuncType copy_func) {
@@ -91,7 +102,7 @@
   offset = symtab_offset + 0x100;
   if (init_gnu_debugdata) {
     // Read in the compressed elf data and copy it in.
-    name = "tests/files/";
+    name = TestGetFileDirectory();
     if (elf_class == ELFCLASS32) {
       name += "elf32.xz";
     } else {
@@ -127,3 +138,5 @@
                                                            TestCopyFuncType);
 template void TestInitGnuDebugdata<Elf64_Ehdr, Elf64_Shdr>(uint32_t, uint32_t, bool,
                                                            TestCopyFuncType);
+
+}  // namespace unwindstack
diff --git a/libunwindstack/tests/ElfTestUtils.h b/libunwindstack/tests/ElfTestUtils.h
index 4fd3bbc..62cd59a 100644
--- a/libunwindstack/tests/ElfTestUtils.h
+++ b/libunwindstack/tests/ElfTestUtils.h
@@ -18,6 +18,9 @@
 #define _LIBUNWINDSTACK_TESTS_ELF_TEST_UTILS_H
 
 #include <functional>
+#include <string>
+
+namespace unwindstack {
 
 typedef std::function<void(uint64_t, const void*, size_t)> TestCopyFuncType;
 
@@ -28,4 +31,8 @@
 void TestInitGnuDebugdata(uint32_t elf_class, uint32_t machine_type, bool init_gnu_debudata,
                           TestCopyFuncType copy_func);
 
+std::string TestGetFileDirectory();
+
+}  // namespace unwindstack
+
 #endif  // _LIBUNWINDSTACK_TESTS_ELF_TEST_UTILS_H
diff --git a/libunwindstack/tests/JitDebugTest.cpp b/libunwindstack/tests/JitDebugTest.cpp
new file mode 100644
index 0000000..c1c45f8
--- /dev/null
+++ b/libunwindstack/tests/JitDebugTest.cpp
@@ -0,0 +1,408 @@
+/*
+ * 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 <elf.h>
+#include <string.h>
+
+#include <memory>
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include <unwindstack/Elf.h>
+#include <unwindstack/JitDebug.h>
+#include <unwindstack/MapInfo.h>
+#include <unwindstack/Maps.h>
+#include <unwindstack/Memory.h>
+
+#include "ElfFake.h"
+#include "MemoryFake.h"
+
+namespace unwindstack {
+
+class JitDebugTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    memory_ = new MemoryFake;
+    process_memory_.reset(memory_);
+
+    jit_debug_.reset(new JitDebug(process_memory_));
+    jit_debug_->SetArch(ARCH_ARM);
+
+    maps_.reset(
+        new BufferMaps("1000-4000 ---s 00000000 00:00 0\n"
+                       "4000-6000 r--s 00000000 00:00 0\n"
+                       "6000-8000 -w-s 00000000 00:00 0\n"
+                       "a000-c000 --xp 00000000 00:00 0\n"
+                       "c000-f000 rwxp 00000000 00:00 0\n"
+                       "f000-11000 r-xp 00000000 00:00 0\n"
+                       "12000-14000 r-xp 00000000 00:00 0\n"
+                       "100000-110000 rw-p 0000000 00:00 0\n"
+                       "200000-210000 rw-p 0000000 00:00 0\n"));
+    ASSERT_TRUE(maps_->Parse());
+
+    MapInfo* map_info = maps_->Get(3);
+    ASSERT_TRUE(map_info != nullptr);
+    MemoryFake* memory = new MemoryFake;
+    ElfFake* elf = new ElfFake(memory);
+    elf->FakeSetValid(true);
+    ElfInterfaceFake* interface = new ElfInterfaceFake(memory);
+    elf->FakeSetInterface(interface);
+    interface->FakeSetGlobalVariable("__jit_debug_descriptor", 0x800);
+    map_info->elf.reset(elf);
+
+    map_info = maps_->Get(5);
+    ASSERT_TRUE(map_info != nullptr);
+    memory = new MemoryFake;
+    elf = new ElfFake(memory);
+    elf->FakeSetValid(true);
+    interface = new ElfInterfaceFake(memory);
+    elf->FakeSetInterface(interface);
+    interface->FakeSetGlobalVariable("__jit_debug_descriptor", 0x800);
+    map_info->elf.reset(elf);
+
+    map_info = maps_->Get(6);
+    ASSERT_TRUE(map_info != nullptr);
+    memory = new MemoryFake;
+    elf = new ElfFake(memory);
+    elf->FakeSetValid(true);
+    interface = new ElfInterfaceFake(memory);
+    elf->FakeSetInterface(interface);
+    interface->FakeSetGlobalVariable("__jit_debug_descriptor", 0x800);
+    map_info->elf.reset(elf);
+  }
+
+  template <typename EhdrType, typename ShdrType>
+  void CreateElf(uint64_t offset, uint8_t class_type, uint8_t machine_type, uint32_t pc,
+                 uint32_t size) {
+    EhdrType ehdr;
+    memset(&ehdr, 0, sizeof(ehdr));
+    uint64_t sh_offset = sizeof(ehdr);
+    memcpy(ehdr.e_ident, ELFMAG, SELFMAG);
+    ehdr.e_ident[EI_CLASS] = class_type;
+    ehdr.e_machine = machine_type;
+    ehdr.e_shstrndx = 1;
+    ehdr.e_shoff = sh_offset;
+    ehdr.e_shentsize = sizeof(ShdrType);
+    ehdr.e_shnum = 3;
+    memory_->SetMemory(offset, &ehdr, sizeof(ehdr));
+
+    ShdrType shdr;
+    memset(&shdr, 0, sizeof(shdr));
+    shdr.sh_type = SHT_NULL;
+    memory_->SetMemory(offset + sh_offset, &shdr, sizeof(shdr));
+
+    sh_offset += sizeof(shdr);
+    memset(&shdr, 0, sizeof(shdr));
+    shdr.sh_type = SHT_STRTAB;
+    shdr.sh_name = 1;
+    shdr.sh_offset = 0x500;
+    shdr.sh_size = 0x100;
+    memory_->SetMemory(offset + sh_offset, &shdr, sizeof(shdr));
+    memory_->SetMemory(offset + 0x500, ".debug_frame");
+
+    sh_offset += sizeof(shdr);
+    memset(&shdr, 0, sizeof(shdr));
+    shdr.sh_type = SHT_PROGBITS;
+    shdr.sh_name = 0;
+    shdr.sh_addr = 0x600;
+    shdr.sh_offset = 0x600;
+    shdr.sh_size = 0x200;
+    memory_->SetMemory(offset + sh_offset, &shdr, sizeof(shdr));
+
+    // Now add a single cie/fde.
+    uint64_t dwarf_offset = offset + 0x600;
+    if (class_type == ELFCLASS32) {
+      // CIE 32 information.
+      memory_->SetData32(dwarf_offset, 0xfc);
+      memory_->SetData32(dwarf_offset + 0x4, 0xffffffff);
+      memory_->SetData8(dwarf_offset + 0x8, 1);
+      memory_->SetData8(dwarf_offset + 0x9, '\0');
+      memory_->SetData8(dwarf_offset + 0xa, 0x4);
+      memory_->SetData8(dwarf_offset + 0xb, 0x4);
+      memory_->SetData8(dwarf_offset + 0xc, 0x1);
+
+      // FDE 32 information.
+      memory_->SetData32(dwarf_offset + 0x100, 0xfc);
+      memory_->SetData32(dwarf_offset + 0x104, 0);
+      memory_->SetData32(dwarf_offset + 0x108, pc);
+      memory_->SetData32(dwarf_offset + 0x10c, size);
+    } else {
+      // CIE 64 information.
+      memory_->SetData32(dwarf_offset, 0xffffffff);
+      memory_->SetData64(dwarf_offset + 4, 0xf4);
+      memory_->SetData64(dwarf_offset + 0xc, 0xffffffffffffffffULL);
+      memory_->SetData8(dwarf_offset + 0x14, 1);
+      memory_->SetData8(dwarf_offset + 0x15, '\0');
+      memory_->SetData8(dwarf_offset + 0x16, 0x4);
+      memory_->SetData8(dwarf_offset + 0x17, 0x4);
+      memory_->SetData8(dwarf_offset + 0x18, 0x1);
+
+      // FDE 64 information.
+      memory_->SetData32(dwarf_offset + 0x100, 0xffffffff);
+      memory_->SetData64(dwarf_offset + 0x104, 0xf4);
+      memory_->SetData64(dwarf_offset + 0x10c, 0);
+      memory_->SetData64(dwarf_offset + 0x114, pc);
+      memory_->SetData64(dwarf_offset + 0x11c, size);
+    }
+  }
+
+  void WriteDescriptor32(uint64_t addr, uint32_t entry);
+  void WriteDescriptor64(uint64_t addr, uint64_t entry);
+  void WriteEntry32Pack(uint64_t addr, uint32_t prev, uint32_t next, uint32_t elf_addr,
+                        uint64_t elf_size);
+  void WriteEntry32Pad(uint64_t addr, uint32_t prev, uint32_t next, uint32_t elf_addr,
+                       uint64_t elf_size);
+  void WriteEntry64(uint64_t addr, uint64_t prev, uint64_t next, uint64_t elf_addr,
+                    uint64_t elf_size);
+
+  std::shared_ptr<Memory> process_memory_;
+  MemoryFake* memory_;
+  std::unique_ptr<JitDebug> jit_debug_;
+  std::unique_ptr<BufferMaps> maps_;
+};
+
+void JitDebugTest::WriteDescriptor32(uint64_t addr, uint32_t entry) {
+  // Format of the 32 bit JITDescriptor structure:
+  //   uint32_t version
+  memory_->SetData32(addr, 1);
+  //   uint32_t action_flag
+  memory_->SetData32(addr + 4, 0);
+  //   uint32_t relevant_entry
+  memory_->SetData32(addr + 8, 0);
+  //   uint32_t first_entry
+  memory_->SetData32(addr + 12, entry);
+}
+
+void JitDebugTest::WriteDescriptor64(uint64_t addr, uint64_t entry) {
+  // Format of the 64 bit JITDescriptor structure:
+  //   uint32_t version
+  memory_->SetData32(addr, 1);
+  //   uint32_t action_flag
+  memory_->SetData32(addr + 4, 0);
+  //   uint64_t relevant_entry
+  memory_->SetData64(addr + 8, 0);
+  //   uint64_t first_entry
+  memory_->SetData64(addr + 16, entry);
+}
+
+void JitDebugTest::WriteEntry32Pack(uint64_t addr, uint32_t prev, uint32_t next, uint32_t elf_addr,
+                                    uint64_t elf_size) {
+  // Format of the 32 bit JITCodeEntry structure:
+  //   uint32_t next
+  memory_->SetData32(addr, next);
+  //   uint32_t prev
+  memory_->SetData32(addr + 4, prev);
+  //   uint32_t symfile_addr
+  memory_->SetData32(addr + 8, elf_addr);
+  //   uint64_t symfile_size
+  memory_->SetData64(addr + 12, elf_size);
+}
+
+void JitDebugTest::WriteEntry32Pad(uint64_t addr, uint32_t prev, uint32_t next, uint32_t elf_addr,
+                                   uint64_t elf_size) {
+  // Format of the 32 bit JITCodeEntry structure:
+  //   uint32_t next
+  memory_->SetData32(addr, next);
+  //   uint32_t prev
+  memory_->SetData32(addr + 4, prev);
+  //   uint32_t symfile_addr
+  memory_->SetData32(addr + 8, elf_addr);
+  //   uint32_t pad
+  memory_->SetData32(addr + 12, 0);
+  //   uint64_t symfile_size
+  memory_->SetData64(addr + 16, elf_size);
+}
+
+void JitDebugTest::WriteEntry64(uint64_t addr, uint64_t prev, uint64_t next, uint64_t elf_addr,
+                                uint64_t elf_size) {
+  // Format of the 64 bit JITCodeEntry structure:
+  //   uint64_t next
+  memory_->SetData64(addr, next);
+  //   uint64_t prev
+  memory_->SetData64(addr + 8, prev);
+  //   uint64_t symfile_addr
+  memory_->SetData64(addr + 16, elf_addr);
+  //   uint64_t symfile_size
+  memory_->SetData64(addr + 24, elf_size);
+}
+
+TEST_F(JitDebugTest, get_elf_invalid) {
+  Elf* elf = jit_debug_->GetElf(maps_.get(), 0x1500);
+  ASSERT_TRUE(elf == nullptr);
+}
+
+TEST_F(JitDebugTest, get_elf_no_global_variable) {
+  maps_.reset(new BufferMaps(""));
+  Elf* elf = jit_debug_->GetElf(maps_.get(), 0x1500);
+  ASSERT_TRUE(elf == nullptr);
+}
+
+TEST_F(JitDebugTest, get_elf_no_valid_descriptor_in_memory) {
+  CreateElf<Elf32_Ehdr, Elf32_Shdr>(0x4000, ELFCLASS32, EM_ARM, 0x1500, 0x200);
+
+  Elf* elf = jit_debug_->GetElf(maps_.get(), 0x1500);
+  ASSERT_TRUE(elf == nullptr);
+}
+
+TEST_F(JitDebugTest, get_elf_no_valid_code_entry) {
+  CreateElf<Elf32_Ehdr, Elf32_Shdr>(0x4000, ELFCLASS32, EM_ARM, 0x1500, 0x200);
+
+  WriteDescriptor32(0xf800, 0x200000);
+
+  Elf* elf = jit_debug_->GetElf(maps_.get(), 0x1500);
+  ASSERT_TRUE(elf == nullptr);
+}
+
+TEST_F(JitDebugTest, get_elf_invalid_descriptor_first_entry) {
+  CreateElf<Elf32_Ehdr, Elf32_Shdr>(0x4000, ELFCLASS32, EM_ARM, 0x1500, 0x200);
+
+  WriteDescriptor32(0xf800, 0);
+
+  Elf* elf = jit_debug_->GetElf(maps_.get(), 0x1500);
+  ASSERT_TRUE(elf == nullptr);
+}
+
+TEST_F(JitDebugTest, get_elf_invalid_descriptor_version) {
+  CreateElf<Elf32_Ehdr, Elf32_Shdr>(0x4000, ELFCLASS32, EM_ARM, 0x1500, 0x200);
+
+  WriteDescriptor32(0xf800, 0x20000);
+  // Set the version to an invalid value.
+  memory_->SetData32(0xf800, 2);
+
+  Elf* elf = jit_debug_->GetElf(maps_.get(), 0x1500);
+  ASSERT_TRUE(elf == nullptr);
+}
+
+TEST_F(JitDebugTest, get_elf_32) {
+  CreateElf<Elf32_Ehdr, Elf32_Shdr>(0x4000, ELFCLASS32, EM_ARM, 0x1500, 0x200);
+
+  WriteDescriptor32(0xf800, 0x200000);
+  WriteEntry32Pad(0x200000, 0, 0, 0x4000, 0x1000);
+
+  Elf* elf = jit_debug_->GetElf(maps_.get(), 0x1500);
+  ASSERT_TRUE(elf != nullptr);
+
+  // Clear the memory and verify all of the data is cached.
+  memory_->Clear();
+  Elf* elf2 = jit_debug_->GetElf(maps_.get(), 0x1500);
+  ASSERT_TRUE(elf2 != nullptr);
+  EXPECT_EQ(elf, elf2);
+}
+
+TEST_F(JitDebugTest, get_multiple_jit_debug_descriptors_valid) {
+  CreateElf<Elf32_Ehdr, Elf32_Shdr>(0x4000, ELFCLASS32, EM_ARM, 0x1500, 0x200);
+  CreateElf<Elf32_Ehdr, Elf32_Shdr>(0x5000, ELFCLASS32, EM_ARM, 0x2000, 0x300);
+
+  WriteDescriptor32(0xf800, 0x200000);
+  WriteEntry32Pad(0x200000, 0, 0, 0x4000, 0x1000);
+  WriteDescriptor32(0x12800, 0x201000);
+  WriteEntry32Pad(0x201000, 0, 0, 0x5000, 0x1000);
+
+  ASSERT_TRUE(jit_debug_->GetElf(maps_.get(), 0x1500) != nullptr);
+  ASSERT_TRUE(jit_debug_->GetElf(maps_.get(), 0x2000) == nullptr);
+
+  // Now clear the descriptor entry for the first one.
+  WriteDescriptor32(0xf800, 0);
+  jit_debug_.reset(new JitDebug(process_memory_));
+  jit_debug_->SetArch(ARCH_ARM);
+
+  ASSERT_TRUE(jit_debug_->GetElf(maps_.get(), 0x1500) == nullptr);
+  ASSERT_TRUE(jit_debug_->GetElf(maps_.get(), 0x2000) != nullptr);
+}
+
+TEST_F(JitDebugTest, get_elf_x86) {
+  CreateElf<Elf32_Ehdr, Elf32_Shdr>(0x4000, ELFCLASS32, EM_ARM, 0x1500, 0x200);
+
+  WriteDescriptor32(0xf800, 0x200000);
+  WriteEntry32Pack(0x200000, 0, 0, 0x4000, 0x1000);
+
+  jit_debug_->SetArch(ARCH_X86);
+  Elf* elf = jit_debug_->GetElf(maps_.get(), 0x1500);
+  ASSERT_TRUE(elf != nullptr);
+
+  // Clear the memory and verify all of the data is cached.
+  memory_->Clear();
+  Elf* elf2 = jit_debug_->GetElf(maps_.get(), 0x1500);
+  ASSERT_TRUE(elf2 != nullptr);
+  EXPECT_EQ(elf, elf2);
+}
+
+TEST_F(JitDebugTest, get_elf_64) {
+  CreateElf<Elf64_Ehdr, Elf64_Shdr>(0x4000, ELFCLASS64, EM_AARCH64, 0x1500, 0x200);
+
+  WriteDescriptor64(0xf800, 0x200000);
+  WriteEntry64(0x200000, 0, 0, 0x4000, 0x1000);
+
+  jit_debug_->SetArch(ARCH_ARM64);
+  Elf* elf = jit_debug_->GetElf(maps_.get(), 0x1500);
+  ASSERT_TRUE(elf != nullptr);
+
+  // Clear the memory and verify all of the data is cached.
+  memory_->Clear();
+  Elf* elf2 = jit_debug_->GetElf(maps_.get(), 0x1500);
+  ASSERT_TRUE(elf2 != nullptr);
+  EXPECT_EQ(elf, elf2);
+}
+
+TEST_F(JitDebugTest, get_elf_multiple_entries) {
+  CreateElf<Elf32_Ehdr, Elf32_Shdr>(0x4000, ELFCLASS32, EM_ARM, 0x1500, 0x200);
+  CreateElf<Elf32_Ehdr, Elf32_Shdr>(0x5000, ELFCLASS32, EM_ARM, 0x2300, 0x400);
+
+  WriteDescriptor32(0xf800, 0x200000);
+  WriteEntry32Pad(0x200000, 0, 0x200100, 0x4000, 0x1000);
+  WriteEntry32Pad(0x200100, 0x200100, 0, 0x5000, 0x1000);
+
+  Elf* elf_2 = jit_debug_->GetElf(maps_.get(), 0x2400);
+  ASSERT_TRUE(elf_2 != nullptr);
+
+  Elf* elf_1 = jit_debug_->GetElf(maps_.get(), 0x1600);
+  ASSERT_TRUE(elf_1 != nullptr);
+
+  // Clear the memory and verify all of the data is cached.
+  memory_->Clear();
+  EXPECT_EQ(elf_1, jit_debug_->GetElf(maps_.get(), 0x1500));
+  EXPECT_EQ(elf_1, jit_debug_->GetElf(maps_.get(), 0x16ff));
+  EXPECT_EQ(elf_2, jit_debug_->GetElf(maps_.get(), 0x2300));
+  EXPECT_EQ(elf_2, jit_debug_->GetElf(maps_.get(), 0x26ff));
+  EXPECT_EQ(nullptr, jit_debug_->GetElf(maps_.get(), 0x1700));
+  EXPECT_EQ(nullptr, jit_debug_->GetElf(maps_.get(), 0x2700));
+}
+
+TEST_F(JitDebugTest, get_elf_search_libs) {
+  CreateElf<Elf32_Ehdr, Elf32_Shdr>(0x4000, ELFCLASS32, EM_ARM, 0x1500, 0x200);
+
+  WriteDescriptor32(0xf800, 0x200000);
+  WriteEntry32Pad(0x200000, 0, 0, 0x4000, 0x1000);
+
+  // Only search a given named list of libs.
+  std::vector<std::string> libs{"libart.so"};
+  jit_debug_.reset(new JitDebug(process_memory_, libs));
+  jit_debug_->SetArch(ARCH_ARM);
+  EXPECT_TRUE(jit_debug_->GetElf(maps_.get(), 0x1500) == nullptr);
+
+  // Change the name of the map that includes the value and verify this works.
+  MapInfo* map_info = maps_->Get(5);
+  map_info->name = "/system/lib/libart.so";
+  jit_debug_.reset(new JitDebug(process_memory_, libs));
+  // Make sure that clearing our copy of the libs doesn't affect the
+  // JitDebug object.
+  libs.clear();
+  jit_debug_->SetArch(ARCH_ARM);
+  EXPECT_TRUE(jit_debug_->GetElf(maps_.get(), 0x1500) != nullptr);
+}
+
+}  // namespace unwindstack
diff --git a/libunwindstack/tests/LocalUnwinderTest.cpp b/libunwindstack/tests/LocalUnwinderTest.cpp
new file mode 100644
index 0000000..56a18cd
--- /dev/null
+++ b/libunwindstack/tests/LocalUnwinderTest.cpp
@@ -0,0 +1,207 @@
+/*
+ * 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 <dlfcn.h>
+#include <inttypes.h>
+#include <signal.h>
+#include <stdint.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include <android-base/stringprintf.h>
+
+#include <unwindstack/LocalUnwinder.h>
+
+namespace unwindstack {
+
+static std::vector<LocalFrameData>* g_frame_info;
+static LocalUnwinder* g_unwinder;
+
+extern "C" void SignalLocalInnerFunction() {
+  g_unwinder->Unwind(g_frame_info, 256);
+}
+
+extern "C" void SignalLocalMiddleFunction() {
+  SignalLocalInnerFunction();
+}
+
+extern "C" void SignalLocalOuterFunction() {
+  SignalLocalMiddleFunction();
+}
+
+static void SignalLocalCallerHandler(int, siginfo_t*, void*) {
+  SignalLocalOuterFunction();
+}
+
+static std::string ErrorMsg(const std::vector<const char*>& function_names,
+                            const std::vector<LocalFrameData>& frame_info) {
+  std::string unwind;
+  size_t i = 0;
+  for (const auto& frame : frame_info) {
+    unwind += android::base::StringPrintf("#%02zu pc 0x%" PRIx64 " rel_pc 0x%" PRIx64, i++,
+                                          frame.pc, frame.rel_pc);
+    if (frame.map_info != nullptr) {
+      if (!frame.map_info->name.empty()) {
+        unwind += " " + frame.map_info->name;
+      } else {
+        unwind += android::base::StringPrintf(" 0x%" PRIx64 "-0x%" PRIx64, frame.map_info->start,
+                                              frame.map_info->end);
+      }
+      if (frame.map_info->offset != 0) {
+        unwind += android::base::StringPrintf(" offset 0x%" PRIx64, frame.map_info->offset);
+      }
+    }
+    if (!frame.function_name.empty()) {
+      unwind += " " + frame.function_name;
+      if (frame.function_offset != 0) {
+        unwind += android::base::StringPrintf("+%" PRId64, frame.function_offset);
+      }
+    }
+    unwind += '\n';
+  }
+
+  return std::string(
+             "Unwind completed without finding all frames\n"
+             "  Looking for function: ") +
+         function_names.front() + "\n" + "Unwind data:\n" + unwind;
+}
+
+// This test assumes that this code is compiled with optimizations turned
+// off. If this doesn't happen, then all of the calls will be optimized
+// away.
+extern "C" void LocalInnerFunction(LocalUnwinder* unwinder, bool unwind_through_signal) {
+  std::vector<LocalFrameData> frame_info;
+  g_frame_info = &frame_info;
+  g_unwinder = unwinder;
+  std::vector<const char*> expected_function_names;
+
+  if (unwind_through_signal) {
+    struct sigaction act, oldact;
+    memset(&act, 0, sizeof(act));
+    act.sa_sigaction = SignalLocalCallerHandler;
+    act.sa_flags = SA_RESTART | SA_ONSTACK;
+    ASSERT_EQ(0, sigaction(SIGUSR1, &act, &oldact));
+
+    raise(SIGUSR1);
+
+    ASSERT_EQ(0, sigaction(SIGUSR1, &oldact, nullptr));
+
+    expected_function_names = {"LocalOuterFunction",        "LocalMiddleFunction",
+                               "LocalInnerFunction",        "SignalLocalOuterFunction",
+                               "SignalLocalMiddleFunction", "SignalLocalInnerFunction"};
+  } else {
+    ASSERT_TRUE(unwinder->Unwind(&frame_info, 256));
+
+    expected_function_names = {"LocalOuterFunction", "LocalMiddleFunction", "LocalInnerFunction"};
+  }
+
+  for (auto& frame : frame_info) {
+    if (frame.function_name == expected_function_names.back()) {
+      expected_function_names.pop_back();
+      if (expected_function_names.empty()) {
+        break;
+      }
+    }
+  }
+
+  ASSERT_TRUE(expected_function_names.empty()) << ErrorMsg(expected_function_names, frame_info);
+}
+
+extern "C" void LocalMiddleFunction(LocalUnwinder* unwinder, bool unwind_through_signal) {
+  LocalInnerFunction(unwinder, unwind_through_signal);
+}
+
+extern "C" void LocalOuterFunction(LocalUnwinder* unwinder, bool unwind_through_signal) {
+  LocalMiddleFunction(unwinder, unwind_through_signal);
+}
+
+class LocalUnwinderTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    unwinder_.reset(new LocalUnwinder);
+    ASSERT_TRUE(unwinder_->Init());
+  }
+
+  std::unique_ptr<LocalUnwinder> unwinder_;
+};
+
+TEST_F(LocalUnwinderTest, local) {
+  LocalOuterFunction(unwinder_.get(), false);
+}
+
+TEST_F(LocalUnwinderTest, local_signal) {
+  LocalOuterFunction(unwinder_.get(), true);
+}
+
+TEST_F(LocalUnwinderTest, local_multiple) {
+  ASSERT_NO_FATAL_FAILURE(LocalOuterFunction(unwinder_.get(), false));
+
+  ASSERT_NO_FATAL_FAILURE(LocalOuterFunction(unwinder_.get(), true));
+
+  ASSERT_NO_FATAL_FAILURE(LocalOuterFunction(unwinder_.get(), false));
+
+  ASSERT_NO_FATAL_FAILURE(LocalOuterFunction(unwinder_.get(), true));
+}
+
+// This test verifies that doing an unwind before and after a dlopen
+// works. It's verifying that the maps read during the first unwind
+// do not cause a problem when doing the unwind using the code in
+// the dlopen'd code.
+TEST_F(LocalUnwinderTest, unwind_after_dlopen) {
+  // Prime the maps data.
+  ASSERT_NO_FATAL_FAILURE(LocalOuterFunction(unwinder_.get(), false));
+
+  std::string testlib(testing::internal::GetArgvs()[0]);
+  auto const value = testlib.find_last_of('/');
+  if (value == std::string::npos) {
+    testlib = "../";
+  } else {
+    testlib = testlib.substr(0, value + 1) + "../";
+  }
+  testlib += "libunwindstack_local.so";
+
+  void* handle = dlopen(testlib.c_str(), RTLD_NOW);
+  ASSERT_TRUE(handle != nullptr);
+
+  void (*unwind_function)(void*, void*) =
+      reinterpret_cast<void (*)(void*, void*)>(dlsym(handle, "TestlibLevel1"));
+  ASSERT_TRUE(unwind_function != nullptr);
+
+  std::vector<LocalFrameData> frame_info;
+  unwind_function(unwinder_.get(), &frame_info);
+
+  ASSERT_EQ(0, dlclose(handle));
+
+  std::vector<const char*> expected_function_names{"TestlibLevel1", "TestlibLevel2",
+                                                   "TestlibLevel3", "TestlibLevel4"};
+
+  for (auto& frame : frame_info) {
+    if (frame.function_name == expected_function_names.back()) {
+      expected_function_names.pop_back();
+      if (expected_function_names.empty()) {
+        break;
+      }
+    }
+  }
+
+  ASSERT_TRUE(expected_function_names.empty()) << ErrorMsg(expected_function_names, frame_info);
+}
+
+}  // namespace unwindstack
diff --git a/libunwindstack/tests/LogFake.cpp b/libunwindstack/tests/LogFake.cpp
index 411594a..537ccaf 100644
--- a/libunwindstack/tests/LogFake.cpp
+++ b/libunwindstack/tests/LogFake.cpp
@@ -25,7 +25,6 @@
 #include "LogFake.h"
 
 // Forward declarations.
-class Backtrace;
 struct EventTagMap;
 struct AndroidLogEntry;
 
@@ -33,6 +32,8 @@
 
 std::string g_fake_log_print;
 
+namespace unwindstack {
+
 void ResetLogs() {
   g_fake_log_buf = "";
   g_fake_log_print = "";
@@ -46,6 +47,8 @@
   return g_fake_log_print;
 }
 
+}  // namespace unwindstack
+
 extern "C" int __android_log_buf_write(int bufId, int prio, const char* tag, const char* msg) {
   g_fake_log_buf += std::to_string(bufId) + ' ' + std::to_string(prio) + ' ';
   g_fake_log_buf += tag;
diff --git a/libunwindstack/tests/LogFake.h b/libunwindstack/tests/LogFake.h
index 006d393..e1dc50d 100644
--- a/libunwindstack/tests/LogFake.h
+++ b/libunwindstack/tests/LogFake.h
@@ -19,8 +19,12 @@
 
 #include <string>
 
+namespace unwindstack {
+
 void ResetLogs();
 std::string GetFakeLogBuf();
 std::string GetFakeLogPrint();
 
+}  // namespace unwindstack
+
 #endif  // _LIBUNWINDSTACK_TESTS_LOG_FAKE_H
diff --git a/libunwindstack/tests/MapInfoCreateMemoryTest.cpp b/libunwindstack/tests/MapInfoCreateMemoryTest.cpp
index d7433e1..866b5b4 100644
--- a/libunwindstack/tests/MapInfoCreateMemoryTest.cpp
+++ b/libunwindstack/tests/MapInfoCreateMemoryTest.cpp
@@ -30,92 +30,161 @@
 #include <android-base/test_utils.h>
 #include <gtest/gtest.h>
 
-#include "Elf.h"
-#include "MapInfo.h"
-#include "Memory.h"
+#include <unwindstack/Elf.h>
+#include <unwindstack/MapInfo.h>
+#include <unwindstack/Memory.h>
+
+#include "MemoryFake.h"
+
+namespace unwindstack {
 
 class MapInfoCreateMemoryTest : public ::testing::Test {
  protected:
+  template <typename Ehdr, typename Shdr>
+  static void InitElf(int fd, uint64_t file_offset, uint64_t sh_offset, uint8_t class_type) {
+    std::vector<uint8_t> buffer(20000);
+    memset(buffer.data(), 0, buffer.size());
+
+    Ehdr ehdr;
+    memset(&ehdr, 0, sizeof(ehdr));
+    memcpy(ehdr.e_ident, ELFMAG, SELFMAG);
+    ehdr.e_ident[EI_CLASS] = class_type;
+    ehdr.e_shoff = sh_offset;
+    ehdr.e_shentsize = sizeof(Shdr) + 100;
+    ehdr.e_shnum = 4;
+    memcpy(&buffer[file_offset], &ehdr, sizeof(ehdr));
+
+    ASSERT_TRUE(android::base::WriteFully(fd, buffer.data(), buffer.size()));
+  }
+
   static void SetUpTestCase() {
     std::vector<uint8_t> buffer(1024);
+    memset(buffer.data(), 0, buffer.size());
     memcpy(buffer.data(), ELFMAG, SELFMAG);
-    for (size_t i = SELFMAG; i < buffer.size(); i++) {
-      buffer[i] = i / 256 + 1;
-    }
+    buffer[EI_CLASS] = ELFCLASS32;
     ASSERT_TRUE(android::base::WriteFully(elf_.fd, buffer.data(), buffer.size()));
 
-    for (size_t i = 0; i < 0x100; i++) {
-      buffer[i] = i / 256 + 1;
-    }
+    memset(buffer.data(), 0, buffer.size());
     memcpy(&buffer[0x100], ELFMAG, SELFMAG);
-    for (size_t i = 0x100 + SELFMAG; i < buffer.size(); i++) {
-      buffer[i] = i / 256 + 1;
-    }
+    buffer[0x100 + EI_CLASS] = ELFCLASS64;
     ASSERT_TRUE(android::base::WriteFully(elf_at_100_.fd, buffer.data(), buffer.size()));
+
+    InitElf<Elf32_Ehdr, Elf32_Shdr>(elf32_at_map_.fd, 0x1000, 0x2000, ELFCLASS32);
+    InitElf<Elf64_Ehdr, Elf64_Shdr>(elf64_at_map_.fd, 0x2000, 0x3000, ELFCLASS64);
   }
 
+  void SetUp() override {
+    memory_ = new MemoryFake;
+    process_memory_.reset(memory_);
+  }
+
+  MemoryFake* memory_;
+  std::shared_ptr<Memory> process_memory_;
+
   static TemporaryFile elf_;
 
   static TemporaryFile elf_at_100_;
+
+  static TemporaryFile elf32_at_map_;
+  static TemporaryFile elf64_at_map_;
 };
 TemporaryFile MapInfoCreateMemoryTest::elf_;
 TemporaryFile MapInfoCreateMemoryTest::elf_at_100_;
+TemporaryFile MapInfoCreateMemoryTest::elf32_at_map_;
+TemporaryFile MapInfoCreateMemoryTest::elf64_at_map_;
 
 TEST_F(MapInfoCreateMemoryTest, end_le_start) {
-  MapInfo info{.start = 0x100, .end = 0x100, .offset = 0, .name = elf_.path};
+  MapInfo info(0x100, 0x100, 0, 0, elf_.path);
 
-  std::unique_ptr<Memory> memory;
-  memory.reset(info.CreateMemory(getpid()));
+  std::unique_ptr<Memory> memory(info.CreateMemory(process_memory_));
   ASSERT_TRUE(memory.get() == nullptr);
 
   info.end = 0xff;
-  memory.reset(info.CreateMemory(getpid()));
+  memory.reset(info.CreateMemory(process_memory_));
   ASSERT_TRUE(memory.get() == nullptr);
 
   // Make sure this test is valid.
   info.end = 0x101;
-  memory.reset(info.CreateMemory(getpid()));
+  memory.reset(info.CreateMemory(process_memory_));
   ASSERT_TRUE(memory.get() != nullptr);
 }
 
 // Verify that if the offset is non-zero but there is no elf at the offset,
 // that the full file is used.
 TEST_F(MapInfoCreateMemoryTest, file_backed_non_zero_offset_full_file) {
-  MapInfo info{.start = 0x100, .end = 0x200, .offset = 0x100, .name = elf_.path};
+  MapInfo info(0x100, 0x200, 0x100, 0, elf_.path);
 
-  std::unique_ptr<Memory> memory(info.CreateMemory(getpid()));
+  std::unique_ptr<Memory> memory(info.CreateMemory(process_memory_));
   ASSERT_TRUE(memory.get() != nullptr);
   ASSERT_EQ(0x100U, info.elf_offset);
 
   // Read the entire file.
   std::vector<uint8_t> buffer(1024);
-  ASSERT_TRUE(memory->Read(0, buffer.data(), 1024));
+  ASSERT_TRUE(memory->ReadFully(0, buffer.data(), 1024));
   ASSERT_TRUE(memcmp(buffer.data(), ELFMAG, SELFMAG) == 0);
-  for (size_t i = SELFMAG; i < buffer.size(); i++) {
-    ASSERT_EQ(i / 256 + 1, buffer[i]) << "Failed at byte " << i;
+  ASSERT_EQ(ELFCLASS32, buffer[EI_CLASS]);
+  for (size_t i = EI_CLASS + 1; i < buffer.size(); i++) {
+    ASSERT_EQ(0, buffer[i]) << "Failed at byte " << i;
   }
 
-  ASSERT_FALSE(memory->Read(1024, buffer.data(), 1));
+  ASSERT_FALSE(memory->ReadFully(1024, buffer.data(), 1));
 }
 
 // Verify that if the offset is non-zero and there is an elf at that
 // offset, that only part of the file is used.
 TEST_F(MapInfoCreateMemoryTest, file_backed_non_zero_offset_partial_file) {
-  MapInfo info{.start = 0x100, .end = 0x200, .offset = 0x100, .name = elf_at_100_.path};
+  MapInfo info(0x100, 0x200, 0x100, 0, elf_at_100_.path);
 
-  std::unique_ptr<Memory> memory(info.CreateMemory(getpid()));
+  std::unique_ptr<Memory> memory(info.CreateMemory(process_memory_));
   ASSERT_TRUE(memory.get() != nullptr);
   ASSERT_EQ(0U, info.elf_offset);
 
   // Read the valid part of the file.
   std::vector<uint8_t> buffer(0x100);
-  ASSERT_TRUE(memory->Read(0, buffer.data(), 0x100));
+  ASSERT_TRUE(memory->ReadFully(0, buffer.data(), 0x100));
   ASSERT_TRUE(memcmp(buffer.data(), ELFMAG, SELFMAG) == 0);
-  for (size_t i = SELFMAG; i < buffer.size(); i++) {
-    ASSERT_EQ(2, buffer[i]) << "Failed at byte " << i;
+  ASSERT_EQ(ELFCLASS64, buffer[EI_CLASS]);
+  for (size_t i = EI_CLASS + 1; i < buffer.size(); i++) {
+    ASSERT_EQ(0, buffer[i]) << "Failed at byte " << i;
   }
 
-  ASSERT_FALSE(memory->Read(0x100, buffer.data(), 1));
+  ASSERT_FALSE(memory->ReadFully(0x100, buffer.data(), 1));
+}
+
+// Verify that if the offset is non-zero and there is an elf at that
+// offset, that only part of the file is used. Further verify that if the
+// embedded elf is bigger than the initial map, the new object is larger
+// than the original map size. Do this for a 32 bit elf and a 64 bit elf.
+TEST_F(MapInfoCreateMemoryTest, file_backed_non_zero_offset_partial_file_whole_elf32) {
+  MapInfo info(0x5000, 0x6000, 0x1000, 0, elf32_at_map_.path);
+
+  std::unique_ptr<Memory> memory(info.CreateMemory(process_memory_));
+  ASSERT_TRUE(memory.get() != nullptr);
+  ASSERT_EQ(0U, info.elf_offset);
+
+  // Verify the memory is a valid elf.
+  uint8_t e_ident[SELFMAG + 1];
+  ASSERT_TRUE(memory->ReadFully(0, e_ident, SELFMAG));
+  ASSERT_EQ(0, memcmp(e_ident, ELFMAG, SELFMAG));
+
+  // Read past the end of what would normally be the size of the map.
+  ASSERT_TRUE(memory->ReadFully(0x1000, e_ident, 1));
+}
+
+TEST_F(MapInfoCreateMemoryTest, file_backed_non_zero_offset_partial_file_whole_elf64) {
+  MapInfo info(0x7000, 0x8000, 0x2000, 0, elf64_at_map_.path);
+
+  std::unique_ptr<Memory> memory(info.CreateMemory(process_memory_));
+  ASSERT_TRUE(memory.get() != nullptr);
+  ASSERT_EQ(0U, info.elf_offset);
+
+  // Verify the memory is a valid elf.
+  uint8_t e_ident[SELFMAG + 1];
+  ASSERT_TRUE(memory->ReadFully(0, e_ident, SELFMAG));
+  ASSERT_EQ(0, memcmp(e_ident, ELFMAG, SELFMAG));
+
+  // Read past the end of what would normally be the size of the map.
+  ASSERT_TRUE(memory->ReadFully(0x1000, e_ident, 1));
 }
 
 // Verify that device file names will never result in Memory object creation.
@@ -127,78 +196,38 @@
   info.start = reinterpret_cast<uint64_t>(buffer.data());
   info.end = info.start + buffer.size();
   info.offset = 0;
-  std::unique_ptr<Memory> memory;
 
   info.flags = 0x8000;
   info.name = "/dev/something";
-  memory.reset(info.CreateMemory(getpid()));
+  std::unique_ptr<Memory> memory(info.CreateMemory(process_memory_));
   ASSERT_TRUE(memory.get() == nullptr);
 }
 
-TEST_F(MapInfoCreateMemoryTest, local_memory) {
-  // Set up some memory for a valid local memory object.
+TEST_F(MapInfoCreateMemoryTest, process_memory) {
+  MapInfo info;
+  info.start = 0x2000;
+  info.end = 0x3000;
+  info.offset = 0;
+
+  // Verify that the the process_memory object is used, so seed it
+  // with memory.
   std::vector<uint8_t> buffer(1024);
   for (size_t i = 0; i < buffer.size(); i++) {
     buffer[i] = i % 256;
   }
+  memory_->SetMemory(info.start, buffer.data(), buffer.size());
 
-  MapInfo info;
-  info.start = reinterpret_cast<uint64_t>(buffer.data());
-  info.end = info.start + buffer.size();
-  info.offset = 0;
-
-  std::unique_ptr<Memory> memory;
-  memory.reset(info.CreateMemory(getpid()));
+  std::unique_ptr<Memory> memory(info.CreateMemory(process_memory_));
   ASSERT_TRUE(memory.get() != nullptr);
 
-  std::vector<uint8_t> read_buffer(1024);
-  ASSERT_TRUE(memory->Read(0, read_buffer.data(), read_buffer.size()));
-  for (size_t i = 0; i < read_buffer.size(); i++) {
-    ASSERT_EQ(i % 256, read_buffer[i]) << "Failed at byte " << i;
+  memset(buffer.data(), 0, buffer.size());
+  ASSERT_TRUE(memory->ReadFully(0, buffer.data(), buffer.size()));
+  for (size_t i = 0; i < buffer.size(); i++) {
+    ASSERT_EQ(i % 256, buffer[i]) << "Failed at byte " << i;
   }
 
-  ASSERT_FALSE(memory->Read(read_buffer.size(), read_buffer.data(), 1));
+  // Try to read outside of the map size.
+  ASSERT_FALSE(memory->ReadFully(buffer.size(), buffer.data(), 1));
 }
 
-TEST_F(MapInfoCreateMemoryTest, remote_memory) {
-  std::vector<uint8_t> buffer(1024);
-  memset(buffer.data(), 0xa, buffer.size());
-
-  pid_t pid;
-  if ((pid = fork()) == 0) {
-    while (true)
-      ;
-    exit(1);
-  }
-  ASSERT_LT(0, pid);
-
-  ASSERT_TRUE(ptrace(PTRACE_ATTACH, pid, 0, 0) != -1);
-  uint64_t iterations = 0;
-  siginfo_t si;
-  while (TEMP_FAILURE_RETRY(ptrace(PTRACE_GETSIGINFO, pid, 0, &si)) < 0 && errno == ESRCH) {
-    usleep(30);
-    iterations++;
-    ASSERT_LT(iterations, 500000000ULL);
-  }
-
-  MapInfo info;
-  info.start = reinterpret_cast<uint64_t>(buffer.data());
-  info.end = info.start + buffer.size();
-  info.offset = 0;
-
-  std::unique_ptr<Memory> memory;
-  memory.reset(info.CreateMemory(pid));
-  ASSERT_TRUE(memory.get() != nullptr);
-  // Set the local memory to a different value to guarantee we are reading
-  // from the remote process.
-  memset(buffer.data(), 0x1, buffer.size());
-  std::vector<uint8_t> read_buffer(1024);
-  ASSERT_TRUE(memory->Read(0, read_buffer.data(), read_buffer.size()));
-  for (size_t i = 0; i < read_buffer.size(); i++) {
-    ASSERT_EQ(0xaU, read_buffer[i]) << "Failed at byte " << i;
-  }
-
-  ASSERT_TRUE(ptrace(PTRACE_DETACH, pid, 0, 0) == 0);
-
-  kill(pid, SIGKILL);
-}
+}  // namespace unwindstack
diff --git a/libunwindstack/tests/MapInfoGetElfTest.cpp b/libunwindstack/tests/MapInfoGetElfTest.cpp
index fc36cc6..f599503 100644
--- a/libunwindstack/tests/MapInfoGetElfTest.cpp
+++ b/libunwindstack/tests/MapInfoGetElfTest.cpp
@@ -23,74 +23,98 @@
 #include <sys/types.h>
 #include <unistd.h>
 
+#include <atomic>
 #include <memory>
+#include <thread>
 #include <vector>
 
 #include <android-base/file.h>
 #include <android-base/test_utils.h>
 #include <gtest/gtest.h>
 
-#include "Elf.h"
+#include <unwindstack/Elf.h>
+#include <unwindstack/MapInfo.h>
+#include <unwindstack/Maps.h>
+#include <unwindstack/Memory.h>
+
 #include "ElfTestUtils.h"
-#include "MapInfo.h"
+#include "MemoryFake.h"
+
+namespace unwindstack {
 
 class MapInfoGetElfTest : public ::testing::Test {
  protected:
   void SetUp() override {
-    map_ = mmap(nullptr, kMapSize, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
-    ASSERT_NE(MAP_FAILED, map_);
-
-    uint64_t start = reinterpret_cast<uint64_t>(map_);
-    info_.reset(new MapInfo{.start = start, .end = start + 1024, .offset = 0, .name = ""});
+    memory_ = new MemoryFake;
+    process_memory_.reset(memory_);
   }
 
-  void TearDown() override { munmap(map_, kMapSize); }
+  template <typename Ehdr, typename Shdr>
+  static void InitElf(uint64_t sh_offset, Ehdr* ehdr, uint8_t class_type, uint8_t machine_type) {
+    memset(ehdr, 0, sizeof(*ehdr));
+    memcpy(ehdr->e_ident, ELFMAG, SELFMAG);
+    ehdr->e_ident[EI_CLASS] = class_type;
+    ehdr->e_machine = machine_type;
+    ehdr->e_shoff = sh_offset;
+    ehdr->e_shentsize = sizeof(Shdr) + 100;
+    ehdr->e_shnum = 4;
+  }
 
   const size_t kMapSize = 4096;
 
-  void* map_ = nullptr;
-  std::unique_ptr<MapInfo> info_;
+  std::shared_ptr<Memory> process_memory_;
+  MemoryFake* memory_;
+
+  TemporaryFile elf_;
 };
 
 TEST_F(MapInfoGetElfTest, invalid) {
+  MapInfo info(0x1000, 0x2000, 0, PROT_READ, "");
+
   // The map is empty, but this should still create an invalid elf object.
-  std::unique_ptr<Elf> elf(info_->GetElf(getpid(), false));
-  ASSERT_TRUE(elf.get() != nullptr);
+  Elf* elf = info.GetElf(process_memory_, false);
+  ASSERT_TRUE(elf != nullptr);
   ASSERT_FALSE(elf->valid());
 }
 
 TEST_F(MapInfoGetElfTest, valid32) {
+  MapInfo info(0x3000, 0x4000, 0, PROT_READ, "");
+
   Elf32_Ehdr ehdr;
   TestInitEhdr<Elf32_Ehdr>(&ehdr, ELFCLASS32, EM_ARM);
-  memcpy(map_, &ehdr, sizeof(ehdr));
+  memory_->SetMemory(0x3000, &ehdr, sizeof(ehdr));
 
-  std::unique_ptr<Elf> elf(info_->GetElf(getpid(), false));
-  ASSERT_TRUE(elf.get() != nullptr);
+  Elf* elf = info.GetElf(process_memory_, false);
+  ASSERT_TRUE(elf != nullptr);
   ASSERT_TRUE(elf->valid());
   EXPECT_EQ(static_cast<uint32_t>(EM_ARM), elf->machine_type());
   EXPECT_EQ(ELFCLASS32, elf->class_type());
 }
 
 TEST_F(MapInfoGetElfTest, valid64) {
+  MapInfo info(0x8000, 0x9000, 0, PROT_READ, "");
+
   Elf64_Ehdr ehdr;
   TestInitEhdr<Elf64_Ehdr>(&ehdr, ELFCLASS64, EM_AARCH64);
-  memcpy(map_, &ehdr, sizeof(ehdr));
+  memory_->SetMemory(0x8000, &ehdr, sizeof(ehdr));
 
-  std::unique_ptr<Elf> elf(info_->GetElf(getpid(), false));
-  ASSERT_TRUE(elf.get() != nullptr);
+  Elf* elf = info.GetElf(process_memory_, false);
+  ASSERT_TRUE(elf != nullptr);
   ASSERT_TRUE(elf->valid());
   EXPECT_EQ(static_cast<uint32_t>(EM_AARCH64), elf->machine_type());
   EXPECT_EQ(ELFCLASS64, elf->class_type());
 }
 
 TEST_F(MapInfoGetElfTest, gnu_debugdata_do_not_init32) {
-  TestInitGnuDebugdata<Elf32_Ehdr, Elf32_Shdr>(
-      ELFCLASS32, EM_ARM, false, [&](uint64_t offset, const void* ptr, size_t size) {
-        memcpy(&reinterpret_cast<uint8_t*>(map_)[offset], ptr, size);
-      });
+  MapInfo info(0x4000, 0x8000, 0, PROT_READ, "");
 
-  std::unique_ptr<Elf> elf(info_->GetElf(getpid(), false));
-  ASSERT_TRUE(elf.get() != nullptr);
+  TestInitGnuDebugdata<Elf32_Ehdr, Elf32_Shdr>(ELFCLASS32, EM_ARM, false,
+                                               [&](uint64_t offset, const void* ptr, size_t size) {
+                                                 memory_->SetMemory(0x4000 + offset, ptr, size);
+                                               });
+
+  Elf* elf = info.GetElf(process_memory_, false);
+  ASSERT_TRUE(elf != nullptr);
   ASSERT_TRUE(elf->valid());
   EXPECT_EQ(static_cast<uint32_t>(EM_ARM), elf->machine_type());
   EXPECT_EQ(ELFCLASS32, elf->class_type());
@@ -98,13 +122,15 @@
 }
 
 TEST_F(MapInfoGetElfTest, gnu_debugdata_do_not_init64) {
-  TestInitGnuDebugdata<Elf64_Ehdr, Elf64_Shdr>(
-      ELFCLASS64, EM_AARCH64, false, [&](uint64_t offset, const void* ptr, size_t size) {
-        memcpy(&reinterpret_cast<uint8_t*>(map_)[offset], ptr, size);
-      });
+  MapInfo info(0x6000, 0x8000, 0, PROT_READ, "");
 
-  std::unique_ptr<Elf> elf(info_->GetElf(getpid(), false));
-  ASSERT_TRUE(elf.get() != nullptr);
+  TestInitGnuDebugdata<Elf64_Ehdr, Elf64_Shdr>(ELFCLASS64, EM_AARCH64, false,
+                                               [&](uint64_t offset, const void* ptr, size_t size) {
+                                                 memory_->SetMemory(0x6000 + offset, ptr, size);
+                                               });
+
+  Elf* elf = info.GetElf(process_memory_, false);
+  ASSERT_TRUE(elf != nullptr);
   ASSERT_TRUE(elf->valid());
   EXPECT_EQ(static_cast<uint32_t>(EM_AARCH64), elf->machine_type());
   EXPECT_EQ(ELFCLASS64, elf->class_type());
@@ -112,13 +138,15 @@
 }
 
 TEST_F(MapInfoGetElfTest, gnu_debugdata_init32) {
-  TestInitGnuDebugdata<Elf32_Ehdr, Elf32_Shdr>(
-      ELFCLASS32, EM_ARM, true, [&](uint64_t offset, const void* ptr, size_t size) {
-        memcpy(&reinterpret_cast<uint8_t*>(map_)[offset], ptr, size);
-      });
+  MapInfo info(0x2000, 0x3000, 0, PROT_READ, "");
 
-  std::unique_ptr<Elf> elf(info_->GetElf(getpid(), true));
-  ASSERT_TRUE(elf.get() != nullptr);
+  TestInitGnuDebugdata<Elf32_Ehdr, Elf32_Shdr>(ELFCLASS32, EM_ARM, true,
+                                               [&](uint64_t offset, const void* ptr, size_t size) {
+                                                 memory_->SetMemory(0x2000 + offset, ptr, size);
+                                               });
+
+  Elf* elf = info.GetElf(process_memory_, true);
+  ASSERT_TRUE(elf != nullptr);
   ASSERT_TRUE(elf->valid());
   EXPECT_EQ(static_cast<uint32_t>(EM_ARM), elf->machine_type());
   EXPECT_EQ(ELFCLASS32, elf->class_type());
@@ -126,15 +154,256 @@
 }
 
 TEST_F(MapInfoGetElfTest, gnu_debugdata_init64) {
-  TestInitGnuDebugdata<Elf64_Ehdr, Elf64_Shdr>(
-      ELFCLASS64, EM_AARCH64, true, [&](uint64_t offset, const void* ptr, size_t size) {
-        memcpy(&reinterpret_cast<uint8_t*>(map_)[offset], ptr, size);
-      });
+  MapInfo info(0x5000, 0x8000, 0, PROT_READ, "");
 
-  std::unique_ptr<Elf> elf(info_->GetElf(getpid(), true));
-  ASSERT_TRUE(elf.get() != nullptr);
+  TestInitGnuDebugdata<Elf64_Ehdr, Elf64_Shdr>(ELFCLASS64, EM_AARCH64, true,
+                                               [&](uint64_t offset, const void* ptr, size_t size) {
+                                                 memory_->SetMemory(0x5000 + offset, ptr, size);
+                                               });
+
+  Elf* elf = info.GetElf(process_memory_, true);
+  ASSERT_TRUE(elf != nullptr);
   ASSERT_TRUE(elf->valid());
   EXPECT_EQ(static_cast<uint32_t>(EM_AARCH64), elf->machine_type());
   EXPECT_EQ(ELFCLASS64, elf->class_type());
   EXPECT_TRUE(elf->gnu_debugdata_interface() != nullptr);
 }
+
+TEST_F(MapInfoGetElfTest, end_le_start) {
+  MapInfo info(0x1000, 0x1000, 0, PROT_READ, elf_.path);
+
+  Elf32_Ehdr ehdr;
+  TestInitEhdr<Elf32_Ehdr>(&ehdr, ELFCLASS32, EM_ARM);
+  ASSERT_TRUE(android::base::WriteFully(elf_.fd, &ehdr, sizeof(ehdr)));
+
+  Elf* elf = info.GetElf(process_memory_, false);
+  ASSERT_TRUE(elf != nullptr);
+  ASSERT_FALSE(elf->valid());
+
+  info.elf.reset();
+  info.end = 0xfff;
+  elf = info.GetElf(process_memory_, false);
+  ASSERT_TRUE(elf != nullptr);
+  ASSERT_FALSE(elf->valid());
+
+  // Make sure this test is valid.
+  info.elf.reset();
+  info.end = 0x2000;
+  elf = info.GetElf(process_memory_, false);
+  ASSERT_TRUE(elf != nullptr);
+  ASSERT_TRUE(elf->valid());
+}
+
+// Verify that if the offset is non-zero but there is no elf at the offset,
+// that the full file is used.
+TEST_F(MapInfoGetElfTest, file_backed_non_zero_offset_full_file) {
+  MapInfo info(0x1000, 0x2000, 0x100, PROT_READ, elf_.path);
+
+  std::vector<uint8_t> buffer(0x1000);
+  memset(buffer.data(), 0, buffer.size());
+  Elf32_Ehdr ehdr;
+  TestInitEhdr<Elf32_Ehdr>(&ehdr, ELFCLASS32, EM_ARM);
+  memcpy(buffer.data(), &ehdr, sizeof(ehdr));
+  ASSERT_TRUE(android::base::WriteFully(elf_.fd, buffer.data(), buffer.size()));
+
+  Elf* elf = info.GetElf(process_memory_, false);
+  ASSERT_TRUE(elf != nullptr);
+  ASSERT_TRUE(elf->valid());
+  ASSERT_TRUE(elf->memory() != nullptr);
+  ASSERT_EQ(0x100U, info.elf_offset);
+
+  // Read the entire file.
+  memset(buffer.data(), 0, buffer.size());
+  ASSERT_TRUE(elf->memory()->ReadFully(0, buffer.data(), buffer.size()));
+  ASSERT_EQ(0, memcmp(buffer.data(), &ehdr, sizeof(ehdr)));
+  for (size_t i = sizeof(ehdr); i < buffer.size(); i++) {
+    ASSERT_EQ(0, buffer[i]) << "Failed at byte " << i;
+  }
+
+  ASSERT_FALSE(elf->memory()->ReadFully(buffer.size(), buffer.data(), 1));
+}
+
+// Verify that if the offset is non-zero and there is an elf at that
+// offset, that only part of the file is used.
+TEST_F(MapInfoGetElfTest, file_backed_non_zero_offset_partial_file) {
+  MapInfo info(0x1000, 0x2000, 0x2000, PROT_READ, elf_.path);
+
+  std::vector<uint8_t> buffer(0x4000);
+  memset(buffer.data(), 0, buffer.size());
+  Elf32_Ehdr ehdr;
+  TestInitEhdr<Elf32_Ehdr>(&ehdr, ELFCLASS32, EM_ARM);
+  memcpy(&buffer[info.offset], &ehdr, sizeof(ehdr));
+  ASSERT_TRUE(android::base::WriteFully(elf_.fd, buffer.data(), buffer.size()));
+
+  Elf* elf = info.GetElf(process_memory_, false);
+  ASSERT_TRUE(elf != nullptr);
+  ASSERT_TRUE(elf->valid());
+  ASSERT_TRUE(elf->memory() != nullptr);
+  ASSERT_EQ(0U, info.elf_offset);
+
+  // Read the valid part of the file.
+  ASSERT_TRUE(elf->memory()->ReadFully(0, buffer.data(), 0x1000));
+  ASSERT_EQ(0, memcmp(buffer.data(), &ehdr, sizeof(ehdr)));
+  for (size_t i = sizeof(ehdr); i < 0x1000; i++) {
+    ASSERT_EQ(0, buffer[i]) << "Failed at byte " << i;
+  }
+
+  ASSERT_FALSE(elf->memory()->ReadFully(0x1000, buffer.data(), 1));
+}
+
+// Verify that if the offset is non-zero and there is an elf at that
+// offset, that only part of the file is used. Further verify that if the
+// embedded elf is bigger than the initial map, the new object is larger
+// than the original map size. Do this for a 32 bit elf and a 64 bit elf.
+TEST_F(MapInfoGetElfTest, file_backed_non_zero_offset_partial_file_whole_elf32) {
+  MapInfo info(0x5000, 0x6000, 0x1000, PROT_READ, elf_.path);
+
+  std::vector<uint8_t> buffer(0x4000);
+  memset(buffer.data(), 0, buffer.size());
+  Elf32_Ehdr ehdr;
+  TestInitEhdr<Elf32_Ehdr>(&ehdr, ELFCLASS32, EM_ARM);
+  ehdr.e_shoff = 0x2000;
+  ehdr.e_shentsize = sizeof(Elf32_Shdr) + 100;
+  ehdr.e_shnum = 4;
+  memcpy(&buffer[info.offset], &ehdr, sizeof(ehdr));
+  ASSERT_TRUE(android::base::WriteFully(elf_.fd, buffer.data(), buffer.size()));
+
+  Elf* elf = info.GetElf(process_memory_, false);
+  ASSERT_TRUE(elf != nullptr);
+  ASSERT_TRUE(elf->valid());
+  ASSERT_TRUE(elf->memory() != nullptr);
+  ASSERT_EQ(0U, info.elf_offset);
+
+  // Verify the memory is a valid elf.
+  memset(buffer.data(), 0, buffer.size());
+  ASSERT_TRUE(elf->memory()->ReadFully(0, buffer.data(), 0x1000));
+  ASSERT_EQ(0, memcmp(buffer.data(), &ehdr, sizeof(ehdr)));
+
+  // Read past the end of what would normally be the size of the map.
+  ASSERT_TRUE(elf->memory()->ReadFully(0x1000, buffer.data(), 1));
+}
+
+TEST_F(MapInfoGetElfTest, file_backed_non_zero_offset_partial_file_whole_elf64) {
+  MapInfo info(0x7000, 0x8000, 0x1000, PROT_READ, elf_.path);
+
+  std::vector<uint8_t> buffer(0x4000);
+  memset(buffer.data(), 0, buffer.size());
+  Elf64_Ehdr ehdr;
+  TestInitEhdr<Elf64_Ehdr>(&ehdr, ELFCLASS64, EM_AARCH64);
+  ehdr.e_shoff = 0x2000;
+  ehdr.e_shentsize = sizeof(Elf64_Shdr) + 100;
+  ehdr.e_shnum = 4;
+  memcpy(&buffer[info.offset], &ehdr, sizeof(ehdr));
+  ASSERT_TRUE(android::base::WriteFully(elf_.fd, buffer.data(), buffer.size()));
+
+  Elf* elf = info.GetElf(process_memory_, false);
+  ASSERT_TRUE(elf != nullptr);
+  ASSERT_TRUE(elf->valid());
+  ASSERT_TRUE(elf->memory() != nullptr);
+  ASSERT_EQ(0U, info.elf_offset);
+
+  // Verify the memory is a valid elf.
+  memset(buffer.data(), 0, buffer.size());
+  ASSERT_TRUE(elf->memory()->ReadFully(0, buffer.data(), 0x1000));
+  ASSERT_EQ(0, memcmp(buffer.data(), &ehdr, sizeof(ehdr)));
+
+  // Read past the end of what would normally be the size of the map.
+  ASSERT_TRUE(elf->memory()->ReadFully(0x1000, buffer.data(), 1));
+}
+
+TEST_F(MapInfoGetElfTest, process_memory_not_read_only) {
+  MapInfo info(0x9000, 0xa000, 0x1000, 0, "");
+
+  // Create valid elf data in process memory only.
+  Elf64_Ehdr ehdr;
+  TestInitEhdr<Elf64_Ehdr>(&ehdr, ELFCLASS64, EM_AARCH64);
+  ehdr.e_shoff = 0x2000;
+  ehdr.e_shentsize = sizeof(Elf64_Shdr) + 100;
+  ehdr.e_shnum = 4;
+  memory_->SetMemory(0x9000, &ehdr, sizeof(ehdr));
+
+  Elf* elf = info.GetElf(process_memory_, false);
+  ASSERT_TRUE(elf != nullptr);
+  ASSERT_FALSE(elf->valid());
+
+  info.elf.reset();
+  info.flags = PROT_READ;
+  elf = info.GetElf(process_memory_, false);
+  ASSERT_TRUE(elf->valid());
+}
+
+TEST_F(MapInfoGetElfTest, check_device_maps) {
+  MapInfo info(0x7000, 0x8000, 0x1000, PROT_READ | MAPS_FLAGS_DEVICE_MAP, "/dev/something");
+
+  // Create valid elf data in process memory for this to verify that only
+  // the name is causing invalid elf data.
+  Elf64_Ehdr ehdr;
+  TestInitEhdr<Elf64_Ehdr>(&ehdr, ELFCLASS64, EM_X86_64);
+  ehdr.e_shoff = 0x2000;
+  ehdr.e_shentsize = sizeof(Elf64_Shdr) + 100;
+  ehdr.e_shnum = 4;
+  memory_->SetMemory(0x7000, &ehdr, sizeof(ehdr));
+
+  Elf* elf = info.GetElf(process_memory_, false);
+  ASSERT_TRUE(elf != nullptr);
+  ASSERT_FALSE(elf->valid());
+
+  // Set the name to nothing to verify that it still fails.
+  info.elf.reset();
+  info.name = "";
+  elf = info.GetElf(process_memory_, false);
+  ASSERT_FALSE(elf->valid());
+
+  // Change the flags and verify the elf is valid now.
+  info.elf.reset();
+  info.flags = PROT_READ;
+  elf = info.GetElf(process_memory_, false);
+  ASSERT_TRUE(elf->valid());
+}
+
+TEST_F(MapInfoGetElfTest, multiple_thread_get_elf) {
+  static constexpr size_t kNumConcurrentThreads = 100;
+
+  Elf64_Ehdr ehdr;
+  TestInitEhdr<Elf64_Ehdr>(&ehdr, ELFCLASS64, EM_X86_64);
+  ehdr.e_shoff = 0x2000;
+  ehdr.e_shentsize = sizeof(Elf64_Shdr) + 100;
+  ehdr.e_shnum = 4;
+  memory_->SetMemory(0x7000, &ehdr, sizeof(ehdr));
+
+  Elf* elf_in_threads[kNumConcurrentThreads];
+  std::vector<std::thread*> threads;
+
+  std::atomic_bool wait;
+  wait = true;
+  // Create all of the threads and have them do the GetElf at the same time
+  // to make it likely that a race will occur.
+  MapInfo info(0x7000, 0x8000, 0x1000, PROT_READ, "");
+  for (size_t i = 0; i < kNumConcurrentThreads; i++) {
+    std::thread* thread = new std::thread([i, this, &wait, &info, &elf_in_threads]() {
+      while (wait)
+        ;
+      Elf* elf = info.GetElf(process_memory_, false);
+      elf_in_threads[i] = elf;
+    });
+    threads.push_back(thread);
+  }
+  ASSERT_TRUE(info.elf == nullptr);
+
+  // Set them all going and wait for the threads to finish.
+  wait = false;
+  for (auto thread : threads) {
+    thread->join();
+    delete thread;
+  }
+
+  // Now verify that all of the elf files are exactly the same and valid.
+  Elf* elf = info.elf.get();
+  ASSERT_TRUE(elf != nullptr);
+  EXPECT_TRUE(elf->valid());
+  for (size_t i = 0; i < kNumConcurrentThreads; i++) {
+    EXPECT_EQ(elf, elf_in_threads[i]) << "Thread " << i << " mismatched.";
+  }
+}
+
+}  // namespace unwindstack
diff --git a/libunwindstack/tests/MapInfoGetLoadBiasTest.cpp b/libunwindstack/tests/MapInfoGetLoadBiasTest.cpp
new file mode 100644
index 0000000..7e64a8a
--- /dev/null
+++ b/libunwindstack/tests/MapInfoGetLoadBiasTest.cpp
@@ -0,0 +1,170 @@
+/*
+ * Copyright (C) 2016 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 <elf.h>
+#include <errno.h>
+#include <signal.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/ptrace.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <atomic>
+#include <memory>
+#include <thread>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/test_utils.h>
+#include <gtest/gtest.h>
+
+#include <unwindstack/Elf.h>
+#include <unwindstack/MapInfo.h>
+#include <unwindstack/Maps.h>
+#include <unwindstack/Memory.h>
+
+#include "ElfFake.h"
+#include "ElfTestUtils.h"
+#include "MemoryFake.h"
+
+namespace unwindstack {
+
+class MapInfoGetLoadBiasTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    memory_ = new MemoryFake;
+    process_memory_.reset(memory_);
+    elf_ = new ElfFake(new MemoryFake);
+    elf_container_.reset(elf_);
+    map_info_.reset(new MapInfo(0x1000, 0x20000, 0, PROT_READ | PROT_WRITE, ""));
+  }
+
+  void MultipleThreadTest(uint64_t expected_load_bias);
+
+  std::shared_ptr<Memory> process_memory_;
+  MemoryFake* memory_;
+  ElfFake* elf_;
+  std::unique_ptr<ElfFake> elf_container_;
+  std::unique_ptr<MapInfo> map_info_;
+};
+
+TEST_F(MapInfoGetLoadBiasTest, no_elf_and_no_valid_elf_in_memory) {
+  MapInfo info(0x1000, 0x2000, 0, PROT_READ, "");
+
+  EXPECT_EQ(0U, info.GetLoadBias(process_memory_));
+}
+
+TEST_F(MapInfoGetLoadBiasTest, load_bias_cached_from_elf) {
+  map_info_->elf.reset(elf_container_.release());
+
+  elf_->FakeSetLoadBias(0);
+  EXPECT_EQ(0U, map_info_->GetLoadBias(process_memory_));
+
+  elf_->FakeSetLoadBias(0x1000);
+  EXPECT_EQ(0U, map_info_->GetLoadBias(process_memory_));
+}
+
+TEST_F(MapInfoGetLoadBiasTest, elf_exists) {
+  map_info_->elf.reset(elf_container_.release());
+
+  elf_->FakeSetLoadBias(0);
+  EXPECT_EQ(0U, map_info_->GetLoadBias(process_memory_));
+
+  map_info_->load_bias = static_cast<uint64_t>(-1);
+  elf_->FakeSetLoadBias(0x1000);
+  EXPECT_EQ(0x1000U, map_info_->GetLoadBias(process_memory_));
+}
+
+void MapInfoGetLoadBiasTest::MultipleThreadTest(uint64_t expected_load_bias) {
+  static constexpr size_t kNumConcurrentThreads = 100;
+
+  uint64_t load_bias_values[kNumConcurrentThreads];
+  std::vector<std::thread*> threads;
+
+  std::atomic_bool wait;
+  wait = true;
+  // Create all of the threads and have them do the GetLoadBias at the same time
+  // to make it likely that a race will occur.
+  for (size_t i = 0; i < kNumConcurrentThreads; i++) {
+    std::thread* thread = new std::thread([i, this, &wait, &load_bias_values]() {
+      while (wait)
+        ;
+      load_bias_values[i] = map_info_->GetLoadBias(process_memory_);
+    });
+    threads.push_back(thread);
+  }
+
+  // Set them all going and wait for the threads to finish.
+  wait = false;
+  for (auto thread : threads) {
+    thread->join();
+    delete thread;
+  }
+
+  // Now verify that all of the elf files are exactly the same and valid.
+  for (size_t i = 0; i < kNumConcurrentThreads; i++) {
+    EXPECT_EQ(expected_load_bias, load_bias_values[i]) << "Thread " << i << " mismatched.";
+  }
+}
+
+TEST_F(MapInfoGetLoadBiasTest, multiple_thread_elf_exists) {
+  map_info_->elf.reset(elf_container_.release());
+  elf_->FakeSetLoadBias(0x1000);
+
+  MultipleThreadTest(0x1000);
+}
+
+static void InitElfData(MemoryFake* memory, uint64_t offset) {
+  Elf32_Ehdr ehdr;
+  TestInitEhdr(&ehdr, ELFCLASS32, EM_ARM);
+  ehdr.e_phoff = 0x5000;
+  ehdr.e_phnum = 2;
+  ehdr.e_phentsize = sizeof(Elf32_Phdr);
+  memory->SetMemory(offset, &ehdr, sizeof(ehdr));
+
+  Elf32_Phdr phdr;
+  memset(&phdr, 0, sizeof(phdr));
+  phdr.p_type = PT_NULL;
+  memory->SetMemory(offset + 0x5000, &phdr, sizeof(phdr));
+  phdr.p_type = PT_LOAD;
+  phdr.p_offset = 0;
+  phdr.p_vaddr = 0xe000;
+  memory->SetMemory(offset + 0x5000 + sizeof(phdr), &phdr, sizeof(phdr));
+}
+
+TEST_F(MapInfoGetLoadBiasTest, elf_exists_in_memory) {
+  InitElfData(memory_, map_info_->start);
+
+  EXPECT_EQ(0xe000U, map_info_->GetLoadBias(process_memory_));
+}
+
+TEST_F(MapInfoGetLoadBiasTest, elf_exists_in_memory_cached) {
+  InitElfData(memory_, map_info_->start);
+
+  EXPECT_EQ(0xe000U, map_info_->GetLoadBias(process_memory_));
+
+  memory_->Clear();
+  EXPECT_EQ(0xe000U, map_info_->GetLoadBias(process_memory_));
+}
+
+TEST_F(MapInfoGetLoadBiasTest, multiple_thread_elf_exists_in_memory) {
+  InitElfData(memory_, map_info_->start);
+
+  MultipleThreadTest(0xe000);
+}
+
+}  // namespace unwindstack
diff --git a/libunwindstack/tests/MapsTest.cpp b/libunwindstack/tests/MapsTest.cpp
index 7eb9bae..9622ba5 100644
--- a/libunwindstack/tests/MapsTest.cpp
+++ b/libunwindstack/tests/MapsTest.cpp
@@ -14,86 +14,232 @@
  * limitations under the License.
  */
 
+#include <inttypes.h>
 #include <sys/mman.h>
 
 #include <android-base/file.h>
+#include <android-base/stringprintf.h>
 #include <android-base/test_utils.h>
 #include <gtest/gtest.h>
 
-#include "Maps.h"
+#include <unwindstack/Maps.h>
+
+namespace unwindstack {
+
+static void VerifyLine(std::string line, MapInfo* info) {
+  BufferMaps maps(line.c_str());
+
+  if (info == nullptr) {
+    ASSERT_FALSE(maps.Parse()) << "Failed on: " + line;
+  } else {
+    ASSERT_TRUE(maps.Parse()) << "Failed on: " + line;
+    MapInfo* element = maps.Get(0);
+    ASSERT_TRUE(element != nullptr) << "Failed on: " + line;
+    info->start = element->start;
+    info->end = element->end;
+    info->offset = element->offset;
+    info->flags = element->flags;
+    info->name = element->name;
+    info->elf_offset = element->elf_offset;
+  }
+}
+
+TEST(MapsTest, map_add) {
+  Maps maps;
+
+  maps.Add(0x1000, 0x2000, 0, PROT_READ, "fake_map", 0);
+  maps.Add(0x3000, 0x4000, 0x10, 0, "", 0x1234);
+  maps.Add(0x5000, 0x6000, 1, 2, "fake_map2", static_cast<uint64_t>(-1));
+
+  ASSERT_EQ(3U, maps.Total());
+  MapInfo* info = maps.Get(0);
+  ASSERT_EQ(0x1000U, info->start);
+  ASSERT_EQ(0x2000U, info->end);
+  ASSERT_EQ(0U, info->offset);
+  ASSERT_EQ(PROT_READ, info->flags);
+  ASSERT_EQ("fake_map", info->name);
+  ASSERT_EQ(0U, info->elf_offset);
+  ASSERT_EQ(0U, info->load_bias.load());
+}
+
+TEST(MapsTest, verify_parse_line) {
+  MapInfo info;
+
+  VerifyLine("01-02 rwxp 03 04:05 06\n", &info);
+  EXPECT_EQ(1U, info.start);
+  EXPECT_EQ(2U, info.end);
+  EXPECT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, info.flags);
+  EXPECT_EQ(3U, info.offset);
+  EXPECT_EQ("", info.name);
+
+  VerifyLine("0a-0b ---s 0c 0d:0e 06 /fake/name\n", &info);
+  EXPECT_EQ(0xaU, info.start);
+  EXPECT_EQ(0xbU, info.end);
+  EXPECT_EQ(0U, info.flags);
+  EXPECT_EQ(0xcU, info.offset);
+  EXPECT_EQ("/fake/name", info.name);
+
+  VerifyLine("01-02   rwxp   03    04:05    06    /fake/name/again\n", &info);
+  EXPECT_EQ(1U, info.start);
+  EXPECT_EQ(2U, info.end);
+  EXPECT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, info.flags);
+  EXPECT_EQ(3U, info.offset);
+  EXPECT_EQ("/fake/name/again", info.name);
+
+  VerifyLine("-00 rwxp 00 00:00 0\n", nullptr);
+  VerifyLine("00- rwxp 00 00:00 0\n", nullptr);
+  VerifyLine("00-00 rwxp 00 :00 0\n", nullptr);
+  VerifyLine("00-00 rwxp 00 00:00 \n", nullptr);
+  VerifyLine("x-00 rwxp 00 00:00 0\n", nullptr);
+  VerifyLine("00 -00 rwxp 00 00:00 0\n", nullptr);
+  VerifyLine("00-x rwxp 00 00:00 0\n", nullptr);
+  VerifyLine("00-x rwxp 00 00:00 0\n", nullptr);
+  VerifyLine("00-00x rwxp 00 00:00 0\n", nullptr);
+  VerifyLine("00-00 rwxp0 00 00:00 0\n", nullptr);
+  VerifyLine("00-00 rwxp0 00 00:00 0\n", nullptr);
+  VerifyLine("00-00 rwp 00 00:00 0\n", nullptr);
+  VerifyLine("00-00 rwxp 0000:00 0\n", nullptr);
+  VerifyLine("00-00 rwxp 00 00 :00 0\n", nullptr);
+  VerifyLine("00-00 rwxp 00 00: 00 0\n", nullptr);
+  VerifyLine("00-00 rwxp 00 00:000\n", nullptr);
+  VerifyLine("00-00 rwxp 00 00:00 0/fake\n", nullptr);
+  VerifyLine("00-00 xxxx 00 00:00 0 /fake\n", nullptr);
+  VerifyLine("00-00 ywxp 00 00:00 0 /fake\n", nullptr);
+  VerifyLine("00-00 ryxp 00 00:00 0 /fake\n", nullptr);
+  VerifyLine("00-00 rwyp 00 00:00 0 /fake\n", nullptr);
+  VerifyLine("00-00 rwx- 00 00:00 0 /fake\n", nullptr);
+  VerifyLine("0\n", nullptr);
+  VerifyLine("00\n", nullptr);
+  VerifyLine("00-\n", nullptr);
+  VerifyLine("00-0\n", nullptr);
+  VerifyLine("00-00\n", nullptr);
+  VerifyLine("00-00 \n", nullptr);
+  VerifyLine("00-00 -\n", nullptr);
+  VerifyLine("00-00 r\n", nullptr);
+  VerifyLine("00-00 --\n", nullptr);
+  VerifyLine("00-00 rw\n", nullptr);
+  VerifyLine("00-00 ---\n", nullptr);
+  VerifyLine("00-00 rwx\n", nullptr);
+  VerifyLine("00-00 ---s\n", nullptr);
+  VerifyLine("00-00 ---p\n", nullptr);
+  VerifyLine("00-00 ---s 0\n", nullptr);
+  VerifyLine("00-00 ---p 0 \n", nullptr);
+  VerifyLine("00-00 ---p 0 0\n", nullptr);
+  VerifyLine("00-00 ---p 0 0:\n", nullptr);
+  VerifyLine("00-00 ---p 0 0:0\n", nullptr);
+  VerifyLine("00-00 ---p 0 0:0 \n", nullptr);
+
+  // Line to verify that the parser will detect a completely malformed line
+  // properly.
+  VerifyLine("7ffff7dda000-7ffff7dfd7ffff7ff3000-7ffff7ff4000 ---p 0000f000 fc:02 44171565\n",
+             nullptr);
+}
+
+TEST(MapsTest, verify_large_values) {
+  MapInfo info;
+#if defined(__LP64__)
+  VerifyLine("fabcdef012345678-f12345678abcdef8 rwxp f0b0d0f010305070 00:00 0\n", &info);
+  EXPECT_EQ(0xfabcdef012345678UL, info.start);
+  EXPECT_EQ(0xf12345678abcdef8UL, info.end);
+  EXPECT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, info.flags);
+  EXPECT_EQ(0xf0b0d0f010305070UL, info.offset);
+#else
+  VerifyLine("f2345678-fabcdef8 rwxp f0305070 00:00 0\n", &info);
+  EXPECT_EQ(0xf2345678UL, info.start);
+  EXPECT_EQ(0xfabcdef8UL, info.end);
+  EXPECT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, info.flags);
+  EXPECT_EQ(0xf0305070UL, info.offset);
+#endif
+}
 
 TEST(MapsTest, parse_permissions) {
   BufferMaps maps(
-      "1000-2000 ---- 00000000 00:00 0\n"
-      "2000-3000 r--- 00000000 00:00 0\n"
-      "3000-4000 -w-- 00000000 00:00 0\n"
-      "4000-5000 --x- 00000000 00:00 0\n"
-      "5000-6000 rwx- 00000000 00:00 0\n");
+      "1000-2000 ---s 00000000 00:00 0\n"
+      "2000-3000 r--s 00000000 00:00 0\n"
+      "3000-4000 -w-s 00000000 00:00 0\n"
+      "4000-5000 --xp 00000000 00:00 0\n"
+      "5000-6000 rwxp 00000000 00:00 0\n");
 
   ASSERT_TRUE(maps.Parse());
   ASSERT_EQ(5U, maps.Total());
-  auto it = maps.begin();
-  ASSERT_EQ(PROT_NONE, it->flags);
-  ASSERT_EQ(0x1000U, it->start);
-  ASSERT_EQ(0x2000U, it->end);
-  ASSERT_EQ(0U, it->offset);
-  ASSERT_EQ("", it->name);
-  ++it;
-  ASSERT_EQ(PROT_READ, it->flags);
-  ASSERT_EQ(0x2000U, it->start);
-  ASSERT_EQ(0x3000U, it->end);
-  ASSERT_EQ(0U, it->offset);
-  ASSERT_EQ("", it->name);
-  ++it;
-  ASSERT_EQ(PROT_WRITE, it->flags);
-  ASSERT_EQ(0x3000U, it->start);
-  ASSERT_EQ(0x4000U, it->end);
-  ASSERT_EQ(0U, it->offset);
-  ASSERT_EQ("", it->name);
-  ++it;
-  ASSERT_EQ(PROT_EXEC, it->flags);
-  ASSERT_EQ(0x4000U, it->start);
-  ASSERT_EQ(0x5000U, it->end);
-  ASSERT_EQ(0U, it->offset);
-  ASSERT_EQ("", it->name);
-  ++it;
-  ASSERT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, it->flags);
-  ASSERT_EQ(0x5000U, it->start);
-  ASSERT_EQ(0x6000U, it->end);
-  ASSERT_EQ(0U, it->offset);
-  ASSERT_EQ("", it->name);
-  ++it;
-  ASSERT_EQ(it, maps.end());
+
+  MapInfo* info = maps.Get(0);
+  ASSERT_TRUE(info != nullptr);
+  EXPECT_EQ(PROT_NONE, info->flags);
+  EXPECT_EQ(0x1000U, info->start);
+  EXPECT_EQ(0x2000U, info->end);
+  EXPECT_EQ(0U, info->offset);
+  EXPECT_EQ("", info->name);
+
+  info = maps.Get(1);
+  ASSERT_TRUE(info != nullptr);
+  EXPECT_EQ(PROT_READ, info->flags);
+  EXPECT_EQ(0x2000U, info->start);
+  EXPECT_EQ(0x3000U, info->end);
+  EXPECT_EQ(0U, info->offset);
+  EXPECT_EQ("", info->name);
+
+  info = maps.Get(2);
+  ASSERT_TRUE(info != nullptr);
+  EXPECT_EQ(PROT_WRITE, info->flags);
+  EXPECT_EQ(0x3000U, info->start);
+  EXPECT_EQ(0x4000U, info->end);
+  EXPECT_EQ(0U, info->offset);
+  EXPECT_EQ("", info->name);
+
+  info = maps.Get(3);
+  ASSERT_TRUE(info != nullptr);
+  EXPECT_EQ(PROT_EXEC, info->flags);
+  EXPECT_EQ(0x4000U, info->start);
+  EXPECT_EQ(0x5000U, info->end);
+  EXPECT_EQ(0U, info->offset);
+  EXPECT_EQ("", info->name);
+
+  info = maps.Get(4);
+  ASSERT_TRUE(info != nullptr);
+  EXPECT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, info->flags);
+  EXPECT_EQ(0x5000U, info->start);
+  EXPECT_EQ(0x6000U, info->end);
+  EXPECT_EQ(0U, info->offset);
+  EXPECT_EQ("", info->name);
+
+  ASSERT_TRUE(maps.Get(5) == nullptr);
 }
 
 TEST(MapsTest, parse_name) {
   BufferMaps maps(
-      "720b29b000-720b29e000 rw-p 00000000 00:00 0\n"
-      "720b29e000-720b29f000 rw-p 00000000 00:00 0 /system/lib/fake.so\n"
-      "720b29f000-720b2a0000 rw-p 00000000 00:00 0");
+      "7b29b000-7b29e000 rw-p 00000000 00:00 0\n"
+      "7b29e000-7b29f000 rw-p 00000000 00:00 0 /system/lib/fake.so\n"
+      "7b29f000-7b2a0000 rw-p 00000000 00:00 0");
 
   ASSERT_TRUE(maps.Parse());
   ASSERT_EQ(3U, maps.Total());
-  auto it = maps.begin();
-  ASSERT_EQ("", it->name);
-  ASSERT_EQ(0x720b29b000U, it->start);
-  ASSERT_EQ(0x720b29e000U, it->end);
-  ASSERT_EQ(0U, it->offset);
-  ASSERT_EQ(PROT_READ | PROT_WRITE, it->flags);
-  ++it;
-  ASSERT_EQ("/system/lib/fake.so", it->name);
-  ASSERT_EQ(0x720b29e000U, it->start);
-  ASSERT_EQ(0x720b29f000U, it->end);
-  ASSERT_EQ(0U, it->offset);
-  ASSERT_EQ(PROT_READ | PROT_WRITE, it->flags);
-  ++it;
-  ASSERT_EQ("", it->name);
-  ASSERT_EQ(0x720b29f000U, it->start);
-  ASSERT_EQ(0x720b2a0000U, it->end);
-  ASSERT_EQ(0U, it->offset);
-  ASSERT_EQ(PROT_READ | PROT_WRITE, it->flags);
-  ++it;
-  ASSERT_EQ(it, maps.end());
+
+  MapInfo* info = maps.Get(0);
+  ASSERT_TRUE(info != nullptr);
+  EXPECT_EQ("", info->name);
+  EXPECT_EQ(0x7b29b000U, info->start);
+  EXPECT_EQ(0x7b29e000U, info->end);
+  EXPECT_EQ(0U, info->offset);
+  EXPECT_EQ(PROT_READ | PROT_WRITE, info->flags);
+
+  info = maps.Get(1);
+  ASSERT_TRUE(info != nullptr);
+  EXPECT_EQ("/system/lib/fake.so", info->name);
+  EXPECT_EQ(0x7b29e000U, info->start);
+  EXPECT_EQ(0x7b29f000U, info->end);
+  EXPECT_EQ(0U, info->offset);
+  EXPECT_EQ(PROT_READ | PROT_WRITE, info->flags);
+
+  info = maps.Get(2);
+  ASSERT_TRUE(info != nullptr);
+  EXPECT_EQ("", info->name);
+  EXPECT_EQ(0x7b29f000U, info->start);
+  EXPECT_EQ(0x7b2a0000U, info->end);
+  EXPECT_EQ(0U, info->offset);
+  EXPECT_EQ(PROT_READ | PROT_WRITE, info->flags);
+
+  ASSERT_TRUE(maps.Get(3) == nullptr);
 }
 
 TEST(MapsTest, parse_offset) {
@@ -103,20 +249,60 @@
 
   ASSERT_TRUE(maps.Parse());
   ASSERT_EQ(2U, maps.Total());
-  auto it = maps.begin();
-  ASSERT_EQ(0U, it->offset);
-  ASSERT_EQ(0xa000U, it->start);
-  ASSERT_EQ(0xe000U, it->end);
-  ASSERT_EQ(PROT_READ | PROT_WRITE, it->flags);
-  ASSERT_EQ("/system/lib/fake.so", it->name);
+
+  MapInfo* info = maps.Get(0);
+  ASSERT_TRUE(info != nullptr);
+  EXPECT_EQ(0U, info->offset);
+  EXPECT_EQ(0xa000U, info->start);
+  EXPECT_EQ(0xe000U, info->end);
+  EXPECT_EQ(PROT_READ | PROT_WRITE, info->flags);
+  EXPECT_EQ("/system/lib/fake.so", info->name);
+
+  info = maps.Get(1);
+  ASSERT_TRUE(info != nullptr);
+  EXPECT_EQ(0xa12345U, info->offset);
+  EXPECT_EQ(0xe000U, info->start);
+  EXPECT_EQ(0xf000U, info->end);
+  EXPECT_EQ(PROT_READ | PROT_WRITE, info->flags);
+  EXPECT_EQ("/system/lib/fake.so", info->name);
+
+  ASSERT_TRUE(maps.Get(2) == nullptr);
+}
+
+TEST(MapsTest, iterate) {
+  BufferMaps maps(
+      "a000-e000 rw-p 00000000 00:00 0 /system/lib/fake.so\n"
+      "e000-f000 rw-p 00a12345 00:00 0 /system/lib/fake.so\n");
+
+  ASSERT_TRUE(maps.Parse());
+  ASSERT_EQ(2U, maps.Total());
+
+  Maps::iterator it = maps.begin();
+  EXPECT_EQ(0xa000U, (*it)->start);
+  EXPECT_EQ(0xe000U, (*it)->end);
   ++it;
-  ASSERT_EQ(0xa12345U, it->offset);
-  ASSERT_EQ(0xe000U, it->start);
-  ASSERT_EQ(0xf000U, it->end);
-  ASSERT_EQ(PROT_READ | PROT_WRITE, it->flags);
-  ASSERT_EQ("/system/lib/fake.so", it->name);
+  EXPECT_EQ(0xe000U, (*it)->start);
+  EXPECT_EQ(0xf000U, (*it)->end);
   ++it;
-  ASSERT_EQ(maps.end(), it);
+  EXPECT_EQ(maps.end(), it);
+}
+
+TEST(MapsTest, const_iterate) {
+  BufferMaps maps(
+      "a000-e000 rw-p 00000000 00:00 0 /system/lib/fake.so\n"
+      "e000-f000 rw-p 00a12345 00:00 0 /system/lib/fake.so\n");
+
+  ASSERT_TRUE(maps.Parse());
+  ASSERT_EQ(2U, maps.Total());
+
+  Maps::const_iterator it = maps.begin();
+  EXPECT_EQ(0xa000U, (*it)->start);
+  EXPECT_EQ(0xe000U, (*it)->end);
+  ++it;
+  EXPECT_EQ(0xe000U, (*it)->start);
+  EXPECT_EQ(0xf000U, (*it)->end);
+  ++it;
+  EXPECT_EQ(maps.end(), it);
 }
 
 TEST(MapsTest, device) {
@@ -128,18 +314,23 @@
 
   ASSERT_TRUE(maps.Parse());
   ASSERT_EQ(4U, maps.Total());
-  auto it = maps.begin();
-  ASSERT_TRUE(it->flags & 0x8000);
-  ASSERT_EQ("/dev/", it->name);
-  ++it;
-  ASSERT_TRUE(it->flags & 0x8000);
-  ASSERT_EQ("/dev/does_not_exist", it->name);
-  ++it;
-  ASSERT_FALSE(it->flags & 0x8000);
-  ASSERT_EQ("/dev/ashmem/does_not_exist", it->name);
-  ++it;
-  ASSERT_FALSE(it->flags & 0x8000);
-  ASSERT_EQ("/devsomething/does_not_exist", it->name);
+
+  MapInfo* info = maps.Get(0);
+  ASSERT_TRUE(info != nullptr);
+  EXPECT_TRUE(info->flags & 0x8000);
+  EXPECT_EQ("/dev/", info->name);
+
+  info = maps.Get(1);
+  EXPECT_TRUE(info->flags & 0x8000);
+  EXPECT_EQ("/dev/does_not_exist", info->name);
+
+  info = maps.Get(2);
+  EXPECT_FALSE(info->flags & 0x8000);
+  EXPECT_EQ("/dev/ashmem/does_not_exist", info->name);
+
+  info = maps.Get(3);
+  EXPECT_FALSE(info->flags & 0x8000);
+  EXPECT_EQ("/devsomething/does_not_exist", info->name);
 }
 
 TEST(MapsTest, file_smoke) {
@@ -147,35 +338,204 @@
   ASSERT_TRUE(tf.fd != -1);
 
   ASSERT_TRUE(
-      android::base::WriteStringToFile("720b29b000-720b29e000 r-xp a0000000 00:00 0   /fake.so\n"
-                                       "720b2b0000-720b2e0000 r-xp b0000000 00:00 0   /fake2.so\n"
-                                       "720b2e0000-720b2f0000 r-xp c0000000 00:00 0   /fake3.so\n",
+      android::base::WriteStringToFile("7b29b000-7b29e000 r-xp a0000000 00:00 0   /fake.so\n"
+                                       "7b2b0000-7b2e0000 r-xp b0000000 00:00 0   /fake2.so\n"
+                                       "7b2e0000-7b2f0000 r-xp c0000000 00:00 0   /fake3.so\n",
                                        tf.path, 0660, getuid(), getgid()));
 
   FileMaps maps(tf.path);
 
   ASSERT_TRUE(maps.Parse());
   ASSERT_EQ(3U, maps.Total());
-  auto it = maps.begin();
-  ASSERT_EQ(0x720b29b000U, it->start);
-  ASSERT_EQ(0x720b29e000U, it->end);
-  ASSERT_EQ(0xa0000000U, it->offset);
-  ASSERT_EQ(PROT_READ | PROT_EXEC, it->flags);
-  ASSERT_EQ("/fake.so", it->name);
-  ++it;
-  ASSERT_EQ(0x720b2b0000U, it->start);
-  ASSERT_EQ(0x720b2e0000U, it->end);
-  ASSERT_EQ(0xb0000000U, it->offset);
-  ASSERT_EQ(PROT_READ | PROT_EXEC, it->flags);
-  ASSERT_EQ("/fake2.so", it->name);
-  ++it;
-  ASSERT_EQ(0x720b2e0000U, it->start);
-  ASSERT_EQ(0x720b2f0000U, it->end);
-  ASSERT_EQ(0xc0000000U, it->offset);
-  ASSERT_EQ(PROT_READ | PROT_EXEC, it->flags);
-  ASSERT_EQ("/fake3.so", it->name);
-  ++it;
-  ASSERT_EQ(it, maps.end());
+
+  MapInfo* info = maps.Get(0);
+  ASSERT_TRUE(info != nullptr);
+  EXPECT_EQ(0x7b29b000U, info->start);
+  EXPECT_EQ(0x7b29e000U, info->end);
+  EXPECT_EQ(0xa0000000U, info->offset);
+  EXPECT_EQ(PROT_READ | PROT_EXEC, info->flags);
+  EXPECT_EQ("/fake.so", info->name);
+
+  info = maps.Get(1);
+  ASSERT_TRUE(info != nullptr);
+  EXPECT_EQ(0x7b2b0000U, info->start);
+  EXPECT_EQ(0x7b2e0000U, info->end);
+  EXPECT_EQ(0xb0000000U, info->offset);
+  EXPECT_EQ(PROT_READ | PROT_EXEC, info->flags);
+  EXPECT_EQ("/fake2.so", info->name);
+
+  info = maps.Get(2);
+  ASSERT_TRUE(info != nullptr);
+  EXPECT_EQ(0x7b2e0000U, info->start);
+  EXPECT_EQ(0x7b2f0000U, info->end);
+  EXPECT_EQ(0xc0000000U, info->offset);
+  EXPECT_EQ(PROT_READ | PROT_EXEC, info->flags);
+  EXPECT_EQ("/fake3.so", info->name);
+
+  ASSERT_TRUE(maps.Get(3) == nullptr);
+}
+
+TEST(MapsTest, file_no_map_name) {
+  TemporaryFile tf;
+  ASSERT_TRUE(tf.fd != -1);
+
+  ASSERT_TRUE(
+      android::base::WriteStringToFile("7b29b000-7b29e000 r-xp a0000000 00:00 0\n"
+                                       "7b2b0000-7b2e0000 r-xp b0000000 00:00 0   /fake2.so\n"
+                                       "7b2e0000-7b2f0000 r-xp c0000000 00:00 0 \n",
+                                       tf.path, 0660, getuid(), getgid()));
+
+  FileMaps maps(tf.path);
+
+  ASSERT_TRUE(maps.Parse());
+  ASSERT_EQ(3U, maps.Total());
+
+  MapInfo* info = maps.Get(0);
+  ASSERT_TRUE(info != nullptr);
+  EXPECT_EQ(0x7b29b000U, info->start);
+  EXPECT_EQ(0x7b29e000U, info->end);
+  EXPECT_EQ(0xa0000000U, info->offset);
+  EXPECT_EQ(PROT_READ | PROT_EXEC, info->flags);
+  EXPECT_EQ("", info->name);
+
+  info = maps.Get(1);
+  ASSERT_TRUE(info != nullptr);
+  EXPECT_EQ(0x7b2b0000U, info->start);
+  EXPECT_EQ(0x7b2e0000U, info->end);
+  EXPECT_EQ(0xb0000000U, info->offset);
+  EXPECT_EQ(PROT_READ | PROT_EXEC, info->flags);
+  EXPECT_EQ("/fake2.so", info->name);
+
+  info = maps.Get(2);
+  ASSERT_TRUE(info != nullptr);
+  EXPECT_EQ(0x7b2e0000U, info->start);
+  EXPECT_EQ(0x7b2f0000U, info->end);
+  EXPECT_EQ(0xc0000000U, info->offset);
+  EXPECT_EQ(PROT_READ | PROT_EXEC, info->flags);
+  EXPECT_EQ("", info->name);
+
+  ASSERT_TRUE(maps.Get(3) == nullptr);
+}
+
+// Verify that a file that crosses a buffer is parsed correctly.
+static std::string CreateEntry(size_t index) {
+  return android::base::StringPrintf("%08zx-%08zx rwxp 0000 00:00 0\n", index * 4096,
+                                     (index + 1) * 4096);
+}
+
+TEST(MapsTest, file_buffer_cross) {
+  constexpr size_t kBufferSize = 2048;
+  TemporaryFile tf;
+  ASSERT_TRUE(tf.fd != -1);
+
+  // Compute how many to add in the first buffer.
+  size_t entry_len = CreateEntry(0).size();
+  size_t index;
+  std::string file_data;
+  for (index = 0; index < kBufferSize / entry_len; index++) {
+    file_data += CreateEntry(index);
+  }
+  // Add a long name to make sure that the first buffer does not contain a
+  // complete line.
+  // Remove the last newline.
+  size_t extra = 0;
+  size_t leftover = kBufferSize % entry_len;
+  size_t overlap1_index = 0;
+  std::string overlap1_name;
+  if (leftover == 0) {
+    // Exact match, add a long name to cross over the value.
+    overlap1_name = "/fake/name/is/long/on/purpose";
+    file_data.erase(file_data.size() - 1);
+    file_data += ' ' + overlap1_name + '\n';
+    extra = entry_len + overlap1_name.size() + 1;
+    overlap1_index = index;
+  }
+
+  // Compute how many need to go in to hit the buffer boundary exactly.
+  size_t bytes_left_in_buffer = kBufferSize - extra;
+  size_t entries_to_add = bytes_left_in_buffer / entry_len + index;
+  for (; index < entries_to_add; index++) {
+    file_data += CreateEntry(index);
+  }
+
+  // Now figure out how many bytes to add to get exactly to the buffer boundary.
+  leftover = bytes_left_in_buffer % entry_len;
+  std::string overlap2_name;
+  size_t overlap2_index = 0;
+  if (leftover != 0) {
+    file_data.erase(file_data.size() - 1);
+    file_data += ' ';
+    overlap2_name = std::string(leftover - 1, 'x');
+    file_data += overlap2_name + '\n';
+    overlap2_index = index - 1;
+  }
+
+  // Now add a few entries on the next page.
+  for (size_t start = index; index < start + 10; index++) {
+    file_data += CreateEntry(index);
+  }
+
+  ASSERT_TRUE(android::base::WriteStringToFile(file_data, tf.path, 0660, getuid(), getgid()));
+
+  FileMaps maps(tf.path);
+  ASSERT_TRUE(maps.Parse());
+  EXPECT_EQ(index, maps.Total());
+  // Verify all of the maps.
+  for (size_t i = 0; i < index; i++) {
+    MapInfo* info = maps.Get(i);
+    ASSERT_TRUE(info != nullptr) << "Failed verifying index " + std::to_string(i);
+    EXPECT_EQ(i * 4096, info->start) << "Failed verifying index " + std::to_string(i);
+    EXPECT_EQ((i + 1) * 4096, info->end) << "Failed verifying index " + std::to_string(i);
+    EXPECT_EQ(0U, info->offset) << "Failed verifying index " + std::to_string(i);
+    if (overlap1_index != 0 && i == overlap1_index) {
+      EXPECT_EQ(overlap1_name, info->name) << "Failed verifying overlap1 name " + std::to_string(i);
+    } else if (overlap2_index != 0 && i == overlap2_index) {
+      EXPECT_EQ(overlap2_name, info->name) << "Failed verifying overlap2 name " + std::to_string(i);
+    } else {
+      EXPECT_EQ("", info->name) << "Failed verifying index " + std::to_string(i);
+    }
+  }
+}
+
+TEST(MapsTest, file_should_fail) {
+  TemporaryFile tf;
+  ASSERT_TRUE(tf.fd != -1);
+
+  ASSERT_TRUE(android::base::WriteStringToFile(
+      "7ffff7dda000-7ffff7dfd7ffff7ff3000-7ffff7ff4000 ---p 0000f000 fc:02 44171565\n", tf.path,
+      0660, getuid(), getgid()));
+
+  FileMaps maps(tf.path);
+
+  ASSERT_FALSE(maps.Parse());
+}
+
+// Create a maps file that is extremely large.
+TEST(MapsTest, large_file) {
+  TemporaryFile tf;
+  ASSERT_TRUE(tf.fd != -1);
+
+  std::string file_data;
+  uint64_t start = 0x700000;
+  for (size_t i = 0; i < 5000; i++) {
+    file_data +=
+        android::base::StringPrintf("%" PRIx64 "-%" PRIx64 " r-xp 1000 00:0 0 /fake%zu.so\n",
+                                    start + i * 4096, start + (i + 1) * 4096, i);
+  }
+
+  ASSERT_TRUE(android::base::WriteStringToFile(file_data, tf.path, 0660, getuid(), getgid()));
+
+  FileMaps maps(tf.path);
+
+  ASSERT_TRUE(maps.Parse());
+  ASSERT_EQ(5000U, maps.Total());
+  for (size_t i = 0; i < 5000; i++) {
+    MapInfo* info = maps.Get(i);
+    EXPECT_EQ(start + i * 4096, info->start) << "Failed at map " + std::to_string(i);
+    EXPECT_EQ(start + (i + 1) * 4096, info->end) << "Failed at map " + std::to_string(i);
+    std::string name = "/fake" + std::to_string(i) + ".so";
+    EXPECT_EQ(name, info->name) << "Failed at map " + std::to_string(i);
+  }
 }
 
 TEST(MapsTest, find) {
@@ -188,50 +548,52 @@
   ASSERT_TRUE(maps.Parse());
   ASSERT_EQ(5U, maps.Total());
 
-  ASSERT_TRUE(maps.Find(0x500) == nullptr);
-  ASSERT_TRUE(maps.Find(0x2000) == nullptr);
-  ASSERT_TRUE(maps.Find(0x5010) == nullptr);
-  ASSERT_TRUE(maps.Find(0x9a00) == nullptr);
-  ASSERT_TRUE(maps.Find(0xf000) == nullptr);
-  ASSERT_TRUE(maps.Find(0xf010) == nullptr);
+  EXPECT_TRUE(maps.Find(0x500) == nullptr);
+  EXPECT_TRUE(maps.Find(0x2000) == nullptr);
+  EXPECT_TRUE(maps.Find(0x5010) == nullptr);
+  EXPECT_TRUE(maps.Find(0x9a00) == nullptr);
+  EXPECT_TRUE(maps.Find(0xf000) == nullptr);
+  EXPECT_TRUE(maps.Find(0xf010) == nullptr);
 
   MapInfo* info = maps.Find(0x1000);
   ASSERT_TRUE(info != nullptr);
-  ASSERT_EQ(0x1000U, info->start);
-  ASSERT_EQ(0x2000U, info->end);
-  ASSERT_EQ(0x10U, info->offset);
-  ASSERT_EQ(PROT_READ, info->flags);
-  ASSERT_EQ("/system/lib/fake1.so", info->name);
+  EXPECT_EQ(0x1000U, info->start);
+  EXPECT_EQ(0x2000U, info->end);
+  EXPECT_EQ(0x10U, info->offset);
+  EXPECT_EQ(PROT_READ, info->flags);
+  EXPECT_EQ("/system/lib/fake1.so", info->name);
 
   info = maps.Find(0x3020);
   ASSERT_TRUE(info != nullptr);
-  ASSERT_EQ(0x3000U, info->start);
-  ASSERT_EQ(0x4000U, info->end);
-  ASSERT_EQ(0x20U, info->offset);
-  ASSERT_EQ(PROT_WRITE, info->flags);
-  ASSERT_EQ("/system/lib/fake2.so", info->name);
+  EXPECT_EQ(0x3000U, info->start);
+  EXPECT_EQ(0x4000U, info->end);
+  EXPECT_EQ(0x20U, info->offset);
+  EXPECT_EQ(PROT_WRITE, info->flags);
+  EXPECT_EQ("/system/lib/fake2.so", info->name);
 
   info = maps.Find(0x6020);
   ASSERT_TRUE(info != nullptr);
-  ASSERT_EQ(0x6000U, info->start);
-  ASSERT_EQ(0x8000U, info->end);
-  ASSERT_EQ(0x30U, info->offset);
-  ASSERT_EQ(PROT_EXEC, info->flags);
-  ASSERT_EQ("/system/lib/fake3.so", info->name);
+  EXPECT_EQ(0x6000U, info->start);
+  EXPECT_EQ(0x8000U, info->end);
+  EXPECT_EQ(0x30U, info->offset);
+  EXPECT_EQ(PROT_EXEC, info->flags);
+  EXPECT_EQ("/system/lib/fake3.so", info->name);
 
   info = maps.Find(0xafff);
   ASSERT_TRUE(info != nullptr);
-  ASSERT_EQ(0xa000U, info->start);
-  ASSERT_EQ(0xb000U, info->end);
-  ASSERT_EQ(0x40U, info->offset);
-  ASSERT_EQ(PROT_READ | PROT_WRITE, info->flags);
-  ASSERT_EQ("/system/lib/fake4.so", info->name);
+  EXPECT_EQ(0xa000U, info->start);
+  EXPECT_EQ(0xb000U, info->end);
+  EXPECT_EQ(0x40U, info->offset);
+  EXPECT_EQ(PROT_READ | PROT_WRITE, info->flags);
+  EXPECT_EQ("/system/lib/fake4.so", info->name);
 
   info = maps.Find(0xe500);
   ASSERT_TRUE(info != nullptr);
-  ASSERT_EQ(0xe000U, info->start);
-  ASSERT_EQ(0xf000U, info->end);
-  ASSERT_EQ(0x50U, info->offset);
-  ASSERT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, info->flags);
-  ASSERT_EQ("/system/lib/fake5.so", info->name);
+  EXPECT_EQ(0xe000U, info->start);
+  EXPECT_EQ(0xf000U, info->end);
+  EXPECT_EQ(0x50U, info->offset);
+  EXPECT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, info->flags);
+  EXPECT_EQ("/system/lib/fake5.so", info->name);
 }
+
+}  // namespace unwindstack
diff --git a/libunwindstack/tests/MemoryBuffer.cpp b/libunwindstack/tests/MemoryBuffer.cpp
deleted file mode 100644
index af3d6b9..0000000
--- a/libunwindstack/tests/MemoryBuffer.cpp
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * Copyright (C) 2016 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 <vector>
-
-#include <gtest/gtest.h>
-
-#include "Memory.h"
-
-#include "LogFake.h"
-
-class MemoryBufferTest : public ::testing::Test {
- protected:
-  void SetUp() override {
-    ResetLogs();
-    memory_.reset(new MemoryBuffer);
-  }
-  std::unique_ptr<MemoryBuffer> memory_;
-};
-
-TEST_F(MemoryBufferTest, empty) {
-  ASSERT_EQ(0U, memory_->Size());
-  std::vector<uint8_t> buffer(1024);
-  ASSERT_FALSE(memory_->Read(0, buffer.data(), 1));
-  ASSERT_EQ(nullptr, memory_->GetPtr(0));
-  ASSERT_EQ(nullptr, memory_->GetPtr(1));
-}
-
-TEST_F(MemoryBufferTest, write_read) {
-  memory_->Resize(256);
-  ASSERT_EQ(256U, memory_->Size());
-  ASSERT_TRUE(memory_->GetPtr(0) != nullptr);
-  ASSERT_TRUE(memory_->GetPtr(1) != nullptr);
-  ASSERT_TRUE(memory_->GetPtr(255) != nullptr);
-  ASSERT_TRUE(memory_->GetPtr(256) == nullptr);
-
-  uint8_t* data = memory_->GetPtr(0);
-  for (size_t i = 0; i < memory_->Size(); i++) {
-    data[i] = i;
-  }
-
-  std::vector<uint8_t> buffer(memory_->Size());
-  ASSERT_TRUE(memory_->Read(0, buffer.data(), buffer.size()));
-  for (size_t i = 0; i < buffer.size(); i++) {
-    ASSERT_EQ(i, buffer[i]) << "Failed at byte " << i;
-  }
-}
-
-TEST_F(MemoryBufferTest, read_failures) {
-  memory_->Resize(100);
-  std::vector<uint8_t> buffer(200);
-  ASSERT_FALSE(memory_->Read(0, buffer.data(), 101));
-  ASSERT_FALSE(memory_->Read(100, buffer.data(), 1));
-  ASSERT_FALSE(memory_->Read(101, buffer.data(), 2));
-  ASSERT_FALSE(memory_->Read(99, buffer.data(), 2));
-  ASSERT_TRUE(memory_->Read(99, buffer.data(), 1));
-}
-
-TEST_F(MemoryBufferTest, read_failure_overflow) {
-  memory_->Resize(100);
-  std::vector<uint8_t> buffer(200);
-
-  ASSERT_FALSE(memory_->Read(UINT64_MAX - 100, buffer.data(), 200));
-}
diff --git a/libunwindstack/tests/MemoryBufferTest.cpp b/libunwindstack/tests/MemoryBufferTest.cpp
new file mode 100644
index 0000000..28e0e76
--- /dev/null
+++ b/libunwindstack/tests/MemoryBufferTest.cpp
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2016 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 <vector>
+
+#include <gtest/gtest.h>
+
+#include <unwindstack/Memory.h>
+
+#include "LogFake.h"
+
+namespace unwindstack {
+
+class MemoryBufferTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    ResetLogs();
+    memory_.reset(new MemoryBuffer);
+  }
+  std::unique_ptr<MemoryBuffer> memory_;
+};
+
+TEST_F(MemoryBufferTest, empty) {
+  ASSERT_EQ(0U, memory_->Size());
+  std::vector<uint8_t> buffer(1024);
+  ASSERT_FALSE(memory_->ReadFully(0, buffer.data(), 1));
+  ASSERT_EQ(nullptr, memory_->GetPtr(0));
+  ASSERT_EQ(nullptr, memory_->GetPtr(1));
+}
+
+TEST_F(MemoryBufferTest, write_read) {
+  memory_->Resize(256);
+  ASSERT_EQ(256U, memory_->Size());
+  ASSERT_TRUE(memory_->GetPtr(0) != nullptr);
+  ASSERT_TRUE(memory_->GetPtr(1) != nullptr);
+  ASSERT_TRUE(memory_->GetPtr(255) != nullptr);
+  ASSERT_TRUE(memory_->GetPtr(256) == nullptr);
+
+  uint8_t* data = memory_->GetPtr(0);
+  for (size_t i = 0; i < memory_->Size(); i++) {
+    data[i] = i;
+  }
+
+  std::vector<uint8_t> buffer(memory_->Size());
+  ASSERT_TRUE(memory_->ReadFully(0, buffer.data(), buffer.size()));
+  for (size_t i = 0; i < buffer.size(); i++) {
+    ASSERT_EQ(i, buffer[i]) << "Failed at byte " << i;
+  }
+}
+
+TEST_F(MemoryBufferTest, read_failures) {
+  memory_->Resize(100);
+  std::vector<uint8_t> buffer(200);
+  ASSERT_FALSE(memory_->ReadFully(0, buffer.data(), 101));
+  ASSERT_FALSE(memory_->ReadFully(100, buffer.data(), 1));
+  ASSERT_FALSE(memory_->ReadFully(101, buffer.data(), 2));
+  ASSERT_FALSE(memory_->ReadFully(99, buffer.data(), 2));
+  ASSERT_TRUE(memory_->ReadFully(99, buffer.data(), 1));
+}
+
+TEST_F(MemoryBufferTest, read_failure_overflow) {
+  memory_->Resize(100);
+  std::vector<uint8_t> buffer(200);
+
+  ASSERT_FALSE(memory_->ReadFully(UINT64_MAX - 100, buffer.data(), 200));
+}
+
+TEST_F(MemoryBufferTest, Read) {
+  memory_->Resize(256);
+  ASSERT_EQ(256U, memory_->Size());
+  ASSERT_TRUE(memory_->GetPtr(0) != nullptr);
+  ASSERT_TRUE(memory_->GetPtr(1) != nullptr);
+  ASSERT_TRUE(memory_->GetPtr(255) != nullptr);
+  ASSERT_TRUE(memory_->GetPtr(256) == nullptr);
+
+  uint8_t* data = memory_->GetPtr(0);
+  for (size_t i = 0; i < memory_->Size(); i++) {
+    data[i] = i;
+  }
+
+  std::vector<uint8_t> buffer(memory_->Size());
+  ASSERT_EQ(128U, memory_->Read(128, buffer.data(), buffer.size()));
+  for (size_t i = 0; i < 128; i++) {
+    ASSERT_EQ(128 + i, buffer[i]) << "Failed at byte " << i;
+  }
+}
+
+}  // namespace unwindstack
diff --git a/libunwindstack/tests/MemoryFake.cpp b/libunwindstack/tests/MemoryFake.cpp
index afb1029..60936cd 100644
--- a/libunwindstack/tests/MemoryFake.cpp
+++ b/libunwindstack/tests/MemoryFake.cpp
@@ -21,6 +21,8 @@
 
 #include "MemoryFake.h"
 
+namespace unwindstack {
+
 void MemoryFake::SetMemory(uint64_t addr, const void* memory, size_t length) {
   const uint8_t* src = reinterpret_cast<const uint8_t*>(memory);
   for (size_t i = 0; i < length; i++, addr++) {
@@ -33,14 +35,16 @@
   }
 }
 
-bool MemoryFake::Read(uint64_t addr, void* memory, size_t size) {
+size_t MemoryFake::Read(uint64_t addr, void* memory, size_t size) {
   uint8_t* dst = reinterpret_cast<uint8_t*>(memory);
   for (size_t i = 0; i < size; i++, addr++) {
     auto value = data_.find(addr);
     if (value == data_.end()) {
-      return false;
+      return i;
     }
     dst[i] = value->second;
   }
-  return true;
+  return size;
 }
+
+}  // namespace unwindstack
diff --git a/libunwindstack/tests/MemoryFake.h b/libunwindstack/tests/MemoryFake.h
index 70ef30a..764a6c3 100644
--- a/libunwindstack/tests/MemoryFake.h
+++ b/libunwindstack/tests/MemoryFake.h
@@ -23,14 +23,16 @@
 #include <vector>
 #include <unordered_map>
 
-#include "Memory.h"
+#include <unwindstack/Memory.h>
+
+namespace unwindstack {
 
 class MemoryFake : public Memory {
  public:
   MemoryFake() = default;
   virtual ~MemoryFake() = default;
 
-  bool Read(uint64_t addr, void* buffer, size_t size) override;
+  size_t Read(uint64_t addr, void* buffer, size_t size) override;
 
   void SetMemory(uint64_t addr, const void* memory, size_t length);
 
@@ -69,22 +71,12 @@
   MemoryFakeAlwaysReadZero() = default;
   virtual ~MemoryFakeAlwaysReadZero() = default;
 
-  bool Read(uint64_t, void* buffer, size_t size) override {
+  size_t Read(uint64_t, void* buffer, size_t size) override {
     memset(buffer, 0, size);
-    return true;
+    return size;
   }
 };
 
-class MemoryFakeRemote : public MemoryRemote {
- public:
-  MemoryFakeRemote() : MemoryRemote(0) {}
-  virtual ~MemoryFakeRemote() = default;
-
- protected:
-  bool PtraceRead(uint64_t, long* value) override {
-    *value = 0;
-    return true;
-  }
-};
+}  // namespace unwindstack
 
 #endif  // _LIBUNWINDSTACK_TESTS_MEMORY_FAKE_H
diff --git a/libunwindstack/tests/MemoryFileTest.cpp b/libunwindstack/tests/MemoryFileTest.cpp
index aa7a23a..d7d1ace 100644
--- a/libunwindstack/tests/MemoryFileTest.cpp
+++ b/libunwindstack/tests/MemoryFileTest.cpp
@@ -21,7 +21,9 @@
 #include <android-base/file.h>
 #include <gtest/gtest.h>
 
-#include "Memory.h"
+#include <unwindstack/Memory.h>
+
+namespace unwindstack {
 
 class MemoryFileTest : public ::testing::Test {
  protected:
@@ -47,7 +49,7 @@
 
   ASSERT_TRUE(memory_.Init(tf_->path, 0));
   std::vector<char> buffer(11);
-  ASSERT_TRUE(memory_.Read(0, buffer.data(), 10));
+  ASSERT_TRUE(memory_.ReadFully(0, buffer.data(), 10));
   buffer[10] = '\0';
   ASSERT_STREQ("0123456789", buffer.data());
 }
@@ -57,7 +59,7 @@
 
   ASSERT_TRUE(memory_.Init(tf_->path, 10));
   std::vector<char> buffer(11);
-  ASSERT_TRUE(memory_.Read(0, buffer.data(), 10));
+  ASSERT_TRUE(memory_.ReadFully(0, buffer.data(), 10));
   buffer[10] = '\0';
   ASSERT_STREQ("abcdefghij", buffer.data());
 }
@@ -73,7 +75,7 @@
 
   ASSERT_TRUE(memory_.Init(tf_->path, pagesize + 15));
   std::vector<char> buffer(9);
-  ASSERT_TRUE(memory_.Read(0, buffer.data(), 8));
+  ASSERT_TRUE(memory_.ReadFully(0, buffer.data(), 8));
   buffer[8] = '\0';
   ASSERT_STREQ("abcdefgh", buffer.data());
 }
@@ -89,7 +91,7 @@
 
   ASSERT_TRUE(memory_.Init(tf_->path, 2 * pagesize));
   std::vector<char> buffer(11);
-  ASSERT_TRUE(memory_.Read(0, buffer.data(), 10));
+  ASSERT_TRUE(memory_.ReadFully(0, buffer.data(), 10));
   buffer[10] = '\0';
   std::string expected_str;
   for (size_t i = 0; i < 5; i++) {
@@ -110,7 +112,7 @@
 
   ASSERT_TRUE(memory_.Init(tf_->path, 2 * pagesize + 10));
   std::vector<char> buffer(11);
-  ASSERT_TRUE(memory_.Read(0, buffer.data(), 10));
+  ASSERT_TRUE(memory_.ReadFully(0, buffer.data(), 10));
   buffer[10] = '\0';
   std::string expected_str;
   for (size_t i = 0; i < 5; i++) {
@@ -147,19 +149,19 @@
   std::vector<char> buffer(100);
 
   // Read before init.
-  ASSERT_FALSE(memory_.Read(0, buffer.data(), 10));
+  ASSERT_FALSE(memory_.ReadFully(0, buffer.data(), 10));
 
   ASSERT_TRUE(memory_.Init(tf_->path, 0));
 
-  ASSERT_FALSE(memory_.Read(10000, buffer.data(), 10));
-  ASSERT_FALSE(memory_.Read(5000, buffer.data(), 10));
-  ASSERT_FALSE(memory_.Read(4990, buffer.data(), 11));
-  ASSERT_TRUE(memory_.Read(4990, buffer.data(), 10));
-  ASSERT_FALSE(memory_.Read(4999, buffer.data(), 2));
-  ASSERT_TRUE(memory_.Read(4999, buffer.data(), 1));
+  ASSERT_FALSE(memory_.ReadFully(10000, buffer.data(), 10));
+  ASSERT_FALSE(memory_.ReadFully(5000, buffer.data(), 10));
+  ASSERT_FALSE(memory_.ReadFully(4990, buffer.data(), 11));
+  ASSERT_TRUE(memory_.ReadFully(4990, buffer.data(), 10));
+  ASSERT_FALSE(memory_.ReadFully(4999, buffer.data(), 2));
+  ASSERT_TRUE(memory_.ReadFully(4999, buffer.data(), 1));
 
   // Check that overflow fails properly.
-  ASSERT_FALSE(memory_.Read(UINT64_MAX - 100, buffer.data(), 200));
+  ASSERT_FALSE(memory_.ReadFully(UINT64_MAX - 100, buffer.data(), 200));
 }
 
 TEST_F(MemoryFileTest, read_past_file_within_mapping) {
@@ -176,7 +178,8 @@
 
   for (size_t i = 0; i < 100; i++) {
     uint8_t value;
-    ASSERT_FALSE(memory_.Read(buffer.size() + i, &value, 1)) << "Should have failed at value " << i;
+    ASSERT_FALSE(memory_.ReadFully(buffer.size() + i, &value, 1))
+        << "Should have failed at value " << i;
   }
 }
 
@@ -193,8 +196,8 @@
 
   std::vector<uint8_t> read_buffer(pagesize * 2);
   // Make sure that reading after mapped data is a failure.
-  ASSERT_FALSE(memory_.Read(pagesize * 2, read_buffer.data(), 1));
-  ASSERT_TRUE(memory_.Read(0, read_buffer.data(), pagesize * 2));
+  ASSERT_FALSE(memory_.ReadFully(pagesize * 2, read_buffer.data(), 1));
+  ASSERT_TRUE(memory_.ReadFully(0, read_buffer.data(), pagesize * 2));
   for (size_t i = 0; i < pagesize; i++) {
     ASSERT_EQ(2, read_buffer[i]) << "Failed at byte " << i;
   }
@@ -217,8 +220,8 @@
 
   std::vector<uint8_t> read_buffer(pagesize * 2);
   // Make sure that reading after mapped data is a failure.
-  ASSERT_FALSE(memory_.Read(pagesize * 2, read_buffer.data(), 1));
-  ASSERT_TRUE(memory_.Read(0, read_buffer.data(), pagesize * 2));
+  ASSERT_FALSE(memory_.ReadFully(pagesize * 2, read_buffer.data(), 1));
+  ASSERT_TRUE(memory_.ReadFully(0, read_buffer.data(), pagesize * 2));
   for (size_t i = 0; i < pagesize - 0x100; i++) {
     ASSERT_EQ(2, read_buffer[i]) << "Failed at byte " << i;
   }
@@ -243,8 +246,8 @@
   ASSERT_TRUE(memory_.Init(tf_->path, pagesize + 0x100, UINT64_MAX));
 
   std::vector<uint8_t> read_buffer(pagesize * 10);
-  ASSERT_FALSE(memory_.Read(pagesize * 9 - 0x100 + 1, read_buffer.data(), 1));
-  ASSERT_TRUE(memory_.Read(0, read_buffer.data(), pagesize * 9 - 0x100));
+  ASSERT_FALSE(memory_.ReadFully(pagesize * 9 - 0x100 + 1, read_buffer.data(), 1));
+  ASSERT_TRUE(memory_.ReadFully(0, read_buffer.data(), pagesize * 9 - 0x100));
 }
 
 TEST_F(MemoryFileTest, init_reinit) {
@@ -257,15 +260,17 @@
 
   ASSERT_TRUE(memory_.Init(tf_->path, 0));
   std::vector<uint8_t> read_buffer(buffer.size());
-  ASSERT_TRUE(memory_.Read(0, read_buffer.data(), pagesize));
+  ASSERT_TRUE(memory_.ReadFully(0, read_buffer.data(), pagesize));
   for (size_t i = 0; i < pagesize; i++) {
     ASSERT_EQ(1, read_buffer[i]) << "Failed at byte " << i;
   }
 
   // Now reinit.
   ASSERT_TRUE(memory_.Init(tf_->path, pagesize));
-  ASSERT_TRUE(memory_.Read(0, read_buffer.data(), pagesize));
+  ASSERT_TRUE(memory_.ReadFully(0, read_buffer.data(), pagesize));
   for (size_t i = 0; i < pagesize; i++) {
     ASSERT_EQ(2, read_buffer[i]) << "Failed at byte " << i;
   }
 }
+
+}  // namespace unwindstack
diff --git a/libunwindstack/tests/MemoryLocalTest.cpp b/libunwindstack/tests/MemoryLocalTest.cpp
index ab999da..5a389d0 100644
--- a/libunwindstack/tests/MemoryLocalTest.cpp
+++ b/libunwindstack/tests/MemoryLocalTest.cpp
@@ -16,12 +16,15 @@
 
 #include <stdint.h>
 #include <string.h>
+#include <sys/mman.h>
 
 #include <vector>
 
 #include <gtest/gtest.h>
 
-#include "Memory.h"
+#include <unwindstack/Memory.h>
+
+namespace unwindstack {
 
 TEST(MemoryLocalTest, read) {
   std::vector<uint8_t> src(1024);
@@ -30,14 +33,14 @@
   MemoryLocal local;
 
   std::vector<uint8_t> dst(1024);
-  ASSERT_TRUE(local.Read(reinterpret_cast<uint64_t>(src.data()), dst.data(), 1024));
+  ASSERT_TRUE(local.ReadFully(reinterpret_cast<uint64_t>(src.data()), dst.data(), 1024));
   ASSERT_EQ(0, memcmp(src.data(), dst.data(), 1024));
   for (size_t i = 0; i < 1024; i++) {
     ASSERT_EQ(0x4cU, dst[i]);
   }
 
   memset(src.data(), 0x23, 512);
-  ASSERT_TRUE(local.Read(reinterpret_cast<uint64_t>(src.data()), dst.data(), 1024));
+  ASSERT_TRUE(local.ReadFully(reinterpret_cast<uint64_t>(src.data()), dst.data(), 1024));
   ASSERT_EQ(0, memcmp(src.data(), dst.data(), 1024));
   for (size_t i = 0; i < 512; i++) {
     ASSERT_EQ(0x23U, dst[i]);
@@ -51,8 +54,8 @@
   MemoryLocal local;
 
   std::vector<uint8_t> dst(100);
-  ASSERT_FALSE(local.Read(0, dst.data(), 1));
-  ASSERT_FALSE(local.Read(0, dst.data(), 100));
+  ASSERT_FALSE(local.ReadFully(0, dst.data(), 1));
+  ASSERT_FALSE(local.ReadFully(0, dst.data(), 100));
 }
 
 TEST(MemoryLocalTest, read_overflow) {
@@ -62,5 +65,47 @@
   // version will always go through the overflow check.
   std::vector<uint8_t> dst(100);
   uint64_t value;
-  ASSERT_FALSE(local.Read(reinterpret_cast<uint64_t>(&value), dst.data(), SIZE_MAX));
+  ASSERT_FALSE(local.ReadFully(reinterpret_cast<uint64_t>(&value), dst.data(), SIZE_MAX));
 }
+
+TEST(MemoryLocalTest, Read) {
+  char* mapping = static_cast<char*>(
+      mmap(nullptr, 2 * getpagesize(), PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0));
+
+  ASSERT_NE(MAP_FAILED, mapping);
+
+  mprotect(mapping + getpagesize(), getpagesize(), PROT_NONE);
+  memset(mapping + getpagesize() - 1024, 0x4c, 1024);
+
+  MemoryLocal local;
+
+  std::vector<uint8_t> dst(4096);
+  ASSERT_EQ(1024U, local.Read(reinterpret_cast<uint64_t>(mapping + getpagesize() - 1024),
+                              dst.data(), 4096));
+  for (size_t i = 0; i < 1024; i++) {
+    ASSERT_EQ(0x4cU, dst[i]) << "Failed at byte " << i;
+  }
+
+  ASSERT_EQ(0, munmap(mapping, 2 * getpagesize()));
+}
+
+TEST(MemoryLocalTest, read_hole) {
+  void* mapping =
+      mmap(nullptr, 3 * 4096, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
+  ASSERT_NE(MAP_FAILED, mapping);
+  memset(mapping, 0xFF, 3 * 4096);
+  mprotect(static_cast<char*>(mapping) + 4096, 4096, PROT_NONE);
+
+  MemoryLocal local;
+  std::vector<uint8_t> dst(4096 * 3, 0xCC);
+  ASSERT_EQ(4096U, local.Read(reinterpret_cast<uintptr_t>(mapping), dst.data(), 4096 * 3));
+  for (size_t i = 0; i < 4096; ++i) {
+    ASSERT_EQ(0xFF, dst[i]);
+  }
+  for (size_t i = 4096; i < 4096 * 3; ++i) {
+    ASSERT_EQ(0xCC, dst[i]);
+  }
+  ASSERT_EQ(0, munmap(mapping, 3 * 4096));
+}
+
+}  // namespace unwindstack
diff --git a/libunwindstack/tests/MemoryOfflineBufferTest.cpp b/libunwindstack/tests/MemoryOfflineBufferTest.cpp
new file mode 100644
index 0000000..f022884
--- /dev/null
+++ b/libunwindstack/tests/MemoryOfflineBufferTest.cpp
@@ -0,0 +1,97 @@
+/*
+ * 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 <vector>
+
+#include <gtest/gtest.h>
+
+#include <unwindstack/Memory.h>
+
+#include "LogFake.h"
+
+namespace unwindstack {
+
+class MemoryOfflineBufferTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    ResetLogs();
+    memory_.reset(new MemoryOfflineBuffer(buffer_.data(), kStart, kEnd));
+  }
+
+  static void SetUpTestCase() {
+    buffer_.resize(kLength);
+    for (size_t i = 0; i < kLength; i++) {
+      buffer_[i] = i % 189;
+    }
+  }
+
+  std::unique_ptr<MemoryOfflineBuffer> memory_;
+
+  static constexpr size_t kLength = 0x2000;
+  static constexpr uint64_t kStart = 0x1000;
+  static constexpr uint64_t kEnd = kStart + kLength;
+  static std::vector<uint8_t> buffer_;
+};
+
+std::vector<uint8_t> MemoryOfflineBufferTest::buffer_;
+
+static void VerifyBuffer(uint8_t* buffer, size_t start_value, size_t length) {
+  for (size_t i = 0; i < length; i++) {
+    ASSERT_EQ((start_value + i) % 189, buffer[i]) << "Failed at byte " << i;
+  }
+}
+
+TEST_F(MemoryOfflineBufferTest, read_out_of_bounds) {
+  std::vector<uint8_t> buffer(1024);
+  ASSERT_FALSE(memory_->ReadFully(0, buffer.data(), 1));
+  ASSERT_FALSE(memory_->ReadFully(0xfff, buffer.data(), 1));
+  ASSERT_FALSE(memory_->ReadFully(0xfff, buffer.data(), 2));
+  ASSERT_FALSE(memory_->ReadFully(0x3000, buffer.data(), 1));
+  ASSERT_FALSE(memory_->ReadFully(0x3001, buffer.data(), 1));
+}
+
+TEST_F(MemoryOfflineBufferTest, read) {
+  std::vector<uint8_t> buffer(1024);
+  ASSERT_TRUE(memory_->ReadFully(kStart, buffer.data(), 10));
+  ASSERT_NO_FATAL_FAILURE(VerifyBuffer(buffer.data(), 0, 10));
+
+  ASSERT_TRUE(memory_->ReadFully(kStart + 555, buffer.data(), 40));
+  ASSERT_NO_FATAL_FAILURE(VerifyBuffer(buffer.data(), 555, 40));
+
+  ASSERT_TRUE(memory_->ReadFully(kStart + kLength - 105, buffer.data(), 105));
+  ASSERT_NO_FATAL_FAILURE(VerifyBuffer(buffer.data(), kLength - 105, 105));
+}
+
+TEST_F(MemoryOfflineBufferTest, read_past_end) {
+  std::vector<uint8_t> buffer(1024);
+  ASSERT_EQ(100U, memory_->Read(kStart + kLength - 100, buffer.data(), buffer.size()));
+  VerifyBuffer(buffer.data(), kLength - 100, 100);
+}
+
+TEST_F(MemoryOfflineBufferTest, read_after_reset) {
+  std::vector<uint8_t> buffer(1024);
+  ASSERT_TRUE(memory_->ReadFully(kStart, buffer.data(), 100));
+  ASSERT_NO_FATAL_FAILURE(VerifyBuffer(buffer.data(), 0, 100));
+
+  memory_->Reset(&buffer_[10], 0x12000, 0x13000);
+  ASSERT_TRUE(memory_->ReadFully(0x12000, buffer.data(), 100));
+  ASSERT_NO_FATAL_FAILURE(VerifyBuffer(buffer.data(), 10, 100));
+
+  ASSERT_EQ(50U, memory_->Read(0x13000 - 50, buffer.data(), buffer.size()));
+  ASSERT_NO_FATAL_FAILURE(VerifyBuffer(buffer.data(), 0x1000 - 50 + 10, 50));
+}
+
+}  // namespace unwindstack
diff --git a/libunwindstack/tests/MemoryOfflineTest.cpp b/libunwindstack/tests/MemoryOfflineTest.cpp
new file mode 100644
index 0000000..14d58e6
--- /dev/null
+++ b/libunwindstack/tests/MemoryOfflineTest.cpp
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2017 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 <vector>
+
+#include <gtest/gtest.h>
+
+#include <android-base/file.h>
+#include <android-base/test_utils.h>
+#include <unwindstack/Memory.h>
+
+namespace unwindstack {
+
+class MemoryOfflineTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    for (size_t i = 0; i < 1024; ++i) {
+      data.push_back(i & 0xff);
+    }
+
+    ASSERT_TRUE(android::base::WriteFully(temp_file.fd, &offset, sizeof(offset)));
+    ASSERT_TRUE(android::base::WriteFully(temp_file.fd, data.data(), data.size()));
+
+    memory = std::make_unique<MemoryOffline>();
+    ASSERT_TRUE(memory != nullptr);
+
+    ASSERT_TRUE(memory->Init(temp_file.path, 0));
+  }
+
+  TemporaryFile temp_file;
+  uint64_t offset = 4096;
+  std::vector<char> data;
+  std::unique_ptr<MemoryOffline> memory;
+};
+
+TEST_F(MemoryOfflineTest, read_boundaries) {
+  char buf = '\0';
+  ASSERT_EQ(0U, memory->Read(offset - 1, &buf, 1));
+  ASSERT_EQ(0U, memory->Read(offset + data.size(), &buf, 1));
+  ASSERT_EQ(1U, memory->Read(offset, &buf, 1));
+  ASSERT_EQ(buf, data.front());
+  ASSERT_EQ(1U, memory->Read(offset + data.size() - 1, &buf, 1));
+  ASSERT_EQ(buf, data.back());
+}
+
+TEST_F(MemoryOfflineTest, read_values) {
+  std::vector<char> buf;
+  buf.resize(2 * data.size());
+  ASSERT_EQ(data.size(), memory->Read(offset, buf.data(), buf.size()));
+  buf.resize(data.size());
+  ASSERT_EQ(buf, data);
+}
+
+}  // namespace unwindstack
diff --git a/libunwindstack/tests/MemoryRangeTest.cpp b/libunwindstack/tests/MemoryRangeTest.cpp
index 469afcc..cb1a0c9 100644
--- a/libunwindstack/tests/MemoryRangeTest.cpp
+++ b/libunwindstack/tests/MemoryRangeTest.cpp
@@ -22,20 +22,23 @@
 
 #include <gtest/gtest.h>
 
-#include "Memory.h"
+#include <unwindstack/Memory.h>
 
 #include "MemoryFake.h"
 
+namespace unwindstack {
+
 TEST(MemoryRangeTest, read) {
   std::vector<uint8_t> src(1024);
   memset(src.data(), 0x4c, 1024);
-  MemoryFake* memory = new MemoryFake;
-  memory->SetMemory(9001, src);
+  MemoryFake* memory_fake = new MemoryFake;
+  std::shared_ptr<Memory> process_memory(memory_fake);
+  memory_fake->SetMemory(9001, src);
 
-  MemoryRange range(memory, 9001, 9001 + src.size());
+  MemoryRange range(process_memory, 9001, src.size(), 0);
 
   std::vector<uint8_t> dst(1024);
-  ASSERT_TRUE(range.Read(0, dst.data(), src.size()));
+  ASSERT_TRUE(range.ReadFully(0, dst.data(), src.size()));
   for (size_t i = 0; i < 1024; i++) {
     ASSERT_EQ(0x4cU, dst[i]) << "Failed at byte " << i;
   }
@@ -44,29 +47,48 @@
 TEST(MemoryRangeTest, read_near_limit) {
   std::vector<uint8_t> src(4096);
   memset(src.data(), 0x4c, 4096);
-  MemoryFake* memory = new MemoryFake;
-  memory->SetMemory(1000, src);
+  MemoryFake* memory_fake = new MemoryFake;
+  std::shared_ptr<Memory> process_memory(memory_fake);
+  memory_fake->SetMemory(1000, src);
 
-  MemoryRange range(memory, 1000, 2024);
+  MemoryRange range(process_memory, 1000, 1024, 0);
 
   std::vector<uint8_t> dst(1024);
-  ASSERT_TRUE(range.Read(1020, dst.data(), 4));
+  ASSERT_TRUE(range.ReadFully(1020, dst.data(), 4));
   for (size_t i = 0; i < 4; i++) {
     ASSERT_EQ(0x4cU, dst[i]) << "Failed at byte " << i;
   }
 
   // Verify that reads outside of the range will fail.
-  ASSERT_FALSE(range.Read(1020, dst.data(), 5));
-  ASSERT_FALSE(range.Read(1024, dst.data(), 1));
-  ASSERT_FALSE(range.Read(1024, dst.data(), 1024));
+  ASSERT_FALSE(range.ReadFully(1020, dst.data(), 5));
+  ASSERT_FALSE(range.ReadFully(1024, dst.data(), 1));
+  ASSERT_FALSE(range.ReadFully(1024, dst.data(), 1024));
 
   // Verify that reading up to the end works.
-  ASSERT_TRUE(range.Read(1020, dst.data(), 4));
+  ASSERT_TRUE(range.ReadFully(1020, dst.data(), 4));
 }
 
 TEST(MemoryRangeTest, read_overflow) {
   std::vector<uint8_t> buffer(100);
 
-  std::unique_ptr<MemoryRange> overflow(new MemoryRange(new MemoryFakeAlwaysReadZero, 100, 200));
-  ASSERT_FALSE(overflow->Read(UINT64_MAX - 10, buffer.data(), 100));
+  std::shared_ptr<Memory> process_memory(new MemoryFakeAlwaysReadZero);
+  std::unique_ptr<MemoryRange> overflow(new MemoryRange(process_memory, 100, 200, 0));
+  ASSERT_FALSE(overflow->ReadFully(UINT64_MAX - 10, buffer.data(), 100));
 }
+
+TEST(MemoryRangeTest, Read) {
+  std::vector<uint8_t> src(4096);
+  memset(src.data(), 0x4c, 4096);
+  MemoryFake* memory_fake = new MemoryFake;
+  std::shared_ptr<Memory> process_memory(memory_fake);
+  memory_fake->SetMemory(1000, src);
+
+  MemoryRange range(process_memory, 1000, 1024, 0);
+  std::vector<uint8_t> dst(1024);
+  ASSERT_EQ(4U, range.Read(1020, dst.data(), 1024));
+  for (size_t i = 0; i < 4; i++) {
+    ASSERT_EQ(0x4cU, dst[i]) << "Failed at byte " << i;
+  }
+}
+
+}  // namespace unwindstack
diff --git a/libunwindstack/tests/MemoryRemoteTest.cpp b/libunwindstack/tests/MemoryRemoteTest.cpp
index e48edf7..fb56e8a 100644
--- a/libunwindstack/tests/MemoryRemoteTest.cpp
+++ b/libunwindstack/tests/MemoryRemoteTest.cpp
@@ -22,7 +22,6 @@
 #include <sys/mman.h>
 #include <sys/ptrace.h>
 #include <sys/types.h>
-#include <time.h>
 #include <unistd.h>
 
 #include <vector>
@@ -31,33 +30,21 @@
 #include <android-base/file.h>
 #include <gtest/gtest.h>
 
-#include "Memory.h"
+#include <unwindstack/Memory.h>
 
 #include "MemoryFake.h"
+#include "TestUtils.h"
+
+namespace unwindstack {
 
 class MemoryRemoteTest : public ::testing::Test {
  protected:
-  static uint64_t NanoTime() {
-    struct timespec t = { 0, 0 };
-    clock_gettime(CLOCK_MONOTONIC, &t);
-    return static_cast<uint64_t>(t.tv_sec * NS_PER_SEC + t.tv_nsec);
-  }
-
   static bool Attach(pid_t pid) {
     if (ptrace(PTRACE_ATTACH, pid, 0, 0) == -1) {
       return false;
     }
 
-    uint64_t start = NanoTime();
-    siginfo_t si;
-    while (TEMP_FAILURE_RETRY(ptrace(PTRACE_GETSIGINFO, pid, 0, &si)) < 0 && errno == ESRCH) {
-      if ((NanoTime() - start) > 10 * NS_PER_SEC) {
-        printf("%d: Failed to stop after 10 seconds.\n", pid);
-        return false;
-      }
-      usleep(30);
-    }
-    return true;
+    return TestQuiescePid(pid);
   }
 
   static bool Detach(pid_t pid) {
@@ -77,20 +64,92 @@
     exit(1);
   }
   ASSERT_LT(0, pid);
+  TestScopedPidReaper reap(pid);
 
   ASSERT_TRUE(Attach(pid));
 
   MemoryRemote remote(pid);
 
   std::vector<uint8_t> dst(1024);
-  ASSERT_TRUE(remote.Read(reinterpret_cast<uint64_t>(src.data()), dst.data(), 1024));
+  ASSERT_TRUE(remote.ReadFully(reinterpret_cast<uint64_t>(src.data()), dst.data(), 1024));
   for (size_t i = 0; i < 1024; i++) {
     ASSERT_EQ(0x4cU, dst[i]) << "Failed at byte " << i;
   }
 
   ASSERT_TRUE(Detach(pid));
+}
 
-  kill(pid, SIGKILL);
+TEST_F(MemoryRemoteTest, read_large) {
+  static constexpr size_t kTotalPages = 245;
+  std::vector<uint8_t> src(kTotalPages * getpagesize());
+  for (size_t i = 0; i < kTotalPages; i++) {
+    memset(&src[i * getpagesize()], i, getpagesize());
+  }
+
+  pid_t pid;
+  if ((pid = fork()) == 0) {
+    while (true)
+      ;
+    exit(1);
+  }
+  ASSERT_LT(0, pid);
+  TestScopedPidReaper reap(pid);
+
+  ASSERT_TRUE(Attach(pid));
+
+  MemoryRemote remote(pid);
+
+  std::vector<uint8_t> dst(kTotalPages * getpagesize());
+  ASSERT_TRUE(remote.ReadFully(reinterpret_cast<uint64_t>(src.data()), dst.data(), src.size()));
+  for (size_t i = 0; i < kTotalPages * getpagesize(); i++) {
+    ASSERT_EQ(i / getpagesize(), dst[i]) << "Failed at byte " << i;
+  }
+
+  ASSERT_TRUE(Detach(pid));
+}
+
+TEST_F(MemoryRemoteTest, read_partial) {
+  char* mapping = static_cast<char*>(
+      mmap(nullptr, 4 * getpagesize(), PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0));
+  ASSERT_NE(MAP_FAILED, mapping);
+  memset(mapping, 0x4c, 4 * getpagesize());
+  ASSERT_EQ(0, mprotect(mapping + getpagesize(), getpagesize(), PROT_NONE));
+  ASSERT_EQ(0, munmap(mapping + 3 * getpagesize(), getpagesize()));
+
+  pid_t pid;
+  if ((pid = fork()) == 0) {
+    while (true)
+      ;
+    exit(1);
+  }
+  ASSERT_LT(0, pid);
+  TestScopedPidReaper reap(pid);
+
+  // Unmap from our process.
+  ASSERT_EQ(0, munmap(mapping, 3 * getpagesize()));
+
+  ASSERT_TRUE(Attach(pid));
+
+  MemoryRemote remote(pid);
+
+  std::vector<uint8_t> dst(4096);
+  size_t bytes =
+      remote.Read(reinterpret_cast<uint64_t>(mapping + getpagesize() - 1024), dst.data(), 4096);
+  // Some read methods can read PROT_NONE maps, allow that.
+  ASSERT_LE(1024U, bytes);
+  for (size_t i = 0; i < bytes; i++) {
+    ASSERT_EQ(0x4cU, dst[i]) << "Failed at byte " << i;
+  }
+
+  // Now verify that reading stops at the end of a map.
+  bytes =
+      remote.Read(reinterpret_cast<uint64_t>(mapping + 3 * getpagesize() - 1024), dst.data(), 4096);
+  ASSERT_EQ(1024U, bytes);
+  for (size_t i = 0; i < bytes; i++) {
+    ASSERT_EQ(0x4cU, dst[i]) << "Failed at byte " << i;
+  }
+
+  ASSERT_TRUE(Detach(pid));
 }
 
 TEST_F(MemoryRemoteTest, read_fail) {
@@ -108,37 +167,49 @@
     exit(1);
   }
   ASSERT_LT(0, pid);
+  TestScopedPidReaper reap(pid);
 
   ASSERT_TRUE(Attach(pid));
 
   MemoryRemote remote(pid);
 
   std::vector<uint8_t> dst(pagesize);
-  ASSERT_TRUE(remote.Read(reinterpret_cast<uint64_t>(src), dst.data(), pagesize));
+  ASSERT_TRUE(remote.ReadFully(reinterpret_cast<uint64_t>(src), dst.data(), pagesize));
   for (size_t i = 0; i < 1024; i++) {
     ASSERT_EQ(0x4cU, dst[i]) << "Failed at byte " << i;
   }
 
-  ASSERT_FALSE(remote.Read(reinterpret_cast<uint64_t>(src) + pagesize, dst.data(), 1));
-  ASSERT_TRUE(remote.Read(reinterpret_cast<uint64_t>(src) + pagesize - 1, dst.data(), 1));
-  ASSERT_FALSE(remote.Read(reinterpret_cast<uint64_t>(src) + pagesize - 4, dst.data(), 8));
+  ASSERT_FALSE(remote.ReadFully(reinterpret_cast<uint64_t>(src) + pagesize, dst.data(), 1));
+  ASSERT_TRUE(remote.ReadFully(reinterpret_cast<uint64_t>(src) + pagesize - 1, dst.data(), 1));
+  ASSERT_FALSE(remote.ReadFully(reinterpret_cast<uint64_t>(src) + pagesize - 4, dst.data(), 8));
 
   // Check overflow condition is caught properly.
-  ASSERT_FALSE(remote.Read(UINT64_MAX - 100, dst.data(), 200));
+  ASSERT_FALSE(remote.ReadFully(UINT64_MAX - 100, dst.data(), 200));
 
   ASSERT_EQ(0, munmap(src, pagesize));
 
   ASSERT_TRUE(Detach(pid));
-
-  kill(pid, SIGKILL);
 }
 
 TEST_F(MemoryRemoteTest, read_overflow) {
-  MemoryFakeRemote remote;
+  pid_t pid;
+  if ((pid = fork()) == 0) {
+    while (true)
+      ;
+    exit(1);
+  }
+  ASSERT_LT(0, pid);
+  TestScopedPidReaper reap(pid);
+
+  ASSERT_TRUE(Attach(pid));
+
+  MemoryRemote remote(pid);
 
   // Check overflow condition is caught properly.
   std::vector<uint8_t> dst(200);
-  ASSERT_FALSE(remote.Read(UINT64_MAX - 100, dst.data(), 200));
+  ASSERT_FALSE(remote.ReadFully(UINT64_MAX - 100, dst.data(), 200));
+
+  ASSERT_TRUE(Detach(pid));
 }
 
 TEST_F(MemoryRemoteTest, read_illegal) {
@@ -148,16 +219,135 @@
     exit(1);
   }
   ASSERT_LT(0, pid);
+  TestScopedPidReaper reap(pid);
 
   ASSERT_TRUE(Attach(pid));
 
   MemoryRemote remote(pid);
 
   std::vector<uint8_t> dst(100);
-  ASSERT_FALSE(remote.Read(0, dst.data(), 1));
-  ASSERT_FALSE(remote.Read(0, dst.data(), 100));
+  ASSERT_FALSE(remote.ReadFully(0, dst.data(), 1));
+  ASSERT_FALSE(remote.ReadFully(0, dst.data(), 100));
 
   ASSERT_TRUE(Detach(pid));
-
-  kill(pid, SIGKILL);
 }
+
+TEST_F(MemoryRemoteTest, read_mprotect_hole) {
+  size_t page_size = getpagesize();
+  void* mapping =
+      mmap(nullptr, 3 * getpagesize(), PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
+  ASSERT_NE(MAP_FAILED, mapping);
+  memset(mapping, 0xFF, 3 * page_size);
+  ASSERT_EQ(0, mprotect(static_cast<char*>(mapping) + page_size, page_size, PROT_NONE));
+
+  pid_t pid;
+  if ((pid = fork()) == 0) {
+    while (true);
+    exit(1);
+  }
+  ASSERT_LT(0, pid);
+  TestScopedPidReaper reap(pid);
+
+  ASSERT_EQ(0, munmap(mapping, 3 * page_size));
+
+  ASSERT_TRUE(Attach(pid));
+
+  MemoryRemote remote(pid);
+  std::vector<uint8_t> dst(getpagesize() * 4, 0xCC);
+  size_t read_size = remote.Read(reinterpret_cast<uint64_t>(mapping), dst.data(), page_size * 3);
+  // Some read methods can read PROT_NONE maps, allow that.
+  ASSERT_LE(page_size, read_size);
+  for (size_t i = 0; i < read_size; ++i) {
+    ASSERT_EQ(0xFF, dst[i]);
+  }
+  for (size_t i = read_size; i < dst.size(); ++i) {
+    ASSERT_EQ(0xCC, dst[i]);
+  }
+}
+
+TEST_F(MemoryRemoteTest, read_munmap_hole) {
+  size_t page_size = getpagesize();
+  void* mapping =
+      mmap(nullptr, 3 * getpagesize(), PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
+  ASSERT_NE(MAP_FAILED, mapping);
+  memset(mapping, 0xFF, 3 * page_size);
+  ASSERT_EQ(0, munmap(static_cast<char*>(mapping) + page_size, page_size));
+
+  pid_t pid;
+  if ((pid = fork()) == 0) {
+    while (true)
+      ;
+    exit(1);
+  }
+  ASSERT_LT(0, pid);
+  TestScopedPidReaper reap(pid);
+
+  ASSERT_EQ(0, munmap(mapping, page_size));
+  ASSERT_EQ(0, munmap(static_cast<char*>(mapping) + 2 * page_size, page_size));
+
+  ASSERT_TRUE(Attach(pid));
+
+  MemoryRemote remote(pid);
+  std::vector<uint8_t> dst(getpagesize() * 4, 0xCC);
+  size_t read_size = remote.Read(reinterpret_cast<uint64_t>(mapping), dst.data(), page_size * 3);
+  ASSERT_EQ(page_size, read_size);
+  for (size_t i = 0; i < read_size; ++i) {
+    ASSERT_EQ(0xFF, dst[i]);
+  }
+  for (size_t i = read_size; i < dst.size(); ++i) {
+    ASSERT_EQ(0xCC, dst[i]);
+  }
+}
+
+// Verify that the memory remote object chooses a memory read function
+// properly. Either process_vm_readv or ptrace.
+TEST_F(MemoryRemoteTest, read_choose_correctly) {
+  size_t page_size = getpagesize();
+  void* mapping =
+      mmap(nullptr, 2 * getpagesize(), PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
+  ASSERT_NE(MAP_FAILED, mapping);
+  memset(mapping, 0xFC, 2 * page_size);
+  ASSERT_EQ(0, mprotect(static_cast<char*>(mapping), page_size, PROT_NONE));
+
+  pid_t pid;
+  if ((pid = fork()) == 0) {
+    while (true)
+      ;
+    exit(1);
+  }
+  ASSERT_LT(0, pid);
+  TestScopedPidReaper reap(pid);
+
+  ASSERT_EQ(0, munmap(mapping, 2 * page_size));
+
+  ASSERT_TRUE(Attach(pid));
+
+  // We know that process_vm_readv of a mprotect'd PROT_NONE region will fail.
+  // Read from the PROT_NONE area first to force the choice of ptrace.
+  MemoryRemote remote_ptrace(pid);
+  uint32_t value;
+  size_t bytes = remote_ptrace.Read(reinterpret_cast<uint64_t>(mapping), &value, sizeof(value));
+  ASSERT_EQ(sizeof(value), bytes);
+  ASSERT_EQ(0xfcfcfcfcU, value);
+  bytes = remote_ptrace.Read(reinterpret_cast<uint64_t>(mapping) + page_size, &value, sizeof(value));
+  ASSERT_EQ(sizeof(value), bytes);
+  ASSERT_EQ(0xfcfcfcfcU, value);
+  bytes = remote_ptrace.Read(reinterpret_cast<uint64_t>(mapping), &value, sizeof(value));
+  ASSERT_EQ(sizeof(value), bytes);
+  ASSERT_EQ(0xfcfcfcfcU, value);
+
+  // Now verify that choosing process_vm_readv results in failing reads of
+  // the PROT_NONE part of the map. Read from a valid map first which
+  // should prefer process_vm_readv, and keep that as the read function.
+  MemoryRemote remote_readv(pid);
+  bytes = remote_readv.Read(reinterpret_cast<uint64_t>(mapping) + page_size, &value, sizeof(value));
+  ASSERT_EQ(sizeof(value), bytes);
+  ASSERT_EQ(0xfcfcfcfcU, value);
+  bytes = remote_readv.Read(reinterpret_cast<uint64_t>(mapping), &value, sizeof(value));
+  ASSERT_EQ(0U, bytes);
+  bytes = remote_readv.Read(reinterpret_cast<uint64_t>(mapping) + page_size, &value, sizeof(value));
+  ASSERT_EQ(sizeof(value), bytes);
+  ASSERT_EQ(0xfcfcfcfcU, value);
+}
+
+}  // namespace unwindstack
diff --git a/libunwindstack/tests/MemoryTest.cpp b/libunwindstack/tests/MemoryTest.cpp
index 51b5d7d..3655984 100644
--- a/libunwindstack/tests/MemoryTest.cpp
+++ b/libunwindstack/tests/MemoryTest.cpp
@@ -22,10 +22,12 @@
 
 #include <gtest/gtest.h>
 
-#include "Memory.h"
+#include <unwindstack/Memory.h>
 
 #include "MemoryFake.h"
 
+namespace unwindstack {
+
 TEST(MemoryTest, read32) {
   MemoryFakeAlwaysReadZero memory;
 
@@ -49,40 +51,6 @@
   uint64_t four;
 };
 
-TEST(MemoryTest, read_field) {
-  MemoryFakeAlwaysReadZero memory;
-
-  FakeStruct data;
-  memset(&data, 0xff, sizeof(data));
-  ASSERT_TRUE(memory.ReadField(0, &data, &data.one, sizeof(data.one)));
-  ASSERT_EQ(0, data.one);
-
-  memset(&data, 0xff, sizeof(data));
-  ASSERT_TRUE(memory.ReadField(0, &data, &data.two, sizeof(data.two)));
-  ASSERT_FALSE(data.two);
-
-  memset(&data, 0xff, sizeof(data));
-  ASSERT_TRUE(memory.ReadField(0, &data, &data.three, sizeof(data.three)));
-  ASSERT_EQ(0U, data.three);
-
-  memset(&data, 0xff, sizeof(data));
-  ASSERT_TRUE(memory.ReadField(0, &data, &data.four, sizeof(data.four)));
-  ASSERT_EQ(0U, data.four);
-}
-
-TEST(MemoryTest, read_field_fails) {
-  MemoryFakeAlwaysReadZero memory;
-
-  FakeStruct data;
-  memset(&data, 0xff, sizeof(data));
-
-  ASSERT_FALSE(memory.ReadField(UINT64_MAX, &data, &data.three, sizeof(data.three)));
-
-  // Field and start reversed, should fail.
-  ASSERT_FALSE(memory.ReadField(100, &data.two, &data, sizeof(data.two)));
-  ASSERT_FALSE(memory.ReadField(0, &data.two, &data, sizeof(data.two)));
-}
-
 TEST(MemoryTest, read_string) {
   std::string name("string_in_memory");
 
@@ -124,3 +92,5 @@
   ASSERT_TRUE(memory.ReadString(0, &dst_name));
   ASSERT_EQ("short", dst_name);
 }
+
+}  // namespace unwindstack
diff --git a/libunwindstack/tests/RegsFake.h b/libunwindstack/tests/RegsFake.h
index ff030c8..d6ca9b7 100644
--- a/libunwindstack/tests/RegsFake.h
+++ b/libunwindstack/tests/RegsFake.h
@@ -19,18 +19,79 @@
 
 #include <stdint.h>
 
-#include "Regs.h"
+#include <unwindstack/Elf.h>
+#include <unwindstack/Memory.h>
+#include <unwindstack/Regs.h>
 
-template <typename TypeParam>
-class RegsFake : public RegsImpl<TypeParam> {
+namespace unwindstack {
+
+class RegsFake : public Regs {
  public:
-  RegsFake(uint16_t total_regs, uint16_t sp_reg)
-      : RegsImpl<TypeParam>(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;
 
-  uint64_t GetRelPc(Elf*, const MapInfo*) override { return 0; }
-  uint64_t GetAdjustedPc(uint64_t, Elf*) override { return 0; }
-  bool GetReturnAddressFromDefault(Memory*, uint64_t*) { return false; }
+  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;
+    }
+    fake_pc_ = fake_return_address_;
+    return true;
+  }
+
+  void IterateRegisters(std::function<void(const char*, uint64_t)>) override {}
+
+  bool Is32Bit() { return false; }
+
+  uint64_t GetPcAdjustment(uint64_t, Elf*) override { return 2; }
+
+  bool StepIfSignalHandler(uint64_t, Elf*, Memory*) override { return false; }
+
+  void FakeSetArch(ArchEnum arch) { fake_arch_ = arch; }
+  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; }
+
+  Regs* Clone() override { return nullptr; }
+
+ private:
+  ArchEnum fake_arch_ = ARCH_UNKNOWN;
+  uint64_t fake_pc_ = 0;
+  uint64_t fake_sp_ = 0;
+  bool fake_return_address_valid_ = false;
+  uint64_t fake_return_address_ = 0;
 };
 
+template <typename TypeParam>
+class RegsImplFake : public RegsImpl<TypeParam> {
+ public:
+  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; }
+  bool SetPcFromReturnAddress(Memory*) override { return false; }
+  bool StepIfSignalHandler(uint64_t, Elf*, Memory*) override { return false; }
+
+  Regs* Clone() override { return nullptr; }
+
+ private:
+  uint64_t fake_pc_ = 0;
+  uint64_t fake_sp_ = 0;
+};
+
+}  // namespace unwindstack
+
 #endif  // _LIBUNWINDSTACK_TESTS_REGS_FAKE_H
diff --git a/libunwindstack/tests/RegsIterateTest.cpp b/libunwindstack/tests/RegsIterateTest.cpp
new file mode 100644
index 0000000..9a27dbd
--- /dev/null
+++ b/libunwindstack/tests/RegsIterateTest.cpp
@@ -0,0 +1,256 @@
+/*
+ * Copyright (C) 2017 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 <stdint.h>
+
+#include <utility>
+#include <type_traits>
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include <unwindstack/Elf.h>
+#include <unwindstack/ElfInterface.h>
+#include <unwindstack/MachineArm.h>
+#include <unwindstack/MachineArm64.h>
+#include <unwindstack/MachineMips.h>
+#include <unwindstack/MachineMips64.h>
+#include <unwindstack/MachineX86.h>
+#include <unwindstack/MachineX86_64.h>
+#include <unwindstack/MapInfo.h>
+#include <unwindstack/RegsArm.h>
+#include <unwindstack/RegsArm64.h>
+#include <unwindstack/RegsMips.h>
+#include <unwindstack/RegsMips64.h>
+#include <unwindstack/RegsX86.h>
+#include <unwindstack/RegsX86_64.h>
+
+namespace unwindstack {
+
+struct Register {
+  std::string expected_name;
+  uint64_t offset;
+
+  bool operator==(const Register& rhs) const {
+    return std::tie(expected_name, offset) == std::tie(rhs.expected_name, rhs.offset);
+  }
+};
+
+template<typename T>
+class RegsIterateTest : public ::testing::Test {
+};
+
+template<typename RegsType>
+std::vector<Register> ExpectedRegisters();
+
+template<>
+std::vector<Register> ExpectedRegisters<RegsArm>() {
+  std::vector<Register> result;
+  result.push_back({"r0", ARM_REG_R0});
+  result.push_back({"r1", ARM_REG_R1});
+  result.push_back({"r2", ARM_REG_R2});
+  result.push_back({"r3", ARM_REG_R3});
+  result.push_back({"r4", ARM_REG_R4});
+  result.push_back({"r5", ARM_REG_R5});
+  result.push_back({"r6", ARM_REG_R6});
+  result.push_back({"r7", ARM_REG_R7});
+  result.push_back({"r8", ARM_REG_R8});
+  result.push_back({"r9", ARM_REG_R9});
+  result.push_back({"r10", ARM_REG_R10});
+  result.push_back({"r11", ARM_REG_R11});
+  result.push_back({"ip", ARM_REG_R12});
+  result.push_back({"sp", ARM_REG_SP});
+  result.push_back({"lr", ARM_REG_LR});
+  result.push_back({"pc", ARM_REG_PC});
+  return result;
+}
+
+template<>
+std::vector<Register> ExpectedRegisters<RegsArm64>() {
+  std::vector<Register> result;
+  result.push_back({"x0", ARM64_REG_R0});
+  result.push_back({"x1", ARM64_REG_R1});
+  result.push_back({"x2", ARM64_REG_R2});
+  result.push_back({"x3", ARM64_REG_R3});
+  result.push_back({"x4", ARM64_REG_R4});
+  result.push_back({"x5", ARM64_REG_R5});
+  result.push_back({"x6", ARM64_REG_R6});
+  result.push_back({"x7", ARM64_REG_R7});
+  result.push_back({"x8", ARM64_REG_R8});
+  result.push_back({"x9", ARM64_REG_R9});
+  result.push_back({"x10", ARM64_REG_R10});
+  result.push_back({"x11", ARM64_REG_R11});
+  result.push_back({"x12", ARM64_REG_R12});
+  result.push_back({"x13", ARM64_REG_R13});
+  result.push_back({"x14", ARM64_REG_R14});
+  result.push_back({"x15", ARM64_REG_R15});
+  result.push_back({"x16", ARM64_REG_R16});
+  result.push_back({"x17", ARM64_REG_R17});
+  result.push_back({"x18", ARM64_REG_R18});
+  result.push_back({"x19", ARM64_REG_R19});
+  result.push_back({"x20", ARM64_REG_R20});
+  result.push_back({"x21", ARM64_REG_R21});
+  result.push_back({"x22", ARM64_REG_R22});
+  result.push_back({"x23", ARM64_REG_R23});
+  result.push_back({"x24", ARM64_REG_R24});
+  result.push_back({"x25", ARM64_REG_R25});
+  result.push_back({"x26", ARM64_REG_R26});
+  result.push_back({"x27", ARM64_REG_R27});
+  result.push_back({"x28", ARM64_REG_R28});
+  result.push_back({"x29", ARM64_REG_R29});
+  result.push_back({"sp", ARM64_REG_SP});
+  result.push_back({"lr", ARM64_REG_LR});
+  result.push_back({"pc", ARM64_REG_PC});
+  return result;
+}
+
+template<>
+std::vector<Register> ExpectedRegisters<RegsX86>() {
+  std::vector<Register> result;
+  result.push_back({"eax", X86_REG_EAX});
+  result.push_back({"ebx", X86_REG_EBX});
+  result.push_back({"ecx", X86_REG_ECX});
+  result.push_back({"edx", X86_REG_EDX});
+  result.push_back({"ebp", X86_REG_EBP});
+  result.push_back({"edi", X86_REG_EDI});
+  result.push_back({"esi", X86_REG_ESI});
+  result.push_back({"esp", X86_REG_ESP});
+  result.push_back({"eip", X86_REG_EIP});
+  return result;
+}
+
+template<>
+std::vector<Register> ExpectedRegisters<RegsX86_64>() {
+  std::vector<Register> result;
+  result.push_back({"rax", X86_64_REG_RAX});
+  result.push_back({"rbx", X86_64_REG_RBX});
+  result.push_back({"rcx", X86_64_REG_RCX});
+  result.push_back({"rdx", X86_64_REG_RDX});
+  result.push_back({"r8", X86_64_REG_R8});
+  result.push_back({"r9", X86_64_REG_R9});
+  result.push_back({"r10", X86_64_REG_R10});
+  result.push_back({"r11", X86_64_REG_R11});
+  result.push_back({"r12", X86_64_REG_R12});
+  result.push_back({"r13", X86_64_REG_R13});
+  result.push_back({"r14", X86_64_REG_R14});
+  result.push_back({"r15", X86_64_REG_R15});
+  result.push_back({"rdi", X86_64_REG_RDI});
+  result.push_back({"rsi", X86_64_REG_RSI});
+  result.push_back({"rbp", X86_64_REG_RBP});
+  result.push_back({"rsp", X86_64_REG_RSP});
+  result.push_back({"rip", X86_64_REG_RIP});
+  return result;
+}
+
+template<>
+std::vector<Register> ExpectedRegisters<RegsMips>() {
+  std::vector<Register> result;
+  result.push_back({"r0", MIPS_REG_R0});
+  result.push_back({"r1", MIPS_REG_R1});
+  result.push_back({"r2", MIPS_REG_R2});
+  result.push_back({"r3", MIPS_REG_R3});
+  result.push_back({"r4", MIPS_REG_R4});
+  result.push_back({"r5", MIPS_REG_R5});
+  result.push_back({"r6", MIPS_REG_R6});
+  result.push_back({"r7", MIPS_REG_R7});
+  result.push_back({"r8", MIPS_REG_R8});
+  result.push_back({"r9", MIPS_REG_R9});
+  result.push_back({"r10", MIPS_REG_R10});
+  result.push_back({"r11", MIPS_REG_R11});
+  result.push_back({"r12", MIPS_REG_R12});
+  result.push_back({"r13", MIPS_REG_R13});
+  result.push_back({"r14", MIPS_REG_R14});
+  result.push_back({"r15", MIPS_REG_R15});
+  result.push_back({"r16", MIPS_REG_R16});
+  result.push_back({"r17", MIPS_REG_R17});
+  result.push_back({"r18", MIPS_REG_R18});
+  result.push_back({"r19", MIPS_REG_R19});
+  result.push_back({"r20", MIPS_REG_R20});
+  result.push_back({"r21", MIPS_REG_R21});
+  result.push_back({"r22", MIPS_REG_R22});
+  result.push_back({"r23", MIPS_REG_R23});
+  result.push_back({"r24", MIPS_REG_R24});
+  result.push_back({"r25", MIPS_REG_R25});
+  result.push_back({"r26", MIPS_REG_R26});
+  result.push_back({"r27", MIPS_REG_R27});
+  result.push_back({"r28", MIPS_REG_R28});
+  result.push_back({"sp", MIPS_REG_SP});
+  result.push_back({"r30", MIPS_REG_R30});
+  result.push_back({"ra", MIPS_REG_RA});
+  result.push_back({"pc", MIPS_REG_PC});
+
+  return result;
+}
+
+template<>
+std::vector<Register> ExpectedRegisters<RegsMips64>() {
+  std::vector<Register> result;
+  result.push_back({"r0", MIPS64_REG_R0});
+  result.push_back({"r1", MIPS64_REG_R1});
+  result.push_back({"r2", MIPS64_REG_R2});
+  result.push_back({"r3", MIPS64_REG_R3});
+  result.push_back({"r4", MIPS64_REG_R4});
+  result.push_back({"r5", MIPS64_REG_R5});
+  result.push_back({"r6", MIPS64_REG_R6});
+  result.push_back({"r7", MIPS64_REG_R7});
+  result.push_back({"r8", MIPS64_REG_R8});
+  result.push_back({"r9", MIPS64_REG_R9});
+  result.push_back({"r10", MIPS64_REG_R10});
+  result.push_back({"r11", MIPS64_REG_R11});
+  result.push_back({"r12", MIPS64_REG_R12});
+  result.push_back({"r13", MIPS64_REG_R13});
+  result.push_back({"r14", MIPS64_REG_R14});
+  result.push_back({"r15", MIPS64_REG_R15});
+  result.push_back({"r16", MIPS64_REG_R16});
+  result.push_back({"r17", MIPS64_REG_R17});
+  result.push_back({"r18", MIPS64_REG_R18});
+  result.push_back({"r19", MIPS64_REG_R19});
+  result.push_back({"r20", MIPS64_REG_R20});
+  result.push_back({"r21", MIPS64_REG_R21});
+  result.push_back({"r22", MIPS64_REG_R22});
+  result.push_back({"r23", MIPS64_REG_R23});
+  result.push_back({"r24", MIPS64_REG_R24});
+  result.push_back({"r25", MIPS64_REG_R25});
+  result.push_back({"r26", MIPS64_REG_R26});
+  result.push_back({"r27", MIPS64_REG_R27});
+  result.push_back({"r28", MIPS64_REG_R28});
+  result.push_back({"sp", MIPS64_REG_SP});
+  result.push_back({"r30", MIPS64_REG_R30});
+  result.push_back({"ra", MIPS64_REG_RA});
+  result.push_back({"pc", MIPS64_REG_PC});
+
+  return result;
+}
+
+using RegTypes = ::testing::Types<RegsArm, RegsArm64, RegsX86, RegsX86_64, RegsMips, RegsMips64>;
+TYPED_TEST_CASE(RegsIterateTest, RegTypes);
+
+TYPED_TEST(RegsIterateTest, iterate) {
+  std::vector<Register> expected = ExpectedRegisters<TypeParam>();
+  TypeParam regs;
+  for (const auto& reg : expected) {
+    regs[reg.offset] = reg.offset;
+  }
+
+  std::vector<Register> actual;
+  regs.IterateRegisters([&actual](const char* name, uint64_t value) {
+    actual.push_back({name, value});
+  });
+
+  ASSERT_EQ(expected, actual);
+}
+
+}  // namespace unwindstack
diff --git a/libunwindstack/tests/RegsStepIfSignalHandlerTest.cpp b/libunwindstack/tests/RegsStepIfSignalHandlerTest.cpp
new file mode 100644
index 0000000..eac12ca
--- /dev/null
+++ b/libunwindstack/tests/RegsStepIfSignalHandlerTest.cpp
@@ -0,0 +1,261 @@
+/*
+ * Copyright (C) 2017 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 <stdint.h>
+
+#include <gtest/gtest.h>
+
+#include <unwindstack/Elf.h>
+#include <unwindstack/MachineArm.h>
+#include <unwindstack/MachineArm64.h>
+#include <unwindstack/MachineMips.h>
+#include <unwindstack/MachineMips64.h>
+#include <unwindstack/MachineX86.h>
+#include <unwindstack/MachineX86_64.h>
+#include <unwindstack/RegsArm.h>
+#include <unwindstack/RegsArm64.h>
+#include <unwindstack/RegsMips.h>
+#include <unwindstack/RegsMips64.h>
+#include <unwindstack/RegsX86.h>
+#include <unwindstack/RegsX86_64.h>
+
+#include "MemoryFake.h"
+
+namespace unwindstack {
+
+class RegsStepIfSignalHandlerTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    elf_memory_ = new MemoryFake;
+    elf_.reset(new Elf(elf_memory_));
+  }
+
+  void ArmStepIfSignalHandlerNonRt(uint32_t pc_data);
+  void ArmStepIfSignalHandlerRt(uint32_t pc_data);
+
+  MemoryFake* elf_memory_;
+  MemoryFake process_memory_;
+  std::unique_ptr<Elf> elf_;
+};
+
+void RegsStepIfSignalHandlerTest::ArmStepIfSignalHandlerNonRt(uint32_t pc_data) {
+  uint64_t addr = 0x1000;
+  RegsArm regs;
+  regs[ARM_REG_PC] = 0x5000;
+  regs[ARM_REG_SP] = addr;
+
+  elf_memory_->SetData32(0x5000, pc_data);
+
+  for (uint64_t index = 0; index <= 30; index++) {
+    process_memory_.SetData32(addr + index * 4, index * 0x10);
+  }
+
+  ASSERT_TRUE(regs.StepIfSignalHandler(0x5000, elf_.get(), &process_memory_));
+  EXPECT_EQ(0x100U, regs[ARM_REG_SP]);
+  EXPECT_EQ(0x120U, regs[ARM_REG_PC]);
+  EXPECT_EQ(0x100U, regs.sp());
+  EXPECT_EQ(0x120U, regs.pc());
+}
+
+TEST_F(RegsStepIfSignalHandlerTest, arm_step_if_signal_handler_non_rt) {
+  // Form 1
+  ArmStepIfSignalHandlerNonRt(0xe3a07077);
+
+  // Form 2
+  ArmStepIfSignalHandlerNonRt(0xef900077);
+
+  // Form 3
+  ArmStepIfSignalHandlerNonRt(0xdf002777);
+}
+
+void RegsStepIfSignalHandlerTest::ArmStepIfSignalHandlerRt(uint32_t pc_data) {
+  uint64_t addr = 0x1000;
+  RegsArm regs;
+  regs[ARM_REG_PC] = 0x5000;
+  regs[ARM_REG_SP] = addr;
+
+  elf_memory_->SetData32(0x5000, pc_data);
+
+  for (uint64_t index = 0; index <= 100; index++) {
+    process_memory_.SetData32(addr + index * 4, index * 0x10);
+  }
+
+  ASSERT_TRUE(regs.StepIfSignalHandler(0x5000, elf_.get(), &process_memory_));
+  EXPECT_EQ(0x350U, regs[ARM_REG_SP]);
+  EXPECT_EQ(0x370U, regs[ARM_REG_PC]);
+  EXPECT_EQ(0x350U, regs.sp());
+  EXPECT_EQ(0x370U, regs.pc());
+}
+
+TEST_F(RegsStepIfSignalHandlerTest, arm_step_if_signal_handler_rt) {
+  // Form 1
+  ArmStepIfSignalHandlerRt(0xe3a070ad);
+
+  // Form 2
+  ArmStepIfSignalHandlerRt(0xef9000ad);
+
+  // Form 3
+  ArmStepIfSignalHandlerRt(0xdf0027ad);
+}
+
+TEST_F(RegsStepIfSignalHandlerTest, arm64_step_if_signal_handler) {
+  uint64_t addr = 0x1000;
+  RegsArm64 regs;
+  regs[ARM64_REG_PC] = 0x8000;
+  regs[ARM64_REG_SP] = addr;
+
+  elf_memory_->SetData64(0x8000, 0xd4000001d2801168ULL);
+
+  for (uint64_t index = 0; index <= 100; index++) {
+    process_memory_.SetData64(addr + index * 8, index * 0x10);
+  }
+
+  ASSERT_TRUE(regs.StepIfSignalHandler(0x8000, elf_.get(), &process_memory_));
+  EXPECT_EQ(0x460U, regs[ARM64_REG_SP]);
+  EXPECT_EQ(0x470U, regs[ARM64_REG_PC]);
+  EXPECT_EQ(0x460U, regs.sp());
+  EXPECT_EQ(0x470U, regs.pc());
+}
+
+TEST_F(RegsStepIfSignalHandlerTest, x86_step_if_signal_handler_no_siginfo) {
+  uint64_t addr = 0xa00;
+  RegsX86 regs;
+  regs[X86_REG_EIP] = 0x4100;
+  regs[X86_REG_ESP] = addr;
+
+  elf_memory_->SetData64(0x4100, 0x80cd00000077b858ULL);
+  for (uint64_t index = 0; index <= 25; index++) {
+    process_memory_.SetData32(addr + index * 4, index * 0x10);
+  }
+
+  ASSERT_TRUE(regs.StepIfSignalHandler(0x4100, elf_.get(), &process_memory_));
+  EXPECT_EQ(0x70U, regs[X86_REG_EBP]);
+  EXPECT_EQ(0x80U, regs[X86_REG_ESP]);
+  EXPECT_EQ(0x90U, regs[X86_REG_EBX]);
+  EXPECT_EQ(0xa0U, regs[X86_REG_EDX]);
+  EXPECT_EQ(0xb0U, regs[X86_REG_ECX]);
+  EXPECT_EQ(0xc0U, regs[X86_REG_EAX]);
+  EXPECT_EQ(0xf0U, regs[X86_REG_EIP]);
+  EXPECT_EQ(0x80U, regs.sp());
+  EXPECT_EQ(0xf0U, regs.pc());
+}
+
+TEST_F(RegsStepIfSignalHandlerTest, x86_step_if_signal_handler_siginfo) {
+  uint64_t addr = 0xa00;
+  RegsX86 regs;
+  regs[X86_REG_EIP] = 0x4100;
+  regs[X86_REG_ESP] = addr;
+
+  elf_memory_->SetData64(0x4100, 0x0080cd000000adb8ULL);
+  addr += 8;
+  // Pointer to ucontext data.
+  process_memory_.SetData32(addr, 0x8100);
+
+  addr = 0x8100;
+  for (uint64_t index = 0; index <= 30; index++) {
+    process_memory_.SetData32(addr + index * 4, index * 0x10);
+  }
+
+  ASSERT_TRUE(regs.StepIfSignalHandler(0x4100, elf_.get(), &process_memory_));
+  EXPECT_EQ(0xb0U, regs[X86_REG_EBP]);
+  EXPECT_EQ(0xc0U, regs[X86_REG_ESP]);
+  EXPECT_EQ(0xd0U, regs[X86_REG_EBX]);
+  EXPECT_EQ(0xe0U, regs[X86_REG_EDX]);
+  EXPECT_EQ(0xf0U, regs[X86_REG_ECX]);
+  EXPECT_EQ(0x100U, regs[X86_REG_EAX]);
+  EXPECT_EQ(0x130U, regs[X86_REG_EIP]);
+  EXPECT_EQ(0xc0U, regs.sp());
+  EXPECT_EQ(0x130U, regs.pc());
+}
+
+TEST_F(RegsStepIfSignalHandlerTest, x86_64_step_if_signal_handler) {
+  uint64_t addr = 0x500;
+  RegsX86_64 regs;
+  regs[X86_64_REG_RIP] = 0x7000;
+  regs[X86_64_REG_RSP] = addr;
+
+  elf_memory_->SetData64(0x7000, 0x0f0000000fc0c748);
+  elf_memory_->SetData16(0x7008, 0x0f05);
+
+  for (uint64_t index = 0; index <= 30; index++) {
+    process_memory_.SetData64(addr + index * 8, index * 0x10);
+  }
+
+  ASSERT_TRUE(regs.StepIfSignalHandler(0x7000, elf_.get(), &process_memory_));
+  EXPECT_EQ(0x140U, regs[X86_64_REG_RSP]);
+  EXPECT_EQ(0x150U, regs[X86_64_REG_RIP]);
+  EXPECT_EQ(0x140U, regs.sp());
+  EXPECT_EQ(0x150U, regs.pc());
+}
+
+TEST_F(RegsStepIfSignalHandlerTest, mips_step_if_signal_handler_non_rt) {
+  uint64_t addr = 0x1000;
+  RegsMips regs;
+  regs[MIPS_REG_PC] = 0x8000;
+  regs[MIPS_REG_SP] = addr;
+
+  elf_memory_->SetData64(0x8000, 0x0000000c24021017ULL);
+
+  for (uint64_t index = 0; index <= 50; index++) {
+    process_memory_.SetData64(addr + index * 8, index * 0x10);
+  }
+
+  ASSERT_TRUE(regs.StepIfSignalHandler(0x8000, elf_.get(), &process_memory_));
+  EXPECT_EQ(0x220U, regs[MIPS_REG_SP]);
+  EXPECT_EQ(0x040U, regs[MIPS_REG_PC]);
+  EXPECT_EQ(0x220U, regs.sp());
+  EXPECT_EQ(0x040U, regs.pc());
+}
+
+TEST_F(RegsStepIfSignalHandlerTest, mips_step_if_signal_handler_rt) {
+  uint64_t addr = 0x1000;
+  RegsMips regs;
+  regs[MIPS_REG_PC] = 0x8000;
+  regs[MIPS_REG_SP] = addr;
+
+  elf_memory_->SetData64(0x8000, 0x0000000c24021061ULL);
+
+  for (uint64_t index = 0; index <= 100; index++) {
+    process_memory_.SetData64(addr + index * 8, index * 0x10);
+  }
+
+  ASSERT_TRUE(regs.StepIfSignalHandler(0x8000, elf_.get(), &process_memory_));
+  EXPECT_EQ(0x350U, regs[MIPS_REG_SP]);
+  EXPECT_EQ(0x170U, regs[MIPS_REG_PC]);
+  EXPECT_EQ(0x350U, regs.sp());
+  EXPECT_EQ(0x170U, regs.pc());
+}
+
+TEST_F(RegsStepIfSignalHandlerTest, mips64_step_if_signal_handler) {
+  uint64_t addr = 0x1000;
+  RegsMips64 regs;
+  regs[MIPS64_REG_PC] = 0x8000;
+  regs[MIPS64_REG_SP] = addr;
+
+  elf_memory_->SetData64(0x8000, 0x0000000c2402145bULL);
+
+  for (uint64_t index = 0; index <= 100; index++) {
+    process_memory_.SetData64(addr + index * 8, index * 0x10);
+  }
+
+  ASSERT_TRUE(regs.StepIfSignalHandler(0x8000, elf_.get(), &process_memory_));
+  EXPECT_EQ(0x350U, regs[MIPS64_REG_SP]);
+  EXPECT_EQ(0x600U, regs[MIPS64_REG_PC]);
+  EXPECT_EQ(0x350U, regs.sp());
+  EXPECT_EQ(0x600U, regs.pc());
+}
+
+}  // namespace unwindstack
diff --git a/libunwindstack/tests/RegsTest.cpp b/libunwindstack/tests/RegsTest.cpp
index 3056373..90c3fe6 100644
--- a/libunwindstack/tests/RegsTest.cpp
+++ b/libunwindstack/tests/RegsTest.cpp
@@ -18,46 +18,21 @@
 
 #include <gtest/gtest.h>
 
-#include "Elf.h"
-#include "ElfInterface.h"
-#include "MapInfo.h"
-#include "Regs.h"
+#include <unwindstack/Elf.h>
+#include <unwindstack/ElfInterface.h>
+#include <unwindstack/MapInfo.h>
+#include <unwindstack/RegsArm.h>
+#include <unwindstack/RegsArm64.h>
+#include <unwindstack/RegsX86.h>
+#include <unwindstack/RegsX86_64.h>
+#include <unwindstack/RegsMips.h>
+#include <unwindstack/RegsMips64.h>
 
+#include "ElfFake.h"
 #include "MemoryFake.h"
+#include "RegsFake.h"
 
-class ElfFake : public Elf {
- public:
-  ElfFake(Memory* memory) : Elf(memory) { valid_ = true; }
-  virtual ~ElfFake() = default;
-
-  void set_elf_interface(ElfInterface* interface) { interface_.reset(interface); }
-};
-
-class ElfInterfaceFake : public ElfInterface {
- public:
-  ElfInterfaceFake(Memory* memory) : ElfInterface(memory) {}
-  virtual ~ElfInterfaceFake() = default;
-
-  void set_load_bias(uint64_t load_bias) { load_bias_ = load_bias; }
-
-  bool Init() override { return false; }
-  void InitHeaders() override {}
-  bool GetSoname(std::string*) override { return false; }
-  bool GetFunctionName(uint64_t, std::string*, uint64_t*) override { return false; }
-  bool Step(uint64_t, Regs*, Memory*) override { return false; }
-};
-
-template <typename TypeParam>
-class RegsTestImpl : public RegsImpl<TypeParam> {
- public:
-  RegsTestImpl(uint16_t total_regs, uint16_t regs_sp)
-      : RegsImpl<TypeParam>(total_regs, regs_sp, Regs::Location(Regs::LOCATION_UNKNOWN, 0)) {}
-  RegsTestImpl(uint16_t total_regs, uint16_t regs_sp, Regs::Location return_loc)
-      : RegsImpl<TypeParam>(total_regs, regs_sp, return_loc) {}
-  virtual ~RegsTestImpl() = default;
-
-  uint64_t GetAdjustedPc(uint64_t, Elf*) { return 0; }
-};
+namespace unwindstack {
 
 class RegsTest : public ::testing::Test {
  protected:
@@ -65,24 +40,17 @@
     memory_ = new MemoryFake;
     elf_.reset(new ElfFake(memory_));
     elf_interface_ = new ElfInterfaceFake(elf_->memory());
-    elf_->set_elf_interface(elf_interface_);
+    elf_->FakeSetInterface(elf_interface_);
   }
 
-  template <typename AddressType>
-  void regs_rel_pc();
-
-  template <typename AddressType>
-  void regs_return_address_register();
-
   ElfInterfaceFake* elf_interface_;
   MemoryFake* memory_;
   std::unique_ptr<ElfFake> elf_;
 };
 
 TEST_F(RegsTest, regs32) {
-  RegsTestImpl<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++) {
@@ -103,9 +71,8 @@
 }
 
 TEST_F(RegsTest, regs64) {
-  RegsTestImpl<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++) {
@@ -125,142 +92,233 @@
   ASSERT_EQ(10U, regs64[8]);
 }
 
-template <typename AddressType>
-void RegsTest::regs_rel_pc() {
-  RegsTestImpl<AddressType> regs(30, 12);
-
-  elf_interface_->set_load_bias(0);
-  MapInfo map_info{.start = 0x1000, .end = 0x2000};
-  regs.set_pc(0x1101);
-  ASSERT_EQ(0x101U, regs.GetRelPc(elf_.get(), &map_info));
-  elf_interface_->set_load_bias(0x3000);
-  ASSERT_EQ(0x3101U, regs.GetRelPc(elf_.get(), &map_info));
-}
-
-TEST_F(RegsTest, regs32_rel_pc) {
-  regs_rel_pc<uint32_t>();
-}
-
-TEST_F(RegsTest, regs64_rel_pc) {
-  regs_rel_pc<uint64_t>();
-}
-
-template <typename AddressType>
-void RegsTest::regs_return_address_register() {
-  RegsTestImpl<AddressType> regs(20, 10, Regs::Location(Regs::LOCATION_REGISTER, 5));
-
-  regs[5] = 0x12345;
-  uint64_t value;
-  ASSERT_TRUE(regs.GetReturnAddressFromDefault(memory_, &value));
-  ASSERT_EQ(0x12345U, value);
-}
-
-TEST_F(RegsTest, regs32_return_address_register) {
-  regs_return_address_register<uint32_t>();
-}
-
-TEST_F(RegsTest, regs64_return_address_register) {
-  regs_return_address_register<uint64_t>();
-}
-
-TEST_F(RegsTest, regs32_return_address_sp_offset) {
-  RegsTestImpl<uint32_t> regs(20, 10, Regs::Location(Regs::LOCATION_SP_OFFSET, -2));
-
-  regs.set_sp(0x2002);
-  memory_->SetData32(0x2000, 0x12345678);
-  uint64_t value;
-  ASSERT_TRUE(regs.GetReturnAddressFromDefault(memory_, &value));
-  ASSERT_EQ(0x12345678U, value);
-}
-
-TEST_F(RegsTest, regs64_return_address_sp_offset) {
-  RegsTestImpl<uint64_t> regs(20, 10, Regs::Location(Regs::LOCATION_SP_OFFSET, -8));
-
-  regs.set_sp(0x2008);
-  memory_->SetData64(0x2000, 0x12345678aabbccddULL);
-  uint64_t value;
-  ASSERT_TRUE(regs.GetReturnAddressFromDefault(memory_, &value));
-  ASSERT_EQ(0x12345678aabbccddULL, value);
-}
-
 TEST_F(RegsTest, rel_pc) {
   RegsArm64 arm64;
-  ASSERT_EQ(0xcU, arm64.GetAdjustedPc(0x10, elf_.get()));
-  ASSERT_EQ(0x0U, arm64.GetAdjustedPc(0x4, elf_.get()));
-  ASSERT_EQ(0x3U, arm64.GetAdjustedPc(0x3, elf_.get()));
-  ASSERT_EQ(0x2U, arm64.GetAdjustedPc(0x2, elf_.get()));
-  ASSERT_EQ(0x1U, arm64.GetAdjustedPc(0x1, elf_.get()));
-  ASSERT_EQ(0x0U, arm64.GetAdjustedPc(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(0xffU,  x86.GetAdjustedPc(0x100, elf_.get()));
-  ASSERT_EQ(0x1U,  x86.GetAdjustedPc(0x2, elf_.get()));
-  ASSERT_EQ(0x0U,  x86.GetAdjustedPc(0x1, elf_.get()));
-  ASSERT_EQ(0x0U,  x86.GetAdjustedPc(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(0xffU,  x86_64.GetAdjustedPc(0x100, elf_.get()));
-  ASSERT_EQ(0x1U,  x86_64.GetAdjustedPc(0x2, elf_.get()));
-  ASSERT_EQ(0x0U,  x86_64.GetAdjustedPc(0x1, elf_.get()));
-  ASSERT_EQ(0x0U,  x86_64.GetAdjustedPc(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;
+  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;
+  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) {
   RegsArm arm;
 
   // Check fence posts.
-  elf_interface_->set_load_bias(0);
-  ASSERT_EQ(3U,  arm.GetAdjustedPc(0x5, elf_.get()));
-  ASSERT_EQ(4U,  arm.GetAdjustedPc(0x4, elf_.get()));
-  ASSERT_EQ(3U,  arm.GetAdjustedPc(0x3, elf_.get()));
-  ASSERT_EQ(2U,  arm.GetAdjustedPc(0x2, elf_.get()));
-  ASSERT_EQ(1U,  arm.GetAdjustedPc(0x1, elf_.get()));
-  ASSERT_EQ(0U,  arm.GetAdjustedPc(0x0, elf_.get()));
+  elf_->FakeSetLoadBias(0);
+  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_interface_->set_load_bias(0x100);
-  ASSERT_EQ(0xffU,  arm.GetAdjustedPc(0xff, elf_.get()));
-  ASSERT_EQ(0x103U,  arm.GetAdjustedPc(0x105, elf_.get()));
-  ASSERT_EQ(0x104U,  arm.GetAdjustedPc(0x104, elf_.get()));
-  ASSERT_EQ(0x103U,  arm.GetAdjustedPc(0x103, elf_.get()));
-  ASSERT_EQ(0x102U,  arm.GetAdjustedPc(0x102, elf_.get()));
-  ASSERT_EQ(0x101U,  arm.GetAdjustedPc(0x101, elf_.get()));
-  ASSERT_EQ(0x100U,  arm.GetAdjustedPc(0x100, elf_.get()));
+  elf_->FakeSetLoadBias(0x100);
+  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_interface_->set_load_bias(0);
+  elf_->FakeSetLoadBias(0);
   memory_->SetData32(0x2000, 0);
-  ASSERT_EQ(0x2003U,  arm.GetAdjustedPc(0x2005, elf_.get()));
+  EXPECT_EQ(2U, arm.GetPcAdjustment(0x2005, elf_.get()));
   memory_->SetData32(0x2000, 0xe000f000);
-  ASSERT_EQ(0x2001U,  arm.GetAdjustedPc(0x2005, elf_.get()));
+  EXPECT_EQ(4U, arm.GetPcAdjustment(0x2005, elf_.get()));
 
-  elf_interface_->set_load_bias(0x400);
+  elf_->FakeSetLoadBias(0x400);
   memory_->SetData32(0x2100, 0);
-  ASSERT_EQ(0x2503U,  arm.GetAdjustedPc(0x2505, elf_.get()));
+  EXPECT_EQ(2U, arm.GetPcAdjustment(0x2505, elf_.get()));
   memory_->SetData32(0x2100, 0xf111f111);
-  ASSERT_EQ(0x2501U,  arm.GetAdjustedPc(0x2505, elf_.get()));
+  EXPECT_EQ(4U, arm.GetPcAdjustment(0x2505, elf_.get()));
 }
 
 TEST_F(RegsTest, elf_invalid) {
-  Elf invalid_elf(new MemoryFake);
   RegsArm regs_arm;
   RegsArm64 regs_arm64;
   RegsX86 regs_x86;
   RegsX86_64 regs_x86_64;
-  MapInfo map_info{.start = 0x1000, .end = 0x2000};
+  RegsMips regs_mips;
+  RegsMips64 regs_mips64;
+  MapInfo map_info(0x1000, 0x2000);
+  Elf* invalid_elf = new Elf(nullptr);
+  map_info.elf.reset(invalid_elf);
 
   regs_arm.set_pc(0x1500);
-  ASSERT_EQ(0x500U, regs_arm.GetRelPc(&invalid_elf, &map_info));
-  ASSERT_EQ(0x500U, regs_arm.GetAdjustedPc(0x500U, &invalid_elf));
+  EXPECT_EQ(0x500U, invalid_elf->GetRelPc(regs_arm.pc(), &map_info));
+  EXPECT_EQ(2U, regs_arm.GetPcAdjustment(0x500U, invalid_elf));
+  EXPECT_EQ(2U, regs_arm.GetPcAdjustment(0x511U, invalid_elf));
 
   regs_arm64.set_pc(0x1600);
-  ASSERT_EQ(0x600U, regs_arm64.GetRelPc(&invalid_elf, &map_info));
-  ASSERT_EQ(0x600U, regs_arm64.GetAdjustedPc(0x600U, &invalid_elf));
+  EXPECT_EQ(0x600U, invalid_elf->GetRelPc(regs_arm64.pc(), &map_info));
+  EXPECT_EQ(4U, regs_arm64.GetPcAdjustment(0x600U, invalid_elf));
 
   regs_x86.set_pc(0x1700);
-  ASSERT_EQ(0x700U, regs_x86.GetRelPc(&invalid_elf, &map_info));
-  ASSERT_EQ(0x700U, regs_x86.GetAdjustedPc(0x700U, &invalid_elf));
+  EXPECT_EQ(0x700U, invalid_elf->GetRelPc(regs_x86.pc(), &map_info));
+  EXPECT_EQ(1U, regs_x86.GetPcAdjustment(0x700U, invalid_elf));
 
   regs_x86_64.set_pc(0x1800);
-  ASSERT_EQ(0x800U, regs_x86_64.GetRelPc(&invalid_elf, &map_info));
-  ASSERT_EQ(0x800U, regs_x86_64.GetAdjustedPc(0x800U, &invalid_elf));
+  EXPECT_EQ(0x800U, invalid_elf->GetRelPc(regs_x86_64.pc(), &map_info));
+  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(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(8U, regs_mips64.GetPcAdjustment(0xa00U, invalid_elf));
 }
+
+TEST_F(RegsTest, arm_verify_sp_pc) {
+  RegsArm arm;
+  uint32_t* regs = reinterpret_cast<uint32_t*>(arm.RawData());
+  regs[13] = 0x100;
+  regs[15] = 0x200;
+  EXPECT_EQ(0x100U, arm.sp());
+  EXPECT_EQ(0x200U, arm.pc());
+}
+
+TEST_F(RegsTest, arm64_verify_sp_pc) {
+  RegsArm64 arm64;
+  uint64_t* regs = reinterpret_cast<uint64_t*>(arm64.RawData());
+  regs[31] = 0xb100000000ULL;
+  regs[32] = 0xc200000000ULL;
+  EXPECT_EQ(0xb100000000U, arm64.sp());
+  EXPECT_EQ(0xc200000000U, arm64.pc());
+}
+
+TEST_F(RegsTest, x86_verify_sp_pc) {
+  RegsX86 x86;
+  uint32_t* regs = reinterpret_cast<uint32_t*>(x86.RawData());
+  regs[4] = 0x23450000;
+  regs[8] = 0xabcd0000;
+  EXPECT_EQ(0x23450000U, x86.sp());
+  EXPECT_EQ(0xabcd0000U, x86.pc());
+}
+
+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;
+  EXPECT_EQ(0x1200000000U, x86_64.sp());
+  EXPECT_EQ(0x4900000000U, x86_64.pc());
+}
+
+TEST_F(RegsTest, mips_verify_sp_pc) {
+  RegsMips mips;
+  uint32_t* regs = reinterpret_cast<uint32_t*>(mips.RawData());
+  regs[29] = 0x100;
+  regs[32] = 0x200;
+  EXPECT_EQ(0x100U, mips.sp());
+  EXPECT_EQ(0x200U, mips.pc());
+}
+
+TEST_F(RegsTest, mips64_verify_sp_pc) {
+  RegsMips64 mips64;
+  uint64_t* regs = reinterpret_cast<uint64_t*>(mips64.RawData());
+  regs[29] = 0xb100000000ULL;
+  regs[32] = 0xc200000000ULL;
+  EXPECT_EQ(0xb100000000U, mips64.sp());
+  EXPECT_EQ(0xc200000000U, mips64.pc());
+}
+
+TEST_F(RegsTest, machine_type) {
+  RegsArm arm_regs;
+  EXPECT_EQ(ARCH_ARM, arm_regs.Arch());
+
+  RegsArm64 arm64_regs;
+  EXPECT_EQ(ARCH_ARM64, arm64_regs.Arch());
+
+  RegsX86 x86_regs;
+  EXPECT_EQ(ARCH_X86, x86_regs.Arch());
+
+  RegsX86_64 x86_64_regs;
+  EXPECT_EQ(ARCH_X86_64, x86_64_regs.Arch());
+
+  RegsMips mips_regs;
+  EXPECT_EQ(ARCH_MIPS, mips_regs.Arch());
+
+  RegsMips64 mips64_regs;
+  EXPECT_EQ(ARCH_MIPS64, mips64_regs.Arch());
+}
+
+template <typename RegisterType>
+void clone_test(Regs* regs) {
+  RegisterType* register_values = reinterpret_cast<RegisterType*>(regs->RawData());
+  int num_regs = regs->total_regs();
+  for (int i = 0; i < num_regs; ++i) {
+    register_values[i] = i;
+  }
+
+  std::unique_ptr<Regs> clone(regs->Clone());
+  ASSERT_EQ(regs->total_regs(), clone->total_regs());
+  RegisterType* clone_values = reinterpret_cast<RegisterType*>(clone->RawData());
+  for (int i = 0; i < num_regs; ++i) {
+    EXPECT_EQ(register_values[i], clone_values[i]);
+    EXPECT_NE(&register_values[i], &clone_values[i]);
+  }
+}
+
+TEST_F(RegsTest, clone) {
+  std::vector<std::unique_ptr<Regs>> regs;
+  regs.emplace_back(new RegsArm());
+  regs.emplace_back(new RegsArm64());
+  regs.emplace_back(new RegsX86());
+  regs.emplace_back(new RegsX86_64());
+  regs.emplace_back(new RegsMips());
+  regs.emplace_back(new RegsMips64());
+
+  for (auto& r : regs) {
+    if (r->Is32Bit()) {
+      clone_test<uint32_t>(r.get());
+    } else {
+      clone_test<uint64_t>(r.get());
+    }
+  }
+}
+
+}  // namespace unwindstack
diff --git a/libunwindstack/tests/SymbolsTest.cpp b/libunwindstack/tests/SymbolsTest.cpp
index a0a21e6..b40a253 100644
--- a/libunwindstack/tests/SymbolsTest.cpp
+++ b/libunwindstack/tests/SymbolsTest.cpp
@@ -32,10 +32,13 @@
 #include <android-base/test_utils.h>
 #include <gtest/gtest.h>
 
-#include "Memory.h"
+#include <unwindstack/Memory.h>
+
 #include "MemoryFake.h"
 #include "Symbols.h"
 
+namespace unwindstack {
+
 template <typename TypeParam>
 class SymbolsTest : public ::testing::Test {
  protected:
@@ -67,18 +70,18 @@
 
   std::string name;
   uint64_t func_offset;
-  ASSERT_TRUE(symbols.GetName<TypeParam>(0x5000, 0, &this->memory_, &name, &func_offset));
+  ASSERT_TRUE(symbols.GetName<TypeParam>(0x5000, &this->memory_, &name, &func_offset));
   ASSERT_EQ("fake_function", name);
   ASSERT_EQ(0U, func_offset);
 
   name.clear();
-  ASSERT_TRUE(symbols.GetName<TypeParam>(0x500f, 0, &this->memory_, &name, &func_offset));
+  ASSERT_TRUE(symbols.GetName<TypeParam>(0x500f, &this->memory_, &name, &func_offset));
   ASSERT_EQ("fake_function", name);
   ASSERT_EQ(0xfU, func_offset);
 
   // Check one before and one after the function.
-  ASSERT_FALSE(symbols.GetName<TypeParam>(0x4fff, 0, &this->memory_, &name, &func_offset));
-  ASSERT_FALSE(symbols.GetName<TypeParam>(0x5010, 0, &this->memory_, &name, &func_offset));
+  ASSERT_FALSE(symbols.GetName<TypeParam>(0x4fff, &this->memory_, &name, &func_offset));
+  ASSERT_FALSE(symbols.GetName<TypeParam>(0x5010, &this->memory_, &name, &func_offset));
 }
 
 TYPED_TEST_P(SymbolsTest, no_symbol) {
@@ -95,7 +98,7 @@
   // First verify that we can get the name.
   std::string name;
   uint64_t func_offset;
-  ASSERT_TRUE(symbols.GetName<TypeParam>(0x5000, 0, &this->memory_, &name, &func_offset));
+  ASSERT_TRUE(symbols.GetName<TypeParam>(0x5000, &this->memory_, &name, &func_offset));
   ASSERT_EQ("fake_function", name);
   ASSERT_EQ(0U, func_offset);
 
@@ -104,7 +107,7 @@
   this->memory_.SetMemory(offset, &sym, sizeof(sym));
   // Clear the cache to force the symbol data to be re-read.
   symbols.ClearCache();
-  ASSERT_FALSE(symbols.GetName<TypeParam>(0x5000, 0, &this->memory_, &name, &func_offset));
+  ASSERT_FALSE(symbols.GetName<TypeParam>(0x5000, &this->memory_, &name, &func_offset));
 
   // Set the function back, and set the shndx to UNDEF.
   sym.st_info = STT_FUNC;
@@ -112,7 +115,7 @@
   this->memory_.SetMemory(offset, &sym, sizeof(sym));
   // Clear the cache to force the symbol data to be re-read.
   symbols.ClearCache();
-  ASSERT_FALSE(symbols.GetName<TypeParam>(0x5000, 0, &this->memory_, &name, &func_offset));
+  ASSERT_FALSE(symbols.GetName<TypeParam>(0x5000, &this->memory_, &name, &func_offset));
 }
 
 TYPED_TEST_P(SymbolsTest, multiple_entries) {
@@ -141,34 +144,34 @@
 
   std::string name;
   uint64_t func_offset;
-  ASSERT_TRUE(symbols.GetName<TypeParam>(0x3005, 0, &this->memory_, &name, &func_offset));
+  ASSERT_TRUE(symbols.GetName<TypeParam>(0x3005, &this->memory_, &name, &func_offset));
   ASSERT_EQ("function_two", name);
   ASSERT_EQ(1U, func_offset);
 
   name.clear();
-  ASSERT_TRUE(symbols.GetName<TypeParam>(0x5004, 0, &this->memory_, &name, &func_offset));
+  ASSERT_TRUE(symbols.GetName<TypeParam>(0x5004, &this->memory_, &name, &func_offset));
   ASSERT_EQ("function_one", name);
   ASSERT_EQ(4U, func_offset);
 
   name.clear();
-  ASSERT_TRUE(symbols.GetName<TypeParam>(0xa011, 0, &this->memory_, &name, &func_offset));
+  ASSERT_TRUE(symbols.GetName<TypeParam>(0xa011, &this->memory_, &name, &func_offset));
   ASSERT_EQ("function_three", name);
   ASSERT_EQ(1U, func_offset);
 
   // Reget some of the others to verify getting one function name doesn't
   // affect any of the next calls.
   name.clear();
-  ASSERT_TRUE(symbols.GetName<TypeParam>(0x5008, 0, &this->memory_, &name, &func_offset));
+  ASSERT_TRUE(symbols.GetName<TypeParam>(0x5008, &this->memory_, &name, &func_offset));
   ASSERT_EQ("function_one", name);
   ASSERT_EQ(8U, func_offset);
 
   name.clear();
-  ASSERT_TRUE(symbols.GetName<TypeParam>(0x3008, 0, &this->memory_, &name, &func_offset));
+  ASSERT_TRUE(symbols.GetName<TypeParam>(0x3008, &this->memory_, &name, &func_offset));
   ASSERT_EQ("function_two", name);
   ASSERT_EQ(4U, func_offset);
 
   name.clear();
-  ASSERT_TRUE(symbols.GetName<TypeParam>(0xa01a, 0, &this->memory_, &name, &func_offset));
+  ASSERT_TRUE(symbols.GetName<TypeParam>(0xa01a, &this->memory_, &name, &func_offset));
   ASSERT_EQ("function_three", name);
   ASSERT_EQ(0xaU, func_offset);
 }
@@ -200,47 +203,21 @@
 
   std::string name;
   uint64_t func_offset;
-  ASSERT_TRUE(symbols.GetName<TypeParam>(0x3005, 0, &this->memory_, &name, &func_offset));
+  ASSERT_TRUE(symbols.GetName<TypeParam>(0x3005, &this->memory_, &name, &func_offset));
   ASSERT_EQ("function_two", name);
   ASSERT_EQ(1U, func_offset);
 
   name.clear();
-  ASSERT_TRUE(symbols.GetName<TypeParam>(0x5004, 0, &this->memory_, &name, &func_offset));
+  ASSERT_TRUE(symbols.GetName<TypeParam>(0x5004, &this->memory_, &name, &func_offset));
   ASSERT_EQ("function_one", name);
   ASSERT_EQ(4U, func_offset);
 
   name.clear();
-  ASSERT_TRUE(symbols.GetName<TypeParam>(0xa011, 0, &this->memory_, &name, &func_offset));
+  ASSERT_TRUE(symbols.GetName<TypeParam>(0xa011, &this->memory_, &name, &func_offset));
   ASSERT_EQ("function_three", name);
   ASSERT_EQ(1U, func_offset);
 }
 
-TYPED_TEST_P(SymbolsTest, load_bias) {
-  Symbols symbols(0x1000, sizeof(TypeParam), sizeof(TypeParam), 0x2000, 0x100);
-
-  TypeParam sym;
-  this->InitSym(&sym, 0x5000, 0x10, 0x40);
-  uint64_t offset = 0x1000;
-  this->memory_.SetMemory(offset, &sym, sizeof(sym));
-
-  std::string fake_name("fake_function");
-  this->memory_.SetMemory(0x2040, fake_name.c_str(), fake_name.size() + 1);
-
-  // Set a non-zero load_bias that should be a valid function offset.
-  std::string name;
-  uint64_t func_offset;
-  ASSERT_TRUE(symbols.GetName<TypeParam>(0x5004, 0x1000, &this->memory_, &name, &func_offset));
-  ASSERT_EQ("fake_function", name);
-  ASSERT_EQ(4U, func_offset);
-
-  // Set a flag that should cause the load_bias to be ignored.
-  sym.st_shndx = SHN_ABS;
-  this->memory_.SetMemory(offset, &sym, sizeof(sym));
-  // Clear the cache to force the symbol data to be re-read.
-  symbols.ClearCache();
-  ASSERT_FALSE(symbols.GetName<TypeParam>(0x5004, 0x1000, &this->memory_, &name, &func_offset));
-}
-
 TYPED_TEST_P(SymbolsTest, symtab_value_out_of_bounds) {
   Symbols symbols_end_at_100(0x1000, sizeof(TypeParam) * 2, sizeof(TypeParam), 0x2000, 0x100);
   Symbols symbols_end_at_200(0x1000, sizeof(TypeParam) * 2, sizeof(TypeParam), 0x2000, 0x200);
@@ -262,18 +239,16 @@
   std::string name;
   uint64_t func_offset;
   // Verify that we can get the function name properly for both entries.
-  ASSERT_TRUE(symbols_end_at_200.GetName<TypeParam>(0x5000, 0, &this->memory_, &name, &func_offset));
+  ASSERT_TRUE(symbols_end_at_200.GetName<TypeParam>(0x5000, &this->memory_, &name, &func_offset));
   ASSERT_EQ("fake_function", name);
   ASSERT_EQ(0U, func_offset);
-  ASSERT_TRUE(symbols_end_at_200.GetName<TypeParam>(0x3000, 0, &this->memory_, &name, &func_offset));
+  ASSERT_TRUE(symbols_end_at_200.GetName<TypeParam>(0x3000, &this->memory_, &name, &func_offset));
   ASSERT_EQ("function", name);
   ASSERT_EQ(0U, func_offset);
 
   // Now use the symbol table that ends at 0x100.
-  ASSERT_FALSE(
-      symbols_end_at_100.GetName<TypeParam>(0x5000, 0, &this->memory_, &name, &func_offset));
-  ASSERT_FALSE(
-      symbols_end_at_100.GetName<TypeParam>(0x3000, 0, &this->memory_, &name, &func_offset));
+  ASSERT_FALSE(symbols_end_at_100.GetName<TypeParam>(0x5000, &this->memory_, &name, &func_offset));
+  ASSERT_FALSE(symbols_end_at_100.GetName<TypeParam>(0x3000, &this->memory_, &name, &func_offset));
 }
 
 // Verify the entire func table is cached.
@@ -299,9 +274,9 @@
   // Do call that should cache all of the entries (except the string data).
   std::string name;
   uint64_t func_offset;
-  ASSERT_FALSE(symbols.GetName<TypeParam>(0x6000, 0, &this->memory_, &name, &func_offset));
+  ASSERT_FALSE(symbols.GetName<TypeParam>(0x6000, &this->memory_, &name, &func_offset));
   this->memory_.Clear();
-  ASSERT_FALSE(symbols.GetName<TypeParam>(0x6000, 0, &this->memory_, &name, &func_offset));
+  ASSERT_FALSE(symbols.GetName<TypeParam>(0x6000, &this->memory_, &name, &func_offset));
 
   // Clear the memory and only put the symbol data string data in memory.
   this->memory_.Clear();
@@ -314,22 +289,84 @@
   fake_name = "third_entry";
   this->memory_.SetMemory(0xa300, fake_name.c_str(), fake_name.size() + 1);
 
-  ASSERT_TRUE(symbols.GetName<TypeParam>(0x5001, 0, &this->memory_, &name, &func_offset));
+  ASSERT_TRUE(symbols.GetName<TypeParam>(0x5001, &this->memory_, &name, &func_offset));
   ASSERT_EQ("first_entry", name);
   ASSERT_EQ(1U, func_offset);
 
-  ASSERT_TRUE(symbols.GetName<TypeParam>(0x2002, 0, &this->memory_, &name, &func_offset));
+  ASSERT_TRUE(symbols.GetName<TypeParam>(0x2002, &this->memory_, &name, &func_offset));
   ASSERT_EQ("second_entry", name);
   ASSERT_EQ(2U, func_offset);
 
-  ASSERT_TRUE(symbols.GetName<TypeParam>(0x1003, 0, &this->memory_, &name, &func_offset));
+  ASSERT_TRUE(symbols.GetName<TypeParam>(0x1003, &this->memory_, &name, &func_offset));
   ASSERT_EQ("third_entry", name);
   ASSERT_EQ(3U, func_offset);
 }
 
+TYPED_TEST_P(SymbolsTest, get_global) {
+  uint64_t start_offset = 0x1000;
+  uint64_t str_offset = 0xa000;
+  Symbols symbols(start_offset, 4 * sizeof(TypeParam), sizeof(TypeParam), str_offset, 0x1000);
+
+  TypeParam sym;
+  memset(&sym, 0, sizeof(sym));
+  sym.st_shndx = SHN_COMMON;
+  sym.st_info = STT_OBJECT | (STB_GLOBAL << 4);
+  sym.st_name = 0x100;
+  this->memory_.SetMemory(start_offset, &sym, sizeof(sym));
+  this->memory_.SetMemory(str_offset + 0x100, "global_0");
+
+  start_offset += sizeof(sym);
+  memset(&sym, 0, sizeof(sym));
+  sym.st_shndx = SHN_COMMON;
+  sym.st_info = STT_FUNC;
+  sym.st_name = 0x200;
+  sym.st_value = 0x10000;
+  sym.st_size = 0x100;
+  this->memory_.SetMemory(start_offset, &sym, sizeof(sym));
+  this->memory_.SetMemory(str_offset + 0x200, "function_0");
+
+  start_offset += sizeof(sym);
+  memset(&sym, 0, sizeof(sym));
+  sym.st_shndx = SHN_COMMON;
+  sym.st_info = STT_OBJECT | (STB_GLOBAL << 4);
+  sym.st_name = 0x300;
+  this->memory_.SetMemory(start_offset, &sym, sizeof(sym));
+  this->memory_.SetMemory(str_offset + 0x300, "global_1");
+
+  start_offset += sizeof(sym);
+  memset(&sym, 0, sizeof(sym));
+  sym.st_shndx = SHN_COMMON;
+  sym.st_info = STT_FUNC;
+  sym.st_name = 0x400;
+  sym.st_value = 0x12000;
+  sym.st_size = 0x100;
+  this->memory_.SetMemory(start_offset, &sym, sizeof(sym));
+  this->memory_.SetMemory(str_offset + 0x400, "function_1");
+
+  uint64_t offset;
+  EXPECT_TRUE(symbols.GetGlobal<TypeParam>(&this->memory_, "global_0", &offset));
+  EXPECT_TRUE(symbols.GetGlobal<TypeParam>(&this->memory_, "global_1", &offset));
+  EXPECT_TRUE(symbols.GetGlobal<TypeParam>(&this->memory_, "global_0", &offset));
+  EXPECT_TRUE(symbols.GetGlobal<TypeParam>(&this->memory_, "global_1", &offset));
+
+  EXPECT_FALSE(symbols.GetGlobal<TypeParam>(&this->memory_, "function_0", &offset));
+  EXPECT_FALSE(symbols.GetGlobal<TypeParam>(&this->memory_, "function_1", &offset));
+
+  std::string name;
+  EXPECT_TRUE(symbols.GetName<TypeParam>(0x10002, &this->memory_, &name, &offset));
+  EXPECT_EQ("function_0", name);
+  EXPECT_EQ(2U, offset);
+
+  EXPECT_TRUE(symbols.GetName<TypeParam>(0x12004, &this->memory_, &name, &offset));
+  EXPECT_EQ("function_1", name);
+  EXPECT_EQ(4U, offset);
+}
+
 REGISTER_TYPED_TEST_CASE_P(SymbolsTest, function_bounds_check, no_symbol, multiple_entries,
-                           multiple_entries_nonstandard_size, load_bias, symtab_value_out_of_bounds,
-                           symtab_read_cached);
+                           multiple_entries_nonstandard_size, symtab_value_out_of_bounds,
+                           symtab_read_cached, get_global);
 
 typedef ::testing::Types<Elf32_Sym, Elf64_Sym> SymbolsTestTypes;
 INSTANTIATE_TYPED_TEST_CASE_P(, SymbolsTest, SymbolsTestTypes);
+
+}  // namespace unwindstack
diff --git a/libunwindstack/tests/TestLocal.cpp b/libunwindstack/tests/TestLocal.cpp
new file mode 100644
index 0000000..fa0baff
--- /dev/null
+++ b/libunwindstack/tests/TestLocal.cpp
@@ -0,0 +1,39 @@
+/*
+ * 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 <unwindstack/LocalUnwinder.h>
+
+#include <vector>
+
+extern "C" void TestlibLevel4(void* unwinder_data, void* frame_data) {
+  unwindstack::LocalUnwinder* unwinder =
+      reinterpret_cast<unwindstack::LocalUnwinder*>(unwinder_data);
+  std::vector<unwindstack::LocalFrameData>* frame_info =
+      reinterpret_cast<std::vector<unwindstack::LocalFrameData>*>(frame_data);
+  unwinder->Unwind(frame_info, 256);
+}
+
+extern "C" void TestlibLevel3(void* unwinder_data, void* frame_data) {
+  TestlibLevel4(unwinder_data, frame_data);
+}
+
+extern "C" void TestlibLevel2(void* unwinder_data, void* frame_data) {
+  TestlibLevel3(unwinder_data, frame_data);
+}
+
+extern "C" void TestlibLevel1(void* unwinder_data, void* frame_data) {
+  TestlibLevel2(unwinder_data, frame_data);
+}
diff --git a/libunwindstack/tests/TestUtils.h b/libunwindstack/tests/TestUtils.h
new file mode 100644
index 0000000..8c31aa6
--- /dev/null
+++ b/libunwindstack/tests/TestUtils.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2017 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 _LIBUNWINDSTACK_TESTS_TEST_UTILS_H
+#define _LIBUNWINDSTACK_TESTS_TEST_UTILS_H
+
+#include <signal.h>
+#include <sys/ptrace.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+namespace unwindstack {
+
+class TestScopedPidReaper {
+ public:
+  TestScopedPidReaper(pid_t pid) : pid_(pid) {}
+  ~TestScopedPidReaper() {
+    kill(pid_, SIGKILL);
+    waitpid(pid_, nullptr, 0);
+  }
+
+ private:
+  pid_t pid_;
+};
+
+inline bool TestQuiescePid(pid_t pid) {
+  siginfo_t si;
+  bool ready = false;
+  // Wait for up to 5 seconds.
+  for (size_t i = 0; i < 5000; i++) {
+    if (ptrace(PTRACE_GETSIGINFO, pid, 0, &si) == 0) {
+      ready = true;
+      break;
+    }
+    usleep(1000);
+  }
+  return ready;
+}
+
+}  // namespace unwindstack
+
+#endif  // _LIBUNWINDSTACK_TESTS_TEST_UTILS_H
diff --git a/libunwindstack/tests/UnwindOfflineTest.cpp b/libunwindstack/tests/UnwindOfflineTest.cpp
new file mode 100644
index 0000000..a65c077
--- /dev/null
+++ b/libunwindstack/tests/UnwindOfflineTest.cpp
@@ -0,0 +1,1242 @@
+/*
+ * Copyright (C) 2017 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 <inttypes.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/mman.h>
+#include <unistd.h>
+
+#include <gtest/gtest.h>
+
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+#include <android-base/file.h>
+
+#include <unwindstack/JitDebug.h>
+#include <unwindstack/MachineArm.h>
+#include <unwindstack/MachineArm64.h>
+#include <unwindstack/MachineX86.h>
+#include <unwindstack/MachineX86_64.h>
+#include <unwindstack/Maps.h>
+#include <unwindstack/Memory.h>
+#include <unwindstack/RegsArm.h>
+#include <unwindstack/RegsArm64.h>
+#include <unwindstack/RegsX86.h>
+#include <unwindstack/RegsX86_64.h>
+#include <unwindstack/Unwinder.h>
+
+#include "ElfTestUtils.h"
+
+namespace unwindstack {
+
+static void AddMemory(std::string file_name, MemoryOfflineParts* parts) {
+  MemoryOffline* memory = new MemoryOffline;
+  ASSERT_TRUE(memory->Init(file_name.c_str(), 0));
+  parts->Add(memory);
+}
+
+class UnwindOfflineTest : public ::testing::Test {
+ protected:
+  void TearDown() override {
+    if (cwd_ != nullptr) {
+      ASSERT_EQ(0, chdir(cwd_));
+    }
+    free(cwd_);
+  }
+
+  void Init(const char* file_dir, ArchEnum arch) {
+    dir_ = TestGetFileDirectory() + "offline/" + file_dir;
+
+    std::string data;
+    ASSERT_TRUE(android::base::ReadFileToString((dir_ + "maps.txt"), &data));
+
+    maps_.reset(new BufferMaps(data.c_str()));
+    ASSERT_TRUE(maps_->Parse());
+
+    std::string stack_name(dir_ + "stack.data");
+    struct stat st;
+    if (stat(stack_name.c_str(), &st) == 0 && S_ISREG(st.st_mode)) {
+      std::unique_ptr<MemoryOffline> stack_memory(new MemoryOffline);
+      ASSERT_TRUE(stack_memory->Init((dir_ + "stack.data").c_str(), 0));
+      process_memory_.reset(stack_memory.release());
+    } else {
+      std::unique_ptr<MemoryOfflineParts> stack_memory(new MemoryOfflineParts);
+      for (size_t i = 0;; i++) {
+        stack_name = dir_ + "stack" + std::to_string(i) + ".data";
+        if (stat(stack_name.c_str(), &st) == -1 || !S_ISREG(st.st_mode)) {
+          ASSERT_TRUE(i != 0) << "No stack data files found.";
+          break;
+        }
+        AddMemory(stack_name, stack_memory.get());
+      }
+      process_memory_.reset(stack_memory.release());
+    }
+
+    switch (arch) {
+      case ARCH_ARM: {
+        RegsArm* regs = new RegsArm;
+        regs_.reset(regs);
+        ReadRegs<uint32_t>(regs, arm_regs_);
+        break;
+      }
+      case ARCH_ARM64: {
+        RegsArm64* regs = new RegsArm64;
+        regs_.reset(regs);
+        ReadRegs<uint64_t>(regs, arm64_regs_);
+        break;
+      }
+      case ARCH_X86: {
+        RegsX86* regs = new RegsX86;
+        regs_.reset(regs);
+        ReadRegs<uint32_t>(regs, x86_regs_);
+        break;
+      }
+      case ARCH_X86_64: {
+        RegsX86_64* regs = new RegsX86_64;
+        regs_.reset(regs);
+        ReadRegs<uint64_t>(regs, x86_64_regs_);
+        break;
+      }
+      default:
+        ASSERT_TRUE(false) << "Unknown arch " << std::to_string(arch);
+    }
+    cwd_ = getcwd(nullptr, 0);
+    // Make dir_ an absolute directory.
+    if (dir_.empty() || dir_[0] != '/') {
+      dir_ = std::string(cwd_) + '/' + dir_;
+    }
+    ASSERT_EQ(0, chdir(dir_.c_str()));
+  }
+
+  template <typename AddressType>
+  void ReadRegs(RegsImpl<AddressType>* regs,
+                const std::unordered_map<std::string, uint32_t>& name_to_reg) {
+    FILE* fp = fopen((dir_ + "regs.txt").c_str(), "r");
+    ASSERT_TRUE(fp != nullptr);
+    while (!feof(fp)) {
+      uint64_t value;
+      char reg_name[100];
+      ASSERT_EQ(2, fscanf(fp, "%s %" SCNx64 "\n", reg_name, &value));
+      std::string name(reg_name);
+      if (!name.empty()) {
+        // Remove the : from the end.
+        name.resize(name.size() - 1);
+      }
+      auto entry = name_to_reg.find(name);
+      ASSERT_TRUE(entry != name_to_reg.end()) << "Unknown register named " << name;
+      (*regs)[entry->second] = value;
+    }
+    fclose(fp);
+  }
+
+  static std::unordered_map<std::string, uint32_t> arm_regs_;
+  static std::unordered_map<std::string, uint32_t> arm64_regs_;
+  static std::unordered_map<std::string, uint32_t> x86_regs_;
+  static std::unordered_map<std::string, uint32_t> x86_64_regs_;
+
+  char* cwd_ = nullptr;
+  std::string dir_;
+  std::unique_ptr<Regs> regs_;
+  std::unique_ptr<Maps> maps_;
+  std::shared_ptr<Memory> process_memory_;
+};
+
+std::unordered_map<std::string, uint32_t> UnwindOfflineTest::arm_regs_ = {
+    {"r0", ARM_REG_R0},  {"r1", ARM_REG_R1}, {"r2", ARM_REG_R2},   {"r3", ARM_REG_R3},
+    {"r4", ARM_REG_R4},  {"r5", ARM_REG_R5}, {"r6", ARM_REG_R6},   {"r7", ARM_REG_R7},
+    {"r8", ARM_REG_R8},  {"r9", ARM_REG_R9}, {"r10", ARM_REG_R10}, {"r11", ARM_REG_R11},
+    {"ip", ARM_REG_R12}, {"sp", ARM_REG_SP}, {"lr", ARM_REG_LR},   {"pc", ARM_REG_PC},
+};
+
+std::unordered_map<std::string, uint32_t> UnwindOfflineTest::arm64_regs_ = {
+    {"x0", ARM64_REG_R0},   {"x1", ARM64_REG_R1},   {"x2", ARM64_REG_R2},   {"x3", ARM64_REG_R3},
+    {"x4", ARM64_REG_R4},   {"x5", ARM64_REG_R5},   {"x6", ARM64_REG_R6},   {"x7", ARM64_REG_R7},
+    {"x8", ARM64_REG_R8},   {"x9", ARM64_REG_R9},   {"x10", ARM64_REG_R10}, {"x11", ARM64_REG_R11},
+    {"x12", ARM64_REG_R12}, {"x13", ARM64_REG_R13}, {"x14", ARM64_REG_R14}, {"x15", ARM64_REG_R15},
+    {"x16", ARM64_REG_R16}, {"x17", ARM64_REG_R17}, {"x18", ARM64_REG_R18}, {"x19", ARM64_REG_R19},
+    {"x20", ARM64_REG_R20}, {"x21", ARM64_REG_R21}, {"x22", ARM64_REG_R22}, {"x23", ARM64_REG_R23},
+    {"x24", ARM64_REG_R24}, {"x25", ARM64_REG_R25}, {"x26", ARM64_REG_R26}, {"x27", ARM64_REG_R27},
+    {"x28", ARM64_REG_R28}, {"x29", ARM64_REG_R29}, {"sp", ARM64_REG_SP},   {"lr", ARM64_REG_LR},
+    {"pc", ARM64_REG_PC},
+};
+
+std::unordered_map<std::string, uint32_t> UnwindOfflineTest::x86_regs_ = {
+    {"eax", X86_REG_EAX}, {"ebx", X86_REG_EBX}, {"ecx", X86_REG_ECX},
+    {"edx", X86_REG_EDX}, {"ebp", X86_REG_EBP}, {"edi", X86_REG_EDI},
+    {"esi", X86_REG_ESI}, {"esp", X86_REG_ESP}, {"eip", X86_REG_EIP},
+};
+
+std::unordered_map<std::string, uint32_t> UnwindOfflineTest::x86_64_regs_ = {
+    {"rax", X86_64_REG_RAX}, {"rbx", X86_64_REG_RBX}, {"rcx", X86_64_REG_RCX},
+    {"rdx", X86_64_REG_RDX}, {"r8", X86_64_REG_R8},   {"r9", X86_64_REG_R9},
+    {"r10", X86_64_REG_R10}, {"r11", X86_64_REG_R11}, {"r12", X86_64_REG_R12},
+    {"r13", X86_64_REG_R13}, {"r14", X86_64_REG_R14}, {"r15", X86_64_REG_R15},
+    {"rdi", X86_64_REG_RDI}, {"rsi", X86_64_REG_RSI}, {"rbp", X86_64_REG_RBP},
+    {"rsp", X86_64_REG_RSP}, {"rip", X86_64_REG_RIP},
+};
+
+static std::string DumpFrames(Unwinder& unwinder) {
+  std::string str;
+  for (size_t i = 0; i < unwinder.NumFrames(); i++) {
+    str += unwinder.FormatFrame(i) + "\n";
+  }
+  return str;
+}
+
+TEST_F(UnwindOfflineTest, pc_straddle_arm) {
+  ASSERT_NO_FATAL_FAILURE(Init("straddle_arm/", ARCH_ARM));
+
+  Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
+  unwinder.Unwind();
+
+  std::string frame_info(DumpFrames(unwinder));
+  ASSERT_EQ(4U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
+  EXPECT_EQ(
+      "  #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",
+      frame_info);
+  EXPECT_EQ(0xf31ea9f8U, unwinder.frames()[0].pc);
+  EXPECT_EQ(0xe9c866f8U, unwinder.frames()[0].sp);
+  EXPECT_EQ(0xf2da0a1bU, unwinder.frames()[1].pc);
+  EXPECT_EQ(0xe9c86728U, unwinder.frames()[1].sp);
+  EXPECT_EQ(0xf2da1441U, unwinder.frames()[2].pc);
+  EXPECT_EQ(0xe9c86730U, unwinder.frames()[2].sp);
+  EXPECT_EQ(0xf3367147U, unwinder.frames()[3].pc);
+  EXPECT_EQ(0xe9c86778U, unwinder.frames()[3].sp);
+}
+
+TEST_F(UnwindOfflineTest, pc_in_gnu_debugdata_arm) {
+  ASSERT_NO_FATAL_FAILURE(Init("gnu_debugdata_arm/", ARCH_ARM));
+
+  Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
+  unwinder.Unwind();
+
+  std::string frame_info(DumpFrames(unwinder));
+  ASSERT_EQ(2U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
+  EXPECT_EQ(
+      "  #00 pc 0006dc49  libandroid_runtime.so "
+      "(_ZN7android14AndroidRuntime15javaThreadShellEPv+80)\n"
+      "  #01 pc 0006dce5  libandroid_runtime.so "
+      "(_ZN7android14AndroidRuntime19javaCreateThreadEtcEPFiPvES1_PKcijPS1_)\n",
+      frame_info);
+  EXPECT_EQ(0xf1f6dc49U, unwinder.frames()[0].pc);
+  EXPECT_EQ(0xd8fe6930U, unwinder.frames()[0].sp);
+  EXPECT_EQ(0xf1f6dce5U, unwinder.frames()[1].pc);
+  EXPECT_EQ(0xd8fe6958U, unwinder.frames()[1].sp);
+}
+
+TEST_F(UnwindOfflineTest, pc_straddle_arm64) {
+  ASSERT_NO_FATAL_FAILURE(Init("straddle_arm64/", ARCH_ARM64));
+
+  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 0000000000429fd8  libunwindstack_test (SignalInnerFunction+24)\n"
+      "  #01 pc 000000000042a078  libunwindstack_test (SignalMiddleFunction+8)\n"
+      "  #02 pc 000000000042a08c  libunwindstack_test (SignalOuterFunction+8)\n"
+      "  #03 pc 000000000042d8fc  libunwindstack_test "
+      "(_ZN11unwindstackL19RemoteThroughSignalEij+20)\n"
+      "  #04 pc 000000000042d8d8  libunwindstack_test "
+      "(_ZN11unwindstack37UnwindTest_remote_through_signal_Test8TestBodyEv+32)\n"
+      "  #05 pc 0000000000455d70  libunwindstack_test (_ZN7testing4Test3RunEv+392)\n",
+      frame_info);
+  EXPECT_EQ(0x64d09d4fd8U, unwinder.frames()[0].pc);
+  EXPECT_EQ(0x7fe0d84040U, unwinder.frames()[0].sp);
+  EXPECT_EQ(0x64d09d5078U, unwinder.frames()[1].pc);
+  EXPECT_EQ(0x7fe0d84070U, unwinder.frames()[1].sp);
+  EXPECT_EQ(0x64d09d508cU, unwinder.frames()[2].pc);
+  EXPECT_EQ(0x7fe0d84080U, unwinder.frames()[2].sp);
+  EXPECT_EQ(0x64d09d88fcU, unwinder.frames()[3].pc);
+  EXPECT_EQ(0x7fe0d84090U, unwinder.frames()[3].sp);
+  EXPECT_EQ(0x64d09d88d8U, unwinder.frames()[4].pc);
+  EXPECT_EQ(0x7fe0d840f0U, unwinder.frames()[4].sp);
+  EXPECT_EQ(0x64d0a00d70U, unwinder.frames()[5].pc);
+  EXPECT_EQ(0x7fe0d84110U, unwinder.frames()[5].sp);
+}
+
+TEST_F(UnwindOfflineTest, jit_debug_x86) {
+  ASSERT_NO_FATAL_FAILURE(Init("jit_debug_x86/", ARCH_X86));
+
+  MemoryOfflineParts* memory = new MemoryOfflineParts;
+  AddMemory(dir_ + "descriptor.data", memory);
+  AddMemory(dir_ + "stack.data", memory);
+  for (size_t i = 0; i < 7; 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(69U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
+  EXPECT_EQ(
+      "  #00 pc 00068fb8  libarttestd.so (_ZN3artL13CauseSegfaultEv+72)\n"
+      "  #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 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"
+      "  #06 pc 0039cf0d  libartd.so "
+      "(_ZN3art11interpreter34ArtInterpreterToCompiledCodeBridgeEPNS_6ThreadEPNS_9ArtMethodEPNS_"
+      "11ShadowFrameEtPNS_6JValueE+653)\n"
+      "  #07 pc 00392552  libartd.so "
+      "(_ZN3art11interpreterL7ExecuteEPNS_6ThreadERKNS_20CodeItemDataAccessorERNS_11ShadowFrameENS_"
+      "6JValueEb+354)\n"
+      "  #08 pc 0039399a  libartd.so "
+      "(_ZN3art11interpreter30EnterInterpreterFromEntryPointEPNS_6ThreadERKNS_"
+      "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 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"
+      "  #14 pc 0039cf0d  libartd.so "
+      "(_ZN3art11interpreter34ArtInterpreterToCompiledCodeBridgeEPNS_6ThreadEPNS_9ArtMethodEPNS_"
+      "11ShadowFrameEtPNS_6JValueE+653)\n"
+      "  #15 pc 00392552  libartd.so "
+      "(_ZN3art11interpreterL7ExecuteEPNS_6ThreadERKNS_20CodeItemDataAccessorERNS_11ShadowFrameENS_"
+      "6JValueEb+354)\n"
+      "  #16 pc 0039399a  libartd.so "
+      "(_ZN3art11interpreter30EnterInterpreterFromEntryPointEPNS_6ThreadERKNS_"
+      "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 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"
+      "  #22 pc 0039cf0d  libartd.so "
+      "(_ZN3art11interpreter34ArtInterpreterToCompiledCodeBridgeEPNS_6ThreadEPNS_9ArtMethodEPNS_"
+      "11ShadowFrameEtPNS_6JValueE+653)\n"
+      "  #23 pc 00392552  libartd.so "
+      "(_ZN3art11interpreterL7ExecuteEPNS_6ThreadERKNS_20CodeItemDataAccessorERNS_11ShadowFrameENS_"
+      "6JValueEb+354)\n"
+      "  #24 pc 0039399a  libartd.so "
+      "(_ZN3art11interpreter30EnterInterpreterFromEntryPointEPNS_6ThreadERKNS_"
+      "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 0000fbdb  anonymous:ee74c000 (int "
+      "java.util.Arrays.binarySearch0(java.lang.Object[], int, int, java.lang.Object, "
+      "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"
+      "  #30 pc 0039cf0d  libartd.so "
+      "(_ZN3art11interpreter34ArtInterpreterToCompiledCodeBridgeEPNS_6ThreadEPNS_9ArtMethodEPNS_"
+      "11ShadowFrameEtPNS_6JValueE+653)\n"
+      "  #31 pc 00392552  libartd.so "
+      "(_ZN3art11interpreterL7ExecuteEPNS_6ThreadERKNS_20CodeItemDataAccessorERNS_11ShadowFrameENS_"
+      "6JValueEb+354)\n"
+      "  #32 pc 0039399a  libartd.so "
+      "(_ZN3art11interpreter30EnterInterpreterFromEntryPointEPNS_6ThreadERKNS_"
+      "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 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"
+      "  #38 pc 0039cf0d  libartd.so "
+      "(_ZN3art11interpreter34ArtInterpreterToCompiledCodeBridgeEPNS_6ThreadEPNS_9ArtMethodEPNS_"
+      "11ShadowFrameEtPNS_6JValueE+653)\n"
+      "  #39 pc 00392552  libartd.so "
+      "(_ZN3art11interpreterL7ExecuteEPNS_6ThreadERKNS_20CodeItemDataAccessorERNS_11ShadowFrameENS_"
+      "6JValueEb+354)\n"
+      "  #40 pc 0039399a  libartd.so "
+      "(_ZN3art11interpreter30EnterInterpreterFromEntryPointEPNS_6ThreadERKNS_"
+      "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 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"
+      "  #46 pc 0039cf0d  libartd.so "
+      "(_ZN3art11interpreter34ArtInterpreterToCompiledCodeBridgeEPNS_6ThreadEPNS_9ArtMethodEPNS_"
+      "11ShadowFrameEtPNS_6JValueE+653)\n"
+      "  #47 pc 00392552  libartd.so "
+      "(_ZN3art11interpreterL7ExecuteEPNS_6ThreadERKNS_20CodeItemDataAccessorERNS_11ShadowFrameENS_"
+      "6JValueEb+354)\n"
+      "  #48 pc 0039399a  libartd.so "
+      "(_ZN3art11interpreter30EnterInterpreterFromEntryPointEPNS_6ThreadERKNS_"
+      "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 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"
+      "  #54 pc 0039cf0d  libartd.so "
+      "(_ZN3art11interpreter34ArtInterpreterToCompiledCodeBridgeEPNS_6ThreadEPNS_9ArtMethodEPNS_"
+      "11ShadowFrameEtPNS_6JValueE+653)\n"
+      "  #55 pc 00392552  libartd.so "
+      "(_ZN3art11interpreterL7ExecuteEPNS_6ThreadERKNS_20CodeItemDataAccessorERNS_11ShadowFrameENS_"
+      "6JValueEb+354)\n"
+      "  #56 pc 0039399a  libartd.so "
+      "(_ZN3art11interpreter30EnterInterpreterFromEntryPointEPNS_6ThreadERKNS_"
+      "20CodeItemDataAccessorEPNS_11ShadowFrameE+234)\n"
+      "  #57 pc 00684362  libartd.so (artQuickToInterpreterBridge+1058)\n"
+      "  #58 pc 006b35bd  libartd.so (art_quick_to_interpreter_bridge+77)\n"
+      "  #59 pc 006ad6a2  libartd.so (art_quick_invoke_static_stub+418)\n"
+      "  #60 pc 00146acb  libartd.so "
+      "(_ZN3art9ArtMethod6InvokeEPNS_6ThreadEPjjPNS_6JValueEPKc+907)\n"
+      "  #61 pc 005aac95  libartd.so "
+      "(_ZN3artL18InvokeWithArgArrayERKNS_33ScopedObjectAccessAlreadyRunnableEPNS_9ArtMethodEPNS_"
+      "8ArgArrayEPNS_6JValueEPKc+85)\n"
+      "  #62 pc 005aab5a  libartd.so "
+      "(_ZN3art17InvokeWithVarArgsERKNS_33ScopedObjectAccessAlreadyRunnableEP8_jobjectP10_"
+      "jmethodIDPc+362)\n"
+      "  #63 pc 0048a3dd  libartd.so "
+      "(_ZN3art3JNI21CallStaticVoidMethodVEP7_JNIEnvP7_jclassP10_jmethodIDPc+125)\n"
+      "  #64 pc 0018448c  libartd.so "
+      "(_ZN3art8CheckJNI11CallMethodVEPKcP7_JNIEnvP8_jobjectP7_jclassP10_jmethodIDPcNS_"
+      "9Primitive4TypeENS_10InvokeTypeE+1964)\n"
+      "  #65 pc 0017cf06  libartd.so "
+      "(_ZN3art8CheckJNI21CallStaticVoidMethodVEP7_JNIEnvP7_jclassP10_jmethodIDPc+70)\n"
+      "  #66 pc 00001d8c  dalvikvm32 "
+      "(_ZN7_JNIEnv20CallStaticVoidMethodEP7_jclassP10_jmethodIDz+60)\n"
+      "  #67 pc 00001a80  dalvikvm32 (main+1312)\n"
+      "  #68 pc 00018275  libc.so\n",
+      frame_info);
+  EXPECT_EQ(0xeb89bfb8U, unwinder.frames()[0].pc);
+  EXPECT_EQ(0xffeb5280U, unwinder.frames()[0].sp);
+  EXPECT_EQ(0xeb89af00U, unwinder.frames()[1].pc);
+  EXPECT_EQ(0xffeb52a0U, unwinder.frames()[1].sp);
+  EXPECT_EQ(0xec6061a8U, unwinder.frames()[2].pc);
+  EXPECT_EQ(0xffeb5ce0U, unwinder.frames()[2].sp);
+  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);
+  EXPECT_EQ(0xf6d27ab5U, unwinder.frames()[5].pc);
+  EXPECT_EQ(0xffeb5d80U, unwinder.frames()[5].sp);
+  EXPECT_EQ(0xf6f7df0dU, unwinder.frames()[6].pc);
+  EXPECT_EQ(0xffeb5e20U, unwinder.frames()[6].sp);
+  EXPECT_EQ(0xf6f73552U, unwinder.frames()[7].pc);
+  EXPECT_EQ(0xffeb5ec0U, unwinder.frames()[7].sp);
+  EXPECT_EQ(0xf6f7499aU, unwinder.frames()[8].pc);
+  EXPECT_EQ(0xffeb5f40U, unwinder.frames()[8].sp);
+  EXPECT_EQ(0xf7265362U, unwinder.frames()[9].pc);
+  EXPECT_EQ(0xffeb5fb0U, unwinder.frames()[9].sp);
+  EXPECT_EQ(0xf72945bdU, unwinder.frames()[10].pc);
+  EXPECT_EQ(0xffeb6110U, unwinder.frames()[10].sp);
+  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);
+  EXPECT_EQ(0xf6d27ab5U, unwinder.frames()[13].pc);
+  EXPECT_EQ(0xffeb61b0U, unwinder.frames()[13].sp);
+  EXPECT_EQ(0xf6f7df0dU, unwinder.frames()[14].pc);
+  EXPECT_EQ(0xffeb6250U, unwinder.frames()[14].sp);
+  EXPECT_EQ(0xf6f73552U, unwinder.frames()[15].pc);
+  EXPECT_EQ(0xffeb62f0U, unwinder.frames()[15].sp);
+  EXPECT_EQ(0xf6f7499aU, unwinder.frames()[16].pc);
+  EXPECT_EQ(0xffeb6370U, unwinder.frames()[16].sp);
+  EXPECT_EQ(0xf7265362U, unwinder.frames()[17].pc);
+  EXPECT_EQ(0xffeb63e0U, unwinder.frames()[17].sp);
+  EXPECT_EQ(0xf72945bdU, unwinder.frames()[18].pc);
+  EXPECT_EQ(0xffeb6530U, unwinder.frames()[18].sp);
+  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);
+  EXPECT_EQ(0xf6d27ab5U, unwinder.frames()[21].pc);
+  EXPECT_EQ(0xffeb65e0U, unwinder.frames()[21].sp);
+  EXPECT_EQ(0xf6f7df0dU, unwinder.frames()[22].pc);
+  EXPECT_EQ(0xffeb6680U, unwinder.frames()[22].sp);
+  EXPECT_EQ(0xf6f73552U, unwinder.frames()[23].pc);
+  EXPECT_EQ(0xffeb6720U, unwinder.frames()[23].sp);
+  EXPECT_EQ(0xf6f7499aU, unwinder.frames()[24].pc);
+  EXPECT_EQ(0xffeb67a0U, unwinder.frames()[24].sp);
+  EXPECT_EQ(0xf7265362U, unwinder.frames()[25].pc);
+  EXPECT_EQ(0xffeb6810U, unwinder.frames()[25].sp);
+  EXPECT_EQ(0xf72945bdU, unwinder.frames()[26].pc);
+  EXPECT_EQ(0xffeb6960U, unwinder.frames()[26].sp);
+  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);
+  EXPECT_EQ(0xf6d27acbU, unwinder.frames()[29].pc);
+  EXPECT_EQ(0xffeb6a20U, unwinder.frames()[29].sp);
+  EXPECT_EQ(0xf6f7df0dU, unwinder.frames()[30].pc);
+  EXPECT_EQ(0xffeb6ac0U, unwinder.frames()[30].sp);
+  EXPECT_EQ(0xf6f73552U, unwinder.frames()[31].pc);
+  EXPECT_EQ(0xffeb6b60U, unwinder.frames()[31].sp);
+  EXPECT_EQ(0xf6f7499aU, unwinder.frames()[32].pc);
+  EXPECT_EQ(0xffeb6be0U, unwinder.frames()[32].sp);
+  EXPECT_EQ(0xf7265362U, unwinder.frames()[33].pc);
+  EXPECT_EQ(0xffeb6c50U, unwinder.frames()[33].sp);
+  EXPECT_EQ(0xf72945bdU, unwinder.frames()[34].pc);
+  EXPECT_EQ(0xffeb6dd0U, unwinder.frames()[34].sp);
+  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);
+  EXPECT_EQ(0xf6d27ab5U, unwinder.frames()[37].pc);
+  EXPECT_EQ(0xffeb6e70U, unwinder.frames()[37].sp);
+  EXPECT_EQ(0xf6f7df0dU, unwinder.frames()[38].pc);
+  EXPECT_EQ(0xffeb6f10U, unwinder.frames()[38].sp);
+  EXPECT_EQ(0xf6f73552U, unwinder.frames()[39].pc);
+  EXPECT_EQ(0xffeb6fb0U, unwinder.frames()[39].sp);
+  EXPECT_EQ(0xf6f7499aU, unwinder.frames()[40].pc);
+  EXPECT_EQ(0xffeb7030U, unwinder.frames()[40].sp);
+  EXPECT_EQ(0xf7265362U, unwinder.frames()[41].pc);
+  EXPECT_EQ(0xffeb70a0U, unwinder.frames()[41].sp);
+  EXPECT_EQ(0xf72945bdU, unwinder.frames()[42].pc);
+  EXPECT_EQ(0xffeb71f0U, unwinder.frames()[42].sp);
+  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);
+  EXPECT_EQ(0xf6d27ab5U, unwinder.frames()[45].pc);
+  EXPECT_EQ(0xffeb72c0U, unwinder.frames()[45].sp);
+  EXPECT_EQ(0xf6f7df0dU, unwinder.frames()[46].pc);
+  EXPECT_EQ(0xffeb7360U, unwinder.frames()[46].sp);
+  EXPECT_EQ(0xf6f73552U, unwinder.frames()[47].pc);
+  EXPECT_EQ(0xffeb7400U, unwinder.frames()[47].sp);
+  EXPECT_EQ(0xf6f7499aU, unwinder.frames()[48].pc);
+  EXPECT_EQ(0xffeb7480U, unwinder.frames()[48].sp);
+  EXPECT_EQ(0xf7265362U, unwinder.frames()[49].pc);
+  EXPECT_EQ(0xffeb74f0U, unwinder.frames()[49].sp);
+  EXPECT_EQ(0xf72945bdU, unwinder.frames()[50].pc);
+  EXPECT_EQ(0xffeb7680U, unwinder.frames()[50].sp);
+  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);
+  EXPECT_EQ(0xf6d27acbU, unwinder.frames()[53].pc);
+  EXPECT_EQ(0xffeb7710U, unwinder.frames()[53].sp);
+  EXPECT_EQ(0xf6f7df0dU, unwinder.frames()[54].pc);
+  EXPECT_EQ(0xffeb77b0U, unwinder.frames()[54].sp);
+  EXPECT_EQ(0xf6f73552U, unwinder.frames()[55].pc);
+  EXPECT_EQ(0xffeb7850U, unwinder.frames()[55].sp);
+  EXPECT_EQ(0xf6f7499aU, unwinder.frames()[56].pc);
+  EXPECT_EQ(0xffeb78d0U, unwinder.frames()[56].sp);
+  EXPECT_EQ(0xf7265362U, unwinder.frames()[57].pc);
+  EXPECT_EQ(0xffeb7940U, unwinder.frames()[57].sp);
+  EXPECT_EQ(0xf72945bdU, unwinder.frames()[58].pc);
+  EXPECT_EQ(0xffeb7a80U, unwinder.frames()[58].sp);
+  EXPECT_EQ(0xf728e6a2U, unwinder.frames()[59].pc);
+  EXPECT_EQ(0xffeb7ad0U, unwinder.frames()[59].sp);
+  EXPECT_EQ(0xf6d27acbU, unwinder.frames()[60].pc);
+  EXPECT_EQ(0xffeb7af0U, unwinder.frames()[60].sp);
+  EXPECT_EQ(0xf718bc95U, unwinder.frames()[61].pc);
+  EXPECT_EQ(0xffeb7b90U, unwinder.frames()[61].sp);
+  EXPECT_EQ(0xf718bb5aU, unwinder.frames()[62].pc);
+  EXPECT_EQ(0xffeb7c50U, unwinder.frames()[62].sp);
+  EXPECT_EQ(0xf706b3ddU, unwinder.frames()[63].pc);
+  EXPECT_EQ(0xffeb7d10U, unwinder.frames()[63].sp);
+  EXPECT_EQ(0xf6d6548cU, unwinder.frames()[64].pc);
+  EXPECT_EQ(0xffeb7d70U, unwinder.frames()[64].sp);
+  EXPECT_EQ(0xf6d5df06U, unwinder.frames()[65].pc);
+  EXPECT_EQ(0xffeb7df0U, unwinder.frames()[65].sp);
+  EXPECT_EQ(0x56574d8cU, unwinder.frames()[66].pc);
+  EXPECT_EQ(0xffeb7e40U, unwinder.frames()[66].sp);
+  EXPECT_EQ(0x56574a80U, unwinder.frames()[67].pc);
+  EXPECT_EQ(0xffeb7e70U, unwinder.frames()[67].sp);
+  EXPECT_EQ(0xf7363275U, unwinder.frames()[68].pc);
+  EXPECT_EQ(0xffeb7ef0U, unwinder.frames()[68].sp);
+}
+
+TEST_F(UnwindOfflineTest, jit_debug_arm) {
+  ASSERT_NO_FATAL_FAILURE(Init("jit_debug_arm/", ARCH_ARM));
+
+  MemoryOfflineParts* memory = new MemoryOfflineParts;
+  AddMemory(dir_ + "descriptor.data", memory);
+  AddMemory(dir_ + "descriptor1.data", memory);
+  AddMemory(dir_ + "stack.data", memory);
+  for (size_t i = 0; i < 7; 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(76U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
+  EXPECT_EQ(
+      "  #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"
+      "  #03 pc 00462175  libartd.so (art_quick_invoke_stub_internal+68)\n"
+      "  #04 pc 00467129  libartd.so (art_quick_invoke_stub+228)\n"
+      "  #05 pc 000bf7a9  libartd.so "
+      "(_ZN3art9ArtMethod6InvokeEPNS_6ThreadEPjjPNS_6JValueEPKc+864)\n"
+      "  #06 pc 00247833  libartd.so "
+      "(_ZN3art11interpreter34ArtInterpreterToCompiledCodeBridgeEPNS_6ThreadEPNS_9ArtMethodEPNS_"
+      "11ShadowFrameEtPNS_6JValueE+382)\n"
+      "  #07 pc 0022e935  libartd.so "
+      "(_ZN3art11interpreterL7ExecuteEPNS_6ThreadERKNS_20CodeItemDataAccessorERNS_11ShadowFrameENS_"
+      "6JValueEb+244)\n"
+      "  #08 pc 0022f71d  libartd.so "
+      "(_ZN3art11interpreter30EnterInterpreterFromEntryPointEPNS_6ThreadERKNS_"
+      "20CodeItemDataAccessorEPNS_11ShadowFrameE+128)\n"
+      "  #09 pc 00442865  libartd.so (artQuickToInterpreterBridge+796)\n"
+      "  #10 pc 004666ff  libartd.so (art_quick_to_interpreter_bridge+30)\n"
+      "  #11 pc 00011c31  anonymous:e2796000 (int Main.compare(Main, Main)+64)\n"
+      "  #12 pc 00462175  libartd.so (art_quick_invoke_stub_internal+68)\n"
+      "  #13 pc 00467129  libartd.so (art_quick_invoke_stub+228)\n"
+      "  #14 pc 000bf7a9  libartd.so "
+      "(_ZN3art9ArtMethod6InvokeEPNS_6ThreadEPjjPNS_6JValueEPKc+864)\n"
+      "  #15 pc 00247833  libartd.so "
+      "(_ZN3art11interpreter34ArtInterpreterToCompiledCodeBridgeEPNS_6ThreadEPNS_9ArtMethodEPNS_"
+      "11ShadowFrameEtPNS_6JValueE+382)\n"
+      "  #16 pc 0022e935  libartd.so "
+      "(_ZN3art11interpreterL7ExecuteEPNS_6ThreadERKNS_20CodeItemDataAccessorERNS_11ShadowFrameENS_"
+      "6JValueEb+244)\n"
+      "  #17 pc 0022f71d  libartd.so "
+      "(_ZN3art11interpreter30EnterInterpreterFromEntryPointEPNS_6ThreadERKNS_"
+      "20CodeItemDataAccessorEPNS_11ShadowFrameE+128)\n"
+      "  #18 pc 00442865  libartd.so (artQuickToInterpreterBridge+796)\n"
+      "  #19 pc 004666ff  libartd.so (art_quick_to_interpreter_bridge+30)\n"
+      "  #20 pc 00011b77  anonymous:e2796000 (int Main.compare(java.lang.Object, "
+      "java.lang.Object)+118)\n"
+      "  #21 pc 00462175  libartd.so (art_quick_invoke_stub_internal+68)\n"
+      "  #22 pc 00467129  libartd.so (art_quick_invoke_stub+228)\n"
+      "  #23 pc 000bf7a9  libartd.so "
+      "(_ZN3art9ArtMethod6InvokeEPNS_6ThreadEPjjPNS_6JValueEPKc+864)\n"
+      "  #24 pc 00247833  libartd.so "
+      "(_ZN3art11interpreter34ArtInterpreterToCompiledCodeBridgeEPNS_6ThreadEPNS_9ArtMethodEPNS_"
+      "11ShadowFrameEtPNS_6JValueE+382)\n"
+      "  #25 pc 0022e935  libartd.so "
+      "(_ZN3art11interpreterL7ExecuteEPNS_6ThreadERKNS_20CodeItemDataAccessorERNS_11ShadowFrameENS_"
+      "6JValueEb+244)\n"
+      "  #26 pc 0022f71d  libartd.so "
+      "(_ZN3art11interpreter30EnterInterpreterFromEntryPointEPNS_6ThreadERKNS_"
+      "20CodeItemDataAccessorEPNS_11ShadowFrameE+128)\n"
+      "  #27 pc 00442865  libartd.so (artQuickToInterpreterBridge+796)\n"
+      "  #28 pc 004666ff  libartd.so (art_quick_to_interpreter_bridge+30)\n"
+      "  #29 pc 00011a29  anonymous:e2796000 (int "
+      "java.util.Arrays.binarySearch0(java.lang.Object[], int, int, java.lang.Object, "
+      "java.util.Comparator)+304)\n"
+      "  #30 pc 00462175  libartd.so (art_quick_invoke_stub_internal+68)\n"
+      "  #31 pc 0046722f  libartd.so (art_quick_invoke_static_stub+226)\n"
+      "  #32 pc 000bf7bb  libartd.so "
+      "(_ZN3art9ArtMethod6InvokeEPNS_6ThreadEPjjPNS_6JValueEPKc+882)\n"
+      "  #33 pc 00247833  libartd.so "
+      "(_ZN3art11interpreter34ArtInterpreterToCompiledCodeBridgeEPNS_6ThreadEPNS_9ArtMethodEPNS_"
+      "11ShadowFrameEtPNS_6JValueE+382)\n"
+      "  #34 pc 0022e935  libartd.so "
+      "(_ZN3art11interpreterL7ExecuteEPNS_6ThreadERKNS_20CodeItemDataAccessorERNS_11ShadowFrameENS_"
+      "6JValueEb+244)\n"
+      "  #35 pc 0022f71d  libartd.so "
+      "(_ZN3art11interpreter30EnterInterpreterFromEntryPointEPNS_6ThreadERKNS_"
+      "20CodeItemDataAccessorEPNS_11ShadowFrameE+128)\n"
+      "  #36 pc 00442865  libartd.so (artQuickToInterpreterBridge+796)\n"
+      "  #37 pc 004666ff  libartd.so (art_quick_to_interpreter_bridge+30)\n"
+      "  #38 pc 0001139b  anonymous:e2796000 (boolean Main.foo()+178)\n"
+      "  #39 pc 00462175  libartd.so (art_quick_invoke_stub_internal+68)\n"
+      "  #40 pc 00467129  libartd.so (art_quick_invoke_stub+228)\n"
+      "  #41 pc 000bf7a9  libartd.so "
+      "(_ZN3art9ArtMethod6InvokeEPNS_6ThreadEPjjPNS_6JValueEPKc+864)\n"
+      "  #42 pc 00247833  libartd.so "
+      "(_ZN3art11interpreter34ArtInterpreterToCompiledCodeBridgeEPNS_6ThreadEPNS_9ArtMethodEPNS_"
+      "11ShadowFrameEtPNS_6JValueE+382)\n"
+      "  #43 pc 0022e935  libartd.so "
+      "(_ZN3art11interpreterL7ExecuteEPNS_6ThreadERKNS_20CodeItemDataAccessorERNS_11ShadowFrameENS_"
+      "6JValueEb+244)\n"
+      "  #44 pc 0022f71d  libartd.so "
+      "(_ZN3art11interpreter30EnterInterpreterFromEntryPointEPNS_6ThreadERKNS_"
+      "20CodeItemDataAccessorEPNS_11ShadowFrameE+128)\n"
+      "  #45 pc 00442865  libartd.so (artQuickToInterpreterBridge+796)\n"
+      "  #46 pc 004666ff  libartd.so (art_quick_to_interpreter_bridge+30)\n"
+      "  #47 pc 00010aa7  anonymous:e2796000 (void Main.runPrimary()+70)\n"
+      "  #48 pc 00462175  libartd.so (art_quick_invoke_stub_internal+68)\n"
+      "  #49 pc 00467129  libartd.so (art_quick_invoke_stub+228)\n"
+      "  #50 pc 000bf7a9  libartd.so "
+      "(_ZN3art9ArtMethod6InvokeEPNS_6ThreadEPjjPNS_6JValueEPKc+864)\n"
+      "  #51 pc 00247833  libartd.so "
+      "(_ZN3art11interpreter34ArtInterpreterToCompiledCodeBridgeEPNS_6ThreadEPNS_9ArtMethodEPNS_"
+      "11ShadowFrameEtPNS_6JValueE+382)\n"
+      "  #52 pc 0022e935  libartd.so "
+      "(_ZN3art11interpreterL7ExecuteEPNS_6ThreadERKNS_20CodeItemDataAccessorERNS_11ShadowFrameENS_"
+      "6JValueEb+244)\n"
+      "  #53 pc 0022f71d  libartd.so "
+      "(_ZN3art11interpreter30EnterInterpreterFromEntryPointEPNS_6ThreadERKNS_"
+      "20CodeItemDataAccessorEPNS_11ShadowFrameE+128)\n"
+      "  #54 pc 00442865  libartd.so (artQuickToInterpreterBridge+796)\n"
+      "  #55 pc 004666ff  libartd.so (art_quick_to_interpreter_bridge+30)\n"
+      "  #56 pc 0000ba99  anonymous:e2796000 (void Main.main(java.lang.String[])+144)\n"
+      "  #57 pc 00462175  libartd.so (art_quick_invoke_stub_internal+68)\n"
+      "  #58 pc 0046722f  libartd.so (art_quick_invoke_static_stub+226)\n"
+      "  #59 pc 000bf7bb  libartd.so "
+      "(_ZN3art9ArtMethod6InvokeEPNS_6ThreadEPjjPNS_6JValueEPKc+882)\n"
+      "  #60 pc 00247833  libartd.so "
+      "(_ZN3art11interpreter34ArtInterpreterToCompiledCodeBridgeEPNS_6ThreadEPNS_9ArtMethodEPNS_"
+      "11ShadowFrameEtPNS_6JValueE+382)\n"
+      "  #61 pc 0022e935  libartd.so "
+      "(_ZN3art11interpreterL7ExecuteEPNS_6ThreadERKNS_20CodeItemDataAccessorERNS_11ShadowFrameENS_"
+      "6JValueEb+244)\n"
+      "  #62 pc 0022f71d  libartd.so "
+      "(_ZN3art11interpreter30EnterInterpreterFromEntryPointEPNS_6ThreadERKNS_"
+      "20CodeItemDataAccessorEPNS_11ShadowFrameE+128)\n"
+      "  #63 pc 00442865  libartd.so (artQuickToInterpreterBridge+796)\n"
+      "  #64 pc 004666ff  libartd.so (art_quick_to_interpreter_bridge+30)\n"
+      "  #65 pc 00462175  libartd.so (art_quick_invoke_stub_internal+68)\n"
+      "  #66 pc 0046722f  libartd.so (art_quick_invoke_static_stub+226)\n"
+      "  #67 pc 000bf7bb  libartd.so "
+      "(_ZN3art9ArtMethod6InvokeEPNS_6ThreadEPjjPNS_6JValueEPKc+882)\n"
+      "  #68 pc 003b292d  libartd.so "
+      "(_ZN3artL18InvokeWithArgArrayERKNS_33ScopedObjectAccessAlreadyRunnableEPNS_9ArtMethodEPNS_"
+      "8ArgArrayEPNS_6JValueEPKc+52)\n"
+      "  #69 pc 003b26c3  libartd.so "
+      "(_ZN3art17InvokeWithVarArgsERKNS_33ScopedObjectAccessAlreadyRunnableEP8_jobjectP10_"
+      "jmethodIDSt9__va_list+210)\n"
+      "  #70 pc 00308411  libartd.so "
+      "(_ZN3art3JNI21CallStaticVoidMethodVEP7_JNIEnvP7_jclassP10_jmethodIDSt9__va_list+76)\n"
+      "  #71 pc 000e6a9f  libartd.so "
+      "(_ZN3art8CheckJNI11CallMethodVEPKcP7_JNIEnvP8_jobjectP7_jclassP10_jmethodIDSt9__va_listNS_"
+      "9Primitive4TypeENS_10InvokeTypeE+1486)\n"
+      "  #72 pc 000e19b9  libartd.so "
+      "(_ZN3art8CheckJNI21CallStaticVoidMethodVEP7_JNIEnvP7_jclassP10_jmethodIDSt9__va_list+40)\n"
+      "  #73 pc 0000159f  dalvikvm32 "
+      "(_ZN7_JNIEnv20CallStaticVoidMethodEP7_jclassP10_jmethodIDz+30)\n"
+      "  #74 pc 00001349  dalvikvm32 (main+896)\n"
+      "  #75 pc 000850c9  libc.so\n",
+      frame_info);
+  EXPECT_EQ(0xdfe66a5eU, unwinder.frames()[0].pc);
+  EXPECT_EQ(0xff85d180U, unwinder.frames()[0].sp);
+  EXPECT_EQ(0xe044712dU, unwinder.frames()[1].pc);
+  EXPECT_EQ(0xff85d200U, unwinder.frames()[1].sp);
+  EXPECT_EQ(0xe27a7cb1U, unwinder.frames()[2].pc);
+  EXPECT_EQ(0xff85d290U, unwinder.frames()[2].sp);
+  EXPECT_EQ(0xed75c175U, unwinder.frames()[3].pc);
+  EXPECT_EQ(0xff85d2b0U, unwinder.frames()[3].sp);
+  EXPECT_EQ(0xed761129U, unwinder.frames()[4].pc);
+  EXPECT_EQ(0xff85d2e8U, unwinder.frames()[4].sp);
+  EXPECT_EQ(0xed3b97a9U, unwinder.frames()[5].pc);
+  EXPECT_EQ(0xff85d370U, unwinder.frames()[5].sp);
+  EXPECT_EQ(0xed541833U, unwinder.frames()[6].pc);
+  EXPECT_EQ(0xff85d3d8U, unwinder.frames()[6].sp);
+  EXPECT_EQ(0xed528935U, unwinder.frames()[7].pc);
+  EXPECT_EQ(0xff85d428U, unwinder.frames()[7].sp);
+  EXPECT_EQ(0xed52971dU, unwinder.frames()[8].pc);
+  EXPECT_EQ(0xff85d470U, unwinder.frames()[8].sp);
+  EXPECT_EQ(0xed73c865U, unwinder.frames()[9].pc);
+  EXPECT_EQ(0xff85d4b0U, unwinder.frames()[9].sp);
+  EXPECT_EQ(0xed7606ffU, unwinder.frames()[10].pc);
+  EXPECT_EQ(0xff85d5d0U, unwinder.frames()[10].sp);
+  EXPECT_EQ(0xe27a7c31U, unwinder.frames()[11].pc);
+  EXPECT_EQ(0xff85d640U, unwinder.frames()[11].sp);
+  EXPECT_EQ(0xed75c175U, unwinder.frames()[12].pc);
+  EXPECT_EQ(0xff85d660U, unwinder.frames()[12].sp);
+  EXPECT_EQ(0xed761129U, unwinder.frames()[13].pc);
+  EXPECT_EQ(0xff85d698U, unwinder.frames()[13].sp);
+  EXPECT_EQ(0xed3b97a9U, unwinder.frames()[14].pc);
+  EXPECT_EQ(0xff85d720U, unwinder.frames()[14].sp);
+  EXPECT_EQ(0xed541833U, unwinder.frames()[15].pc);
+  EXPECT_EQ(0xff85d788U, unwinder.frames()[15].sp);
+  EXPECT_EQ(0xed528935U, unwinder.frames()[16].pc);
+  EXPECT_EQ(0xff85d7d8U, unwinder.frames()[16].sp);
+  EXPECT_EQ(0xed52971dU, unwinder.frames()[17].pc);
+  EXPECT_EQ(0xff85d820U, unwinder.frames()[17].sp);
+  EXPECT_EQ(0xed73c865U, unwinder.frames()[18].pc);
+  EXPECT_EQ(0xff85d860U, unwinder.frames()[18].sp);
+  EXPECT_EQ(0xed7606ffU, unwinder.frames()[19].pc);
+  EXPECT_EQ(0xff85d970U, unwinder.frames()[19].sp);
+  EXPECT_EQ(0xe27a7b77U, unwinder.frames()[20].pc);
+  EXPECT_EQ(0xff85d9e0U, unwinder.frames()[20].sp);
+  EXPECT_EQ(0xed75c175U, unwinder.frames()[21].pc);
+  EXPECT_EQ(0xff85da10U, unwinder.frames()[21].sp);
+  EXPECT_EQ(0xed761129U, unwinder.frames()[22].pc);
+  EXPECT_EQ(0xff85da48U, unwinder.frames()[22].sp);
+  EXPECT_EQ(0xed3b97a9U, unwinder.frames()[23].pc);
+  EXPECT_EQ(0xff85dad0U, unwinder.frames()[23].sp);
+  EXPECT_EQ(0xed541833U, unwinder.frames()[24].pc);
+  EXPECT_EQ(0xff85db38U, unwinder.frames()[24].sp);
+  EXPECT_EQ(0xed528935U, unwinder.frames()[25].pc);
+  EXPECT_EQ(0xff85db88U, unwinder.frames()[25].sp);
+  EXPECT_EQ(0xed52971dU, unwinder.frames()[26].pc);
+  EXPECT_EQ(0xff85dbd0U, unwinder.frames()[26].sp);
+  EXPECT_EQ(0xed73c865U, unwinder.frames()[27].pc);
+  EXPECT_EQ(0xff85dc10U, unwinder.frames()[27].sp);
+  EXPECT_EQ(0xed7606ffU, unwinder.frames()[28].pc);
+  EXPECT_EQ(0xff85dd20U, unwinder.frames()[28].sp);
+  EXPECT_EQ(0xe27a7a29U, unwinder.frames()[29].pc);
+  EXPECT_EQ(0xff85dd90U, unwinder.frames()[29].sp);
+  EXPECT_EQ(0xed75c175U, unwinder.frames()[30].pc);
+  EXPECT_EQ(0xff85ddc0U, unwinder.frames()[30].sp);
+  EXPECT_EQ(0xed76122fU, unwinder.frames()[31].pc);
+  EXPECT_EQ(0xff85de08U, unwinder.frames()[31].sp);
+  EXPECT_EQ(0xed3b97bbU, unwinder.frames()[32].pc);
+  EXPECT_EQ(0xff85de90U, unwinder.frames()[32].sp);
+  EXPECT_EQ(0xed541833U, unwinder.frames()[33].pc);
+  EXPECT_EQ(0xff85def8U, unwinder.frames()[33].sp);
+  EXPECT_EQ(0xed528935U, unwinder.frames()[34].pc);
+  EXPECT_EQ(0xff85df48U, unwinder.frames()[34].sp);
+  EXPECT_EQ(0xed52971dU, unwinder.frames()[35].pc);
+  EXPECT_EQ(0xff85df90U, unwinder.frames()[35].sp);
+  EXPECT_EQ(0xed73c865U, unwinder.frames()[36].pc);
+  EXPECT_EQ(0xff85dfd0U, unwinder.frames()[36].sp);
+  EXPECT_EQ(0xed7606ffU, unwinder.frames()[37].pc);
+  EXPECT_EQ(0xff85e110U, unwinder.frames()[37].sp);
+  EXPECT_EQ(0xe27a739bU, unwinder.frames()[38].pc);
+  EXPECT_EQ(0xff85e180U, unwinder.frames()[38].sp);
+  EXPECT_EQ(0xed75c175U, unwinder.frames()[39].pc);
+  EXPECT_EQ(0xff85e1b0U, unwinder.frames()[39].sp);
+  EXPECT_EQ(0xed761129U, unwinder.frames()[40].pc);
+  EXPECT_EQ(0xff85e1e0U, unwinder.frames()[40].sp);
+  EXPECT_EQ(0xed3b97a9U, unwinder.frames()[41].pc);
+  EXPECT_EQ(0xff85e268U, unwinder.frames()[41].sp);
+  EXPECT_EQ(0xed541833U, unwinder.frames()[42].pc);
+  EXPECT_EQ(0xff85e2d0U, unwinder.frames()[42].sp);
+  EXPECT_EQ(0xed528935U, unwinder.frames()[43].pc);
+  EXPECT_EQ(0xff85e320U, unwinder.frames()[43].sp);
+  EXPECT_EQ(0xed52971dU, unwinder.frames()[44].pc);
+  EXPECT_EQ(0xff85e368U, unwinder.frames()[44].sp);
+  EXPECT_EQ(0xed73c865U, unwinder.frames()[45].pc);
+  EXPECT_EQ(0xff85e3a8U, unwinder.frames()[45].sp);
+  EXPECT_EQ(0xed7606ffU, unwinder.frames()[46].pc);
+  EXPECT_EQ(0xff85e4c0U, unwinder.frames()[46].sp);
+  EXPECT_EQ(0xe27a6aa7U, unwinder.frames()[47].pc);
+  EXPECT_EQ(0xff85e530U, unwinder.frames()[47].sp);
+  EXPECT_EQ(0xed75c175U, unwinder.frames()[48].pc);
+  EXPECT_EQ(0xff85e5a0U, unwinder.frames()[48].sp);
+  EXPECT_EQ(0xed761129U, unwinder.frames()[49].pc);
+  EXPECT_EQ(0xff85e5d8U, unwinder.frames()[49].sp);
+  EXPECT_EQ(0xed3b97a9U, unwinder.frames()[50].pc);
+  EXPECT_EQ(0xff85e660U, unwinder.frames()[50].sp);
+  EXPECT_EQ(0xed541833U, unwinder.frames()[51].pc);
+  EXPECT_EQ(0xff85e6c8U, unwinder.frames()[51].sp);
+  EXPECT_EQ(0xed528935U, unwinder.frames()[52].pc);
+  EXPECT_EQ(0xff85e718U, unwinder.frames()[52].sp);
+  EXPECT_EQ(0xed52971dU, unwinder.frames()[53].pc);
+  EXPECT_EQ(0xff85e760U, unwinder.frames()[53].sp);
+  EXPECT_EQ(0xed73c865U, unwinder.frames()[54].pc);
+  EXPECT_EQ(0xff85e7a0U, unwinder.frames()[54].sp);
+  EXPECT_EQ(0xed7606ffU, unwinder.frames()[55].pc);
+  EXPECT_EQ(0xff85e8f0U, unwinder.frames()[55].sp);
+  EXPECT_EQ(0xe27a1a99U, unwinder.frames()[56].pc);
+  EXPECT_EQ(0xff85e960U, unwinder.frames()[56].sp);
+  EXPECT_EQ(0xed75c175U, unwinder.frames()[57].pc);
+  EXPECT_EQ(0xff85e990U, unwinder.frames()[57].sp);
+  EXPECT_EQ(0xed76122fU, unwinder.frames()[58].pc);
+  EXPECT_EQ(0xff85e9c8U, unwinder.frames()[58].sp);
+  EXPECT_EQ(0xed3b97bbU, unwinder.frames()[59].pc);
+  EXPECT_EQ(0xff85ea50U, unwinder.frames()[59].sp);
+  EXPECT_EQ(0xed541833U, unwinder.frames()[60].pc);
+  EXPECT_EQ(0xff85eab8U, unwinder.frames()[60].sp);
+  EXPECT_EQ(0xed528935U, unwinder.frames()[61].pc);
+  EXPECT_EQ(0xff85eb08U, unwinder.frames()[61].sp);
+  EXPECT_EQ(0xed52971dU, unwinder.frames()[62].pc);
+  EXPECT_EQ(0xff85eb50U, unwinder.frames()[62].sp);
+  EXPECT_EQ(0xed73c865U, unwinder.frames()[63].pc);
+  EXPECT_EQ(0xff85eb90U, unwinder.frames()[63].sp);
+  EXPECT_EQ(0xed7606ffU, unwinder.frames()[64].pc);
+  EXPECT_EQ(0xff85ec90U, unwinder.frames()[64].sp);
+  EXPECT_EQ(0xed75c175U, unwinder.frames()[65].pc);
+  EXPECT_EQ(0xff85ed00U, unwinder.frames()[65].sp);
+  EXPECT_EQ(0xed76122fU, unwinder.frames()[66].pc);
+  EXPECT_EQ(0xff85ed38U, unwinder.frames()[66].sp);
+  EXPECT_EQ(0xed3b97bbU, unwinder.frames()[67].pc);
+  EXPECT_EQ(0xff85edc0U, unwinder.frames()[67].sp);
+  EXPECT_EQ(0xed6ac92dU, unwinder.frames()[68].pc);
+  EXPECT_EQ(0xff85ee28U, unwinder.frames()[68].sp);
+  EXPECT_EQ(0xed6ac6c3U, unwinder.frames()[69].pc);
+  EXPECT_EQ(0xff85eeb8U, unwinder.frames()[69].sp);
+  EXPECT_EQ(0xed602411U, unwinder.frames()[70].pc);
+  EXPECT_EQ(0xff85ef48U, unwinder.frames()[70].sp);
+  EXPECT_EQ(0xed3e0a9fU, unwinder.frames()[71].pc);
+  EXPECT_EQ(0xff85ef90U, unwinder.frames()[71].sp);
+  EXPECT_EQ(0xed3db9b9U, unwinder.frames()[72].pc);
+  EXPECT_EQ(0xff85f008U, unwinder.frames()[72].sp);
+  EXPECT_EQ(0xab0d459fU, unwinder.frames()[73].pc);
+  EXPECT_EQ(0xff85f038U, unwinder.frames()[73].sp);
+  EXPECT_EQ(0xab0d4349U, unwinder.frames()[74].pc);
+  EXPECT_EQ(0xff85f050U, unwinder.frames()[74].sp);
+  EXPECT_EQ(0xedb0d0c9U, unwinder.frames()[75].pc);
+  EXPECT_EQ(0xff85f0c0U, unwinder.frames()[75].sp);
+}
+
+// The eh_frame_hdr data is present but set to zero fdes. This should
+// fallback to iterating over the cies/fdes and ignore the eh_frame_hdr.
+// No .gnu_debugdata section in the elf file, so no symbols.
+TEST_F(UnwindOfflineTest, bad_eh_frame_hdr_arm64) {
+  ASSERT_NO_FATAL_FAILURE(Init("bad_eh_frame_hdr_arm64/", ARCH_ARM64));
+
+  Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
+  unwinder.Unwind();
+
+  std::string frame_info(DumpFrames(unwinder));
+  ASSERT_EQ(5U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
+  EXPECT_EQ(
+      "  #00 pc 0000000000000550  waiter64\n"
+      "  #01 pc 0000000000000568  waiter64\n"
+      "  #02 pc 000000000000057c  waiter64\n"
+      "  #03 pc 0000000000000590  waiter64\n"
+      "  #04 pc 00000000000a8e98  libc.so (__libc_init+88)\n",
+      frame_info);
+  EXPECT_EQ(0x60a9fdf550U, unwinder.frames()[0].pc);
+  EXPECT_EQ(0x7fdd141990U, unwinder.frames()[0].sp);
+  EXPECT_EQ(0x60a9fdf568U, unwinder.frames()[1].pc);
+  EXPECT_EQ(0x7fdd1419a0U, unwinder.frames()[1].sp);
+  EXPECT_EQ(0x60a9fdf57cU, unwinder.frames()[2].pc);
+  EXPECT_EQ(0x7fdd1419b0U, unwinder.frames()[2].sp);
+  EXPECT_EQ(0x60a9fdf590U, unwinder.frames()[3].pc);
+  EXPECT_EQ(0x7fdd1419c0U, unwinder.frames()[3].sp);
+  EXPECT_EQ(0x7542d68e98U, unwinder.frames()[4].pc);
+  EXPECT_EQ(0x7fdd1419d0U, unwinder.frames()[4].sp);
+}
+
+// The elf has bad eh_frame unwind information for the pcs. If eh_frame
+// is used first, the unwind will not match the expected output.
+TEST_F(UnwindOfflineTest, debug_frame_first_x86) {
+  ASSERT_NO_FATAL_FAILURE(Init("debug_frame_first_x86/", ARCH_X86));
+
+  Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
+  unwinder.Unwind();
+
+  std::string frame_info(DumpFrames(unwinder));
+  ASSERT_EQ(5U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
+  EXPECT_EQ(
+      "  #00 pc 00000685  waiter (call_level3+53)\n"
+      "  #01 pc 000006b7  waiter (call_level2+23)\n"
+      "  #02 pc 000006d7  waiter (call_level1+23)\n"
+      "  #03 pc 000006f7  waiter (main+23)\n"
+      "  #04 pc 00018275  libc.so\n",
+      frame_info);
+  EXPECT_EQ(0x56598685U, unwinder.frames()[0].pc);
+  EXPECT_EQ(0xffcf9e38U, unwinder.frames()[0].sp);
+  EXPECT_EQ(0x565986b7U, unwinder.frames()[1].pc);
+  EXPECT_EQ(0xffcf9e50U, unwinder.frames()[1].sp);
+  EXPECT_EQ(0x565986d7U, unwinder.frames()[2].pc);
+  EXPECT_EQ(0xffcf9e60U, unwinder.frames()[2].sp);
+  EXPECT_EQ(0x565986f7U, unwinder.frames()[3].pc);
+  EXPECT_EQ(0xffcf9e70U, unwinder.frames()[3].sp);
+  EXPECT_EQ(0xf744a275U, unwinder.frames()[4].pc);
+  EXPECT_EQ(0xffcf9e80U, unwinder.frames()[4].sp);
+}
+
+// Make sure that a pc that is at the beginning of an fde unwinds correctly.
+TEST_F(UnwindOfflineTest, eh_frame_hdr_begin_x86_64) {
+  ASSERT_NO_FATAL_FAILURE(Init("eh_frame_hdr_begin_x86_64/", ARCH_X86_64));
+
+  Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
+  unwinder.Unwind();
+
+  std::string frame_info(DumpFrames(unwinder));
+  ASSERT_EQ(5U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
+  EXPECT_EQ(
+      "  #00 pc 0000000000000a80  unwind_test64 (calling3)\n"
+      "  #01 pc 0000000000000dd9  unwind_test64 (calling2+633)\n"
+      "  #02 pc 000000000000121e  unwind_test64 (calling1+638)\n"
+      "  #03 pc 00000000000013ed  unwind_test64 (main+13)\n"
+      "  #04 pc 00000000000202b0  libc.so\n",
+      frame_info);
+  EXPECT_EQ(0x561550b17a80U, unwinder.frames()[0].pc);
+  EXPECT_EQ(0x7ffcc8596ce8U, unwinder.frames()[0].sp);
+  EXPECT_EQ(0x561550b17dd9U, unwinder.frames()[1].pc);
+  EXPECT_EQ(0x7ffcc8596cf0U, unwinder.frames()[1].sp);
+  EXPECT_EQ(0x561550b1821eU, unwinder.frames()[2].pc);
+  EXPECT_EQ(0x7ffcc8596f40U, unwinder.frames()[2].sp);
+  EXPECT_EQ(0x561550b183edU, unwinder.frames()[3].pc);
+  EXPECT_EQ(0x7ffcc8597190U, unwinder.frames()[3].sp);
+  EXPECT_EQ(0x7f4de62162b0U, unwinder.frames()[4].pc);
+  EXPECT_EQ(0x7ffcc85971a0U, unwinder.frames()[4].sp);
+}
+
+TEST_F(UnwindOfflineTest, art_quick_osr_stub_arm) {
+  ASSERT_NO_FATAL_FAILURE(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) {
+  ASSERT_NO_FATAL_FAILURE(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);
+}
+
+TEST_F(UnwindOfflineTest, offset_arm) {
+  ASSERT_NO_FATAL_FAILURE(Init("offset_arm/", ARCH_ARM));
+
+  Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
+  unwinder.Unwind();
+
+  std::string frame_info(DumpFrames(unwinder));
+  ASSERT_EQ(19U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
+  EXPECT_EQ(
+      "  #00 pc 0032bfa0 (offset 0x42000)  libunwindstack_test (SignalInnerFunction+40)\n"
+      "  #01 pc 0032bfeb (offset 0x42000)  libunwindstack_test (SignalMiddleFunction+2)\n"
+      "  #02 pc 0032bff3 (offset 0x42000)  libunwindstack_test (SignalOuterFunction+2)\n"
+      "  #03 pc 0032fed3 (offset 0x42000)  libunwindstack_test "
+      "(_ZN11unwindstackL19SignalCallerHandlerEiP7siginfoPv+26)\n"
+      "  #04 pc 00026528 (offset 0x25000)  libc.so\n"
+      "  #05 pc 00000000  <unknown>\n"
+      "  #06 pc 0032c2d9 (offset 0x42000)  libunwindstack_test (InnerFunction+736)\n"
+      "  #07 pc 0032cc4f (offset 0x42000)  libunwindstack_test (MiddleFunction+42)\n"
+      "  #08 pc 0032cc81 (offset 0x42000)  libunwindstack_test (OuterFunction+42)\n"
+      "  #09 pc 0032e547 (offset 0x42000)  libunwindstack_test "
+      "(_ZN11unwindstackL19RemoteThroughSignalEij+270)\n"
+      "  #10 pc 0032ed99 (offset 0x42000)  libunwindstack_test "
+      "(_ZN11unwindstack55UnwindTest_remote_through_signal_with_invalid_func_Test8TestBodyEv+16)\n"
+      "  #11 pc 00354453 (offset 0x42000)  libunwindstack_test (_ZN7testing4Test3RunEv+154)\n"
+      "  #12 pc 00354de7 (offset 0x42000)  libunwindstack_test (_ZN7testing8TestInfo3RunEv+194)\n"
+      "  #13 pc 00355105 (offset 0x42000)  libunwindstack_test (_ZN7testing8TestCase3RunEv+180)\n"
+      "  #14 pc 0035a215 (offset 0x42000)  libunwindstack_test "
+      "(_ZN7testing8internal12UnitTestImpl11RunAllTestsEv+664)\n"
+      "  #15 pc 00359f4f (offset 0x42000)  libunwindstack_test (_ZN7testing8UnitTest3RunEv+110)\n"
+      "  #16 pc 0034d3db (offset 0x42000)  libunwindstack_test (main+38)\n"
+      "  #17 pc 00092c0d (offset 0x25000)  libc.so (__libc_init+48)\n"
+      "  #18 pc 0004202f (offset 0x42000)  libunwindstack_test (_start_main+38)\n",
+      frame_info);
+
+  EXPECT_EQ(0x2e55fa0U, unwinder.frames()[0].pc);
+  EXPECT_EQ(0xf43d2cccU, unwinder.frames()[0].sp);
+  EXPECT_EQ(0x2e55febU, unwinder.frames()[1].pc);
+  EXPECT_EQ(0xf43d2ce0U, unwinder.frames()[1].sp);
+  EXPECT_EQ(0x2e55ff3U, unwinder.frames()[2].pc);
+  EXPECT_EQ(0xf43d2ce8U, unwinder.frames()[2].sp);
+  EXPECT_EQ(0x2e59ed3U, unwinder.frames()[3].pc);
+  EXPECT_EQ(0xf43d2cf0U, unwinder.frames()[3].sp);
+  EXPECT_EQ(0xf4136528U, unwinder.frames()[4].pc);
+  EXPECT_EQ(0xf43d2d10U, unwinder.frames()[4].sp);
+  EXPECT_EQ(0U, unwinder.frames()[5].pc);
+  EXPECT_EQ(0xffcc0ee0U, unwinder.frames()[5].sp);
+  EXPECT_EQ(0x2e562d9U, unwinder.frames()[6].pc);
+  EXPECT_EQ(0xffcc0ee0U, unwinder.frames()[6].sp);
+  EXPECT_EQ(0x2e56c4fU, unwinder.frames()[7].pc);
+  EXPECT_EQ(0xffcc1060U, unwinder.frames()[7].sp);
+  EXPECT_EQ(0x2e56c81U, unwinder.frames()[8].pc);
+  EXPECT_EQ(0xffcc1078U, unwinder.frames()[8].sp);
+  EXPECT_EQ(0x2e58547U, unwinder.frames()[9].pc);
+  EXPECT_EQ(0xffcc1090U, unwinder.frames()[9].sp);
+  EXPECT_EQ(0x2e58d99U, unwinder.frames()[10].pc);
+  EXPECT_EQ(0xffcc1438U, unwinder.frames()[10].sp);
+  EXPECT_EQ(0x2e7e453U, unwinder.frames()[11].pc);
+  EXPECT_EQ(0xffcc1448U, unwinder.frames()[11].sp);
+  EXPECT_EQ(0x2e7ede7U, unwinder.frames()[12].pc);
+  EXPECT_EQ(0xffcc1458U, unwinder.frames()[12].sp);
+  EXPECT_EQ(0x2e7f105U, unwinder.frames()[13].pc);
+  EXPECT_EQ(0xffcc1490U, unwinder.frames()[13].sp);
+  EXPECT_EQ(0x2e84215U, unwinder.frames()[14].pc);
+  EXPECT_EQ(0xffcc14c0U, unwinder.frames()[14].sp);
+  EXPECT_EQ(0x2e83f4fU, unwinder.frames()[15].pc);
+  EXPECT_EQ(0xffcc1510U, unwinder.frames()[15].sp);
+  EXPECT_EQ(0x2e773dbU, unwinder.frames()[16].pc);
+  EXPECT_EQ(0xffcc1528U, unwinder.frames()[16].sp);
+  EXPECT_EQ(0xf41a2c0dU, unwinder.frames()[17].pc);
+  EXPECT_EQ(0xffcc1540U, unwinder.frames()[17].sp);
+  EXPECT_EQ(0x2b6c02fU, unwinder.frames()[18].pc);
+  EXPECT_EQ(0xffcc1558U, unwinder.frames()[18].sp);
+}
+
+// Test using a non-zero load bias library that has the fde entries
+// encoded as 0xb, which is not set as pc relative.
+TEST_F(UnwindOfflineTest, debug_frame_load_bias_arm) {
+  ASSERT_NO_FATAL_FAILURE(Init("debug_frame_load_bias_arm/", ARCH_ARM));
+
+  Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
+  unwinder.Unwind();
+
+  std::string frame_info(DumpFrames(unwinder));
+  ASSERT_EQ(8U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
+  EXPECT_EQ(
+      "  #00 pc 0005138c  libc.so (__ioctl+8)\n"
+      "  #01 pc 0002140f  libc.so (ioctl+30)\n"
+      "  #02 pc 00039535  libbinder.so (_ZN7android14IPCThreadState14talkWithDriverEb+204)\n"
+      "  #03 pc 00039633  libbinder.so (_ZN7android14IPCThreadState20getAndExecuteCommandEv+10)\n"
+      "  #04 pc 00039b57  libbinder.so (_ZN7android14IPCThreadState14joinThreadPoolEb+38)\n"
+      "  #05 pc 00000c21  mediaserver (main+104)\n"
+      "  #06 pc 00084b89  libc.so (__libc_init+48)\n"
+      "  #07 pc 00000b77  mediaserver (_start_main+38)\n",
+      frame_info);
+
+  EXPECT_EQ(0xf0be238cU, unwinder.frames()[0].pc);
+  EXPECT_EQ(0xffd4a638U, unwinder.frames()[0].sp);
+  EXPECT_EQ(0xf0bb240fU, unwinder.frames()[1].pc);
+  EXPECT_EQ(0xffd4a638U, unwinder.frames()[1].sp);
+  EXPECT_EQ(0xf1a75535U, unwinder.frames()[2].pc);
+  EXPECT_EQ(0xffd4a650U, unwinder.frames()[2].sp);
+  EXPECT_EQ(0xf1a75633U, unwinder.frames()[3].pc);
+  EXPECT_EQ(0xffd4a6b0U, unwinder.frames()[3].sp);
+  EXPECT_EQ(0xf1a75b57U, unwinder.frames()[4].pc);
+  EXPECT_EQ(0xffd4a6d0U, unwinder.frames()[4].sp);
+  EXPECT_EQ(0x8d1cc21U, unwinder.frames()[5].pc);
+  EXPECT_EQ(0xffd4a6e8U, unwinder.frames()[5].sp);
+  EXPECT_EQ(0xf0c15b89U, unwinder.frames()[6].pc);
+  EXPECT_EQ(0xffd4a700U, unwinder.frames()[6].sp);
+  EXPECT_EQ(0x8d1cb77U, unwinder.frames()[7].pc);
+  EXPECT_EQ(0xffd4a718U, unwinder.frames()[7].sp);
+}
+
+}  // namespace unwindstack
diff --git a/libunwindstack/tests/UnwindTest.cpp b/libunwindstack/tests/UnwindTest.cpp
new file mode 100644
index 0000000..ea992c7
--- /dev/null
+++ b/libunwindstack/tests/UnwindTest.cpp
@@ -0,0 +1,346 @@
+/*
+ * Copyright (C) 2017 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 <errno.h>
+#include <signal.h>
+#include <stdint.h>
+#include <string.h>
+#include <sys/ptrace.h>
+#include <sys/syscall.h>
+#include <unistd.h>
+
+#include <gtest/gtest.h>
+
+#include <atomic>
+#include <memory>
+#include <sstream>
+#include <string>
+#include <thread>
+#include <vector>
+
+#include <android-base/stringprintf.h>
+#include <android-base/threads.h>
+
+#include <unwindstack/Maps.h>
+#include <unwindstack/Memory.h>
+#include <unwindstack/Regs.h>
+#include <unwindstack/RegsGetLocal.h>
+#include <unwindstack/Unwinder.h>
+
+#include "TestUtils.h"
+
+namespace unwindstack {
+
+static std::atomic_bool g_ready;
+static volatile bool g_ready_for_remote;
+static volatile bool g_signal_ready_for_remote;
+static std::atomic_bool g_finish;
+static std::atomic_uintptr_t g_ucontext;
+
+static void ResetGlobals() {
+  g_ready = false;
+  g_ready_for_remote = false;
+  g_signal_ready_for_remote = false;
+  g_finish = false;
+  g_ucontext = 0;
+}
+
+static std::vector<const char*> kFunctionOrder{"OuterFunction", "MiddleFunction", "InnerFunction"};
+
+static std::vector<const char*> kFunctionSignalOrder{"OuterFunction",        "MiddleFunction",
+                                                     "InnerFunction",        "SignalOuterFunction",
+                                                     "SignalMiddleFunction", "SignalInnerFunction"};
+
+static void SignalHandler(int, siginfo_t*, void* sigcontext) {
+  g_ucontext = reinterpret_cast<uintptr_t>(sigcontext);
+  while (!g_finish.load()) {
+  }
+}
+
+extern "C" void SignalInnerFunction() {
+  g_signal_ready_for_remote = true;
+  while (!g_finish.load()) {
+  }
+}
+
+extern "C" void SignalMiddleFunction() {
+  SignalInnerFunction();
+}
+
+extern "C" void SignalOuterFunction() {
+  SignalMiddleFunction();
+}
+
+static void SignalCallerHandler(int, siginfo_t*, void*) {
+  SignalOuterFunction();
+}
+
+static std::string ErrorMsg(const std::vector<const char*>& function_names, Unwinder& unwinder) {
+  std::string unwind;
+  for (size_t i = 0; i < unwinder.NumFrames(); i++) {
+    unwind += unwinder.FormatFrame(i) + '\n';
+  }
+
+  return std::string(
+             "Unwind completed without finding all frames\n"
+             "  Looking for function: ") +
+         function_names.front() + "\n" + "Unwind data:\n" + unwind;
+}
+
+static void VerifyUnwind(pid_t pid, Maps* maps, Regs* regs,
+                         std::vector<const char*> expected_function_names) {
+  auto process_memory(Memory::CreateProcessMemory(pid));
+
+  Unwinder unwinder(512, maps, regs, process_memory);
+  unwinder.Unwind();
+
+  for (auto& frame : unwinder.frames()) {
+    if (frame.function_name == expected_function_names.back()) {
+      expected_function_names.pop_back();
+      if (expected_function_names.empty()) {
+        break;
+      }
+    }
+  }
+
+  ASSERT_TRUE(expected_function_names.empty()) << ErrorMsg(expected_function_names, unwinder);
+}
+
+// This test assumes that this code is compiled with optimizations turned
+// off. If this doesn't happen, then all of the calls will be optimized
+// away.
+extern "C" void InnerFunction(bool local, bool trigger_invalid_call) {
+  if (local) {
+    LocalMaps maps;
+    ASSERT_TRUE(maps.Parse());
+    std::unique_ptr<Regs> regs(Regs::CreateFromLocal());
+    RegsGetLocal(regs.get());
+
+    VerifyUnwind(getpid(), &maps, regs.get(), kFunctionOrder);
+  } else {
+    g_ready_for_remote = true;
+    g_ready = true;
+    if (trigger_invalid_call) {
+      void (*crash_func)() = nullptr;
+      crash_func();
+    }
+    while (!g_finish.load()) {
+    }
+  }
+}
+
+extern "C" void MiddleFunction(bool local, bool trigger_invalid_call) {
+  InnerFunction(local, trigger_invalid_call);
+}
+
+extern "C" void OuterFunction(bool local, bool trigger_invalid_call) {
+  MiddleFunction(local, trigger_invalid_call);
+}
+
+class UnwindTest : public ::testing::Test {
+ public:
+  void SetUp() override { ResetGlobals(); }
+};
+
+TEST_F(UnwindTest, local) {
+  OuterFunction(true, false);
+}
+
+void WaitForRemote(pid_t pid, uint64_t addr, bool leave_attached, bool* completed) {
+  *completed = false;
+  // Need to sleep before attempting first ptrace. Without this, on the
+  // host it becomes impossible to attach and ptrace sets errno to EPERM.
+  usleep(1000);
+  for (size_t i = 0; i < 1000; i++) {
+    if (ptrace(PTRACE_ATTACH, pid, 0, 0) == 0) {
+      ASSERT_TRUE(TestQuiescePid(pid))
+          << "Waiting for process to quiesce failed: " << strerror(errno);
+
+      MemoryRemote memory(pid);
+      // Read the remote value to see if we are ready.
+      bool value;
+      if (memory.ReadFully(addr, &value, sizeof(value)) && value) {
+        *completed = true;
+      }
+      if (!*completed || !leave_attached) {
+        ASSERT_EQ(0, ptrace(PTRACE_DETACH, pid, 0, 0));
+      }
+      if (*completed) {
+        break;
+      }
+    } else {
+      ASSERT_EQ(ESRCH, errno) << "ptrace attach failed with unexpected error: " << strerror(errno);
+    }
+    usleep(5000);
+  }
+}
+
+TEST_F(UnwindTest, remote) {
+  pid_t pid;
+  if ((pid = fork()) == 0) {
+    OuterFunction(false, false);
+    exit(0);
+  }
+  ASSERT_NE(-1, pid);
+  TestScopedPidReaper reap(pid);
+
+  bool completed;
+  WaitForRemote(pid, reinterpret_cast<uint64_t>(&g_ready_for_remote), true, &completed);
+  ASSERT_TRUE(completed) << "Timed out waiting for remote process to be ready.";
+
+  RemoteMaps maps(pid);
+  ASSERT_TRUE(maps.Parse());
+  std::unique_ptr<Regs> regs(Regs::RemoteGet(pid));
+  ASSERT_TRUE(regs.get() != nullptr);
+
+  VerifyUnwind(pid, &maps, regs.get(), kFunctionOrder);
+
+  ASSERT_EQ(0, ptrace(PTRACE_DETACH, pid, 0, 0))
+      << "ptrace detach failed with unexpected error: " << strerror(errno);
+}
+
+TEST_F(UnwindTest, from_context) {
+  std::atomic_int tid(0);
+  std::thread thread([&]() {
+    tid = syscall(__NR_gettid);
+    OuterFunction(false, false);
+  });
+
+  struct sigaction act, oldact;
+  memset(&act, 0, sizeof(act));
+  act.sa_sigaction = SignalHandler;
+  act.sa_flags = SA_RESTART | SA_SIGINFO | SA_ONSTACK;
+  ASSERT_EQ(0, sigaction(SIGUSR1, &act, &oldact));
+  // Wait for the tid to get set.
+  for (size_t i = 0; i < 100; i++) {
+    if (tid.load() != 0) {
+      break;
+    }
+    usleep(1000);
+  }
+  ASSERT_NE(0, tid.load());
+  ASSERT_EQ(0, tgkill(getpid(), tid.load(), SIGUSR1)) << "Error: " << strerror(errno);
+
+  // Wait for context data.
+  void* ucontext;
+  for (size_t i = 0; i < 2000; i++) {
+    ucontext = reinterpret_cast<void*>(g_ucontext.load());
+    if (ucontext != nullptr) {
+      break;
+    }
+    usleep(1000);
+  }
+  ASSERT_TRUE(ucontext != nullptr) << "Timed out waiting for thread to respond to signal.";
+
+  LocalMaps maps;
+  ASSERT_TRUE(maps.Parse());
+  std::unique_ptr<Regs> regs(Regs::CreateFromUcontext(Regs::CurrentArch(), ucontext));
+
+  VerifyUnwind(getpid(), &maps, regs.get(), kFunctionOrder);
+
+  ASSERT_EQ(0, sigaction(SIGUSR1, &oldact, nullptr));
+
+  g_finish = true;
+  thread.join();
+}
+
+static void RemoteThroughSignal(int signal, unsigned int sa_flags) {
+  pid_t pid;
+  if ((pid = fork()) == 0) {
+    struct sigaction act, oldact;
+    memset(&act, 0, sizeof(act));
+    act.sa_sigaction = SignalCallerHandler;
+    act.sa_flags = SA_RESTART | SA_ONSTACK | sa_flags;
+    ASSERT_EQ(0, sigaction(signal, &act, &oldact));
+
+    OuterFunction(false, signal == SIGSEGV);
+    exit(0);
+  }
+  ASSERT_NE(-1, pid);
+  TestScopedPidReaper reap(pid);
+
+  bool completed;
+  if (signal != SIGSEGV) {
+    WaitForRemote(pid, reinterpret_cast<uint64_t>(&g_ready_for_remote), false, &completed);
+    ASSERT_TRUE(completed) << "Timed out waiting for remote process to be ready.";
+    ASSERT_EQ(0, kill(pid, SIGUSR1));
+  }
+  WaitForRemote(pid, reinterpret_cast<uint64_t>(&g_signal_ready_for_remote), true, &completed);
+  ASSERT_TRUE(completed) << "Timed out waiting for remote process to be in signal handler.";
+
+  RemoteMaps maps(pid);
+  ASSERT_TRUE(maps.Parse());
+  std::unique_ptr<Regs> regs(Regs::RemoteGet(pid));
+  ASSERT_TRUE(regs.get() != nullptr);
+
+  VerifyUnwind(pid, &maps, regs.get(), kFunctionSignalOrder);
+
+  ASSERT_EQ(0, ptrace(PTRACE_DETACH, pid, 0, 0))
+      << "ptrace detach failed with unexpected error: " << strerror(errno);
+}
+
+TEST_F(UnwindTest, remote_through_signal) {
+  RemoteThroughSignal(SIGUSR1, 0);
+}
+
+TEST_F(UnwindTest, remote_through_signal_sa_siginfo) {
+  RemoteThroughSignal(SIGUSR1, SA_SIGINFO);
+}
+
+TEST_F(UnwindTest, remote_through_signal_with_invalid_func) {
+  RemoteThroughSignal(SIGSEGV, 0);
+}
+
+TEST_F(UnwindTest, remote_through_signal_sa_siginfo_with_invalid_func) {
+  RemoteThroughSignal(SIGSEGV, SA_SIGINFO);
+}
+
+// Verify that using the same map while unwinding multiple threads at the
+// same time doesn't cause problems.
+TEST_F(UnwindTest, multiple_threads_unwind_same_map) {
+  static constexpr size_t kNumConcurrentThreads = 100;
+
+  LocalMaps maps;
+  ASSERT_TRUE(maps.Parse());
+  auto process_memory(Memory::CreateProcessMemory(getpid()));
+
+  std::vector<std::thread*> threads;
+
+  std::atomic_bool wait;
+  wait = true;
+  size_t frames[kNumConcurrentThreads];
+  for (size_t i = 0; i < kNumConcurrentThreads; i++) {
+    std::thread* thread = new std::thread([i, &frames, &maps, &process_memory, &wait]() {
+      while (wait)
+        ;
+      std::unique_ptr<Regs> regs(Regs::CreateFromLocal());
+      RegsGetLocal(regs.get());
+
+      Unwinder unwinder(512, &maps, regs.get(), process_memory);
+      unwinder.Unwind();
+      frames[i] = unwinder.NumFrames();
+      ASSERT_LE(3U, frames[i]) << "Failed for thread " << i;
+    });
+    threads.push_back(thread);
+  }
+  wait = false;
+  for (auto thread : threads) {
+    thread->join();
+    delete thread;
+  }
+}
+
+}  // namespace unwindstack
diff --git a/libunwindstack/tests/UnwinderTest.cpp b/libunwindstack/tests/UnwinderTest.cpp
new file mode 100644
index 0000000..2428f68
--- /dev/null
+++ b/libunwindstack/tests/UnwinderTest.cpp
@@ -0,0 +1,1066 @@
+/*
+ * Copyright (C) 2017 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 <elf.h>
+#include <stdint.h>
+#include <sys/mman.h>
+
+#include <memory>
+#include <set>
+#include <string>
+
+#include <gtest/gtest.h>
+
+#include <unwindstack/Elf.h>
+#include <unwindstack/Maps.h>
+#include <unwindstack/Memory.h>
+#include <unwindstack/Regs.h>
+#include <unwindstack/RegsArm.h>
+#include <unwindstack/RegsArm64.h>
+#include <unwindstack/RegsX86.h>
+#include <unwindstack/RegsX86_64.h>
+#include <unwindstack/RegsMips.h>
+#include <unwindstack/RegsMips64.h>
+#include <unwindstack/Unwinder.h>
+
+#include "ElfFake.h"
+#include "MemoryFake.h"
+#include "RegsFake.h"
+
+namespace unwindstack {
+
+class MapsFake : public Maps {
+ public:
+  MapsFake() = default;
+  virtual ~MapsFake() = default;
+
+  bool Parse() { return true; }
+
+  void FakeClear() { maps_.clear(); }
+
+  void FakeAddMapInfo(MapInfo* map_info) { maps_.push_back(map_info); }
+};
+
+class UnwinderTest : public ::testing::Test {
+ protected:
+  static void SetUpTestCase() {
+    maps_.FakeClear();
+    MapInfo* info = new MapInfo(0x1000, 0x8000, 0, PROT_READ | PROT_WRITE, "/system/fake/libc.so");
+    ElfFake* elf = new ElfFake(new MemoryFake);
+    info->elf.reset(elf);
+    elf->FakeSetInterface(new ElfInterfaceFake(nullptr));
+    maps_.FakeAddMapInfo(info);
+
+    info = new MapInfo(0x10000, 0x12000, 0, PROT_READ | PROT_WRITE, "[stack]");
+    maps_.FakeAddMapInfo(info);
+
+    info = new MapInfo(0x13000, 0x15000, 0, PROT_READ | PROT_WRITE | MAPS_FLAGS_DEVICE_MAP,
+                       "/dev/fake_device");
+    maps_.FakeAddMapInfo(info);
+
+    info = new MapInfo(0x20000, 0x22000, 0, PROT_READ | PROT_WRITE, "/system/fake/libunwind.so");
+    elf = new ElfFake(new MemoryFake);
+    info->elf.reset(elf);
+    elf->FakeSetInterface(new ElfInterfaceFake(nullptr));
+    maps_.FakeAddMapInfo(info);
+
+    info = new MapInfo(0x23000, 0x24000, 0, PROT_READ | PROT_WRITE, "/fake/libanother.so");
+    elf = new ElfFake(new MemoryFake);
+    info->elf.reset(elf);
+    elf->FakeSetInterface(new ElfInterfaceFake(nullptr));
+    maps_.FakeAddMapInfo(info);
+
+    info = new MapInfo(0x33000, 0x34000, 0, PROT_READ | PROT_WRITE, "/fake/compressed.so");
+    elf = new ElfFake(new MemoryFake);
+    info->elf.reset(elf);
+    elf->FakeSetInterface(new ElfInterfaceFake(nullptr));
+    maps_.FakeAddMapInfo(info);
+
+    info = new MapInfo(0x43000, 0x44000, 0x1d000, PROT_READ | PROT_WRITE, "/fake/fake.apk");
+    elf = new ElfFake(new MemoryFake);
+    info->elf.reset(elf);
+    elf->FakeSetInterface(new ElfInterfaceFake(nullptr));
+    maps_.FakeAddMapInfo(info);
+
+    info = new MapInfo(0x53000, 0x54000, 0, PROT_READ | PROT_WRITE, "/fake/fake.oat");
+    maps_.FakeAddMapInfo(info);
+
+    info = new MapInfo(0xa3000, 0xa4000, 0, PROT_READ | PROT_WRITE | PROT_EXEC, "/fake/fake.vdex");
+    info->load_bias = 0;
+    maps_.FakeAddMapInfo(info);
+
+    info = new MapInfo(0xa5000, 0xa6000, 0, PROT_READ | PROT_WRITE | PROT_EXEC,
+                       "/fake/fake_load_bias.so");
+    elf = new ElfFake(new MemoryFake);
+    info->elf.reset(elf);
+    elf->FakeSetInterface(new ElfInterfaceFake(nullptr));
+    elf->FakeSetLoadBias(0x5000);
+    maps_.FakeAddMapInfo(info);
+
+    info = new MapInfo(0xa7000, 0xa8000, 0, PROT_READ | PROT_WRITE | PROT_EXEC,
+                       "/fake/fake_offset.oat");
+    elf = new ElfFake(new MemoryFake);
+    info->elf.reset(elf);
+    elf->FakeSetInterface(new ElfInterfaceFake(nullptr));
+    info->elf_offset = 0x8000;
+    maps_.FakeAddMapInfo(info);
+
+    process_memory_.reset(new MemoryFake);
+  }
+
+  void SetUp() override {
+    ElfInterfaceFake::FakeClear();
+    regs_.FakeSetArch(ARCH_ARM);
+    regs_.FakeSetReturnAddressValid(false);
+  }
+
+  static MapsFake maps_;
+  static RegsFake regs_;
+  static std::shared_ptr<Memory> process_memory_;
+};
+
+MapsFake UnwinderTest::maps_;
+RegsFake UnwinderTest::regs_(5);
+std::shared_ptr<Memory> UnwinderTest::process_memory_(nullptr);
+
+TEST_F(UnwinderTest, multiple_frames) {
+  ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 0));
+  ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame1", 1));
+  ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame2", 2));
+
+  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));
+
+  Unwinder unwinder(64, &maps_, &regs_, process_memory_);
+  unwinder.Unwind();
+  EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+
+  ASSERT_EQ(3U, unwinder.NumFrames());
+
+  auto* frame = &unwinder.frames()[0];
+  EXPECT_EQ(0U, frame->num);
+  EXPECT_EQ(0U, frame->rel_pc);
+  EXPECT_EQ(0x1000U, frame->pc);
+  EXPECT_EQ(0x10000U, frame->sp);
+  EXPECT_EQ("Frame0", frame->function_name);
+  EXPECT_EQ(0U, frame->function_offset);
+  EXPECT_EQ("/system/fake/libc.so", frame->map_name);
+  EXPECT_EQ(0U, frame->map_offset);
+  EXPECT_EQ(0x1000U, frame->map_start);
+  EXPECT_EQ(0x8000U, frame->map_end);
+  EXPECT_EQ(0U, frame->map_load_bias);
+  EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+
+  frame = &unwinder.frames()[1];
+  EXPECT_EQ(1U, frame->num);
+  EXPECT_EQ(0x100U, frame->rel_pc);
+  EXPECT_EQ(0x1100U, frame->pc);
+  EXPECT_EQ(0x10010U, frame->sp);
+  EXPECT_EQ("Frame1", frame->function_name);
+  EXPECT_EQ(1U, frame->function_offset);
+  EXPECT_EQ("/system/fake/libc.so", frame->map_name);
+  EXPECT_EQ(0U, frame->map_offset);
+  EXPECT_EQ(0x1000U, frame->map_start);
+  EXPECT_EQ(0x8000U, frame->map_end);
+  EXPECT_EQ(0U, frame->map_load_bias);
+  EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+
+  frame = &unwinder.frames()[2];
+  EXPECT_EQ(2U, frame->num);
+  EXPECT_EQ(0x200U, frame->rel_pc);
+  EXPECT_EQ(0x1200U, frame->pc);
+  EXPECT_EQ(0x10020U, frame->sp);
+  EXPECT_EQ("Frame2", frame->function_name);
+  EXPECT_EQ(2U, frame->function_offset);
+  EXPECT_EQ("/system/fake/libc.so", frame->map_name);
+  EXPECT_EQ(0U, frame->map_offset);
+  EXPECT_EQ(0x1000U, frame->map_start);
+  EXPECT_EQ(0x8000U, frame->map_end);
+  EXPECT_EQ(0U, frame->map_load_bias);
+  EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+}
+
+TEST_F(UnwinderTest, multiple_frames_dont_resolve_names) {
+  ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 0));
+  ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame1", 1));
+  ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame2", 2));
+
+  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));
+
+  Unwinder unwinder(64, &maps_, &regs_, process_memory_);
+  unwinder.SetResolveNames(false);
+  unwinder.Unwind();
+  EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+
+  ASSERT_EQ(3U, unwinder.NumFrames());
+
+  auto* frame = &unwinder.frames()[0];
+  EXPECT_EQ(0U, frame->num);
+  EXPECT_EQ(0U, frame->rel_pc);
+  EXPECT_EQ(0x1000U, frame->pc);
+  EXPECT_EQ(0x10000U, frame->sp);
+  EXPECT_EQ("", frame->function_name);
+  EXPECT_EQ(0U, frame->function_offset);
+  EXPECT_EQ("", frame->map_name);
+  EXPECT_EQ(0U, frame->map_offset);
+  EXPECT_EQ(0x1000U, frame->map_start);
+  EXPECT_EQ(0x8000U, frame->map_end);
+  EXPECT_EQ(0U, frame->map_load_bias);
+  EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+
+  frame = &unwinder.frames()[1];
+  EXPECT_EQ(1U, frame->num);
+  EXPECT_EQ(0x100U, frame->rel_pc);
+  EXPECT_EQ(0x1100U, frame->pc);
+  EXPECT_EQ(0x10010U, frame->sp);
+  EXPECT_EQ("", frame->function_name);
+  EXPECT_EQ(0U, frame->function_offset);
+  EXPECT_EQ("", frame->map_name);
+  EXPECT_EQ(0U, frame->map_offset);
+  EXPECT_EQ(0x1000U, frame->map_start);
+  EXPECT_EQ(0x8000U, frame->map_end);
+  EXPECT_EQ(0U, frame->map_load_bias);
+  EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+
+  frame = &unwinder.frames()[2];
+  EXPECT_EQ(2U, frame->num);
+  EXPECT_EQ(0x200U, frame->rel_pc);
+  EXPECT_EQ(0x1200U, frame->pc);
+  EXPECT_EQ(0x10020U, frame->sp);
+  EXPECT_EQ("", frame->function_name);
+  EXPECT_EQ(0U, frame->function_offset);
+  EXPECT_EQ("", frame->map_name);
+  EXPECT_EQ(0U, frame->map_offset);
+  EXPECT_EQ(0x1000U, frame->map_start);
+  EXPECT_EQ(0x8000U, frame->map_end);
+  EXPECT_EQ(0U, frame->map_load_bias);
+  EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+}
+
+TEST_F(UnwinderTest, non_zero_load_bias) {
+  ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 0));
+
+  regs_.set_pc(0xa5500);
+  regs_.set_sp(0x10000);
+  ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
+
+  Unwinder unwinder(64, &maps_, &regs_, process_memory_);
+  unwinder.Unwind();
+  EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+
+  ASSERT_EQ(1U, unwinder.NumFrames());
+
+  auto* frame = &unwinder.frames()[0];
+  EXPECT_EQ(0U, frame->num);
+  EXPECT_EQ(0x5500U, frame->rel_pc);
+  EXPECT_EQ(0xa5500U, frame->pc);
+  EXPECT_EQ(0x10000U, frame->sp);
+  EXPECT_EQ("Frame0", frame->function_name);
+  EXPECT_EQ(0U, frame->function_offset);
+  EXPECT_EQ("/fake/fake_load_bias.so", frame->map_name);
+  EXPECT_EQ(0U, frame->map_offset);
+  EXPECT_EQ(0xa5000U, frame->map_start);
+  EXPECT_EQ(0xa6000U, frame->map_end);
+  EXPECT_EQ(0x5000U, frame->map_load_bias);
+  EXPECT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, frame->map_flags);
+}
+
+TEST_F(UnwinderTest, non_zero_elf_offset) {
+  ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 0));
+
+  regs_.set_pc(0xa7500);
+  regs_.set_sp(0x10000);
+  ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
+
+  Unwinder unwinder(64, &maps_, &regs_, process_memory_);
+  unwinder.Unwind();
+  EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+
+  ASSERT_EQ(1U, unwinder.NumFrames());
+
+  auto* frame = &unwinder.frames()[0];
+  EXPECT_EQ(0U, frame->num);
+  EXPECT_EQ(0x8500U, frame->rel_pc);
+  EXPECT_EQ(0xa7500U, frame->pc);
+  EXPECT_EQ(0x10000U, frame->sp);
+  EXPECT_EQ("Frame0", frame->function_name);
+  EXPECT_EQ(0U, frame->function_offset);
+  EXPECT_EQ("/fake/fake_offset.oat", frame->map_name);
+  EXPECT_EQ(0U, frame->map_offset);
+  EXPECT_EQ(0xa7000U, frame->map_start);
+  EXPECT_EQ(0xa8000U, frame->map_end);
+  EXPECT_EQ(0U, frame->map_load_bias);
+  EXPECT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, frame->map_flags);
+}
+
+TEST_F(UnwinderTest, non_zero_map_offset) {
+  ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 0));
+
+  regs_.set_pc(0x43000);
+  regs_.set_sp(0x10000);
+  ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
+
+  Unwinder unwinder(64, &maps_, &regs_, process_memory_);
+  unwinder.Unwind();
+  EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+
+  ASSERT_EQ(1U, unwinder.NumFrames());
+
+  auto* frame = &unwinder.frames()[0];
+  EXPECT_EQ(0U, frame->num);
+  EXPECT_EQ(0U, frame->rel_pc);
+  EXPECT_EQ(0x43000U, frame->pc);
+  EXPECT_EQ(0x10000U, frame->sp);
+  EXPECT_EQ("Frame0", frame->function_name);
+  EXPECT_EQ(0U, frame->function_offset);
+  EXPECT_EQ("/fake/fake.apk", frame->map_name);
+  EXPECT_EQ(0x1d000U, frame->map_offset);
+  EXPECT_EQ(0x43000U, frame->map_start);
+  EXPECT_EQ(0x44000U, frame->map_end);
+  EXPECT_EQ(0U, frame->map_load_bias);
+  EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+}
+
+// Verify that no attempt to continue after the step indicates it is done.
+TEST_F(UnwinderTest, no_frames_after_finished) {
+  ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 0));
+  ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame1", 1));
+  ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame2", 2));
+  ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame3", 3));
+  ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame4", 4));
+
+  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));
+
+  Unwinder unwinder(64, &maps_, &regs_, process_memory_);
+  unwinder.Unwind();
+  EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+
+  ASSERT_EQ(1U, unwinder.NumFrames());
+
+  auto* frame = &unwinder.frames()[0];
+  EXPECT_EQ(0U, frame->num);
+  EXPECT_EQ(0U, frame->rel_pc);
+  EXPECT_EQ(0x1000U, frame->pc);
+  EXPECT_EQ(0x10000U, frame->sp);
+  EXPECT_EQ("Frame0", frame->function_name);
+  EXPECT_EQ(0U, frame->function_offset);
+  EXPECT_EQ("/system/fake/libc.so", frame->map_name);
+  EXPECT_EQ(0U, frame->map_offset);
+  EXPECT_EQ(0x1000U, frame->map_start);
+  EXPECT_EQ(0x8000U, frame->map_end);
+  EXPECT_EQ(0U, frame->map_load_bias);
+  EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+}
+
+// Verify the maximum frames to save.
+TEST_F(UnwinderTest, max_frames) {
+  for (size_t i = 0; i < 30; i++) {
+    ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame" + std::to_string(i), i));
+    ElfInterfaceFake::FakePushStepData(StepData(0x1102 + i * 0x100, 0x10010 + i * 0x10, false));
+  }
+
+  regs_.set_pc(0x1000);
+  regs_.set_sp(0x10000);
+
+  Unwinder unwinder(20, &maps_, &regs_, process_memory_);
+  unwinder.Unwind();
+  EXPECT_EQ(ERROR_MAX_FRAMES_EXCEEDED, unwinder.LastErrorCode());
+
+  ASSERT_EQ(20U, unwinder.NumFrames());
+
+  for (size_t i = 0; i < 20; i++) {
+    auto* frame = &unwinder.frames()[i];
+    EXPECT_EQ(i, frame->num);
+    EXPECT_EQ(i * 0x100, frame->rel_pc) << "Failed at frame " << i;
+    EXPECT_EQ(0x1000 + i * 0x100, frame->pc) << "Failed at frame " << i;
+    EXPECT_EQ(0x10000 + 0x10 * i, frame->sp) << "Failed at frame " << i;
+    EXPECT_EQ("Frame" + std::to_string(i), frame->function_name) << "Failed at frame " << i;
+    EXPECT_EQ(i, frame->function_offset) << "Failed at frame " << i;
+    EXPECT_EQ("/system/fake/libc.so", frame->map_name) << "Failed at frame " << i;
+    EXPECT_EQ(0U, frame->map_offset) << "Failed at frame " << i;
+    EXPECT_EQ(0x1000U, frame->map_start) << "Failed at frame " << i;
+    EXPECT_EQ(0x8000U, frame->map_end) << "Failed at frame " << i;
+    EXPECT_EQ(0U, frame->map_load_bias) << "Failed at frame " << i;
+    EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags) << "Failed at frame " << i;
+  }
+}
+
+// Verify that initial map names frames are removed.
+TEST_F(UnwinderTest, verify_frames_skipped) {
+  ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 0));
+  ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame1", 1));
+  ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame2", 2));
+
+  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));
+  ElfInterfaceFake::FakePushStepData(StepData(0x21002, 0x10040, false));
+  ElfInterfaceFake::FakePushStepData(StepData(0x1002, 0x10050, false));
+  ElfInterfaceFake::FakePushStepData(StepData(0x21002, 0x10060, false));
+  ElfInterfaceFake::FakePushStepData(StepData(0x23002, 0x10070, false));
+  ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
+
+  Unwinder unwinder(64, &maps_, &regs_, process_memory_);
+  std::vector<std::string> skip_libs{"libunwind.so", "libanother.so"};
+  unwinder.Unwind(&skip_libs);
+  EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+
+  ASSERT_EQ(3U, unwinder.NumFrames());
+
+  auto* frame = &unwinder.frames()[0];
+  EXPECT_EQ(0U, frame->num);
+  EXPECT_EQ(0U, frame->rel_pc);
+  EXPECT_EQ(0x1000U, frame->pc);
+  EXPECT_EQ(0x10050U, frame->sp);
+  EXPECT_EQ("Frame0", frame->function_name);
+  EXPECT_EQ(0U, frame->function_offset);
+  EXPECT_EQ("/system/fake/libc.so", frame->map_name);
+  EXPECT_EQ(0U, frame->map_offset);
+  EXPECT_EQ(0x1000U, frame->map_start);
+  EXPECT_EQ(0x8000U, frame->map_end);
+  EXPECT_EQ(0U, frame->map_load_bias);
+  EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+
+  frame = &unwinder.frames()[1];
+  EXPECT_EQ(1U, frame->num);
+  EXPECT_EQ(0x1000U, frame->rel_pc);
+  EXPECT_EQ(0x21000U, frame->pc);
+  EXPECT_EQ(0x10060U, frame->sp);
+  EXPECT_EQ("Frame1", frame->function_name);
+  EXPECT_EQ(1U, frame->function_offset);
+  EXPECT_EQ("/system/fake/libunwind.so", frame->map_name);
+  EXPECT_EQ(0U, frame->map_offset);
+  EXPECT_EQ(0x20000U, frame->map_start);
+  EXPECT_EQ(0x22000U, frame->map_end);
+  EXPECT_EQ(0U, frame->map_load_bias);
+  EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+
+  frame = &unwinder.frames()[2];
+  EXPECT_EQ(2U, frame->num);
+  EXPECT_EQ(0U, frame->rel_pc);
+  EXPECT_EQ(0x23000U, frame->pc);
+  EXPECT_EQ(0x10070U, frame->sp);
+  EXPECT_EQ("Frame2", frame->function_name);
+  EXPECT_EQ(2U, frame->function_offset);
+  EXPECT_EQ("/fake/libanother.so", frame->map_name);
+  EXPECT_EQ(0U, frame->map_offset);
+  EXPECT_EQ(0x23000U, frame->map_start);
+  EXPECT_EQ(0x24000U, frame->map_end);
+  EXPECT_EQ(0U, frame->map_load_bias);
+  EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+}
+
+// Verify SP in a non-existant map is okay.
+TEST_F(UnwinderTest, sp_not_in_map) {
+  ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 0));
+  ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame1", 1));
+
+  regs_.set_pc(0x1000);
+  regs_.set_sp(0x63000);
+  ElfInterfaceFake::FakePushStepData(StepData(0x21002, 0x50020, false));
+  ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
+
+  Unwinder unwinder(64, &maps_, &regs_, process_memory_);
+  unwinder.Unwind();
+  EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+
+  ASSERT_EQ(2U, unwinder.NumFrames());
+
+  auto* frame = &unwinder.frames()[0];
+  EXPECT_EQ(0U, frame->num);
+  EXPECT_EQ(0U, frame->rel_pc);
+  EXPECT_EQ(0x1000U, frame->pc);
+  EXPECT_EQ(0x63000U, frame->sp);
+  EXPECT_EQ("Frame0", frame->function_name);
+  EXPECT_EQ(0U, frame->function_offset);
+  EXPECT_EQ("/system/fake/libc.so", frame->map_name);
+  EXPECT_EQ(0U, frame->map_offset);
+  EXPECT_EQ(0x1000U, frame->map_start);
+  EXPECT_EQ(0x8000U, frame->map_end);
+  EXPECT_EQ(0U, frame->map_load_bias);
+  EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+
+  frame = &unwinder.frames()[1];
+  EXPECT_EQ(1U, frame->num);
+  EXPECT_EQ(0x1000U, frame->rel_pc);
+  EXPECT_EQ(0x21000U, frame->pc);
+  EXPECT_EQ(0x50020U, frame->sp);
+  EXPECT_EQ("Frame1", frame->function_name);
+  EXPECT_EQ(1U, frame->function_offset);
+  EXPECT_EQ("/system/fake/libunwind.so", frame->map_name);
+  EXPECT_EQ(0U, frame->map_offset);
+  EXPECT_EQ(0x20000U, frame->map_start);
+  EXPECT_EQ(0x22000U, frame->map_end);
+  EXPECT_EQ(0U, frame->map_load_bias);
+  EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+}
+
+// Verify PC in a device stops the unwind.
+TEST_F(UnwinderTest, pc_in_device_stops_unwind) {
+  ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 0));
+  ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame1", 1));
+  ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame2", 2));
+
+  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));
+
+  Unwinder unwinder(64, &maps_, &regs_, process_memory_);
+  unwinder.Unwind();
+  EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+
+  ASSERT_EQ(1U, unwinder.NumFrames());
+}
+
+// Verify SP in a device stops the unwind.
+TEST_F(UnwinderTest, sp_in_device_stops_unwind) {
+  ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 0));
+  ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame1", 1));
+  ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame2", 2));
+
+  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));
+
+  Unwinder unwinder(64, &maps_, &regs_, process_memory_);
+  unwinder.Unwind();
+  EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+
+  ASSERT_EQ(1U, unwinder.NumFrames());
+}
+
+// Verify a no map info frame gets a frame.
+TEST_F(UnwinderTest, pc_without_map) {
+  ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 0));
+
+  regs_.set_pc(0x41000);
+  regs_.set_sp(0x13000);
+
+  Unwinder unwinder(64, &maps_, &regs_, process_memory_);
+  unwinder.Unwind();
+  EXPECT_EQ(ERROR_INVALID_MAP, unwinder.LastErrorCode());
+
+  ASSERT_EQ(1U, unwinder.NumFrames());
+
+  auto* frame = &unwinder.frames()[0];
+  EXPECT_EQ(0U, frame->num);
+  EXPECT_EQ(0x41000U, frame->rel_pc);
+  EXPECT_EQ(0x41000U, frame->pc);
+  EXPECT_EQ(0x13000U, frame->sp);
+  EXPECT_EQ("", frame->function_name);
+  EXPECT_EQ(0U, frame->function_offset);
+  EXPECT_EQ("", frame->map_name);
+  EXPECT_EQ(0U, frame->map_offset);
+  EXPECT_EQ(0U, frame->map_start);
+  EXPECT_EQ(0U, frame->map_end);
+  EXPECT_EQ(0U, frame->map_load_bias);
+  EXPECT_EQ(0, frame->map_flags);
+}
+
+// Verify that a speculative frame is added.
+TEST_F(UnwinderTest, speculative_frame) {
+  ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 0));
+  ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame1", 1));
+
+  // Fake as if code called a nullptr function.
+  regs_.set_pc(0);
+  regs_.set_sp(0x10000);
+  regs_.FakeSetReturnAddress(0x1202);
+  regs_.FakeSetReturnAddressValid(true);
+
+  ElfInterfaceFake::FakePushStepData(StepData(0x23102, 0x10020, false));
+  ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
+
+  Unwinder unwinder(64, &maps_, &regs_, process_memory_);
+  unwinder.Unwind();
+  EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+
+  ASSERT_EQ(3U, unwinder.NumFrames());
+
+  auto* frame = &unwinder.frames()[0];
+  EXPECT_EQ(0U, frame->num);
+  EXPECT_EQ(0U, frame->rel_pc);
+  EXPECT_EQ(0U, frame->pc);
+  EXPECT_EQ(0x10000U, frame->sp);
+  EXPECT_EQ("", frame->function_name);
+  EXPECT_EQ(0U, frame->function_offset);
+  EXPECT_EQ("", frame->map_name);
+  EXPECT_EQ(0U, frame->map_offset);
+  EXPECT_EQ(0U, frame->map_start);
+  EXPECT_EQ(0U, frame->map_end);
+  EXPECT_EQ(0U, frame->map_load_bias);
+  EXPECT_EQ(0, frame->map_flags);
+
+  frame = &unwinder.frames()[1];
+  EXPECT_EQ(1U, frame->num);
+  EXPECT_EQ(0x200U, frame->rel_pc);
+  EXPECT_EQ(0x1200U, frame->pc);
+  EXPECT_EQ(0x10000U, frame->sp);
+  EXPECT_EQ("Frame0", frame->function_name);
+  EXPECT_EQ(0U, frame->function_offset);
+  EXPECT_EQ("/system/fake/libc.so", frame->map_name);
+  EXPECT_EQ(0U, frame->map_offset);
+  EXPECT_EQ(0x1000U, frame->map_start);
+  EXPECT_EQ(0x8000U, frame->map_end);
+  EXPECT_EQ(0U, frame->map_load_bias);
+  EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+
+  frame = &unwinder.frames()[2];
+  EXPECT_EQ(2U, frame->num);
+  EXPECT_EQ(0x100U, frame->rel_pc);
+  EXPECT_EQ(0x23100U, frame->pc);
+  EXPECT_EQ(0x10020U, frame->sp);
+  EXPECT_EQ("Frame1", frame->function_name);
+  EXPECT_EQ(1U, frame->function_offset);
+  EXPECT_EQ("/fake/libanother.so", frame->map_name);
+  EXPECT_EQ(0U, frame->map_offset);
+  EXPECT_EQ(0x23000U, frame->map_start);
+  EXPECT_EQ(0x24000U, frame->map_end);
+  EXPECT_EQ(0U, frame->map_load_bias);
+  EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+}
+
+// Verify that a speculative frame is added then removed because no other
+// frames are added.
+TEST_F(UnwinderTest, speculative_frame_removed) {
+  ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 0));
+  ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame1", 1));
+
+  // Fake as if code called a nullptr function.
+  regs_.set_pc(0);
+  regs_.set_sp(0x10000);
+  regs_.FakeSetReturnAddress(0x1202);
+  regs_.FakeSetReturnAddressValid(true);
+
+  Unwinder unwinder(64, &maps_, &regs_, process_memory_);
+  unwinder.Unwind();
+  EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+
+  ASSERT_EQ(1U, unwinder.NumFrames());
+
+  auto* frame = &unwinder.frames()[0];
+  EXPECT_EQ(0U, frame->num);
+  EXPECT_EQ(0U, frame->rel_pc);
+  EXPECT_EQ(0U, frame->pc);
+  EXPECT_EQ(0x10000U, frame->sp);
+  EXPECT_EQ("", frame->function_name);
+  EXPECT_EQ(0U, frame->function_offset);
+  EXPECT_EQ("", frame->map_name);
+  EXPECT_EQ(0U, frame->map_offset);
+  EXPECT_EQ(0U, frame->map_start);
+  EXPECT_EQ(0U, frame->map_end);
+  EXPECT_EQ(0U, frame->map_load_bias);
+  EXPECT_EQ(0, frame->map_flags);
+}
+
+// Verify that an unwind stops when a frame is in given suffix.
+TEST_F(UnwinderTest, map_ignore_suffixes) {
+  ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 0));
+  ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame1", 1));
+  ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame2", 2));
+  ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame3", 3));
+
+  // Fake as if code called a nullptr function.
+  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));
+
+  Unwinder unwinder(64, &maps_, &regs_, process_memory_);
+  std::vector<std::string> suffixes{"oat"};
+  unwinder.Unwind(nullptr, &suffixes);
+  EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+
+  ASSERT_EQ(2U, unwinder.NumFrames());
+  // Make sure the elf was not initialized.
+  MapInfo* map_info = maps_.Find(0x53000);
+  ASSERT_TRUE(map_info != nullptr);
+  EXPECT_TRUE(map_info->elf == nullptr);
+
+  auto* frame = &unwinder.frames()[0];
+  EXPECT_EQ(0U, frame->num);
+  EXPECT_EQ(0U, frame->rel_pc);
+  EXPECT_EQ(0x1000U, frame->pc);
+  EXPECT_EQ(0x10000U, frame->sp);
+  EXPECT_EQ("Frame0", frame->function_name);
+  EXPECT_EQ(0U, frame->function_offset);
+  EXPECT_EQ("/system/fake/libc.so", frame->map_name);
+  EXPECT_EQ(0U, frame->map_offset);
+  EXPECT_EQ(0x1000U, frame->map_start);
+  EXPECT_EQ(0x8000U, frame->map_end);
+  EXPECT_EQ(0U, frame->map_load_bias);
+  EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+
+  frame = &unwinder.frames()[1];
+  EXPECT_EQ(1U, frame->num);
+  EXPECT_EQ(0x400U, frame->rel_pc);
+  EXPECT_EQ(0x43400U, frame->pc);
+  EXPECT_EQ(0x10010U, frame->sp);
+  EXPECT_EQ("Frame1", frame->function_name);
+  EXPECT_EQ(1U, frame->function_offset);
+  EXPECT_EQ("/fake/fake.apk", frame->map_name);
+  EXPECT_EQ(0x1d000U, frame->map_offset);
+  EXPECT_EQ(0x43000U, frame->map_start);
+  EXPECT_EQ(0x44000U, frame->map_end);
+  EXPECT_EQ(0U, frame->map_load_bias);
+  EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+}
+
+// Verify that an unwind stops when the sp and pc don't change.
+TEST_F(UnwinderTest, sp_pc_do_not_change) {
+  ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 0));
+  ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame1", 1));
+  ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame2", 2));
+  ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame3", 3));
+  ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame4", 4));
+
+  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));
+  ElfInterfaceFake::FakePushStepData(StepData(0x33502, 0x10020, false));
+  ElfInterfaceFake::FakePushStepData(StepData(0x33502, 0x10020, false));
+  ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
+
+  Unwinder unwinder(64, &maps_, &regs_, process_memory_);
+  unwinder.Unwind();
+  EXPECT_EQ(ERROR_REPEATED_FRAME, unwinder.LastErrorCode());
+
+  ASSERT_EQ(3U, unwinder.NumFrames());
+
+  auto* frame = &unwinder.frames()[0];
+  EXPECT_EQ(0U, frame->num);
+  EXPECT_EQ(0U, frame->rel_pc);
+  EXPECT_EQ(0x1000U, frame->pc);
+  EXPECT_EQ(0x10000U, frame->sp);
+  EXPECT_EQ("Frame0", frame->function_name);
+  EXPECT_EQ(0U, frame->function_offset);
+  EXPECT_EQ("/system/fake/libc.so", frame->map_name);
+  EXPECT_EQ(0U, frame->map_offset);
+  EXPECT_EQ(0x1000U, frame->map_start);
+  EXPECT_EQ(0x8000U, frame->map_end);
+  EXPECT_EQ(0U, frame->map_load_bias);
+  EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+
+  frame = &unwinder.frames()[1];
+  EXPECT_EQ(1U, frame->num);
+  EXPECT_EQ(0x400U, frame->rel_pc);
+  EXPECT_EQ(0x33400U, frame->pc);
+  EXPECT_EQ(0x10010U, frame->sp);
+  EXPECT_EQ("Frame1", frame->function_name);
+  EXPECT_EQ(1U, frame->function_offset);
+  EXPECT_EQ("/fake/compressed.so", frame->map_name);
+  EXPECT_EQ(0U, frame->map_offset);
+  EXPECT_EQ(0x33000U, frame->map_start);
+  EXPECT_EQ(0x34000U, frame->map_end);
+  EXPECT_EQ(0U, frame->map_load_bias);
+  EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+
+  frame = &unwinder.frames()[2];
+  EXPECT_EQ(2U, frame->num);
+  EXPECT_EQ(0x500U, frame->rel_pc);
+  EXPECT_EQ(0x33500U, frame->pc);
+  EXPECT_EQ(0x10020U, frame->sp);
+  EXPECT_EQ("Frame2", frame->function_name);
+  EXPECT_EQ(2U, frame->function_offset);
+  EXPECT_EQ("/fake/compressed.so", frame->map_name);
+  EXPECT_EQ(0U, frame->map_offset);
+  EXPECT_EQ(0x33000U, frame->map_start);
+  EXPECT_EQ(0x34000U, frame->map_end);
+  EXPECT_EQ(0U, frame->map_load_bias);
+  EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+}
+
+TEST_F(UnwinderTest, dex_pc_in_map) {
+  ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 0));
+  regs_.set_pc(0x1000);
+  regs_.set_sp(0x10000);
+  regs_.FakeSetDexPc(0xa3400);
+
+  Unwinder unwinder(64, &maps_, &regs_, process_memory_);
+  unwinder.Unwind();
+  EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+
+  ASSERT_EQ(2U, unwinder.NumFrames());
+
+  auto* frame = &unwinder.frames()[0];
+  EXPECT_EQ(0U, frame->num);
+  EXPECT_EQ(0x400U, frame->rel_pc);
+  EXPECT_EQ(0xa3400U, frame->pc);
+  EXPECT_EQ(0x10000U, frame->sp);
+  EXPECT_EQ("", frame->function_name);
+  EXPECT_EQ(0U, frame->function_offset);
+  EXPECT_EQ("/fake/fake.vdex", frame->map_name);
+  EXPECT_EQ(0U, frame->map_offset);
+  EXPECT_EQ(0xa3000U, frame->map_start);
+  EXPECT_EQ(0xa4000U, frame->map_end);
+  EXPECT_EQ(0U, frame->map_load_bias);
+  EXPECT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, frame->map_flags);
+
+  frame = &unwinder.frames()[1];
+  EXPECT_EQ(1U, frame->num);
+  EXPECT_EQ(0U, frame->rel_pc);
+  EXPECT_EQ(0x1000U, frame->pc);
+  EXPECT_EQ(0x10000U, frame->sp);
+  EXPECT_EQ("Frame0", frame->function_name);
+  EXPECT_EQ(0U, frame->function_offset);
+  EXPECT_EQ("/system/fake/libc.so", frame->map_name);
+  EXPECT_EQ(0U, frame->map_offset);
+  EXPECT_EQ(0x1000U, frame->map_start);
+  EXPECT_EQ(0x8000U, frame->map_end);
+  EXPECT_EQ(0U, frame->map_load_bias);
+  EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+}
+
+TEST_F(UnwinderTest, dex_pc_not_in_map) {
+  ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 0));
+  regs_.set_pc(0x1000);
+  regs_.set_sp(0x10000);
+  regs_.FakeSetDexPc(0x50000);
+
+  Unwinder unwinder(64, &maps_, &regs_, process_memory_);
+  unwinder.Unwind();
+  EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+
+  ASSERT_EQ(2U, unwinder.NumFrames());
+
+  auto* frame = &unwinder.frames()[0];
+  EXPECT_EQ(0U, frame->num);
+  EXPECT_EQ(0x50000U, frame->rel_pc);
+  EXPECT_EQ(0x50000U, frame->pc);
+  EXPECT_EQ(0x10000U, frame->sp);
+  EXPECT_EQ("", frame->function_name);
+  EXPECT_EQ(0U, frame->function_offset);
+  EXPECT_EQ("", frame->map_name);
+  EXPECT_EQ(0U, frame->map_offset);
+  EXPECT_EQ(0U, frame->map_start);
+  EXPECT_EQ(0U, frame->map_end);
+  EXPECT_EQ(0U, frame->map_load_bias);
+  EXPECT_EQ(0, frame->map_flags);
+
+  frame = &unwinder.frames()[1];
+  EXPECT_EQ(1U, frame->num);
+  EXPECT_EQ(0U, frame->rel_pc);
+  EXPECT_EQ(0x1000U, frame->pc);
+  EXPECT_EQ(0x10000U, frame->sp);
+  EXPECT_EQ("Frame0", frame->function_name);
+  EXPECT_EQ(0U, frame->function_offset);
+  EXPECT_EQ("/system/fake/libc.so", frame->map_name);
+  EXPECT_EQ(0U, frame->map_offset);
+  EXPECT_EQ(0x1000U, frame->map_start);
+  EXPECT_EQ(0x8000U, frame->map_end);
+  EXPECT_EQ(0U, frame->map_load_bias);
+  EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+}
+
+TEST_F(UnwinderTest, dex_pc_multiple_frames) {
+  ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 0));
+  ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame1", 1));
+  regs_.set_pc(0x1000);
+  regs_.set_sp(0x10000);
+  regs_.FakeSetDexPc(0xa3400);
+  ElfInterfaceFake::FakePushStepData(StepData(0x33402, 0x10010, false));
+  ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
+
+  Unwinder unwinder(64, &maps_, &regs_, process_memory_);
+  unwinder.Unwind();
+  EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+
+  ASSERT_EQ(3U, unwinder.NumFrames());
+
+  auto* frame = &unwinder.frames()[0];
+  EXPECT_EQ(0U, frame->num);
+  EXPECT_EQ(0x400U, frame->rel_pc);
+  EXPECT_EQ(0xa3400U, frame->pc);
+  EXPECT_EQ(0x10000U, frame->sp);
+  EXPECT_EQ("", frame->function_name);
+  EXPECT_EQ(0U, frame->function_offset);
+  EXPECT_EQ("/fake/fake.vdex", frame->map_name);
+  EXPECT_EQ(0U, frame->map_offset);
+  EXPECT_EQ(0xa3000U, frame->map_start);
+  EXPECT_EQ(0xa4000U, frame->map_end);
+  EXPECT_EQ(0U, frame->map_load_bias);
+  EXPECT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, frame->map_flags);
+
+  frame = &unwinder.frames()[1];
+  EXPECT_EQ(1U, frame->num);
+  EXPECT_EQ(0U, frame->rel_pc);
+  EXPECT_EQ(0x1000U, frame->pc);
+  EXPECT_EQ(0x10000U, frame->sp);
+  EXPECT_EQ("Frame0", frame->function_name);
+  EXPECT_EQ(0U, frame->function_offset);
+  EXPECT_EQ("/system/fake/libc.so", frame->map_name);
+  EXPECT_EQ(0U, frame->map_offset);
+  EXPECT_EQ(0x1000U, frame->map_start);
+  EXPECT_EQ(0x8000U, frame->map_end);
+  EXPECT_EQ(0U, frame->map_load_bias);
+  EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+
+  frame = &unwinder.frames()[2];
+  EXPECT_EQ(2U, frame->num);
+  EXPECT_EQ(0x400U, frame->rel_pc);
+  EXPECT_EQ(0x33400U, frame->pc);
+  EXPECT_EQ(0x10010U, frame->sp);
+  EXPECT_EQ("Frame1", frame->function_name);
+  EXPECT_EQ(1U, frame->function_offset);
+  EXPECT_EQ("/fake/compressed.so", frame->map_name);
+  EXPECT_EQ(0U, frame->map_offset);
+  EXPECT_EQ(0x33000U, frame->map_start);
+  EXPECT_EQ(0x34000U, frame->map_end);
+  EXPECT_EQ(0U, frame->map_load_bias);
+  EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+}
+
+// Verify format frame code.
+TEST_F(UnwinderTest, format_frame_static) {
+  FrameData frame;
+  frame.num = 1;
+  frame.rel_pc = 0x1000;
+  frame.pc = 0x4000;
+  frame.sp = 0x1000;
+  frame.function_name = "function";
+  frame.function_offset = 100;
+  frame.map_name = "/fake/libfake.so";
+  frame.map_offset = 0x2000;
+  frame.map_start = 0x3000;
+  frame.map_end = 0x6000;
+  frame.map_flags = PROT_READ;
+
+  EXPECT_EQ("  #01 pc 0000000000001000 (offset 0x2000)  /fake/libfake.so (function+100)",
+            Unwinder::FormatFrame(frame, false));
+  EXPECT_EQ("  #01 pc 00001000 (offset 0x2000)  /fake/libfake.so (function+100)",
+            Unwinder::FormatFrame(frame, true));
+
+  frame.map_offset = 0;
+  EXPECT_EQ("  #01 pc 0000000000001000  /fake/libfake.so (function+100)",
+            Unwinder::FormatFrame(frame, false));
+  EXPECT_EQ("  #01 pc 00001000  /fake/libfake.so (function+100)",
+            Unwinder::FormatFrame(frame, true));
+
+  frame.function_offset = 0;
+  EXPECT_EQ("  #01 pc 0000000000001000  /fake/libfake.so (function)",
+            Unwinder::FormatFrame(frame, false));
+  EXPECT_EQ("  #01 pc 00001000  /fake/libfake.so (function)", Unwinder::FormatFrame(frame, true));
+
+  frame.function_name = "";
+  EXPECT_EQ("  #01 pc 0000000000001000  /fake/libfake.so", Unwinder::FormatFrame(frame, false));
+  EXPECT_EQ("  #01 pc 00001000  /fake/libfake.so", Unwinder::FormatFrame(frame, true));
+
+  frame.map_name = "";
+  EXPECT_EQ("  #01 pc 0000000000001000  <anonymous:3000>", Unwinder::FormatFrame(frame, false));
+  EXPECT_EQ("  #01 pc 00001000  <anonymous:3000>", Unwinder::FormatFrame(frame, true));
+
+  frame.map_start = 0;
+  frame.map_end = 0;
+  EXPECT_EQ("  #01 pc 0000000000001000  <unknown>", Unwinder::FormatFrame(frame, false));
+  EXPECT_EQ("  #01 pc 00001000  <unknown>", Unwinder::FormatFrame(frame, true));
+}
+
+static std::string ArchToString(ArchEnum arch) {
+  if (arch == ARCH_ARM) {
+    return "Arm";
+  } else if (arch == ARCH_ARM64) {
+    return "Arm64";
+  } else if (arch == ARCH_X86) {
+    return "X86";
+  } else if (arch == ARCH_X86_64) {
+    return "X86_64";
+  } else {
+    return "Unknown";
+  }
+}
+
+// Verify format frame code.
+TEST_F(UnwinderTest, format_frame) {
+  std::vector<Regs*> reg_list;
+  RegsArm* arm = new RegsArm;
+  arm->set_pc(0x2300);
+  arm->set_sp(0x10000);
+  reg_list.push_back(arm);
+
+  RegsArm64* arm64 = new RegsArm64;
+  arm64->set_pc(0x2300);
+  arm64->set_sp(0x10000);
+  reg_list.push_back(arm64);
+
+  RegsX86* x86 = new RegsX86;
+  x86->set_pc(0x2300);
+  x86->set_sp(0x10000);
+  reg_list.push_back(x86);
+
+  RegsX86_64* x86_64 = new RegsX86_64;
+  x86_64->set_pc(0x2300);
+  x86_64->set_sp(0x10000);
+  reg_list.push_back(x86_64);
+
+  RegsMips* mips = new RegsMips;
+  mips->set_pc(0x2300);
+  mips->set_sp(0x10000);
+  reg_list.push_back(mips);
+
+  RegsMips64* mips64 = new RegsMips64;
+  mips64->set_pc(0x2300);
+  mips64->set_sp(0x10000);
+  reg_list.push_back(mips64);
+
+  for (auto regs : reg_list) {
+    ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 10));
+
+    Unwinder unwinder(64, &maps_, regs, process_memory_);
+    unwinder.Unwind();
+
+    ASSERT_EQ(1U, unwinder.NumFrames());
+    std::string expected;
+    switch (regs->Arch()) {
+      case ARCH_ARM:
+      case ARCH_X86:
+      case ARCH_MIPS:
+        expected = "  #00 pc 00001300  /system/fake/libc.so (Frame0+10)";
+        break;
+      case ARCH_ARM64:
+      case ARCH_X86_64:
+      case ARCH_MIPS64:
+        expected = "  #00 pc 0000000000001300  /system/fake/libc.so (Frame0+10)";
+        break;
+      default:
+        expected = "";
+    }
+    EXPECT_EQ(expected, unwinder.FormatFrame(0))
+        << "Mismatch of frame format for regs arch " << ArchToString(regs->Arch());
+    delete regs;
+  }
+}
+
+}  // namespace unwindstack
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/bad_eh_frame_hdr_arm64/libc.so b/libunwindstack/tests/files/offline/bad_eh_frame_hdr_arm64/libc.so
new file mode 100644
index 0000000..78449bf
--- /dev/null
+++ b/libunwindstack/tests/files/offline/bad_eh_frame_hdr_arm64/libc.so
Binary files differ
diff --git a/libunwindstack/tests/files/offline/bad_eh_frame_hdr_arm64/maps.txt b/libunwindstack/tests/files/offline/bad_eh_frame_hdr_arm64/maps.txt
new file mode 100644
index 0000000..7cada15
--- /dev/null
+++ b/libunwindstack/tests/files/offline/bad_eh_frame_hdr_arm64/maps.txt
@@ -0,0 +1,2 @@
+60a9fdf000-60a9fe0000 r-xp 0 00:00 0   waiter64
+7542cc0000-7542d8e000 r-xp 0 00:00 0   libc.so
diff --git a/libunwindstack/tests/files/offline/bad_eh_frame_hdr_arm64/regs.txt b/libunwindstack/tests/files/offline/bad_eh_frame_hdr_arm64/regs.txt
new file mode 100644
index 0000000..c24adbe
--- /dev/null
+++ b/libunwindstack/tests/files/offline/bad_eh_frame_hdr_arm64/regs.txt
@@ -0,0 +1,4 @@
+pc: 60a9fdf550
+sp: 7fdd141990
+lr: 60a9fdf56c
+x29: 7fdd1419a0
diff --git a/libunwindstack/tests/files/offline/bad_eh_frame_hdr_arm64/stack.data b/libunwindstack/tests/files/offline/bad_eh_frame_hdr_arm64/stack.data
new file mode 100644
index 0000000..b56d420
--- /dev/null
+++ b/libunwindstack/tests/files/offline/bad_eh_frame_hdr_arm64/stack.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/bad_eh_frame_hdr_arm64/waiter64 b/libunwindstack/tests/files/offline/bad_eh_frame_hdr_arm64/waiter64
new file mode 100644
index 0000000..81bda1d
--- /dev/null
+++ b/libunwindstack/tests/files/offline/bad_eh_frame_hdr_arm64/waiter64
Binary files differ
diff --git a/libunwindstack/tests/files/offline/debug_frame_first_x86/libc.so b/libunwindstack/tests/files/offline/debug_frame_first_x86/libc.so
new file mode 100644
index 0000000..9c78790
--- /dev/null
+++ b/libunwindstack/tests/files/offline/debug_frame_first_x86/libc.so
Binary files differ
diff --git a/libunwindstack/tests/files/offline/debug_frame_first_x86/maps.txt b/libunwindstack/tests/files/offline/debug_frame_first_x86/maps.txt
new file mode 100644
index 0000000..74fc89f
--- /dev/null
+++ b/libunwindstack/tests/files/offline/debug_frame_first_x86/maps.txt
@@ -0,0 +1,2 @@
+56598000-56599000 r-xp 0 00:00 0   waiter
+f7432000-f75e3000 r-xp 0 00:00 0   libc.so
diff --git a/libunwindstack/tests/files/offline/debug_frame_first_x86/regs.txt b/libunwindstack/tests/files/offline/debug_frame_first_x86/regs.txt
new file mode 100644
index 0000000..48f4440
--- /dev/null
+++ b/libunwindstack/tests/files/offline/debug_frame_first_x86/regs.txt
@@ -0,0 +1,9 @@
+eax: 1d88ef8c
+ebx: 56599fe8
+ecx: 3
+edx: ffcf9ea4
+ebp: ffcf9e48
+edi: f75e5000
+esi: 1
+esp: ffcf9e38
+eip: 56598685
diff --git a/libunwindstack/tests/files/offline/debug_frame_first_x86/stack.data b/libunwindstack/tests/files/offline/debug_frame_first_x86/stack.data
new file mode 100644
index 0000000..0cf7d55
--- /dev/null
+++ b/libunwindstack/tests/files/offline/debug_frame_first_x86/stack.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/debug_frame_first_x86/waiter b/libunwindstack/tests/files/offline/debug_frame_first_x86/waiter
new file mode 100644
index 0000000..b1fc024
--- /dev/null
+++ b/libunwindstack/tests/files/offline/debug_frame_first_x86/waiter
Binary files differ
diff --git a/libunwindstack/tests/files/offline/debug_frame_load_bias_arm/libbinder.so b/libunwindstack/tests/files/offline/debug_frame_load_bias_arm/libbinder.so
new file mode 100644
index 0000000..4b7bf44
--- /dev/null
+++ b/libunwindstack/tests/files/offline/debug_frame_load_bias_arm/libbinder.so
Binary files differ
diff --git a/libunwindstack/tests/files/offline/debug_frame_load_bias_arm/libc.so b/libunwindstack/tests/files/offline/debug_frame_load_bias_arm/libc.so
new file mode 100644
index 0000000..013858e
--- /dev/null
+++ b/libunwindstack/tests/files/offline/debug_frame_load_bias_arm/libc.so
Binary files differ
diff --git a/libunwindstack/tests/files/offline/debug_frame_load_bias_arm/maps.txt b/libunwindstack/tests/files/offline/debug_frame_load_bias_arm/maps.txt
new file mode 100644
index 0000000..10f1325
--- /dev/null
+++ b/libunwindstack/tests/files/offline/debug_frame_load_bias_arm/maps.txt
@@ -0,0 +1,3 @@
+8d1c000-8d1f000 r-xp 0 00:00 0   mediaserver
+f0b91000-f0c2c000 r-xp 0 00:00 0   libc.so
+f1a41000-f1a97000 r-xp 0 00:00 0   libbinder.so
diff --git a/libunwindstack/tests/files/offline/debug_frame_load_bias_arm/mediaserver b/libunwindstack/tests/files/offline/debug_frame_load_bias_arm/mediaserver
new file mode 100644
index 0000000..9e4a83f
--- /dev/null
+++ b/libunwindstack/tests/files/offline/debug_frame_load_bias_arm/mediaserver
Binary files differ
diff --git a/libunwindstack/tests/files/offline/debug_frame_load_bias_arm/regs.txt b/libunwindstack/tests/files/offline/debug_frame_load_bias_arm/regs.txt
new file mode 100644
index 0000000..f147247
--- /dev/null
+++ b/libunwindstack/tests/files/offline/debug_frame_load_bias_arm/regs.txt
@@ -0,0 +1,16 @@
+r0: 3
+r1: c0306201
+r2: ffd4a658
+r3: 0
+r4: f0c36d8c
+r5: ffd4a658
+r6: f0168000
+r7: 36
+r8: ffd4a678
+r9: f016802c
+r10: ffd4a660
+r11: 0
+ip: 0
+sp: ffd4a638
+lr: f0bb2413
+pc: f0be238c
diff --git a/libunwindstack/tests/files/offline/debug_frame_load_bias_arm/stack.data b/libunwindstack/tests/files/offline/debug_frame_load_bias_arm/stack.data
new file mode 100644
index 0000000..847c819
--- /dev/null
+++ b/libunwindstack/tests/files/offline/debug_frame_load_bias_arm/stack.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/eh_frame_hdr_begin_x86_64/libc.so b/libunwindstack/tests/files/offline/eh_frame_hdr_begin_x86_64/libc.so
new file mode 100644
index 0000000..46b6f45
--- /dev/null
+++ b/libunwindstack/tests/files/offline/eh_frame_hdr_begin_x86_64/libc.so
Binary files differ
diff --git a/libunwindstack/tests/files/offline/eh_frame_hdr_begin_x86_64/maps.txt b/libunwindstack/tests/files/offline/eh_frame_hdr_begin_x86_64/maps.txt
new file mode 100644
index 0000000..ac2e564
--- /dev/null
+++ b/libunwindstack/tests/files/offline/eh_frame_hdr_begin_x86_64/maps.txt
@@ -0,0 +1,2 @@
+561550b17000-561550b1a000 r-xp 0 00:00 0   unwind_test64
+7f4de61f6000-7f4de638b000 r-xp 0 00:00 0   libc.so
diff --git a/libunwindstack/tests/files/offline/eh_frame_hdr_begin_x86_64/regs.txt b/libunwindstack/tests/files/offline/eh_frame_hdr_begin_x86_64/regs.txt
new file mode 100644
index 0000000..38af274
--- /dev/null
+++ b/libunwindstack/tests/files/offline/eh_frame_hdr_begin_x86_64/regs.txt
@@ -0,0 +1,11 @@
+rax: 92134c6fbbdc12ff
+rbx: 0
+rcx: 92134c6fbbdc1200
+rdx: 92134c6fbbdc1200
+r8: 561552153034
+r12: 561550b17930
+r13: 7ffcc8597270
+rsi: 561552153034
+rbp: 7ffcc8596f30
+rsp: 7ffcc8596ce8
+rip: 561550b17a80
diff --git a/libunwindstack/tests/files/offline/eh_frame_hdr_begin_x86_64/stack.data b/libunwindstack/tests/files/offline/eh_frame_hdr_begin_x86_64/stack.data
new file mode 100644
index 0000000..cc7882b
--- /dev/null
+++ b/libunwindstack/tests/files/offline/eh_frame_hdr_begin_x86_64/stack.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/eh_frame_hdr_begin_x86_64/unwind_test64 b/libunwindstack/tests/files/offline/eh_frame_hdr_begin_x86_64/unwind_test64
new file mode 100644
index 0000000..ab0ef8f
--- /dev/null
+++ b/libunwindstack/tests/files/offline/eh_frame_hdr_begin_x86_64/unwind_test64
Binary files differ
diff --git a/libunwindstack/tests/files/offline/gnu_debugdata_arm/libandroid_runtime.so b/libunwindstack/tests/files/offline/gnu_debugdata_arm/libandroid_runtime.so
new file mode 100644
index 0000000..e4283e6
--- /dev/null
+++ b/libunwindstack/tests/files/offline/gnu_debugdata_arm/libandroid_runtime.so
Binary files differ
diff --git a/libunwindstack/tests/files/offline/gnu_debugdata_arm/maps.txt b/libunwindstack/tests/files/offline/gnu_debugdata_arm/maps.txt
new file mode 100644
index 0000000..1bcddb6
--- /dev/null
+++ b/libunwindstack/tests/files/offline/gnu_debugdata_arm/maps.txt
@@ -0,0 +1 @@
+f1f10000-f2049000 r-xp 00000000 00:00 0   libandroid_runtime.so
diff --git a/libunwindstack/tests/files/offline/gnu_debugdata_arm/regs.txt b/libunwindstack/tests/files/offline/gnu_debugdata_arm/regs.txt
new file mode 100644
index 0000000..c6a93dc
--- /dev/null
+++ b/libunwindstack/tests/files/offline/gnu_debugdata_arm/regs.txt
@@ -0,0 +1,2 @@
+pc: f1f6dc49
+sp: d8fe6930
diff --git a/libunwindstack/tests/files/offline/gnu_debugdata_arm/stack.data b/libunwindstack/tests/files/offline/gnu_debugdata_arm/stack.data
new file mode 100644
index 0000000..19cdf2d
--- /dev/null
+++ b/libunwindstack/tests/files/offline/gnu_debugdata_arm/stack.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_arm/137-cfi.odex b/libunwindstack/tests/files/offline/jit_debug_arm/137-cfi.odex
new file mode 100644
index 0000000..35a6bc5
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_arm/137-cfi.odex
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_arm/dalvikvm32 b/libunwindstack/tests/files/offline/jit_debug_arm/dalvikvm32
new file mode 100644
index 0000000..def299e
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_arm/dalvikvm32
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_arm/descriptor.data b/libunwindstack/tests/files/offline/jit_debug_arm/descriptor.data
new file mode 100644
index 0000000..7b876b5
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_arm/descriptor.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_arm/descriptor1.data b/libunwindstack/tests/files/offline/jit_debug_arm/descriptor1.data
new file mode 100644
index 0000000..3c468d6
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_arm/descriptor1.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_arm/entry0.data b/libunwindstack/tests/files/offline/jit_debug_arm/entry0.data
new file mode 100644
index 0000000..2c7689b
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_arm/entry0.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_arm/entry1.data b/libunwindstack/tests/files/offline/jit_debug_arm/entry1.data
new file mode 100644
index 0000000..22a35b8
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_arm/entry1.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_arm/entry2.data b/libunwindstack/tests/files/offline/jit_debug_arm/entry2.data
new file mode 100644
index 0000000..61f3927
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_arm/entry2.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_arm/entry3.data b/libunwindstack/tests/files/offline/jit_debug_arm/entry3.data
new file mode 100644
index 0000000..1a37628
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_arm/entry3.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_arm/entry4.data b/libunwindstack/tests/files/offline/jit_debug_arm/entry4.data
new file mode 100644
index 0000000..7ef62ca
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_arm/entry4.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_arm/entry5.data b/libunwindstack/tests/files/offline/jit_debug_arm/entry5.data
new file mode 100644
index 0000000..6d27c89
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_arm/entry5.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_arm/entry6.data b/libunwindstack/tests/files/offline/jit_debug_arm/entry6.data
new file mode 100644
index 0000000..bfbceea
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_arm/entry6.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_arm/jit0.data b/libunwindstack/tests/files/offline/jit_debug_arm/jit0.data
new file mode 100644
index 0000000..b78848e
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_arm/jit0.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_arm/jit1.data b/libunwindstack/tests/files/offline/jit_debug_arm/jit1.data
new file mode 100644
index 0000000..8f927ac
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_arm/jit1.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_arm/jit2.data b/libunwindstack/tests/files/offline/jit_debug_arm/jit2.data
new file mode 100644
index 0000000..1d1dfca
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_arm/jit2.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_arm/jit3.data b/libunwindstack/tests/files/offline/jit_debug_arm/jit3.data
new file mode 100644
index 0000000..89aeb43
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_arm/jit3.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_arm/jit4.data b/libunwindstack/tests/files/offline/jit_debug_arm/jit4.data
new file mode 100644
index 0000000..e076934
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_arm/jit4.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_arm/jit5.data b/libunwindstack/tests/files/offline/jit_debug_arm/jit5.data
new file mode 100644
index 0000000..17d6041
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_arm/jit5.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_arm/jit6.data b/libunwindstack/tests/files/offline/jit_debug_arm/jit6.data
new file mode 100644
index 0000000..aaff037
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_arm/jit6.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_arm/libart.so b/libunwindstack/tests/files/offline/jit_debug_arm/libart.so
new file mode 100644
index 0000000..0527893
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_arm/libart.so
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_arm/libartd.so b/libunwindstack/tests/files/offline/jit_debug_arm/libartd.so
new file mode 100644
index 0000000..8559056
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_arm/libartd.so
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_arm/libarttestd.so b/libunwindstack/tests/files/offline/jit_debug_arm/libarttestd.so
new file mode 100644
index 0000000..06dbf10
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_arm/libarttestd.so
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_arm/libc.so b/libunwindstack/tests/files/offline/jit_debug_arm/libc.so
new file mode 100644
index 0000000..9894e66
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_arm/libc.so
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_arm/maps.txt b/libunwindstack/tests/files/offline/jit_debug_arm/maps.txt
new file mode 100644
index 0000000..f25c781
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_arm/maps.txt
@@ -0,0 +1,8 @@
+ab0d3000-ab0d8000 r-xp 0 00:00 0   dalvikvm32
+dfe4e000-dfe7b000 r-xp 0 00:00 0   libarttestd.so
+e0447000-e0448000 r-xp 2000 00:00 0   137-cfi.odex
+e2796000-e4796000 r-xp 0 00:00 0   anonymous:e2796000
+e648e000-e690f000 r-xp 00000000 00:00 0  libart.so
+ed306000-ed801000 r-xp 0 00:00 0   libartd.so
+eda88000-edb23000 r-xp 0 00:00 0   libc.so
+ede4e000-ede50000 r-xp 0 00:00 0   anonymous:ede4e000
diff --git a/libunwindstack/tests/files/offline/jit_debug_arm/regs.txt b/libunwindstack/tests/files/offline/jit_debug_arm/regs.txt
new file mode 100644
index 0000000..0e20066
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_arm/regs.txt
@@ -0,0 +1,16 @@
+r0: dfe7c0f8
+r1: 0
+r2: 0
+r3: 40000000
+r4: e051ffb4
+r5: 0
+r6: e051ffc0
+r7: ede514e8
+r8: ff85d1a8
+r9: ed9210c0
+r10: 58
+r11: 0
+ip: edb26d04
+sp: ff85d180
+lr: edaff5af
+pc: dfe66a5e
diff --git a/libunwindstack/tests/files/offline/jit_debug_arm/stack.data b/libunwindstack/tests/files/offline/jit_debug_arm/stack.data
new file mode 100644
index 0000000..b2ff14e
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_arm/stack.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86/137-cfi.odex b/libunwindstack/tests/files/offline/jit_debug_x86/137-cfi.odex
new file mode 100644
index 0000000..870ac0a
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_x86/137-cfi.odex
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86/dalvikvm32 b/libunwindstack/tests/files/offline/jit_debug_x86/dalvikvm32
new file mode 100644
index 0000000..76ffad9
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_x86/dalvikvm32
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86/descriptor.data b/libunwindstack/tests/files/offline/jit_debug_x86/descriptor.data
new file mode 100644
index 0000000..466dae2
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_x86/descriptor.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86/entry0.data b/libunwindstack/tests/files/offline/jit_debug_x86/entry0.data
new file mode 100644
index 0000000..3a725e8
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_x86/entry0.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86/entry1.data b/libunwindstack/tests/files/offline/jit_debug_x86/entry1.data
new file mode 100644
index 0000000..767550f
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_x86/entry1.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86/entry2.data b/libunwindstack/tests/files/offline/jit_debug_x86/entry2.data
new file mode 100644
index 0000000..e7e492e
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_x86/entry2.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86/entry3.data b/libunwindstack/tests/files/offline/jit_debug_x86/entry3.data
new file mode 100644
index 0000000..65f9cd4
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_x86/entry3.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86/entry4.data b/libunwindstack/tests/files/offline/jit_debug_x86/entry4.data
new file mode 100644
index 0000000..30aa28c
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_x86/entry4.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86/entry5.data b/libunwindstack/tests/files/offline/jit_debug_x86/entry5.data
new file mode 100644
index 0000000..3c89673
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_x86/entry5.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86/entry6.data b/libunwindstack/tests/files/offline/jit_debug_x86/entry6.data
new file mode 100644
index 0000000..9c9b83c
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_x86/entry6.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86/jit0.data b/libunwindstack/tests/files/offline/jit_debug_x86/jit0.data
new file mode 100644
index 0000000..eaad142
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_x86/jit0.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86/jit1.data b/libunwindstack/tests/files/offline/jit_debug_x86/jit1.data
new file mode 100644
index 0000000..d534816
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_x86/jit1.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86/jit2.data b/libunwindstack/tests/files/offline/jit_debug_x86/jit2.data
new file mode 100644
index 0000000..dbeb886
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_x86/jit2.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86/jit3.data b/libunwindstack/tests/files/offline/jit_debug_x86/jit3.data
new file mode 100644
index 0000000..bf2142d
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_x86/jit3.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86/jit4.data b/libunwindstack/tests/files/offline/jit_debug_x86/jit4.data
new file mode 100644
index 0000000..e2ba1b0
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_x86/jit4.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86/jit5.data b/libunwindstack/tests/files/offline/jit_debug_x86/jit5.data
new file mode 100644
index 0000000..c27ba54
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_x86/jit5.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86/jit6.data b/libunwindstack/tests/files/offline/jit_debug_x86/jit6.data
new file mode 100644
index 0000000..5fc8fae
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_x86/jit6.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86/libartd.so b/libunwindstack/tests/files/offline/jit_debug_x86/libartd.so
new file mode 100644
index 0000000..92ed991
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_x86/libartd.so
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86/libarttestd.so b/libunwindstack/tests/files/offline/jit_debug_x86/libarttestd.so
new file mode 100644
index 0000000..5efae02
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_x86/libarttestd.so
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86/libc.so b/libunwindstack/tests/files/offline/jit_debug_x86/libc.so
new file mode 100644
index 0000000..9c78790
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_x86/libc.so
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86/maps.txt b/libunwindstack/tests/files/offline/jit_debug_x86/maps.txt
new file mode 100644
index 0000000..db4f9f7
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_x86/maps.txt
@@ -0,0 +1,6 @@
+56573000-56577000 r-xp 0 00:00 0   dalvikvm32
+eb833000-eb8cc000 r-xp 0 00:00 0   libarttestd.so
+ec606000-ec607000 r-xp 2000 00:00 0   137-cfi.odex
+ee74c000-f074c000 r-xp 0 00:00 0   anonymous:ee74c000
+f6be1000-f732b000 r-xp 0 00:00 0   libartd.so
+f734b000-f74fc000 r-xp 0 00:00 0   libc.so
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86/regs.txt b/libunwindstack/tests/files/offline/jit_debug_x86/regs.txt
new file mode 100644
index 0000000..f68305b
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_x86/regs.txt
@@ -0,0 +1,9 @@
+eax: eb8cccd0
+ebx: eb8cccd0
+ecx: ff
+edx: ffeb2ca8
+ebp: ffeb5298
+edi: ffeb5c08
+esi: ffeb5c00
+esp: ffeb5280
+eip: eb89bfb8
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86/stack.data b/libunwindstack/tests/files/offline/jit_debug_x86/stack.data
new file mode 100644
index 0000000..c345762
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_x86/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/tests/files/offline/offset_arm/libc.so b/libunwindstack/tests/files/offline/offset_arm/libc.so
new file mode 100644
index 0000000..9f5c8ca
--- /dev/null
+++ b/libunwindstack/tests/files/offline/offset_arm/libc.so
Binary files differ
diff --git a/libunwindstack/tests/files/offline/offset_arm/libunwindstack_test b/libunwindstack/tests/files/offline/offset_arm/libunwindstack_test
new file mode 100644
index 0000000..7a30bfa
--- /dev/null
+++ b/libunwindstack/tests/files/offline/offset_arm/libunwindstack_test
Binary files differ
diff --git a/libunwindstack/tests/files/offline/offset_arm/maps.txt b/libunwindstack/tests/files/offline/offset_arm/maps.txt
new file mode 100644
index 0000000..6224464
--- /dev/null
+++ b/libunwindstack/tests/files/offline/offset_arm/maps.txt
@@ -0,0 +1,2 @@
+2b6c000-2e92000 r-xp 42000 00:00 0   libunwindstack_test
+f4135000-f41a9000 r-xp 25000 00:00 0   libc.so
diff --git a/libunwindstack/tests/files/offline/offset_arm/regs.txt b/libunwindstack/tests/files/offline/offset_arm/regs.txt
new file mode 100644
index 0000000..1f4ac8f
--- /dev/null
+++ b/libunwindstack/tests/files/offline/offset_arm/regs.txt
@@ -0,0 +1,16 @@
+r0: 5
+r1: 5
+r2: 4
+r3: 1
+r4: 73804b6b
+r5: f3c9c000
+r6: 2ea09ac
+r7: 10624dd3
+r8: f41b5d8c
+r9: f3c9c000
+r10: 6f17
+r11: f3c94048
+ip: 2ea0807
+sp: f43d2ccc
+lr: 2e55fef
+pc: 2e55fa0
diff --git a/libunwindstack/tests/files/offline/offset_arm/stack0.data b/libunwindstack/tests/files/offline/offset_arm/stack0.data
new file mode 100644
index 0000000..23a9874
--- /dev/null
+++ b/libunwindstack/tests/files/offline/offset_arm/stack0.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/offset_arm/stack1.data b/libunwindstack/tests/files/offline/offset_arm/stack1.data
new file mode 100644
index 0000000..49bdd1e
--- /dev/null
+++ b/libunwindstack/tests/files/offline/offset_arm/stack1.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/straddle_arm/libbase.so b/libunwindstack/tests/files/offline/straddle_arm/libbase.so
new file mode 100644
index 0000000..d1f16ee
--- /dev/null
+++ b/libunwindstack/tests/files/offline/straddle_arm/libbase.so
Binary files differ
diff --git a/libunwindstack/tests/files/offline/straddle_arm/libc.so b/libunwindstack/tests/files/offline/straddle_arm/libc.so
new file mode 100644
index 0000000..4dc19ca
--- /dev/null
+++ b/libunwindstack/tests/files/offline/straddle_arm/libc.so
Binary files differ
diff --git a/libunwindstack/tests/files/offline/straddle_arm/maps.txt b/libunwindstack/tests/files/offline/straddle_arm/maps.txt
new file mode 100644
index 0000000..8c26479
--- /dev/null
+++ b/libunwindstack/tests/files/offline/straddle_arm/maps.txt
@@ -0,0 +1,4 @@
+f2d9a000-f2da7fff r-xp 00000000 00:00 0   libbase.so
+f3002000-f3005fff rw-p 00000000 00:00 0   [stack:25941]
+f31d0000-f326bfff r-xp 00000000 00:00 0   libc.so
+f3352000-f336bfff r-xp 00000000 00:00 0   /does/not/exist/libhidlbase.so
diff --git a/libunwindstack/tests/files/offline/straddle_arm/regs.txt b/libunwindstack/tests/files/offline/straddle_arm/regs.txt
new file mode 100644
index 0000000..3baedf3
--- /dev/null
+++ b/libunwindstack/tests/files/offline/straddle_arm/regs.txt
@@ -0,0 +1,3 @@
+pc: f31ea9f8
+sp: e9c866f8
+lr: f31f179f
diff --git a/libunwindstack/tests/files/offline/straddle_arm/stack.data b/libunwindstack/tests/files/offline/straddle_arm/stack.data
new file mode 100644
index 0000000..83aeb4a
--- /dev/null
+++ b/libunwindstack/tests/files/offline/straddle_arm/stack.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/straddle_arm64/libunwindstack_test b/libunwindstack/tests/files/offline/straddle_arm64/libunwindstack_test
new file mode 100644
index 0000000..092fc3a
--- /dev/null
+++ b/libunwindstack/tests/files/offline/straddle_arm64/libunwindstack_test
Binary files differ
diff --git a/libunwindstack/tests/files/offline/straddle_arm64/maps.txt b/libunwindstack/tests/files/offline/straddle_arm64/maps.txt
new file mode 100644
index 0000000..bdf29b5
--- /dev/null
+++ b/libunwindstack/tests/files/offline/straddle_arm64/maps.txt
@@ -0,0 +1,2 @@
+00000064d05ab000-00000064d0a6cfff r-xp 00000000 00:00 0  libunwindstack_test
+0000007fe0d64000-0000007fe0d84fff rw-p 00000000 00:00 0  [stack]
diff --git a/libunwindstack/tests/files/offline/straddle_arm64/regs.txt b/libunwindstack/tests/files/offline/straddle_arm64/regs.txt
new file mode 100644
index 0000000..ff8a936
--- /dev/null
+++ b/libunwindstack/tests/files/offline/straddle_arm64/regs.txt
@@ -0,0 +1,4 @@
+pc: 00000064d09d4fd8
+sp: 0000007fe0d84040
+lr: 00000064d09d507c
+x29: 0000007fe0d84070
diff --git a/libunwindstack/tests/files/offline/straddle_arm64/stack.data b/libunwindstack/tests/files/offline/straddle_arm64/stack.data
new file mode 100644
index 0000000..824d0e2
--- /dev/null
+++ b/libunwindstack/tests/files/offline/straddle_arm64/stack.data
Binary files differ
diff --git a/libunwindstack/tools/unwind.cpp b/libunwindstack/tools/unwind.cpp
new file mode 100644
index 0000000..22ca7bf
--- /dev/null
+++ b/libunwindstack/tools/unwind.cpp
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2016 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 <elf.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <signal.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ptrace.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <unwindstack/DexFiles.h>
+#include <unwindstack/Elf.h>
+#include <unwindstack/JitDebug.h>
+#include <unwindstack/Maps.h>
+#include <unwindstack/Memory.h>
+#include <unwindstack/Regs.h>
+#include <unwindstack/Unwinder.h>
+
+static bool Attach(pid_t pid) {
+  if (ptrace(PTRACE_ATTACH, pid, 0, 0) == -1) {
+    return false;
+  }
+
+  // Allow at least 1 second to attach properly.
+  for (size_t i = 0; i < 1000; i++) {
+    siginfo_t si;
+    if (ptrace(PTRACE_GETSIGINFO, pid, 0, &si) == 0) {
+      return true;
+    }
+    usleep(1000);
+  }
+  printf("%d: Failed to stop.\n", pid);
+  return false;
+}
+
+void DoUnwind(pid_t pid) {
+  unwindstack::RemoteMaps remote_maps(pid);
+  if (!remote_maps.Parse()) {
+    printf("Failed to parse map data.\n");
+    return;
+  }
+
+  unwindstack::Regs* regs = unwindstack::Regs::RemoteGet(pid);
+  if (regs == nullptr) {
+    printf("Unable to get remote reg data\n");
+    return;
+  }
+
+  printf("ABI: ");
+  switch (regs->Arch()) {
+    case unwindstack::ARCH_ARM:
+      printf("arm");
+      break;
+    case unwindstack::ARCH_X86:
+      printf("x86");
+      break;
+    case unwindstack::ARCH_ARM64:
+      printf("arm64");
+      break;
+    case unwindstack::ARCH_X86_64:
+      printf("x86_64");
+      break;
+    case unwindstack::ARCH_MIPS:
+      printf("mips");
+      break;
+    case unwindstack::ARCH_MIPS64:
+      printf("mips64");
+      break;
+    default:
+      printf("unknown\n");
+      return;
+  }
+  printf("\n");
+
+  auto process_memory = unwindstack::Memory::CreateProcessMemory(pid);
+  unwindstack::Unwinder unwinder(128, &remote_maps, regs, process_memory);
+
+  unwindstack::JitDebug jit_debug(process_memory);
+  unwinder.SetJitDebug(&jit_debug, regs->Arch());
+
+  unwindstack::DexFiles dex_files(process_memory);
+  unwinder.SetDexFiles(&dex_files, regs->Arch());
+
+  unwinder.Unwind();
+
+  // Print the frames.
+  for (size_t i = 0; i < unwinder.NumFrames(); i++) {
+    printf("%s\n", unwinder.FormatFrame(i).c_str());
+  }
+}
+
+int main(int argc, char** argv) {
+  if (argc != 2) {
+    printf("Usage: unwind <PID>\n");
+    return 1;
+  }
+
+  pid_t pid = atoi(argv[1]);
+  if (!Attach(pid)) {
+    printf("Failed to attach to pid %d: %s\n", pid, strerror(errno));
+    return 1;
+  }
+
+  DoUnwind(pid);
+
+  ptrace(PTRACE_DETACH, pid, 0, 0);
+
+  return 0;
+}
diff --git a/libunwindstack/tools/unwind_for_offline.cpp b/libunwindstack/tools/unwind_for_offline.cpp
new file mode 100644
index 0000000..640992f
--- /dev/null
+++ b/libunwindstack/tools/unwind_for_offline.cpp
@@ -0,0 +1,304 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#define _GNU_SOURCE 1
+#include <errno.h>
+#include <inttypes.h>
+#include <signal.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ptrace.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <algorithm>
+#include <memory>
+#include <string>
+#include <unordered_map>
+#include <utility>
+#include <vector>
+
+#include <unwindstack/Elf.h>
+#include <unwindstack/JitDebug.h>
+#include <unwindstack/Maps.h>
+#include <unwindstack/Memory.h>
+#include <unwindstack/Regs.h>
+#include <unwindstack/Unwinder.h>
+
+#include <android-base/stringprintf.h>
+
+struct map_info_t {
+  uint64_t start;
+  uint64_t end;
+  uint64_t offset;
+  std::string name;
+};
+
+static bool Attach(pid_t pid) {
+  if (ptrace(PTRACE_ATTACH, pid, 0, 0) == -1) {
+    return false;
+  }
+
+  // Allow at least 1 second to attach properly.
+  for (size_t i = 0; i < 1000; i++) {
+    siginfo_t si;
+    if (ptrace(PTRACE_GETSIGINFO, pid, 0, &si) == 0) {
+      return true;
+    }
+    usleep(1000);
+  }
+  printf("%d: Failed to stop.\n", pid);
+  return false;
+}
+
+bool SaveRegs(unwindstack::Regs* regs) {
+  std::unique_ptr<FILE, decltype(&fclose)> fp(fopen("regs.txt", "w+"), &fclose);
+  if (fp == nullptr) {
+    perror("Failed to create file regs.txt");
+    return false;
+  }
+  regs->IterateRegisters([&fp](const char* name, uint64_t value) {
+    fprintf(fp.get(), "%s: %" PRIx64 "\n", name, value);
+  });
+
+  return true;
+}
+
+bool SaveStack(pid_t pid, const std::vector<std::pair<uint64_t, uint64_t>>& stacks) {
+  for (size_t i = 0; i < stacks.size(); i++) {
+    std::string file_name;
+    if (stacks.size() != 1) {
+      file_name = "stack" + std::to_string(i) + ".data";
+    } else {
+      file_name = "stack.data";
+    }
+
+    // Do this first, so if it fails, we don't create the file.
+    uint64_t sp_start = stacks[i].first;
+    uint64_t sp_end = stacks[i].second;
+    std::vector<uint8_t> buffer(sp_end - sp_start);
+    auto process_memory = unwindstack::Memory::CreateProcessMemory(pid);
+    if (!process_memory->Read(sp_start, buffer.data(), buffer.size())) {
+      printf("Unable to read stack data.\n");
+      return false;
+    }
+
+    printf("Saving the stack 0x%" PRIx64 "-0x%" PRIx64 "\n", sp_start, sp_end);
+
+    std::unique_ptr<FILE, decltype(&fclose)> fp(fopen(file_name.c_str(), "w+"), &fclose);
+    if (fp == nullptr) {
+      perror("Failed to create stack.data");
+      return false;
+    }
+
+    size_t bytes = fwrite(&sp_start, 1, sizeof(sp_start), fp.get());
+    if (bytes != sizeof(sp_start)) {
+      printf("Failed to write sp_start data: sizeof(sp_start) %zu, written %zu\n", sizeof(sp_start),
+             bytes);
+      return false;
+    }
+
+    bytes = fwrite(buffer.data(), 1, buffer.size(), fp.get());
+    if (bytes != buffer.size()) {
+      printf("Failed to write all stack data: stack size %zu, written %zu\n", buffer.size(), bytes);
+      return false;
+    }
+  }
+
+  return true;
+}
+
+bool CreateElfFromMemory(std::shared_ptr<unwindstack::Memory>& memory, map_info_t* info) {
+  std::string cur_name;
+  if (info->name.empty()) {
+    cur_name = android::base::StringPrintf("anonymous_%" PRIx64, info->start);
+  } else {
+    cur_name = android::base::StringPrintf("%s_%" PRIx64, basename(info->name.c_str()), info->start);
+  }
+
+  std::vector<uint8_t> buffer(info->end - info->start);
+  // If this is a mapped in file, it might not be possible to read the entire
+  // map, so read all that is readable.
+  size_t bytes = memory->Read(info->start, buffer.data(), buffer.size());
+  if (bytes == 0) {
+    printf("Cannot read data from address %" PRIx64 " length %zu\n", info->start, buffer.size());
+    return false;
+  }
+
+  std::unique_ptr<FILE, decltype(&fclose)> output(fopen(cur_name.c_str(), "w+"), &fclose);
+  if (output == nullptr) {
+    perror((std::string("Cannot create ") + cur_name).c_str());
+    return false;
+  }
+
+  size_t bytes_written = fwrite(buffer.data(), 1, bytes, output.get());
+  if (bytes_written != bytes) {
+    printf("Failed to write all data to file: bytes read %zu, written %zu\n", bytes, bytes_written);
+    return false;
+  }
+
+  // Replace the name with the new name.
+  info->name = cur_name;
+
+  return true;
+}
+
+bool CopyElfFromFile(map_info_t* info) {
+  std::unique_ptr<FILE, decltype(&fclose)> fp(fopen(info->name.c_str(), "r"), &fclose);
+  if (fp == nullptr) {
+    perror((std::string("Cannot open ") + info->name).c_str());
+    return false;
+  }
+
+  std::string cur_name = basename(info->name.c_str());
+  std::unique_ptr<FILE, decltype(&fclose)> output(fopen(cur_name.c_str(), "w+"), &fclose);
+  if (output == nullptr) {
+    perror((std::string("Cannot create file " + cur_name)).c_str());
+    return false;
+  }
+  std::vector<uint8_t> buffer(10000);
+  size_t bytes;
+  while ((bytes = fread(buffer.data(), 1, buffer.size(), fp.get())) > 0) {
+    size_t bytes_written = fwrite(buffer.data(), 1, bytes, output.get());
+    if (bytes_written != bytes) {
+      printf("Bytes written doesn't match bytes read: read %zu, written %zu\n", bytes,
+             bytes_written);
+      return false;
+    }
+  }
+
+  // Replace the name with the new name.
+  info->name = cur_name;
+
+  return true;
+}
+
+int SaveData(pid_t pid) {
+  unwindstack::Regs* regs = unwindstack::Regs::RemoteGet(pid);
+  if (regs == nullptr) {
+    printf("Unable to get remote reg data.\n");
+    return 1;
+  }
+
+  unwindstack::RemoteMaps maps(pid);
+  if (!maps.Parse()) {
+    printf("Unable to parse maps.\n");
+    return 1;
+  }
+
+  // Save the current state of the registers.
+  if (!SaveRegs(regs)) {
+    return 1;
+  }
+
+  // Do an unwind so we know how much of the stack to save, and what
+  // elf files are involved.
+  uint64_t sp = regs->sp();
+  auto process_memory = unwindstack::Memory::CreateProcessMemory(pid);
+  unwindstack::JitDebug jit_debug(process_memory);
+  unwindstack::Unwinder unwinder(1024, &maps, regs, process_memory);
+  unwinder.SetJitDebug(&jit_debug, regs->Arch());
+  unwinder.Unwind();
+
+  std::unordered_map<uint64_t, map_info_t> maps_by_start;
+  std::vector<std::pair<uint64_t, uint64_t>> stacks;
+  uint64_t sp_map_start = 0;
+  unwindstack::MapInfo* map_info = maps.Find(sp);
+  if (map_info != nullptr) {
+    stacks.emplace_back(std::make_pair(sp, map_info->end));
+    sp_map_start = map_info->start;
+  }
+
+  for (auto frame : unwinder.frames()) {
+    map_info = maps.Find(frame.sp);
+    if (map_info != nullptr && sp_map_start != map_info->start) {
+      stacks.emplace_back(std::make_pair(frame.sp, map_info->end));
+      sp_map_start = map_info->start;
+    }
+
+    if (maps_by_start.count(frame.map_start) == 0) {
+      auto info = &maps_by_start[frame.map_start];
+      info->start = frame.map_start;
+      info->end = frame.map_end;
+      info->offset = frame.map_offset;
+      info->name = frame.map_name;
+      if (!CopyElfFromFile(info)) {
+        // Try to create the elf from memory, this will handle cases where
+        // the data only exists in memory such as vdso data on x86.
+        if (!CreateElfFromMemory(process_memory, info)) {
+          printf("Ignoring map ");
+          if (!info->name.empty()) {
+            printf("%s\n", info->name.c_str());
+          } else {
+            printf("anonymous:%" PRIx64 "\n", info->start);
+          }
+        }
+      }
+    }
+  }
+
+  for (size_t i = 0; i < unwinder.NumFrames(); i++) {
+    printf("%s\n", unwinder.FormatFrame(i).c_str());
+  }
+
+  if (!SaveStack(pid, stacks)) {
+    return 1;
+  }
+
+  std::vector<std::pair<uint64_t, map_info_t>> sorted_maps(maps_by_start.begin(),
+                                                           maps_by_start.end());
+  std::sort(sorted_maps.begin(), sorted_maps.end(),
+            [](auto& a, auto& b) { return a.first < b.first; });
+
+  std::unique_ptr<FILE, decltype(&fclose)> fp(fopen("maps.txt", "w+"), &fclose);
+  if (fp == nullptr) {
+    perror("Failed to create maps.txt");
+    return false;
+  }
+
+  for (auto& element : sorted_maps) {
+    map_info_t& map = element.second;
+    fprintf(fp.get(), "%" PRIx64 "-%" PRIx64 " r-xp %" PRIx64 " 00:00 0", map.start, map.end,
+            map.offset);
+    if (!map.name.empty()) {
+      fprintf(fp.get(), "   %s", map.name.c_str());
+    }
+    fprintf(fp.get(), "\n");
+  }
+
+  return 0;
+}
+
+int main(int argc, char** argv) {
+  if (argc != 2) {
+    printf("Usage: unwind_for_offline <PID>\n");
+    return 1;
+  }
+
+  pid_t pid = atoi(argv[1]);
+  if (!Attach(pid)) {
+    printf("Failed to attach to pid %d: %s\n", pid, strerror(errno));
+    return 1;
+  }
+
+  int return_code = SaveData(pid);
+
+  ptrace(PTRACE_DETACH, pid, 0, 0);
+
+  return return_code;
+}
diff --git a/libunwindstack/tools/unwind_info.cpp b/libunwindstack/tools/unwind_info.cpp
new file mode 100644
index 0000000..266a6db
--- /dev/null
+++ b/libunwindstack/tools/unwind_info.cpp
@@ -0,0 +1,201 @@
+/*
+ * Copyright (C) 2016 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 <elf.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <unwindstack/DwarfSection.h>
+#include <unwindstack/DwarfStructs.h>
+#include <unwindstack/Elf.h>
+#include <unwindstack/ElfInterface.h>
+#include <unwindstack/Log.h>
+
+#include "ArmExidx.h"
+#include "ElfInterfaceArm.h"
+
+namespace unwindstack {
+
+void DumpArm(Elf* elf, ElfInterfaceArm* interface) {
+  if (interface == nullptr) {
+    printf("No ARM Unwind Information.\n\n");
+    return;
+  }
+
+  printf("ARM Unwind Information:\n");
+  for (const auto& entry : interface->pt_loads()) {
+    uint64_t load_bias = entry.second.table_offset;
+    printf(" PC Range 0x%" PRIx64 " - 0x%" PRIx64 "\n", entry.second.offset + load_bias,
+           entry.second.table_size + load_bias);
+    for (auto pc : *interface) {
+      std::string name;
+      printf("  PC 0x%" PRIx64, pc + load_bias);
+      uint64_t func_offset;
+      if (elf->GetFunctionName(pc + load_bias, &name, &func_offset) && !name.empty()) {
+        printf(" <%s>", name.c_str());
+      }
+      printf("\n");
+      uint64_t entry;
+      if (!interface->FindEntry(pc, &entry)) {
+        printf("    Cannot find entry for address.\n");
+        continue;
+      }
+      ArmExidx arm(nullptr, interface->memory(), nullptr);
+      arm.set_log(ARM_LOG_FULL);
+      arm.set_log_skip_execution(true);
+      arm.set_log_indent(2);
+      if (!arm.ExtractEntryData(entry)) {
+        if (arm.status() != ARM_STATUS_NO_UNWIND) {
+          printf("    Error trying to extract data.\n");
+        }
+        continue;
+      }
+      if (arm.data()->size() > 0) {
+        if (!arm.Eval() && arm.status() != ARM_STATUS_NO_UNWIND) {
+          printf("      Error trying to evaluate dwarf data.\n");
+        }
+      }
+    }
+  }
+  printf("\n");
+}
+
+void DumpDwarfSection(Elf* elf, DwarfSection* section, uint64_t) {
+  for (const DwarfFde* fde : *section) {
+    // Sometimes there are entries that have empty length, skip those since
+    // they don't contain any interesting information.
+    if (fde == nullptr || fde->pc_start == fde->pc_end) {
+      continue;
+    }
+    printf("\n  PC 0x%" PRIx64 "-0x%" PRIx64, fde->pc_start, fde->pc_end);
+    std::string name;
+    uint64_t func_offset;
+    if (elf->GetFunctionName(fde->pc_start, &name, &func_offset) && !name.empty()) {
+      printf(" <%s>", name.c_str());
+    }
+    printf("\n");
+    if (!section->Log(2, UINT64_MAX, fde)) {
+      printf("Failed to process cfa information for entry at 0x%" PRIx64 "\n", fde->pc_start);
+    }
+  }
+}
+
+int GetElfInfo(const char* file, uint64_t offset) {
+  // Send all log messages to stdout.
+  log_to_stdout(true);
+
+  MemoryFileAtOffset* memory = new MemoryFileAtOffset;
+  if (!memory->Init(file, offset)) {
+    // Initializatation failed.
+    printf("Failed to init\n");
+    return 1;
+  }
+
+  Elf elf(memory);
+  if (!elf.Init(true) || !elf.valid()) {
+    printf("%s is not a valid elf file.\n", file);
+    return 1;
+  }
+
+  std::string soname;
+  if (elf.GetSoname(&soname)) {
+    printf("Soname: %s\n", soname.c_str());
+  }
+
+  ElfInterface* interface = elf.interface();
+  if (elf.machine_type() == EM_ARM) {
+    DumpArm(&elf, reinterpret_cast<ElfInterfaceArm*>(interface));
+    printf("\n");
+  }
+
+  if (interface->eh_frame() != nullptr) {
+    printf("eh_frame information:\n");
+    DumpDwarfSection(&elf, interface->eh_frame(), elf.GetLoadBias());
+    printf("\n");
+  } else {
+    printf("\nno eh_frame information\n");
+  }
+
+  if (interface->debug_frame() != nullptr) {
+    printf("\ndebug_frame information:\n");
+    DumpDwarfSection(&elf, interface->debug_frame(), elf.GetLoadBias());
+    printf("\n");
+  } else {
+    printf("\nno debug_frame information\n");
+  }
+
+  // If there is a gnu_debugdata interface, dump the information for that.
+  ElfInterface* gnu_debugdata_interface = elf.gnu_debugdata_interface();
+  if (gnu_debugdata_interface != nullptr) {
+    if (gnu_debugdata_interface->eh_frame() != nullptr) {
+      printf("\ngnu_debugdata (eh_frame):\n");
+      DumpDwarfSection(&elf, gnu_debugdata_interface->eh_frame(), 0);
+      printf("\n");
+    }
+    if (gnu_debugdata_interface->debug_frame() != nullptr) {
+      printf("\ngnu_debugdata (debug_frame):\n");
+      DumpDwarfSection(&elf, gnu_debugdata_interface->debug_frame(), 0);
+      printf("\n");
+    }
+  } else {
+    printf("\nno valid gnu_debugdata information\n");
+  }
+
+  return 0;
+}
+
+}  // namespace unwindstack
+
+int main(int argc, char** argv) {
+  if (argc != 2 && argc != 3) {
+    printf("Usage: unwind_info ELF_FILE [OFFSET]\n");
+    printf("  ELF_FILE\n");
+    printf("    The path to an elf file.\n");
+    printf("  OFFSET\n");
+    printf("    Use the offset into the ELF file as the beginning of the elf.\n");
+    return 1;
+  }
+
+  struct stat st;
+  if (stat(argv[1], &st) == -1) {
+    printf("Cannot stat %s: %s\n", argv[1], strerror(errno));
+    return 1;
+  }
+  if (!S_ISREG(st.st_mode)) {
+    printf("%s is not a regular file.\n", argv[1]);
+    return 1;
+  }
+
+  uint64_t offset = 0;
+  if (argc == 3) {
+    char* end;
+    offset = strtoull(argv[2], &end, 16);
+    if (*end != '\0') {
+      printf("Malformed OFFSET value: %s\n", argv[2]);
+      return 1;
+    }
+  }
+
+  return unwindstack::GetElfInfo(argv[1], offset);
+}
diff --git a/libunwindstack/tools/unwind_reg_info.cpp b/libunwindstack/tools/unwind_reg_info.cpp
new file mode 100644
index 0000000..0f01566
--- /dev/null
+++ b/libunwindstack/tools/unwind_reg_info.cpp
@@ -0,0 +1,280 @@
+/*
+ * 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 <elf.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <unwindstack/DwarfLocation.h>
+#include <unwindstack/DwarfMemory.h>
+#include <unwindstack/DwarfSection.h>
+#include <unwindstack/DwarfStructs.h>
+#include <unwindstack/Elf.h>
+#include <unwindstack/ElfInterface.h>
+#include <unwindstack/Log.h>
+
+#include "ArmExidx.h"
+#include "DwarfOp.h"
+#include "ElfInterfaceArm.h"
+
+namespace unwindstack {
+
+void PrintSignedValue(int64_t value) {
+  if (value < 0) {
+    printf("- %" PRId64, -value);
+  } else if (value > 0) {
+    printf("+ %" PRId64, value);
+  }
+}
+
+void PrintExpression(Memory* memory, uint8_t class_type, uint64_t end, uint64_t length) {
+  std::vector<std::string> lines;
+  DwarfMemory dwarf_memory(memory);
+  if (class_type == ELFCLASS32) {
+    DwarfOp<uint32_t> op(&dwarf_memory, nullptr);
+    op.GetLogInfo(end - length, end, &lines);
+  } else {
+    DwarfOp<uint64_t> op(&dwarf_memory, nullptr);
+    op.GetLogInfo(end - length, end, &lines);
+  }
+  for (auto& line : lines) {
+    printf("    %s\n", line.c_str());
+  }
+}
+
+void PrintRegInformation(DwarfSection* section, Memory* memory, uint64_t pc, uint8_t class_type) {
+  const DwarfFde* fde = section->GetFdeFromPc(pc);
+  if (fde == nullptr) {
+    printf("  No fde found.\n");
+    return;
+  }
+
+  dwarf_loc_regs_t regs;
+  if (!section->GetCfaLocationInfo(pc, fde, &regs)) {
+    printf("  Cannot get location information.\n");
+    return;
+  }
+
+  std::vector<std::pair<uint32_t, DwarfLocation>> loc_regs;
+  for (auto& loc : regs) {
+    loc_regs.push_back(loc);
+  }
+  std::sort(loc_regs.begin(), loc_regs.end(), [](auto a, auto b) {
+    if (a.first == CFA_REG) {
+      return true;
+    } else if (b.first == CFA_REG) {
+      return false;
+    }
+    return a.first < b.first;
+  });
+
+  for (auto& entry : loc_regs) {
+    const DwarfLocation* loc = &entry.second;
+    if (entry.first == CFA_REG) {
+      printf("  cfa = ");
+    } else {
+      printf("  r%d = ", entry.first);
+    }
+    switch (loc->type) {
+      case DWARF_LOCATION_OFFSET:
+        printf("[cfa ");
+        PrintSignedValue(loc->values[0]);
+        printf("]\n");
+        break;
+
+      case DWARF_LOCATION_VAL_OFFSET:
+        printf("cfa ");
+        PrintSignedValue(loc->values[0]);
+        printf("\n");
+        break;
+
+      case DWARF_LOCATION_REGISTER:
+        printf("r%" PRId64 " ", loc->values[0]);
+        PrintSignedValue(loc->values[1]);
+        printf("\n");
+        break;
+
+      case DWARF_LOCATION_EXPRESSION: {
+        printf("EXPRESSION\n");
+        PrintExpression(memory, class_type, loc->values[1], loc->values[0]);
+        break;
+      }
+
+      case DWARF_LOCATION_VAL_EXPRESSION: {
+        printf("VAL EXPRESSION\n");
+        PrintExpression(memory, class_type, loc->values[1], loc->values[0]);
+        break;
+      }
+
+      case DWARF_LOCATION_UNDEFINED:
+        printf("undefine\n");
+        break;
+
+      case DWARF_LOCATION_INVALID:
+        printf("INVALID\n");
+        break;
+    }
+  }
+}
+
+void PrintArmRegInformation(ElfInterfaceArm* interface, uint64_t pc) {
+  printf("\nArm exidx:\n");
+  uint64_t entry_offset;
+  if (!interface->FindEntry(pc, &entry_offset)) {
+    return;
+  }
+
+  ArmExidx arm(nullptr, interface->memory(), nullptr);
+
+  log_to_stdout(true);
+  arm.set_log(ARM_LOG_BY_REG);
+  arm.set_log_skip_execution(true);
+  arm.set_log_indent(1);
+  if (!arm.ExtractEntryData(entry_offset)) {
+    if (arm.status() != ARM_STATUS_NO_UNWIND) {
+      printf("  Error trying to extract data.\n");
+    }
+    return;
+  }
+  if (arm.data()->size() != 0 && arm.Eval()) {
+    arm.LogByReg();
+  } else {
+    printf("  Error tring to evaluate exidx data.\n");
+  }
+}
+
+int GetInfo(const char* file, uint64_t pc) {
+  MemoryFileAtOffset* memory = new MemoryFileAtOffset;
+  if (!memory->Init(file, 0)) {
+    // Initializatation failed.
+    printf("Failed to init\n");
+    return 1;
+  }
+
+  Elf elf(memory);
+  if (!elf.Init(true) || !elf.valid()) {
+    printf("%s is not a valid elf file.\n", file);
+    return 1;
+  }
+
+  ElfInterface* interface = elf.interface();
+  uint64_t load_bias = elf.GetLoadBias();
+  if (pc < load_bias) {
+    printf("PC is less than load bias.\n");
+    return 1;
+  }
+
+  std::string soname;
+  if (elf.GetSoname(&soname)) {
+    printf("Soname: %s\n\n", soname.c_str());
+  }
+
+  printf("PC 0x%" PRIx64, pc);
+  std::string function_name;
+  uint64_t function_offset;
+  if (elf.GetFunctionName(pc, &function_name, &function_offset)) {
+    printf(" (%s)", function_name.c_str());
+  }
+  printf(":\n");
+
+  if (elf.machine_type() == EM_ARM) {
+    PrintArmRegInformation(reinterpret_cast<ElfInterfaceArm*>(interface), pc - load_bias);
+  }
+
+  DwarfSection* section = interface->eh_frame();
+  if (section != nullptr) {
+    printf("\neh_frame:\n");
+    PrintRegInformation(section, memory, pc, elf.class_type());
+  } else {
+    printf("\nno eh_frame information\n");
+  }
+
+  section = interface->debug_frame();
+  if (section != nullptr) {
+    printf("\ndebug_frame:\n");
+    PrintRegInformation(section, memory, pc, elf.class_type());
+    printf("\n");
+  } else {
+    printf("\nno debug_frame information\n");
+  }
+
+  // If there is a gnu_debugdata interface, dump the information for that.
+  ElfInterface* gnu_debugdata_interface = elf.gnu_debugdata_interface();
+  if (gnu_debugdata_interface != nullptr) {
+    section = gnu_debugdata_interface->eh_frame();
+    if (section != nullptr) {
+      printf("\ngnu_debugdata (eh_frame):\n");
+      PrintRegInformation(section, gnu_debugdata_interface->memory(), pc, elf.class_type());
+      printf("\n");
+    } else {
+      printf("\nno gnu_debugdata (eh_frame)\n");
+    }
+
+    section = gnu_debugdata_interface->debug_frame();
+    if (section != nullptr) {
+      printf("\ngnu_debugdata (debug_frame):\n");
+      PrintRegInformation(section, gnu_debugdata_interface->memory(), pc, elf.class_type());
+      printf("\n");
+    } else {
+      printf("\nno gnu_debugdata (debug_frame)\n");
+    }
+  } else {
+    printf("\nno valid gnu_debugdata information\n");
+  }
+
+  return 0;
+}
+
+}  // namespace unwindstack
+
+int main(int argc, char** argv) {
+  if (argc != 3) {
+    printf("Usage: unwind_reg_info ELF_FILE PC\n");
+    printf("  ELF_FILE\n");
+    printf("    The path to an elf file.\n");
+    printf("  PC\n");
+    printf("    The pc for which the register information should be obtained.\n");
+    return 1;
+  }
+
+  struct stat st;
+  if (stat(argv[1], &st) == -1) {
+    printf("Cannot stat %s: %s\n", argv[1], strerror(errno));
+    return 1;
+  }
+  if (!S_ISREG(st.st_mode)) {
+    printf("%s is not a regular file.\n", argv[1]);
+    return 1;
+  }
+
+  uint64_t pc = 0;
+  char* end;
+  pc = strtoull(argv[2], &end, 16);
+  if (*end != '\0') {
+    printf("Malformed OFFSET value: %s\n", argv[2]);
+    return 1;
+  }
+
+  return unwindstack::GetInfo(argv[1], pc);
+}
diff --git a/libunwindstack/tools/unwind_symbols.cpp b/libunwindstack/tools/unwind_symbols.cpp
new file mode 100644
index 0000000..f8e3e92
--- /dev/null
+++ b/libunwindstack/tools/unwind_symbols.cpp
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2016 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 <elf.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <unwindstack/Elf.h>
+#include <unwindstack/Log.h>
+#include <unwindstack/Memory.h>
+
+int main(int argc, char** argv) {
+  if (argc != 2 && argc != 3) {
+    printf("Usage: unwind_symbols <ELF_FILE> [<FUNC_ADDRESS>]\n");
+    printf("  Dump all function symbols in ELF_FILE. If FUNC_ADDRESS is\n");
+    printf("  specified, then get the function at that address.\n");
+    printf("  FUNC_ADDRESS must be a hex number.\n");
+    return 1;
+  }
+
+  struct stat st;
+  if (stat(argv[1], &st) == -1) {
+    printf("Cannot stat %s: %s\n", argv[1], strerror(errno));
+    return 1;
+  }
+  if (!S_ISREG(st.st_mode)) {
+    printf("%s is not a regular file.\n", argv[1]);
+    return 1;
+  }
+
+  uint64_t func_addr;
+  if (argc == 3) {
+    char* name;
+    func_addr = strtoull(argv[2], &name, 16);
+    if (*name != '\0') {
+      printf("%s is not a hex number.\n", argv[2]);
+      return 1;
+    }
+  }
+
+  // Send all log messages to stdout.
+  unwindstack::log_to_stdout(true);
+
+  unwindstack::MemoryFileAtOffset* memory = new unwindstack::MemoryFileAtOffset;
+  if (!memory->Init(argv[1], 0)) {
+    printf("Failed to init\n");
+    return 1;
+  }
+
+  unwindstack::Elf elf(memory);
+  if (!elf.Init(true) || !elf.valid()) {
+    printf("%s is not a valid elf file.\n", argv[1]);
+    return 1;
+  }
+
+  std::string soname;
+  if (elf.GetSoname(&soname)) {
+    printf("Soname: %s\n\n", soname.c_str());
+  }
+
+  switch (elf.machine_type()) {
+    case EM_ARM:
+      printf("ABI: arm\n");
+      break;
+    case EM_AARCH64:
+      printf("ABI: arm64\n");
+      break;
+    case EM_386:
+      printf("ABI: x86\n");
+      break;
+    case EM_X86_64:
+      printf("ABI: x86_64\n");
+      break;
+    default:
+      printf("ABI: unknown\n");
+      return 1;
+  }
+
+  std::string name;
+  if (argc == 3) {
+    std::string cur_name;
+    uint64_t func_offset;
+    if (!elf.GetFunctionName(func_addr, &cur_name, &func_offset)) {
+      printf("No known function at 0x%" PRIx64 "\n", func_addr);
+      return 1;
+    }
+    printf("<0x%" PRIx64 ">", func_addr - func_offset);
+    if (func_offset != 0) {
+      printf("+%" PRId64, func_offset);
+    }
+    printf(": %s\n", cur_name.c_str());
+    return 0;
+  }
+
+  // This is a crude way to get the symbols in order.
+  for (const auto& entry : elf.interface()->pt_loads()) {
+    uint64_t start = entry.second.offset;
+    uint64_t end = entry.second.table_size;
+    for (uint64_t addr = start; addr < end; addr += 4) {
+      std::string cur_name;
+      uint64_t func_offset;
+      if (elf.GetFunctionName(addr, &cur_name, &func_offset)) {
+        if (cur_name != name) {
+          printf("<0x%" PRIx64 "> Function: %s\n", addr - func_offset, cur_name.c_str());
+        }
+        name = cur_name;
+      }
+    }
+  }
+
+  return 0;
+}
diff --git a/libunwindstack/unwind_info.cpp b/libunwindstack/unwind_info.cpp
deleted file mode 100644
index da1522b..0000000
--- a/libunwindstack/unwind_info.cpp
+++ /dev/null
@@ -1,179 +0,0 @@
-/*
- * Copyright (C) 2016 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 <elf.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <inttypes.h>
-#include <stdio.h>
-#include <string.h>
-#include <sys/mman.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <unistd.h>
-
-#include "ArmExidx.h"
-#include "DwarfSection.h"
-#include "DwarfStructs.h"
-#include "Elf.h"
-#include "ElfInterface.h"
-#include "ElfInterfaceArm.h"
-#include "Log.h"
-
-void DumpArm(ElfInterfaceArm* interface) {
-  if (interface == nullptr) {
-    printf("No ARM Unwind Information.\n\n");
-    return;
-  }
-
-  printf("ARM Unwind Information:\n");
-  for (const auto& entry : interface->pt_loads()) {
-    uint64_t load_bias = entry.second.table_offset;
-    printf(" PC Range 0x%" PRIx64 " - 0x%" PRIx64 "\n", entry.second.offset + load_bias,
-           entry.second.table_size + load_bias);
-    for (auto addr : *interface) {
-      std::string name;
-      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, &name, &func_offset) && !name.empty()) {
-        printf(" <%s>", name.c_str());
-      }
-      printf("\n");
-      uint64_t entry;
-      if (!interface->FindEntry(pc, &entry)) {
-        printf("    Cannot find entry for address.\n");
-        continue;
-      }
-      ArmExidx arm(nullptr, interface->memory(), nullptr);
-      arm.set_log(true);
-      arm.set_log_skip_execution(true);
-      arm.set_log_indent(2);
-      if (!arm.ExtractEntryData(entry)) {
-        if (arm.status() != ARM_STATUS_NO_UNWIND) {
-          printf("    Error trying to extract data.\n");
-        }
-        continue;
-      }
-      if (arm.data()->size() > 0) {
-        if (!arm.Eval() && arm.status() != ARM_STATUS_NO_UNWIND) {
-          printf("      Error trying to evaluate dwarf data.\n");
-        }
-      }
-    }
-  }
-  printf("\n");
-}
-
-void DumpDwarfSection(ElfInterface* interface, DwarfSection* section, uint64_t load_bias) {
-  for (const DwarfFde* fde : *section) {
-    // Sometimes there are entries that have empty length, skip those since
-    // they don't contain any interesting information.
-    if (fde->pc_start == fde->pc_end) {
-      continue;
-    }
-    printf("\n  PC 0x%" PRIx64, fde->pc_start + load_bias);
-    std::string name;
-    uint64_t func_offset;
-    if (interface->GetFunctionName(fde->pc_start + load_bias, &name, &func_offset) &&
-        !name.empty()) {
-      printf(" <%s>", name.c_str());
-    }
-    printf("\n");
-    if (!section->Log(2, UINT64_MAX, load_bias, fde)) {
-      printf("Failed to process cfa information for entry at 0x%" PRIx64 "\n", fde->pc_start);
-    }
-  }
-}
-
-int main(int argc, char** argv) {
-  if (argc != 2) {
-    printf("Need to pass the name of an elf file to the program.\n");
-    return 1;
-  }
-
-  struct stat st;
-  if (stat(argv[1], &st) == -1) {
-    printf("Cannot stat %s: %s\n", argv[1], strerror(errno));
-    return 1;
-  }
-  if (!S_ISREG(st.st_mode)) {
-    printf("%s is not a regular file.\n", argv[1]);
-    return 1;
-  }
-  if (S_ISDIR(st.st_mode)) {
-    printf("%s is a directory.\n", argv[1]);
-    return 1;
-  }
-
-  // Send all log messages to stdout.
-  log_to_stdout(true);
-
-  MemoryFileAtOffset* memory = new MemoryFileAtOffset;
-  if (!memory->Init(argv[1], 0)) {
-    // Initializatation failed.
-    printf("Failed to init\n");
-    return 1;
-  }
-
-  Elf elf(memory);
-  if (!elf.Init() || !elf.valid()) {
-    printf("%s is not a valid elf file.\n", argv[1]);
-    return 1;
-  }
-
-  ElfInterface* interface = elf.interface();
-  if (elf.machine_type() == EM_ARM) {
-    DumpArm(reinterpret_cast<ElfInterfaceArm*>(interface));
-    printf("\n");
-  }
-
-  if (interface->eh_frame() != nullptr) {
-    printf("eh_frame information:\n");
-    DumpDwarfSection(interface, interface->eh_frame(), interface->load_bias());
-    printf("\n");
-  } else {
-    printf("\nno eh_frame information\n");
-  }
-
-  if (interface->debug_frame() != nullptr) {
-    printf("\ndebug_frame information:\n");
-    DumpDwarfSection(interface, interface->debug_frame(), interface->load_bias());
-    printf("\n");
-  } else {
-    printf("\nno debug_frame information\n");
-  }
-
-  // If there is a gnu_debugdata interface, dump the information for that.
-  ElfInterface* gnu_debugdata_interface = elf.gnu_debugdata_interface();
-  if (gnu_debugdata_interface != nullptr) {
-    if (gnu_debugdata_interface->eh_frame() != nullptr) {
-      printf("\ngnu_debugdata (eh_frame):\n");
-      DumpDwarfSection(gnu_debugdata_interface, gnu_debugdata_interface->eh_frame(), 0);
-      printf("\n");
-    }
-    if (gnu_debugdata_interface->debug_frame() != nullptr) {
-      printf("\ngnu_debugdata (debug_frame):\n");
-      DumpDwarfSection(gnu_debugdata_interface, gnu_debugdata_interface->debug_frame(), 0);
-      printf("\n");
-    }
-  } else {
-    printf("\nno valid gnu_debugdata information\n");
-  }
-
-  return 0;
-}
diff --git a/libusbhost/Android.bp b/libusbhost/Android.bp
index a0d6b9b..fc6f305 100644
--- a/libusbhost/Android.bp
+++ b/libusbhost/Android.bp
@@ -16,6 +16,10 @@
 
 cc_library {
     name: "libusbhost",
+    vendor_available: true,
+    vndk: {
+        enabled: true,
+    },
     host_supported: true,
     srcs: ["usbhost.c"],
     cflags: ["-Werror"],
diff --git a/libusbhost/include/usbhost/usbhost.h b/libusbhost/include/usbhost/usbhost.h
index 84594c8..9758b18 100644
--- a/libusbhost/include/usbhost/usbhost.h
+++ b/libusbhost/include/usbhost/usbhost.h
@@ -140,21 +140,39 @@
 const struct usb_device_descriptor* usb_device_get_device_descriptor(struct usb_device *device);
 
 /* Returns a USB descriptor string for the given string ID.
- * Used to implement usb_device_get_manufacturer_name,
- * usb_device_get_product_name and usb_device_get_serial.
+ * Return value: < 0 on error.  0 on success.
+ * The string is returned in ucs2_out in USB-native UCS-2 encoding.
+ *
+ * parameters:
+ *  id - the string descriptor index.
+ *  timeout - in milliseconds (see Documentation/driver-api/usb/usb.rst)
+ *  ucs2_out - Must point to null on call.
+ *             Will be filled in with a buffer on success.
+ *             If this is non-null on return, it must be free()d.
+ *  response_size - size, in bytes, of ucs-2 string in ucs2_out.
+ *                  The size isn't guaranteed to include null termination.
  * Call free() to free the result when you are done with it.
  */
-char* usb_device_get_string(struct usb_device *device, int id);
+int usb_device_get_string_ucs2(struct usb_device* device, int id, int timeout, void** ucs2_out,
+                               size_t* response_size);
+
+/* Returns a USB descriptor string for the given string ID.
+ * Used to implement usb_device_get_manufacturer_name,
+ * usb_device_get_product_name and usb_device_get_serial.
+ * Returns ascii - non ascii characters will be replaced with '?'.
+ * Call free() to free the result when you are done with it.
+ */
+char* usb_device_get_string(struct usb_device *device, int id, int timeout);
 
 /* Returns the manufacturer name for the USB device.
  * Call free() to free the result when you are done with it.
  */
-char* usb_device_get_manufacturer_name(struct usb_device *device);
+char* usb_device_get_manufacturer_name(struct usb_device *device, int timeout);
 
 /* Returns the product name for the USB device.
  * Call free() to free the result when you are done with it.
  */
-char* usb_device_get_product_name(struct usb_device *device);
+char* usb_device_get_product_name(struct usb_device *device, int timeout);
 
 /* Returns the version number for the USB device.
  */
@@ -163,7 +181,7 @@
 /* Returns the USB serial number for the USB device.
  * Call free() to free the result when you are done with it.
  */
-char* usb_device_get_serial(struct usb_device *device);
+char* usb_device_get_serial(struct usb_device *device, int timeout);
 
 /* Returns true if we have write access to the USB device,
  * and false if we only have access to the USB device configuration.
@@ -232,10 +250,11 @@
 /* Submits a read or write request on the specified device */
 int usb_request_queue(struct usb_request *req);
 
- /* Waits for the results of a previous usb_request_queue operation.
+ /* Waits for the results of a previous usb_request_queue operation. timeoutMillis == -1 requests
+  * to wait forever.
   * Returns a usb_request, or NULL for error.
   */
-struct usb_request *usb_request_wait(struct usb_device *dev);
+struct usb_request *usb_request_wait(struct usb_device *dev, int timeoutMillis);
 
 /* Cancels a pending usb_request_queue() operation. */
 int usb_request_cancel(struct usb_request *req);
diff --git a/libusbhost/usbhost.c b/libusbhost/usbhost.c
index 050fc2f..cb8d430 100644
--- a/libusbhost/usbhost.c
+++ b/libusbhost/usbhost.c
@@ -14,6 +14,10 @@
  * limitations under the License.
  */
 
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+
 // #define DEBUG 1
 #if DEBUG
 
@@ -43,6 +47,7 @@
 #include <fcntl.h>
 #include <errno.h>
 #include <ctype.h>
+#include <poll.h>
 #include <pthread.h>
 
 #include <linux/usbdevice_fs.h>
@@ -59,10 +64,6 @@
 // Some devices fail to send string descriptors if we attempt reading > 255 bytes
 #define MAX_STRING_DESCRIPTOR_LENGTH    255
 
-// From drivers/usb/core/devio.c
-// I don't know why this isn't in a kernel header
-#define MAX_USBFS_BUFFER_SIZE   16384
-
 #define MAX_USBFS_WD_COUNT      10
 
 struct usb_host_context {
@@ -319,29 +320,39 @@
 
 struct usb_device *usb_device_open(const char *dev_name)
 {
-    int fd, did_retry = 0, writeable = 1;
-
+    int fd, attempts, writeable = 1;
+    const int SLEEP_BETWEEN_ATTEMPTS_US = 100000; /* 100 ms */
+    const int64_t MAX_ATTEMPTS = 10;              /* 1s */
     D("usb_device_open %s\n", dev_name);
 
-retry:
-    fd = open(dev_name, O_RDWR);
-    if (fd < 0) {
-        /* if we fail, see if have read-only access */
-        fd = open(dev_name, O_RDONLY);
-        D("usb_device_open open returned %d errno %d\n", fd, errno);
-        if (fd < 0 && (errno == EACCES || errno == ENOENT) && !did_retry) {
-            /* work around race condition between inotify and permissions management */
-            sleep(1);
-            did_retry = 1;
-            goto retry;
+    /* Hack around waiting for permissions to be set on the USB device node.
+     * Should really be a timeout instead of attempt count, and should REALLY
+     * be triggered by the perm change via inotify rather than polling.
+     */
+    for (attempts = 0; attempts < MAX_ATTEMPTS; ++attempts) {
+        if (access(dev_name, R_OK | W_OK) == 0) {
+            writeable = 1;
+            break;
+        } else {
+            if (access(dev_name, R_OK) == 0) {
+                /* double check that write permission didn't just come along too! */
+                writeable = (access(dev_name, R_OK | W_OK) == 0);
+                break;
+            }
         }
-
-        if (fd < 0)
-            return NULL;
-        writeable = 0;
-        D("[ usb open read-only %s fd = %d]\n", dev_name, fd);
+        /* not writeable or readable - sleep and try again. */
+        D("usb_device_open no access sleeping\n");
+        usleep(SLEEP_BETWEEN_ATTEMPTS_US);
     }
 
+    if (writeable) {
+        fd = open(dev_name, O_RDWR);
+    } else {
+        fd = open(dev_name, O_RDONLY);
+    }
+    D("usb_device_open open returned %d writeable %d errno %d\n", fd, writeable, errno);
+    if (fd < 0) return NULL;
+
     struct usb_device* result = usb_device_new(dev_name, fd);
     if (result)
         result->writeable = writeable;
@@ -449,56 +460,100 @@
     return (struct usb_device_descriptor*)device->desc;
 }
 
-char* usb_device_get_string(struct usb_device *device, int id)
-{
-    char string[256];
-    __u16 buffer[MAX_STRING_DESCRIPTOR_LENGTH / sizeof(__u16)];
+/* Returns a USB descriptor string for the given string ID.
+ * Return value: < 0 on error.  0 on success.
+ * The string is returned in ucs2_out in USB-native UCS-2 encoding.
+ *
+ * parameters:
+ *  id - the string descriptor index.
+ *  timeout - in milliseconds (see Documentation/driver-api/usb/usb.rst)
+ *  ucs2_out - Must point to null on call.
+ *             Will be filled in with a buffer on success.
+ *             If this is non-null on return, it must be free()d.
+ *  response_size - size, in bytes, of ucs-2 string in ucs2_out.
+ *                  The size isn't guaranteed to include null termination.
+ * Call free() to free the result when you are done with it.
+ */
+int usb_device_get_string_ucs2(struct usb_device* device, int id, int timeout, void** ucs2_out,
+                               size_t* response_size) {
     __u16 languages[MAX_STRING_DESCRIPTOR_LENGTH / sizeof(__u16)];
-    int i, result;
+    char response[MAX_STRING_DESCRIPTOR_LENGTH];
+    int result;
     int languageCount = 0;
 
-    if (id == 0) return NULL;
+    if (id == 0) return -1;
+    if (*ucs2_out != NULL) return -1;
 
-    string[0] = 0;
     memset(languages, 0, sizeof(languages));
 
     // read list of supported languages
     result = usb_device_control_transfer(device,
             USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_DEVICE, USB_REQ_GET_DESCRIPTOR,
-            (USB_DT_STRING << 8) | 0, 0, languages, sizeof(languages), 0);
+            (USB_DT_STRING << 8) | 0, 0, languages, sizeof(languages),
+            timeout);
     if (result > 0)
         languageCount = (result - 2) / 2;
 
-    for (i = 1; i <= languageCount; i++) {
-        memset(buffer, 0, sizeof(buffer));
+    for (int i = 1; i <= languageCount; i++) {
+        memset(response, 0, sizeof(response));
 
-        result = usb_device_control_transfer(device,
-                USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_DEVICE, USB_REQ_GET_DESCRIPTOR,
-                (USB_DT_STRING << 8) | id, languages[i], buffer, sizeof(buffer), 0);
-        if (result > 0) {
-            int i;
-            // skip first word, and copy the rest to the string, changing shorts to bytes.
-            result /= 2;
-            for (i = 1; i < result; i++)
-                string[i - 1] = buffer[i];
-            string[i - 1] = 0;
-            return strdup(string);
+        result = usb_device_control_transfer(
+            device, USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_DEVICE, USB_REQ_GET_DESCRIPTOR,
+            (USB_DT_STRING << 8) | id, languages[i], response, sizeof(response), timeout);
+        if (result >= 2) {  // string contents begin at offset 2.
+            int descriptor_len = result - 2;
+            char* out = malloc(descriptor_len + 3);
+            if (out == NULL) {
+                return -1;
+            }
+            memcpy(out, response + 2, descriptor_len);
+            // trail with three additional NULLs, so that there's guaranteed
+            // to be a UCS-2 NULL character beyond whatever USB returned.
+            // The returned string length is still just what USB returned.
+            memset(out + descriptor_len, '\0', 3);
+            *ucs2_out = (void*)out;
+            *response_size = descriptor_len;
+            return 0;
         }
     }
-
-    return NULL;
+    return -1;
 }
 
-char* usb_device_get_manufacturer_name(struct usb_device *device)
-{
-    struct usb_device_descriptor *desc = (struct usb_device_descriptor *)device->desc;
-    return usb_device_get_string(device, desc->iManufacturer);
+/* Warning: previously this blindly returned the lower 8 bits of
+ * every UCS-2 character in a USB descriptor.  Now it will replace
+ * values > 127 with ascii '?'.
+ */
+char* usb_device_get_string(struct usb_device* device, int id, int timeout) {
+    char* ascii_string = NULL;
+    size_t raw_string_len = 0;
+    size_t i;
+    if (usb_device_get_string_ucs2(device, id, timeout, (void**)&ascii_string, &raw_string_len) < 0)
+        return NULL;
+    if (ascii_string == NULL) return NULL;
+    for (i = 0; i < raw_string_len / 2; ++i) {
+        // wire format for USB is always little-endian.
+        char lower = ascii_string[2 * i];
+        char upper = ascii_string[2 * i + 1];
+        if (upper || (lower & 0x80)) {
+            ascii_string[i] = '?';
+        } else {
+            ascii_string[i] = lower;
+        }
+    }
+    ascii_string[i] = '\0';
+    return ascii_string;
 }
 
-char* usb_device_get_product_name(struct usb_device *device)
+char* usb_device_get_manufacturer_name(struct usb_device *device, int timeout)
 {
     struct usb_device_descriptor *desc = (struct usb_device_descriptor *)device->desc;
-    return usb_device_get_string(device, desc->iProduct);
+    return usb_device_get_string(device, desc->iManufacturer, timeout);
+}
+
+char* usb_device_get_product_name(struct usb_device *device, int timeout)
+{
+    struct usb_device_descriptor *desc = (struct usb_device_descriptor *)device->desc;
+    return usb_device_get_string(device, desc->iProduct, timeout);
 }
 
 int usb_device_get_version(struct usb_device *device)
@@ -507,10 +562,10 @@
     return desc->bcdUSB;
 }
 
-char* usb_device_get_serial(struct usb_device *device)
+char* usb_device_get_serial(struct usb_device *device, int timeout)
 {
     struct usb_device_descriptor *desc = (struct usb_device_descriptor *)device->desc;
-    return usb_device_get_string(device, desc->iSerialNumber);
+    return usb_device_get_string(device, desc->iSerialNumber, timeout);
 }
 
 int usb_device_is_writeable(struct usb_device *device)
@@ -605,10 +660,6 @@
 {
     struct usbdevfs_bulktransfer  ctrl;
 
-    // need to limit request size to avoid EINVAL
-    if (length > MAX_USBFS_BUFFER_SIZE)
-        length = MAX_USBFS_BUFFER_SIZE;
-
     memset(&ctrl, 0, sizeof(ctrl));
     ctrl.ep = endpoint;
     ctrl.len = length;
@@ -668,11 +719,7 @@
 
     urb->status = -1;
     urb->buffer = req->buffer;
-    // need to limit request size to avoid EINVAL
-    if (req->buffer_length > MAX_USBFS_BUFFER_SIZE)
-        urb->buffer_length = MAX_USBFS_BUFFER_SIZE;
-    else
-        urb->buffer_length = req->buffer_length;
+    urb->buffer_length = req->buffer_length;
 
     do {
         res = ioctl(req->dev->fd, USBDEVFS_SUBMITURB, urb);
@@ -681,29 +728,38 @@
     return res;
 }
 
-struct usb_request *usb_request_wait(struct usb_device *dev)
+struct usb_request *usb_request_wait(struct usb_device *dev, int timeoutMillis)
 {
-    struct usbdevfs_urb *urb = NULL;
-    struct usb_request *req = NULL;
+    // Poll until a request becomes available if there is a timeout
+    if (timeoutMillis > 0) {
+        struct pollfd p = {.fd = dev->fd, .events = POLLOUT, .revents = 0};
 
-    while (1) {
-        int res = ioctl(dev->fd, USBDEVFS_REAPURB, &urb);
-        D("USBDEVFS_REAPURB returned %d\n", res);
-        if (res < 0) {
-            if(errno == EINTR) {
-                continue;
-            }
-            D("[ reap urb - error ]\n");
+        int res = poll(&p, 1, timeoutMillis);
+
+        if (res != 1 || p.revents != POLLOUT) {
+            D("[ poll - event %d, error %d]\n", p.revents, errno);
             return NULL;
-        } else {
-            D("[ urb @%p status = %d, actual = %d ]\n",
-                urb, urb->status, urb->actual_length);
-            req = (struct usb_request*)urb->usercontext;
-            req->actual_length = urb->actual_length;
         }
-        break;
     }
-    return req;
+
+    // Read the request. This should usually succeed as we polled before, but it can fail e.g. when
+    // two threads are reading usb requests at the same time and only a single request is available.
+    struct usbdevfs_urb *urb = NULL;
+    int res = TEMP_FAILURE_RETRY(ioctl(dev->fd, timeoutMillis == -1 ? USBDEVFS_REAPURB :
+                                       USBDEVFS_REAPURBNDELAY, &urb));
+    D("%s returned %d\n", timeoutMillis == -1 ? "USBDEVFS_REAPURB" : "USBDEVFS_REAPURBNDELAY", res);
+
+    if (res < 0) {
+        D("[ reap urb - error %d]\n", errno);
+        return NULL;
+    } else {
+        D("[ urb @%p status = %d, actual = %d ]\n", urb, urb->status, urb->actual_length);
+
+        struct usb_request *req = (struct usb_request*)urb->usercontext;
+        req->actual_length = urb->actual_length;
+
+        return req;
+    }
 }
 
 int usb_request_cancel(struct usb_request *req)
@@ -711,4 +767,3 @@
     struct usbdevfs_urb *urb = ((struct usbdevfs_urb*)req->private_data);
     return ioctl(req->dev->fd, USBDEVFS_DISCARDURB, urb);
 }
-
diff --git a/libutils/Android.bp b/libutils/Android.bp
index 33770ba..d635e65 100644
--- a/libutils/Android.bp
+++ b/libutils/Android.bp
@@ -15,15 +15,18 @@
 cc_library_headers {
     name: "libutils_headers",
     vendor_available: true,
+    recovery_available: true,
     host_supported: true,
 
     header_libs: [
+        "liblog_headers",
         "libsystem_headers",
-        "libcutils_headers"
+        "libcutils_headers",
     ],
     export_header_lib_headers: [
+        "liblog_headers",
         "libsystem_headers",
-        "libcutils_headers"
+        "libcutils_headers",
     ],
     export_include_dirs: ["include"],
 
@@ -41,23 +44,97 @@
     },
 }
 
-cc_library {
-    name: "libutils",
+cc_defaults {
+    name: "libutils_defaults",
     vendor_available: true,
+    recovery_available: true,
+    vndk: {
+        enabled: true,
+        support_system_process: true,
+    },
     host_supported: true,
 
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
+    include_dirs: ["external/safe-iop/include"],
+    header_libs: [
+        "libutils_headers",
+    ],
+    export_header_lib_headers: [
+        "libutils_headers",
+    ],
+
+    shared_libs: [
+        "liblog",
+    ],
+
+    arch: {
+        mips: {
+            cflags: ["-DALIGN_DOUBLE"],
+        },
+    },
+
+    target: {
+        android: {
+            cflags: ["-fvisibility=protected"],
+
+            shared_libs: [
+                "libcutils",
+                "libdl",
+                "libvndksupport",
+            ],
+
+            sanitize: {
+                misc_undefined: ["integer"],
+            },
+        },
+
+        recovery: {
+            exclude_shared_libs: ["libvndksupport"],
+        },
+
+        host: {
+            cflags: ["-DLIBUTILS_NATIVE=1"],
+
+            shared: {
+                enabled: false,
+            },
+        },
+
+        linux_bionic: {
+            enabled: true,
+        },
+
+        darwin: {
+            cflags: ["-Wno-unused-parameter"],
+        },
+
+        windows: {
+            cflags: [
+                // Under MinGW, ctype.h doesn't need multi-byte support
+                "-DMB_CUR_MAX=1",
+                "-Wno-unused-private-field",
+            ],
+
+            enabled: true,
+        },
+    },
+}
+
+cc_library {
+    name: "libutils",
+    defaults: ["libutils_defaults"],
+
     srcs: [
-        "CallStack.cpp",
         "FileMap.cpp",
         "JenkinsHash.cpp",
-        "LinearTransform.cpp",
-        "Log.cpp",
         "NativeHandle.cpp",
         "Printer.cpp",
         "PropertyMap.cpp",
         "RefBase.cpp",
         "SharedBuffer.cpp",
-        "Static.cpp",
         "StopWatch.cpp",
         "String8.cpp",
         "String16.cpp",
@@ -71,10 +148,28 @@
         "misc.cpp",
     ],
 
-    cflags: ["-Werror"],
-    include_dirs: ["external/safe-iop/include"],
-    header_libs: ["libutils_headers"],
-    export_header_lib_headers: ["libutils_headers"],
+    target: {
+        android: {
+            srcs: [
+                "Trace.cpp",
+            ],
+        },
+        linux: {
+            shared_libs: ["libbase"],
+            srcs: [
+                "Looper.cpp",
+            ],
+        },
+    },
+}
+
+cc_library {
+    name: "libutilscallstack",
+    defaults: ["libutils_defaults"],
+
+    srcs: [
+        "CallStack.cpp",
+    ],
 
     arch: {
         mips: {
@@ -84,62 +179,17 @@
 
     target: {
         android: {
-            srcs: [
-                "BlobCache.cpp",
-                "Looper.cpp",
-                "ProcessCallStack.cpp",
-                "Trace.cpp",
-            ],
-
-            cflags: ["-fvisibility=protected"],
-
             shared_libs: [
+                "libutils",
                 "libbacktrace",
-                "libcutils",
-                "libdl",
-                "liblog",
             ],
-
-            sanitize: {
-                misc_undefined: ["integer"],
-            },
         },
-
-        host: {
-            cflags: ["-DLIBUTILS_NATIVE=1"],
-
-            shared: {
-                enabled: false,
-            },
-        },
-
         linux: {
             srcs: [
-                "Looper.cpp",
                 "ProcessCallStack.cpp",
             ],
         },
-        linux_bionic: {
-            enabled: true,
-            srcs: [
-                "Looper.cpp",
-                "ProcessCallStack.cpp",
-            ],
-        },
-
-        darwin: {
-            cflags: ["-Wno-unused-parameter"],
-        },
-
-        // Under MinGW, ctype.h doesn't need multi-byte support
-        windows: {
-            cflags: ["-DMB_CUR_MAX=1"],
-
-            enabled: true,
-        },
     },
-
-    clang: true,
 }
 
 // Include subdirectory makefiles
@@ -151,6 +201,8 @@
     static_libs: ["libutils"],
     shared_libs: ["liblog"],
     srcs: ["SharedBufferTest.cpp"],
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
 }
-
-subdirs = ["tests"]
diff --git a/libutils/BlobCache.cpp b/libutils/BlobCache.cpp
deleted file mode 100644
index 126995b..0000000
--- a/libutils/BlobCache.cpp
+++ /dev/null
@@ -1,379 +0,0 @@
-/*
- ** Copyright 2011, 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.
- */
-
-#define LOG_TAG "BlobCache"
-//#define LOG_NDEBUG 0
-
-#include <inttypes.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include <utils/BlobCache.h>
-#include <utils/Errors.h>
-#include <utils/Log.h>
-
-#include <cutils/properties.h>
-
-namespace android {
-
-// BlobCache::Header::mMagicNumber value
-static const uint32_t blobCacheMagic = ('_' << 24) + ('B' << 16) + ('b' << 8) + '$';
-
-// BlobCache::Header::mBlobCacheVersion value
-static const uint32_t blobCacheVersion = 3;
-
-// BlobCache::Header::mDeviceVersion value
-static const uint32_t blobCacheDeviceVersion = 1;
-
-BlobCache::BlobCache(size_t maxKeySize, size_t maxValueSize, size_t maxTotalSize):
-        mMaxKeySize(maxKeySize),
-        mMaxValueSize(maxValueSize),
-        mMaxTotalSize(maxTotalSize),
-        mTotalSize(0) {
-    nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
-#ifdef _WIN32
-    srand(now);
-#else
-    mRandState[0] = (now >> 0) & 0xFFFF;
-    mRandState[1] = (now >> 16) & 0xFFFF;
-    mRandState[2] = (now >> 32) & 0xFFFF;
-#endif
-    ALOGV("initializing random seed using %lld", (unsigned long long)now);
-}
-
-void BlobCache::set(const void* key, size_t keySize, const void* value,
-        size_t valueSize) {
-    if (mMaxKeySize < keySize) {
-        ALOGV("set: not caching because the key is too large: %zu (limit: %zu)",
-                keySize, mMaxKeySize);
-        return;
-    }
-    if (mMaxValueSize < valueSize) {
-        ALOGV("set: not caching because the value is too large: %zu (limit: %zu)",
-                valueSize, mMaxValueSize);
-        return;
-    }
-    if (mMaxTotalSize < keySize + valueSize) {
-        ALOGV("set: not caching because the combined key/value size is too "
-                "large: %zu (limit: %zu)", keySize + valueSize, mMaxTotalSize);
-        return;
-    }
-    if (keySize == 0) {
-        ALOGW("set: not caching because keySize is 0");
-        return;
-    }
-    if (valueSize <= 0) {
-        ALOGW("set: not caching because valueSize is 0");
-        return;
-    }
-
-    sp<Blob> dummyKey(new Blob(key, keySize, false));
-    CacheEntry dummyEntry(dummyKey, NULL);
-
-    while (true) {
-        ssize_t index = mCacheEntries.indexOf(dummyEntry);
-        if (index < 0) {
-            // Create a new cache entry.
-            sp<Blob> keyBlob(new Blob(key, keySize, true));
-            sp<Blob> valueBlob(new Blob(value, valueSize, true));
-            size_t newTotalSize = mTotalSize + keySize + valueSize;
-            if (mMaxTotalSize < newTotalSize) {
-                if (isCleanable()) {
-                    // Clean the cache and try again.
-                    clean();
-                    continue;
-                } else {
-                    ALOGV("set: not caching new key/value pair because the "
-                            "total cache size limit would be exceeded: %zu "
-                            "(limit: %zu)",
-                            keySize + valueSize, mMaxTotalSize);
-                    break;
-                }
-            }
-            mCacheEntries.add(CacheEntry(keyBlob, valueBlob));
-            mTotalSize = newTotalSize;
-            ALOGV("set: created new cache entry with %zu byte key and %zu byte value",
-                    keySize, valueSize);
-        } else {
-            // Update the existing cache entry.
-            sp<Blob> valueBlob(new Blob(value, valueSize, true));
-            sp<Blob> oldValueBlob(mCacheEntries[index].getValue());
-            size_t newTotalSize = mTotalSize + valueSize - oldValueBlob->getSize();
-            if (mMaxTotalSize < newTotalSize) {
-                if (isCleanable()) {
-                    // Clean the cache and try again.
-                    clean();
-                    continue;
-                } else {
-                    ALOGV("set: not caching new value because the total cache "
-                            "size limit would be exceeded: %zu (limit: %zu)",
-                            keySize + valueSize, mMaxTotalSize);
-                    break;
-                }
-            }
-            mCacheEntries.editItemAt(index).setValue(valueBlob);
-            mTotalSize = newTotalSize;
-            ALOGV("set: updated existing cache entry with %zu byte key and %zu byte "
-                    "value", keySize, valueSize);
-        }
-        break;
-    }
-}
-
-size_t BlobCache::get(const void* key, size_t keySize, void* value,
-        size_t valueSize) {
-    if (mMaxKeySize < keySize) {
-        ALOGV("get: not searching because the key is too large: %zu (limit %zu)",
-                keySize, mMaxKeySize);
-        return 0;
-    }
-    sp<Blob> dummyKey(new Blob(key, keySize, false));
-    CacheEntry dummyEntry(dummyKey, NULL);
-    ssize_t index = mCacheEntries.indexOf(dummyEntry);
-    if (index < 0) {
-        ALOGV("get: no cache entry found for key of size %zu", keySize);
-        return 0;
-    }
-
-    // The key was found. Return the value if the caller's buffer is large
-    // enough.
-    sp<Blob> valueBlob(mCacheEntries[index].getValue());
-    size_t valueBlobSize = valueBlob->getSize();
-    if (valueBlobSize <= valueSize) {
-        ALOGV("get: copying %zu bytes to caller's buffer", valueBlobSize);
-        memcpy(value, valueBlob->getData(), valueBlobSize);
-    } else {
-        ALOGV("get: caller's buffer is too small for value: %zu (needs %zu)",
-                valueSize, valueBlobSize);
-    }
-    return valueBlobSize;
-}
-
-static inline size_t align4(size_t size) {
-    return (size + 3) & ~3;
-}
-
-size_t BlobCache::getFlattenedSize() const {
-    size_t size = align4(sizeof(Header) + PROPERTY_VALUE_MAX);
-    for (size_t i = 0; i < mCacheEntries.size(); i++) {
-        const CacheEntry& e(mCacheEntries[i]);
-        sp<Blob> keyBlob = e.getKey();
-        sp<Blob> valueBlob = e.getValue();
-        size += align4(sizeof(EntryHeader) + keyBlob->getSize() +
-                       valueBlob->getSize());
-    }
-    return size;
-}
-
-status_t BlobCache::flatten(void* buffer, size_t size) const {
-    // Write the cache header
-    if (size < sizeof(Header)) {
-        ALOGE("flatten: not enough room for cache header");
-        return BAD_VALUE;
-    }
-    Header* header = reinterpret_cast<Header*>(buffer);
-    header->mMagicNumber = blobCacheMagic;
-    header->mBlobCacheVersion = blobCacheVersion;
-    header->mDeviceVersion = blobCacheDeviceVersion;
-    header->mNumEntries = mCacheEntries.size();
-    char buildId[PROPERTY_VALUE_MAX];
-    header->mBuildIdLength = property_get("ro.build.id", buildId, "");
-    memcpy(header->mBuildId, buildId, header->mBuildIdLength);
-
-    // Write cache entries
-    uint8_t* byteBuffer = reinterpret_cast<uint8_t*>(buffer);
-    off_t byteOffset = align4(sizeof(Header) + header->mBuildIdLength);
-    for (size_t i = 0; i < mCacheEntries.size(); i++) {
-        const CacheEntry& e(mCacheEntries[i]);
-        sp<Blob> keyBlob = e.getKey();
-        sp<Blob> valueBlob = e.getValue();
-        size_t keySize = keyBlob->getSize();
-        size_t valueSize = valueBlob->getSize();
-
-        size_t entrySize = sizeof(EntryHeader) + keySize + valueSize;
-        size_t totalSize = align4(entrySize);
-        if (byteOffset + totalSize > size) {
-            ALOGE("flatten: not enough room for cache entries");
-            return BAD_VALUE;
-        }
-
-        EntryHeader* eheader = reinterpret_cast<EntryHeader*>(
-            &byteBuffer[byteOffset]);
-        eheader->mKeySize = keySize;
-        eheader->mValueSize = valueSize;
-
-        memcpy(eheader->mData, keyBlob->getData(), keySize);
-        memcpy(eheader->mData + keySize, valueBlob->getData(), valueSize);
-
-        if (totalSize > entrySize) {
-            // We have padding bytes. Those will get written to storage, and contribute to the CRC,
-            // so make sure we zero-them to have reproducible results.
-            memset(eheader->mData + keySize + valueSize, 0, totalSize - entrySize);
-        }
-
-        byteOffset += totalSize;
-    }
-
-    return OK;
-}
-
-status_t BlobCache::unflatten(void const* buffer, size_t size) {
-    // All errors should result in the BlobCache being in an empty state.
-    mCacheEntries.clear();
-
-    // Read the cache header
-    if (size < sizeof(Header)) {
-        ALOGE("unflatten: not enough room for cache header");
-        return BAD_VALUE;
-    }
-    const Header* header = reinterpret_cast<const Header*>(buffer);
-    if (header->mMagicNumber != blobCacheMagic) {
-        ALOGE("unflatten: bad magic number: %" PRIu32, header->mMagicNumber);
-        return BAD_VALUE;
-    }
-    char buildId[PROPERTY_VALUE_MAX];
-    int len = property_get("ro.build.id", buildId, "");
-    if (header->mBlobCacheVersion != blobCacheVersion ||
-            header->mDeviceVersion != blobCacheDeviceVersion ||
-            len != header->mBuildIdLength ||
-            strncmp(buildId, header->mBuildId, len)) {
-        // We treat version mismatches as an empty cache.
-        return OK;
-    }
-
-    // Read cache entries
-    const uint8_t* byteBuffer = reinterpret_cast<const uint8_t*>(buffer);
-    off_t byteOffset = align4(sizeof(Header) + header->mBuildIdLength);
-    size_t numEntries = header->mNumEntries;
-    for (size_t i = 0; i < numEntries; i++) {
-        if (byteOffset + sizeof(EntryHeader) > size) {
-            mCacheEntries.clear();
-            ALOGE("unflatten: not enough room for cache entry headers");
-            return BAD_VALUE;
-        }
-
-        const EntryHeader* eheader = reinterpret_cast<const EntryHeader*>(
-                &byteBuffer[byteOffset]);
-        size_t keySize = eheader->mKeySize;
-        size_t valueSize = eheader->mValueSize;
-        size_t entrySize = sizeof(EntryHeader) + keySize + valueSize;
-
-        size_t totalSize = align4(entrySize);
-        if (byteOffset + totalSize > size) {
-            mCacheEntries.clear();
-            ALOGE("unflatten: not enough room for cache entry headers");
-            return BAD_VALUE;
-        }
-
-        const uint8_t* data = eheader->mData;
-        set(data, keySize, data + keySize, valueSize);
-
-        byteOffset += totalSize;
-    }
-
-    return OK;
-}
-
-long int BlobCache::blob_random() {
-#ifdef _WIN32
-    return rand();
-#else
-    return nrand48(mRandState);
-#endif
-}
-
-void BlobCache::clean() {
-    // Remove a random cache entry until the total cache size gets below half
-    // the maximum total cache size.
-    while (mTotalSize > mMaxTotalSize / 2) {
-        size_t i = size_t(blob_random() % (mCacheEntries.size()));
-        const CacheEntry& entry(mCacheEntries[i]);
-        mTotalSize -= entry.getKey()->getSize() + entry.getValue()->getSize();
-        mCacheEntries.removeAt(i);
-    }
-}
-
-bool BlobCache::isCleanable() const {
-    return mTotalSize > mMaxTotalSize / 2;
-}
-
-BlobCache::Blob::Blob(const void* data, size_t size, bool copyData):
-        mData(copyData ? malloc(size) : data),
-        mSize(size),
-        mOwnsData(copyData) {
-    if (data != NULL && copyData) {
-        memcpy(const_cast<void*>(mData), data, size);
-    }
-}
-
-BlobCache::Blob::~Blob() {
-    if (mOwnsData) {
-        free(const_cast<void*>(mData));
-    }
-}
-
-bool BlobCache::Blob::operator<(const Blob& rhs) const {
-    if (mSize == rhs.mSize) {
-        return memcmp(mData, rhs.mData, mSize) < 0;
-    } else {
-        return mSize < rhs.mSize;
-    }
-}
-
-const void* BlobCache::Blob::getData() const {
-    return mData;
-}
-
-size_t BlobCache::Blob::getSize() const {
-    return mSize;
-}
-
-BlobCache::CacheEntry::CacheEntry() {
-}
-
-BlobCache::CacheEntry::CacheEntry(const sp<Blob>& key, const sp<Blob>& value):
-        mKey(key),
-        mValue(value) {
-}
-
-BlobCache::CacheEntry::CacheEntry(const CacheEntry& ce):
-        mKey(ce.mKey),
-        mValue(ce.mValue) {
-}
-
-bool BlobCache::CacheEntry::operator<(const CacheEntry& rhs) const {
-    return *mKey < *rhs.mKey;
-}
-
-const BlobCache::CacheEntry& BlobCache::CacheEntry::operator=(const CacheEntry& rhs) {
-    mKey = rhs.mKey;
-    mValue = rhs.mValue;
-    return *this;
-}
-
-sp<BlobCache::Blob> BlobCache::CacheEntry::getKey() const {
-    return mKey;
-}
-
-sp<BlobCache::Blob> BlobCache::CacheEntry::getValue() const {
-    return mValue;
-}
-
-void BlobCache::CacheEntry::setValue(const sp<Blob>& value) {
-    mValue = value;
-}
-
-} // namespace android
diff --git a/libutils/CallStack.cpp b/libutils/CallStack.cpp
index 699da74..bd6015e 100644
--- a/libutils/CallStack.cpp
+++ b/libutils/CallStack.cpp
@@ -16,9 +16,10 @@
 
 #define LOG_TAG "CallStack"
 
+#include <utils/CallStack.h>
+
 #include <memory>
 
-#include <utils/CallStack.h>
 #include <utils/Printer.h>
 #include <utils/Errors.h>
 #include <utils/Log.h>
diff --git a/libutils/FileMap.cpp b/libutils/FileMap.cpp
index 3c4d81c..583c6b9 100644
--- a/libutils/FileMap.cpp
+++ b/libutils/FileMap.cpp
@@ -48,10 +48,10 @@
 
 // Constructor.  Create an empty object.
 FileMap::FileMap(void)
-    : mFileName(NULL),
-      mBasePtr(NULL),
+    : mFileName(nullptr),
+      mBasePtr(nullptr),
       mBaseLength(0),
-      mDataPtr(NULL),
+      mDataPtr(nullptr),
       mDataLength(0)
 #if defined(__MINGW32__)
       ,
@@ -69,9 +69,9 @@
       , mFileHandle(other.mFileHandle), mFileMapping(other.mFileMapping)
 #endif
 {
-    other.mFileName = NULL;
-    other.mBasePtr = NULL;
-    other.mDataPtr = NULL;
+    other.mFileName = nullptr;
+    other.mBasePtr = nullptr;
+    other.mDataPtr = nullptr;
 #if defined(__MINGW32__)
     other.mFileHandle = INVALID_HANDLE_VALUE;
     other.mFileMapping = NULL;
@@ -86,9 +86,9 @@
     mDataOffset = other.mDataOffset;
     mDataPtr = other.mDataPtr;
     mDataLength = other.mDataLength;
-    other.mFileName = NULL;
-    other.mBasePtr = NULL;
-    other.mDataPtr = NULL;
+    other.mFileName = nullptr;
+    other.mBasePtr = nullptr;
+    other.mDataPtr = nullptr;
 #if defined(__MINGW32__)
     mFileHandle = other.mFileHandle;
     mFileMapping = other.mFileMapping;
@@ -101,7 +101,7 @@
 // Destructor.
 FileMap::~FileMap(void)
 {
-    if (mFileName != NULL) {
+    if (mFileName != nullptr) {
         free(mFileName);
     }
 #if defined(__MINGW32__)
@@ -196,7 +196,7 @@
     if (!readOnly)
         prot |= PROT_WRITE;
 
-    ptr = mmap(NULL, adjLength, prot, flags, fd, adjOffset);
+    ptr = mmap(nullptr, adjLength, prot, flags, fd, adjOffset);
     if (ptr == MAP_FAILED) {
         ALOGE("mmap(%lld,%zu) failed: %s\n",
             (long long)adjOffset, adjLength, strerror(errno));
@@ -205,7 +205,7 @@
     mBasePtr = ptr;
 #endif // !defined(__MINGW32__)
 
-    mFileName = origFileName != NULL ? strdup(origFileName) : NULL;
+    mFileName = origFileName != nullptr ? strdup(origFileName) : nullptr;
     mBaseLength = adjLength;
     mDataOffset = offset;
     mDataPtr = (char*) mBasePtr + adjust;
diff --git a/libutils/LinearTransform.cpp b/libutils/LinearTransform.cpp
deleted file mode 100644
index 138ce8b..0000000
--- a/libutils/LinearTransform.cpp
+++ /dev/null
@@ -1,282 +0,0 @@
-/*
- * Copyright (C) 2011 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.
- */
-
-#define __STDC_LIMIT_MACROS
-
-#include <assert.h>
-#include <stdint.h>
-
-#include <utils/LinearTransform.h>
-
-// disable sanitize as these functions may intentionally overflow (see comments below).
-// the ifdef can be removed when host builds use clang.
-#if defined(__clang__)
-#define ATTRIBUTE_NO_SANITIZE_INTEGER __attribute__((no_sanitize("integer")))
-#else
-#define ATTRIBUTE_NO_SANITIZE_INTEGER
-#endif
-
-namespace android {
-
-// sanitize failure with T = int32_t and x = 0x80000000
-template<class T>
-ATTRIBUTE_NO_SANITIZE_INTEGER
-static inline T ABS(T x) { return (x < 0) ? -x : x; }
-
-// Static math methods involving linear transformations
-// remote sanitize failure on overflow case.
-ATTRIBUTE_NO_SANITIZE_INTEGER
-static bool scale_u64_to_u64(
-        uint64_t val,
-        uint32_t N,
-        uint32_t D,
-        uint64_t* res,
-        bool round_up_not_down) {
-    uint64_t tmp1, tmp2;
-    uint32_t r;
-
-    assert(res);
-    assert(D);
-
-    // Let U32(X) denote a uint32_t containing the upper 32 bits of a 64 bit
-    // integer X.
-    // Let L32(X) denote a uint32_t containing the lower 32 bits of a 64 bit
-    // integer X.
-    // Let X[A, B] with A <= B denote bits A through B of the integer X.
-    // Let (A | B) denote the concatination of two 32 bit ints, A and B.
-    // IOW X = (A | B) => U32(X) == A && L32(X) == B
-    //
-    // compute M = val * N (a 96 bit int)
-    // ---------------------------------
-    // tmp2 = U32(val) * N (a 64 bit int)
-    // tmp1 = L32(val) * N (a 64 bit int)
-    // which means
-    // M = val * N = (tmp2 << 32) + tmp1
-    tmp2 = (val >> 32) * N;
-    tmp1 = (val & UINT32_MAX) * N;
-
-    // compute M[32, 95]
-    // tmp2 = tmp2 + U32(tmp1)
-    //      = (U32(val) * N) + U32(L32(val) * N)
-    //      = M[32, 95]
-    tmp2 += tmp1 >> 32;
-
-    // if M[64, 95] >= D, then M/D has bits > 63 set and we have
-    // an overflow.
-    if ((tmp2 >> 32) >= D) {
-        *res = UINT64_MAX;
-        return false;
-    }
-
-    // Divide.  Going in we know
-    // tmp2 = M[32, 95]
-    // U32(tmp2) < D
-    r = tmp2 % D;
-    tmp2 /= D;
-
-    // At this point
-    // tmp1      = L32(val) * N
-    // tmp2      = M[32, 95] / D
-    //           = (M / D)[32, 95]
-    // r         = M[32, 95] % D
-    // U32(tmp2) = 0
-    //
-    // compute tmp1 = (r | M[0, 31])
-    tmp1 = (tmp1 & UINT32_MAX) | ((uint64_t)r << 32);
-
-    // Divide again.  Keep the remainder around in order to round properly.
-    r = tmp1 % D;
-    tmp1 /= D;
-
-    // At this point
-    // tmp2      = (M / D)[32, 95]
-    // tmp1      = (M / D)[ 0, 31]
-    // r         =  M % D
-    // U32(tmp1) = 0
-    // U32(tmp2) = 0
-
-    // Pack the result and deal with the round-up case (As well as the
-    // remote possiblility over overflow in such a case).
-    *res = (tmp2 << 32) | tmp1;
-    if (r && round_up_not_down) {
-        ++(*res);
-        if (!(*res)) {
-            *res = UINT64_MAX;
-            return false;
-        }
-    }
-
-    return true;
-}
-
-// at least one known sanitize failure (see comment below)
-ATTRIBUTE_NO_SANITIZE_INTEGER
-static bool linear_transform_s64_to_s64(
-        int64_t  val,
-        int64_t  basis1,
-        int32_t  N,
-        uint32_t D,
-        bool     invert_frac,
-        int64_t  basis2,
-        int64_t* out) {
-    uint64_t scaled, res;
-    uint64_t abs_val;
-    bool is_neg;
-
-    if (!out)
-        return false;
-
-    // Compute abs(val - basis_64). Keep track of whether or not this delta
-    // will be negative after the scale opertaion.
-    if (val < basis1) {
-        is_neg = true;
-        abs_val = basis1 - val;
-    } else {
-        is_neg = false;
-        abs_val = val - basis1;
-    }
-
-    if (N < 0)
-        is_neg = !is_neg;
-
-    if (!scale_u64_to_u64(abs_val,
-                          invert_frac ? D : ABS(N),
-                          invert_frac ? ABS(N) : D,
-                          &scaled,
-                          is_neg))
-        return false; // overflow/undeflow
-
-    // if scaled is >= 0x8000<etc>, then we are going to overflow or
-    // underflow unless ABS(basis2) is large enough to pull us back into the
-    // non-overflow/underflow region.
-    if (scaled & INT64_MIN) {
-        if (is_neg && (basis2 < 0))
-            return false; // certain underflow
-
-        if (!is_neg && (basis2 >= 0))
-            return false; // certain overflow
-
-        if (ABS(basis2) <= static_cast<int64_t>(scaled & INT64_MAX))
-            return false; // not enough
-
-        // Looks like we are OK
-        *out = (is_neg ? (-scaled) : scaled) + basis2;
-    } else {
-        // Scaled fits within signed bounds, so we just need to check for
-        // over/underflow for two signed integers.  Basically, if both scaled
-        // and basis2 have the same sign bit, and the result has a different
-        // sign bit, then we have under/overflow.  An easy way to compute this
-        // is
-        // (scaled_signbit XNOR basis_signbit) &&
-        // (scaled_signbit XOR res_signbit)
-        // ==
-        // (scaled_signbit XOR basis_signbit XOR 1) &&
-        // (scaled_signbit XOR res_signbit)
-
-        if (is_neg)
-            scaled = -scaled; // known sanitize failure
-        res = scaled + basis2;
-
-        if ((scaled ^ basis2 ^ INT64_MIN) & (scaled ^ res) & INT64_MIN)
-            return false;
-
-        *out = res;
-    }
-
-    return true;
-}
-
-bool LinearTransform::doForwardTransform(int64_t a_in, int64_t* b_out) const {
-    if (0 == a_to_b_denom)
-        return false;
-
-    return linear_transform_s64_to_s64(a_in,
-                                       a_zero,
-                                       a_to_b_numer,
-                                       a_to_b_denom,
-                                       false,
-                                       b_zero,
-                                       b_out);
-}
-
-bool LinearTransform::doReverseTransform(int64_t b_in, int64_t* a_out) const {
-    if (0 == a_to_b_numer)
-        return false;
-
-    return linear_transform_s64_to_s64(b_in,
-                                       b_zero,
-                                       a_to_b_numer,
-                                       a_to_b_denom,
-                                       true,
-                                       a_zero,
-                                       a_out);
-}
-
-template <class T> void LinearTransform::reduce(T* N, T* D) {
-    T a, b;
-    if (!N || !D || !(*D)) {
-        assert(false);
-        return;
-    }
-
-    a = *N;
-    b = *D;
-
-    if (a == 0) {
-        *D = 1;
-        return;
-    }
-
-    // This implements Euclid's method to find GCD.
-    if (a < b) {
-        T tmp = a;
-        a = b;
-        b = tmp;
-    }
-
-    while (1) {
-        // a is now the greater of the two.
-        const T remainder = a % b;
-        if (remainder == 0) {
-            *N /= b;
-            *D /= b;
-            return;
-        }
-        // by swapping remainder and b, we are guaranteeing that a is
-        // still the greater of the two upon entrance to the loop.
-        a = b;
-        b = remainder;
-    }
-};
-
-template void LinearTransform::reduce<uint64_t>(uint64_t* N, uint64_t* D);
-template void LinearTransform::reduce<uint32_t>(uint32_t* N, uint32_t* D);
-
-// sanitize failure if *N = 0x80000000
-ATTRIBUTE_NO_SANITIZE_INTEGER
-void LinearTransform::reduce(int32_t* N, uint32_t* D) {
-    if (N && D && *D) {
-        if (*N < 0) {
-            *N = -(*N);
-            reduce(reinterpret_cast<uint32_t*>(N), D);
-            *N = -(*N);
-        } else {
-            reduce(reinterpret_cast<uint32_t*>(N), D);
-        }
-    }
-}
-
-}  // namespace android
diff --git a/libutils/Log.cpp b/libutils/Log.cpp
deleted file mode 100644
index bffb56e..0000000
--- a/libutils/Log.cpp
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright (C) 2012 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.
- */
-
-#define LOG_TAG "Log"
-
-#include <utils/Log.h>
-#include <utils/Timers.h>
-
-namespace android {
-
-LogIfSlow::LogIfSlow(const char* tag, android_LogPriority priority,
-        int timeoutMillis, const char* message) :
-        mTag(tag), mPriority(priority), mTimeoutMillis(timeoutMillis), mMessage(message),
-        mStart(systemTime(SYSTEM_TIME_BOOTTIME)) {
-}
-
-LogIfSlow::~LogIfSlow() {
-    int durationMillis = nanoseconds_to_milliseconds(systemTime(SYSTEM_TIME_BOOTTIME) - mStart);
-    if (durationMillis > mTimeoutMillis) {
-        LOG_PRI(mPriority, mTag, "%s: %dms", mMessage, durationMillis);
-    }
-}
-
-} // namespace android
diff --git a/libutils/Looper.cpp b/libutils/Looper.cpp
index 77e69e4..102fdf0 100644
--- a/libutils/Looper.cpp
+++ b/libutils/Looper.cpp
@@ -13,17 +13,8 @@
 // Debugs callback registration and invocation.
 #define DEBUG_CALLBACKS 0
 
-#include <errno.h>
-#include <fcntl.h>
-#include <inttypes.h>
-#include <limits.h>
-#include <string.h>
-#include <sys/eventfd.h>
-#include <unistd.h>
-
-#include <log/log.h>
 #include <utils/Looper.h>
-#include <utils/Timers.h>
+#include <sys/eventfd.h>
 
 namespace android {
 
@@ -38,7 +29,7 @@
 
 void WeakMessageHandler::handleMessage(const Message& message) {
     sp<MessageHandler> handler = mHandler.promote();
-    if (handler != NULL) {
+    if (handler != nullptr) {
         handler->handleMessage(message);
     }
 }
@@ -69,23 +60,22 @@
 static pthread_once_t gTLSOnce = PTHREAD_ONCE_INIT;
 static pthread_key_t gTLSKey = 0;
 
-Looper::Looper(bool allowNonCallbacks) :
-        mAllowNonCallbacks(allowNonCallbacks), mSendingMessage(false),
-        mPolling(false), mEpollFd(-1), mEpollRebuildRequired(false),
-        mNextRequestSeq(0), mResponseIndex(0), mNextMessageUptime(LLONG_MAX) {
-    mWakeEventFd = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC);
-    LOG_ALWAYS_FATAL_IF(mWakeEventFd < 0, "Could not make wake event fd: %s",
-                        strerror(errno));
+Looper::Looper(bool allowNonCallbacks)
+    : mAllowNonCallbacks(allowNonCallbacks),
+      mSendingMessage(false),
+      mPolling(false),
+      mEpollRebuildRequired(false),
+      mNextRequestSeq(0),
+      mResponseIndex(0),
+      mNextMessageUptime(LLONG_MAX) {
+    mWakeEventFd.reset(eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC));
+    LOG_ALWAYS_FATAL_IF(mWakeEventFd.get() < 0, "Could not make wake event fd: %s", strerror(errno));
 
     AutoMutex _l(mLock);
     rebuildEpollLocked();
 }
 
 Looper::~Looper() {
-    close(mWakeEventFd);
-    if (mEpollFd >= 0) {
-        close(mEpollFd);
-    }
 }
 
 void Looper::initTLSKey() {
@@ -95,7 +85,7 @@
 
 void Looper::threadDestructor(void *st) {
     Looper* const self = static_cast<Looper*>(st);
-    if (self != NULL) {
+    if (self != nullptr) {
         self->decStrong((void*)threadDestructor);
     }
 }
@@ -103,13 +93,13 @@
 void Looper::setForThread(const sp<Looper>& looper) {
     sp<Looper> old = getForThread(); // also has side-effect of initializing TLS
 
-    if (looper != NULL) {
+    if (looper != nullptr) {
         looper->incStrong((void*)threadDestructor);
     }
 
     pthread_setspecific(gTLSKey, looper.get());
 
-    if (old != NULL) {
+    if (old != nullptr) {
         old->decStrong((void*)threadDestructor);
     }
 }
@@ -124,7 +114,7 @@
 sp<Looper> Looper::prepare(int opts) {
     bool allowNonCallbacks = opts & PREPARE_ALLOW_NON_CALLBACKS;
     sp<Looper> looper = Looper::getForThread();
-    if (looper == NULL) {
+    if (looper == nullptr) {
         looper = new Looper(allowNonCallbacks);
         Looper::setForThread(looper);
     }
@@ -145,18 +135,18 @@
 #if DEBUG_CALLBACKS
         ALOGD("%p ~ rebuildEpollLocked - rebuilding epoll set", this);
 #endif
-        close(mEpollFd);
+        mEpollFd.reset();
     }
 
     // Allocate the new epoll instance and register the wake pipe.
-    mEpollFd = epoll_create(EPOLL_SIZE_HINT);
+    mEpollFd.reset(epoll_create(EPOLL_SIZE_HINT));
     LOG_ALWAYS_FATAL_IF(mEpollFd < 0, "Could not create epoll instance: %s", strerror(errno));
 
     struct epoll_event eventItem;
     memset(& eventItem, 0, sizeof(epoll_event)); // zero out unused members of data field union
     eventItem.events = EPOLLIN;
-    eventItem.data.fd = mWakeEventFd;
-    int result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeEventFd, & eventItem);
+    eventItem.data.fd = mWakeEventFd.get();
+    int result = epoll_ctl(mEpollFd.get(), EPOLL_CTL_ADD, mWakeEventFd.get(), &eventItem);
     LOG_ALWAYS_FATAL_IF(result != 0, "Could not add wake event fd to epoll instance: %s",
                         strerror(errno));
 
@@ -165,7 +155,7 @@
         struct epoll_event eventItem;
         request.initEventItem(&eventItem);
 
-        int epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, request.fd, & eventItem);
+        int epollResult = epoll_ctl(mEpollFd.get(), EPOLL_CTL_ADD, request.fd, &eventItem);
         if (epollResult < 0) {
             ALOGE("Error adding epoll events for fd %d while rebuilding epoll set: %s",
                   request.fd, strerror(errno));
@@ -198,9 +188,9 @@
                         "fd=%d, events=0x%x, data=%p",
                         this, ident, fd, events, data);
 #endif
-                if (outFd != NULL) *outFd = fd;
-                if (outEvents != NULL) *outEvents = events;
-                if (outData != NULL) *outData = data;
+                if (outFd != nullptr) *outFd = fd;
+                if (outEvents != nullptr) *outEvents = events;
+                if (outData != nullptr) *outData = data;
                 return ident;
             }
         }
@@ -209,9 +199,9 @@
 #if DEBUG_POLL_AND_WAKE
             ALOGD("%p ~ pollOnce - returning result %d", this, result);
 #endif
-            if (outFd != NULL) *outFd = 0;
-            if (outEvents != NULL) *outEvents = 0;
-            if (outData != NULL) *outData = NULL;
+            if (outFd != nullptr) *outFd = 0;
+            if (outEvents != nullptr) *outEvents = 0;
+            if (outData != nullptr) *outData = nullptr;
             return result;
         }
 
@@ -247,7 +237,7 @@
     mPolling = true;
 
     struct epoll_event eventItems[EPOLL_MAX_EVENTS];
-    int eventCount = epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis);
+    int eventCount = epoll_wait(mEpollFd.get(), eventItems, EPOLL_MAX_EVENTS, timeoutMillis);
 
     // No longer idling.
     mPolling = false;
@@ -289,7 +279,7 @@
     for (int i = 0; i < eventCount; i++) {
         int fd = eventItems[i].data.fd;
         uint32_t epollEvents = eventItems[i].events;
-        if (fd == mWakeEventFd) {
+        if (fd == mWakeEventFd.get()) {
             if (epollEvents & EPOLLIN) {
                 awoken();
             } else {
@@ -409,10 +399,11 @@
 #endif
 
     uint64_t inc = 1;
-    ssize_t nWrite = TEMP_FAILURE_RETRY(write(mWakeEventFd, &inc, sizeof(uint64_t)));
+    ssize_t nWrite = TEMP_FAILURE_RETRY(write(mWakeEventFd.get(), &inc, sizeof(uint64_t)));
     if (nWrite != sizeof(uint64_t)) {
         if (errno != EAGAIN) {
-            ALOGW("Could not write wake signal: %s", strerror(errno));
+            LOG_ALWAYS_FATAL("Could not write wake signal to fd %d: %s", mWakeEventFd.get(),
+                             strerror(errno));
         }
     }
 }
@@ -423,7 +414,7 @@
 #endif
 
     uint64_t counter;
-    TEMP_FAILURE_RETRY(read(mWakeEventFd, &counter, sizeof(uint64_t)));
+    TEMP_FAILURE_RETRY(read(mWakeEventFd.get(), &counter, sizeof(uint64_t)));
 }
 
 void Looper::pushResponse(int events, const Request& request) {
@@ -434,7 +425,7 @@
 }
 
 int Looper::addFd(int fd, int ident, int events, Looper_callbackFunc callback, void* data) {
-    return addFd(fd, ident, events, callback ? new SimpleLooperCallback(callback) : NULL, data);
+    return addFd(fd, ident, events, callback ? new SimpleLooperCallback(callback) : nullptr, data);
 }
 
 int Looper::addFd(int fd, int ident, int events, const sp<LooperCallback>& callback, void* data) {
@@ -474,14 +465,14 @@
 
         ssize_t requestIndex = mRequests.indexOfKey(fd);
         if (requestIndex < 0) {
-            int epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, fd, & eventItem);
+            int epollResult = epoll_ctl(mEpollFd.get(), EPOLL_CTL_ADD, fd, &eventItem);
             if (epollResult < 0) {
                 ALOGE("Error adding epoll events for fd %d: %s", fd, strerror(errno));
                 return -1;
             }
             mRequests.add(fd, request);
         } else {
-            int epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_MOD, fd, & eventItem);
+            int epollResult = epoll_ctl(mEpollFd.get(), EPOLL_CTL_MOD, fd, &eventItem);
             if (epollResult < 0) {
                 if (errno == ENOENT) {
                     // Tolerate ENOENT because it means that an older file descriptor was
@@ -502,7 +493,7 @@
                             "being recycled, falling back on EPOLL_CTL_ADD: %s",
                             this, strerror(errno));
 #endif
-                    epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, fd, & eventItem);
+                    epollResult = epoll_ctl(mEpollFd.get(), EPOLL_CTL_ADD, fd, &eventItem);
                     if (epollResult < 0) {
                         ALOGE("Error modifying or adding epoll events for fd %d: %s",
                                 fd, strerror(errno));
@@ -549,7 +540,7 @@
         // updating the epoll set so that we avoid accidentally leaking callbacks.
         mRequests.removeItemsAt(requestIndex);
 
-        int epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_DEL, fd, NULL);
+        int epollResult = epoll_ctl(mEpollFd.get(), EPOLL_CTL_DEL, fd, nullptr);
         if (epollResult < 0) {
             if (seq != -1 && (errno == EBADF || errno == ENOENT)) {
                 // Tolerate EBADF or ENOENT when the sequence number is known because it
diff --git a/libutils/NativeHandle.cpp b/libutils/NativeHandle.cpp
index e4daca7..d437a9f 100644
--- a/libutils/NativeHandle.cpp
+++ b/libutils/NativeHandle.cpp
@@ -19,14 +19,14 @@
 
 namespace android {
 
-sp<NativeHandle> NativeHandle::create(
-        native_handle_t* handle, bool ownsHandle) {
-    return handle ? new NativeHandle(handle, ownsHandle) : NULL;
+sp<NativeHandle> NativeHandle::create(native_handle_t* handle, bool ownsHandle) {
+    return handle ? new NativeHandle(handle, ownsHandle) : nullptr;
 }
 
 NativeHandle::NativeHandle(native_handle_t* handle, bool ownsHandle)
-:   mHandle(handle), mOwnsHandle(ownsHandle)
-{}
+        : mHandle(handle), mOwnsHandle(ownsHandle) {
+
+}
 
 NativeHandle::~NativeHandle() {
     if (mOwnsHandle) {
diff --git a/libutils/OWNERS b/libutils/OWNERS
new file mode 100644
index 0000000..40164aa
--- /dev/null
+++ b/libutils/OWNERS
@@ -0,0 +1 @@
+smoreland@google.com
diff --git a/libutils/Printer.cpp b/libutils/Printer.cpp
index 84af293..c9ae210 100644
--- a/libutils/Printer.cpp
+++ b/libutils/Printer.cpp
@@ -21,8 +21,6 @@
 #include <utils/String8.h>
 #include <utils/Log.h>
 
-#include <string.h>
-#include <stdio.h>
 #include <stdlib.h>
 
 namespace android {
@@ -75,7 +73,7 @@
 }
 
 void LogPrinter::printLine(const char* string) {
-    if (string == NULL) {
+    if (string == nullptr) {
         ALOGW("%s: NULL string passed in", __FUNCTION__);
         return;
     }
@@ -109,7 +107,7 @@
 }
 
 void FdPrinter::printLine(const char* string) {
-    if (string == NULL) {
+    if (string == nullptr) {
         ALOGW("%s: NULL string passed in", __FUNCTION__);
         return;
     } else if (mFd < 0) {
@@ -129,16 +127,16 @@
         mTarget(target),
         mPrefix(prefix ?: "") {
 
-    if (target == NULL) {
+    if (target == nullptr) {
         ALOGW("%s: Target string was NULL", __FUNCTION__);
     }
 }
 
 void String8Printer::printLine(const char* string) {
-    if (string == NULL) {
+    if (string == nullptr) {
         ALOGW("%s: NULL string passed in", __FUNCTION__);
         return;
-    } else if (mTarget == NULL) {
+    } else if (mTarget == nullptr) {
         ALOGW("%s: Target string was NULL", __FUNCTION__);
         return;
     }
diff --git a/libutils/ProcessCallStack.cpp b/libutils/ProcessCallStack.cpp
index 983847c..f054de9 100644
--- a/libutils/ProcessCallStack.cpp
+++ b/libutils/ProcessCallStack.cpp
@@ -17,20 +17,15 @@
 #define LOG_TAG "ProcessCallStack"
 // #define LOG_NDEBUG 0
 
+#include <utils/ProcessCallStack.h>
+
 #include <dirent.h>
-#include <errno.h>
-#include <stdio.h>
-#include <string.h>
 #include <unistd.h>
+
 #include <memory>
 
-#include <utils/Log.h>
-#include <utils/Errors.h>
-#include <utils/ProcessCallStack.h>
 #include <utils/Printer.h>
 
-#include <limits.h>
-
 namespace android {
 
 enum {
@@ -47,14 +42,14 @@
 static const char* PATH_SELF_TASK = "/proc/self/task";
 
 static void dumpProcessHeader(Printer& printer, pid_t pid, const char* timeStr) {
-    if (timeStr == NULL) {
+    if (timeStr == nullptr) {
         ALOGW("%s: timeStr was NULL", __FUNCTION__);
         return;
     }
 
     char path[PATH_MAX];
     char procNameBuf[MAX_PROC_PATH];
-    char* procName = NULL;
+    char* procName = nullptr;
     FILE* fp;
 
     snprintf(path, sizeof(path), "/proc/%d/cmdline", pid);
@@ -81,7 +76,7 @@
 
 static String8 getThreadName(pid_t tid) {
     char path[PATH_MAX];
-    char* procName = NULL;
+    char* procName = nullptr;
     char procNameBuf[MAX_PROC_PATH];
     FILE* fp;
 
@@ -93,7 +88,7 @@
         ALOGE("%s: Failed to open %s", __FUNCTION__, path);
     }
 
-    if (procName == NULL) {
+    if (procName == nullptr) {
         // Reading /proc/self/task/%d/comm failed due to a race
         return String8::format("[err-unknown-tid-%d]", tid);
     }
@@ -133,7 +128,7 @@
 
 void ProcessCallStack::update() {
     std::unique_ptr<DIR, decltype(&closedir)> dp(opendir(PATH_SELF_TASK), closedir);
-    if (dp == NULL) {
+    if (dp == nullptr) {
         ALOGE("%s: Failed to update the process's call stacks: %s",
               __FUNCTION__, strerror(errno));
         return;
@@ -145,7 +140,7 @@
 
     // Get current time.
     {
-        time_t t = time(NULL);
+        time_t t = time(nullptr);
         struct tm tm;
         localtime_r(&t, &tm);
 
@@ -157,7 +152,7 @@
      * - Read every file in directory => get every tid
      */
     dirent* ep;
-    while ((ep = readdir(dp.get())) != NULL) {
+    while ((ep = readdir(dp.get())) != nullptr) {
         pid_t tid = -1;
         sscanf(ep->d_name, "%d", &tid);
 
diff --git a/libutils/PropertyMap.cpp b/libutils/PropertyMap.cpp
index 5520702..b8c065d 100644
--- a/libutils/PropertyMap.cpp
+++ b/libutils/PropertyMap.cpp
@@ -16,11 +16,7 @@
 
 #define LOG_TAG "PropertyMap"
 
-#include <stdlib.h>
-#include <string.h>
-
 #include <utils/PropertyMap.h>
-#include <utils/Log.h>
 
 // Enables debug output for the parser.
 #define DEBUG_PARSER 0
@@ -116,7 +112,7 @@
 }
 
 status_t PropertyMap::load(const String8& filename, PropertyMap** outMap) {
-    *outMap = NULL;
+    *outMap = nullptr;
 
     Tokenizer* tokenizer;
     status_t status = Tokenizer::open(filename, &tokenizer);
diff --git a/libutils/RefBase.cpp b/libutils/RefBase.cpp
index 4252ba6..9074850 100644
--- a/libutils/RefBase.cpp
+++ b/libutils/RefBase.cpp
@@ -17,18 +17,9 @@
 #define LOG_TAG "RefBase"
 // #define LOG_NDEBUG 0
 
-#include <fcntl.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
-
 #include <utils/RefBase.h>
 
 #include <utils/CallStack.h>
-#include <utils/Log.h>
-#include <utils/threads.h>
 
 #ifndef __unused
 #define __unused __attribute__((__unused__))
@@ -419,8 +410,7 @@
         return;
     }
 
-    int32_t old = refs->mStrong.fetch_sub(INITIAL_STRONG_VALUE,
-            std::memory_order_relaxed);
+    int32_t old __unused = refs->mStrong.fetch_sub(INITIAL_STRONG_VALUE, std::memory_order_relaxed);
     // A decStrong() must still happen after us.
     ALOG_ASSERT(old > INITIAL_STRONG_VALUE, "0x%x too small", old);
     refs->mBase->onFirstRef();
@@ -451,6 +441,11 @@
     // and all accesses to refs happen before its deletion in the final decWeak.
     // The destructor can safely access mRefs because either it's deleting
     // mRefs itself, or it's running entirely before the final mWeak decrement.
+    //
+    // Since we're doing atomic loads of `flags`, the static analyzer assumes
+    // they can change between `delete this;` and `refs->decWeak(id);`. This is
+    // not the case. The analyzer may become more okay with this patten when
+    // https://bugs.llvm.org/show_bug.cgi?id=34365 gets resolved. NOLINTNEXTLINE
     refs->decWeak(id);
 }
 
@@ -717,7 +712,7 @@
         delete mRefs;
     }
     // For debugging purposes, clear mRefs.  Ineffective against outstanding wp's.
-    const_cast<weakref_impl*&>(mRefs) = NULL;
+    const_cast<weakref_impl*&>(mRefs) = nullptr;
 }
 
 void RefBase::extendObjectLifetime(int32_t mode)
@@ -769,6 +764,4 @@
     ref->mRefs->renameWeakRefId(old_id, new_id);
 }
 
-VirtualLightRefBase::~VirtualLightRefBase() {}
-
 }; // namespace android
diff --git a/libutils/SharedBuffer.cpp b/libutils/SharedBuffer.cpp
index 957aedb..7910c6e 100644
--- a/libutils/SharedBuffer.cpp
+++ b/libutils/SharedBuffer.cpp
@@ -16,13 +16,13 @@
 
 #define LOG_TAG "sharedbuffer"
 
+#include "SharedBuffer.h"
+
 #include <stdlib.h>
 #include <string.h>
 
 #include <log/log.h>
 
-#include "SharedBuffer.h"
-
 // ---------------------------------------------------------------------------
 
 namespace android {
@@ -75,7 +75,7 @@
                             "Invalid buffer size %zu", newSize);
 
         buf = (SharedBuffer*)realloc(buf, sizeof(SharedBuffer) + newSize);
-        if (buf != NULL) {
+        if (buf != nullptr) {
             buf->mSize = newSize;
             return buf;
         }
@@ -94,7 +94,7 @@
     if (onlyOwner()) {
         return const_cast<SharedBuffer*>(this);
     }
-    return 0;
+    return nullptr;
 }
 
 SharedBuffer* SharedBuffer::reset(size_t new_size) const
@@ -113,16 +113,26 @@
 
 int32_t SharedBuffer::release(uint32_t flags) const
 {
-    int32_t prev = 1;
-    if (onlyOwner()
-            || (((prev = mRefs.fetch_sub(1, std::memory_order_release)) == 1)
-                && (atomic_thread_fence(std::memory_order_acquire), true))) {
+    const bool useDealloc = ((flags & eKeepStorage) == 0);
+    if (onlyOwner()) {
+        // Since we're the only owner, our reference count goes to zero.
         mRefs.store(0, std::memory_order_relaxed);
-        if ((flags & eKeepStorage) == 0) {
-            free(const_cast<SharedBuffer*>(this));
+        if (useDealloc) {
+            dealloc(this);
+        }
+        // As the only owner, our previous reference count was 1.
+        return 1;
+    }
+    // There's multiple owners, we need to use an atomic decrement.
+    int32_t prevRefCount = mRefs.fetch_sub(1, std::memory_order_release);
+    if (prevRefCount == 1) {
+        // We're the last reference, we need the acquire fence.
+        atomic_thread_fence(std::memory_order_acquire);
+        if (useDealloc) {
+            dealloc(this);
         }
     }
-    return prev;
+    return prevRefCount;
 }
 
 
diff --git a/libutils/SharedBuffer.h b/libutils/SharedBuffer.h
index 48358cd..fdf13a9 100644
--- a/libutils/SharedBuffer.h
+++ b/libutils/SharedBuffer.h
@@ -124,11 +124,11 @@
 }
 
 SharedBuffer* SharedBuffer::bufferFromData(void* data) {
-    return data ? static_cast<SharedBuffer *>(data)-1 : 0;
+    return data ? static_cast<SharedBuffer *>(data)-1 : nullptr;
 }
     
 const SharedBuffer* SharedBuffer::bufferFromData(const void* data) {
-    return data ? static_cast<const SharedBuffer *>(data)-1 : 0;
+    return data ? static_cast<const SharedBuffer *>(data)-1 : nullptr;
 }
 
 size_t SharedBuffer::sizeFromData(const void* data) {
@@ -139,7 +139,7 @@
     return (mRefs.load(std::memory_order_acquire) == 1);
 }
 
-}; // namespace android
+}  // namespace android
 
 // ---------------------------------------------------------------------------
 
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/StopWatch.cpp b/libutils/StopWatch.cpp
index 8c7b596..d01865e 100644
--- a/libutils/StopWatch.cpp
+++ b/libutils/StopWatch.cpp
@@ -16,9 +16,7 @@
 
 #define LOG_TAG "StopWatch"
 
-#include <string.h>
-#include <stdlib.h>
-#include <stdio.h>
+#include <utils/StopWatch.h>
 
 /* for PRId64 */
 #ifndef __STDC_FORMAT_MACROS
@@ -27,17 +25,12 @@
 #include <inttypes.h>
 
 #include <utils/Log.h>
-#include <utils/Errors.h>
-#include <utils/StopWatch.h>
 
 /*****************************************************************************/
 
 namespace android {
 
-
-StopWatch::StopWatch(const char *name, int clock, uint32_t flags)
-    :   mName(name), mClock(clock), mFlags(flags)
-{
+StopWatch::StopWatch(const char* name, int clock) : mName(name), mClock(clock) {
     reset();
 }
 
diff --git a/libutils/String16.cpp b/libutils/String16.cpp
index 9f5cfea..f820b8b 100644
--- a/libutils/String16.cpp
+++ b/libutils/String16.cpp
@@ -17,40 +17,23 @@
 #include <utils/String16.h>
 
 #include <utils/Log.h>
-#include <utils/Unicode.h>
-#include <utils/threads.h>
 
-#include <memory.h>
-#include <stdio.h>
 #include <ctype.h>
 
 #include "SharedBuffer.h"
 
 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());
 }
 
 // ---------------------------------------------------------------------------
@@ -91,7 +74,7 @@
 }
 
 String16::String16(StaticLinkage)
-    : mString(0)
+    : mString(nullptr)
 {
     // this constructor is used when we can't rely on the static-initializers
     // having run. In this case we always allocate an empty string. It's less
@@ -353,7 +336,7 @@
 {
     const size_t N = size();
     const char16_t* str = string();
-    char16_t* edit = NULL;
+    char16_t* edit = nullptr;
     for (size_t i=0; i<N; i++) {
         const char16_t v = str[i];
         if (v >= 'A' && v <= 'Z') {
@@ -375,7 +358,7 @@
 {
     const size_t N = size();
     const char16_t* str = string();
-    char16_t* edit = NULL;
+    char16_t* edit = nullptr;
     for (size_t i=0; i<N; i++) {
         if (str[i] == replaceThis) {
             if (!edit) {
diff --git a/libutils/String8.cpp b/libutils/String8.cpp
index cacaf91..8d318f7 100644
--- a/libutils/String8.cpp
+++ b/libutils/String8.cpp
@@ -21,9 +21,7 @@
 
 #include <utils/Compat.h>
 #include <utils/Log.h>
-#include <utils/Unicode.h>
 #include <utils/String16.h>
-#include <utils/threads.h>
 
 #include <ctype.h>
 
@@ -42,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());
 }
 
 // ---------------------------------------------------------------------------
@@ -84,7 +58,7 @@
 {
     if (len > 0) {
         if (len == SIZE_MAX) {
-            return NULL;
+            return nullptr;
         }
         SharedBuffer* buf = SharedBuffer::alloc(len+1);
         ALOG_ASSERT(buf, "Unable to allocate shared buffer");
@@ -94,7 +68,7 @@
             str[len] = 0;
             return str;
         }
-        return NULL;
+        return nullptr;
     }
 
     return getEmptyString();
@@ -152,7 +126,7 @@
 }
 
 String8::String8(StaticLinkage)
-    : mString(0)
+    : mString(nullptr)
 {
     // this constructor is used when we can't rely on the static-initializers
     // having run. In this case we always allocate an empty string. It's less
@@ -173,7 +147,7 @@
 String8::String8(const char* o)
     : mString(allocFromUTF8(o, strlen(o)))
 {
-    if (mString == NULL) {
+    if (mString == nullptr) {
         mString = getEmptyString();
     }
 }
@@ -181,7 +155,7 @@
 String8::String8(const char* o, size_t len)
     : mString(allocFromUTF8(o, len))
 {
-    if (mString == NULL) {
+    if (mString == nullptr) {
         mString = getEmptyString();
     }
 }
@@ -345,7 +319,7 @@
      * second vsnprintf access undefined args.
      */
     va_copy(tmp_args, args);
-    n = vsnprintf(NULL, 0, fmt, tmp_args);
+    n = vsnprintf(nullptr, 0, fmt, tmp_args);
     va_end(tmp_args);
 
     if (n != 0) {
@@ -386,7 +360,7 @@
         mString = str;
         return str;
     }
-    return NULL;
+    return nullptr;
 }
 
 void String8::unlockBuffer()
@@ -538,7 +512,7 @@
     const char*const buf = mString;
 
     cp = strrchr(buf, OS_PATH_SEPARATOR);
-    if (cp == NULL)
+    if (cp == nullptr)
         return String8(*this);
     else
         return String8(cp+1);
@@ -550,7 +524,7 @@
     const char*const str = mString;
 
     cp = strrchr(str, OS_PATH_SEPARATOR);
-    if (cp == NULL)
+    if (cp == nullptr)
         return String8("");
     else
         return String8(str, cp - str);
@@ -569,7 +543,7 @@
         cp = strchr(buf, OS_PATH_SEPARATOR);
     }
 
-    if (cp == NULL) {
+    if (cp == nullptr) {
         String8 res = buf != str ? String8(buf) : *this;
         if (outRemains) *outRemains = String8("");
         return res;
@@ -593,15 +567,15 @@
 
     // only look at the filename
     lastSlash = strrchr(str, OS_PATH_SEPARATOR);
-    if (lastSlash == NULL)
+    if (lastSlash == nullptr)
         lastSlash = str;
     else
         lastSlash++;
 
     // find the last dot
     lastDot = strrchr(lastSlash, '.');
-    if (lastDot == NULL)
-        return NULL;
+    if (lastDot == nullptr)
+        return nullptr;
 
     // looks good, ship it
     return const_cast<char*>(lastDot);
@@ -612,7 +586,7 @@
     char* ext;
 
     ext = find_extension();
-    if (ext != NULL)
+    if (ext != nullptr)
         return String8(ext);
     else
         return String8("");
@@ -624,7 +598,7 @@
     const char* const str = mString;
 
     ext = find_extension();
-    if (ext == NULL)
+    if (ext == nullptr)
         return String8(*this);
     else
         return String8(str, ext - str);
diff --git a/libutils/SystemClock.cpp b/libutils/SystemClock.cpp
index 965e32c..73ec1be 100644
--- a/libutils/SystemClock.cpp
+++ b/libutils/SystemClock.cpp
@@ -19,17 +19,17 @@
  * System clock functions.
  */
 
-#include <sys/time.h>
-#include <limits.h>
-#include <fcntl.h>
+#define LOG_TAG "SystemClock"
+
+#include <utils/SystemClock.h>
+
 #include <string.h>
 #include <errno.h>
+#include <time.h>
 
 #include <cutils/compiler.h>
-#include <utils/SystemClock.h>
-#include <utils/Timers.h>
 
-#define LOG_TAG "SystemClock"
+#include <utils/Timers.h>
 #include <utils/Log.h>
 
 namespace android {
diff --git a/libutils/Threads.cpp b/libutils/Threads.cpp
index def739f..43ec6c1 100644
--- a/libutils/Threads.cpp
+++ b/libutils/Threads.cpp
@@ -18,16 +18,10 @@
 #define LOG_TAG "libutils.threads"
 
 #include <assert.h>
-#include <errno.h>
-#include <memory.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
+#include <utils/Thread.h>
+#include <utils/AndroidThreads.h>
 
 #if !defined(_WIN32)
-# include <pthread.h>
-# include <sched.h>
 # include <sys/resource.h>
 #else
 # include <windows.h>
@@ -40,7 +34,6 @@
 #include <sys/prctl.h>
 #endif
 
-#include <utils/threads.h>
 #include <utils/Log.h>
 
 #include <cutils/sched_policy.h>
@@ -170,7 +163,7 @@
     // Note that *threadID is directly available to the parent only, as it is
     // assigned after the child starts.  Use memory barrier / lock if the child
     // or other threads also need access.
-    if (threadId != NULL) {
+    if (threadId != nullptr) {
         *threadId = (android_thread_id_t)thread; // XXX: this is not portable
     }
     return 1;
@@ -355,7 +348,7 @@
     mState = (void*) hMutex;
 }
 
-Mutex::Mutex(const char* name)
+Mutex::Mutex(const char* /*name*/)
 {
     // XXX: name not used for now
     HANDLE hMutex;
@@ -366,7 +359,7 @@
     mState = (void*) hMutex;
 }
 
-Mutex::Mutex(int type, const char* name)
+Mutex::Mutex(int /*type*/, const char* /*name*/)
 {
     // XXX: type and name not used for now
     HANDLE hMutex;
@@ -775,7 +768,7 @@
         strong.clear();
         // And immediately, re-acquire a strong reference for the next loop
         strong = weak.promote();
-    } while(strong != 0);
+    } while(strong != nullptr);
 
     return 0;
 }
diff --git a/libutils/Timers.cpp b/libutils/Timers.cpp
index 201bc41..c3641ef 100644
--- a/libutils/Timers.cpp
+++ b/libutils/Timers.cpp
@@ -20,7 +20,6 @@
 #include <utils/Timers.h>
 
 #include <limits.h>
-#include <sys/time.h>
 #include <time.h>
 
 #if defined(__ANDROID__)
@@ -46,14 +45,14 @@
     // is windows.
     struct timeval t;
     t.tv_sec = t.tv_usec = 0;
-    gettimeofday(&t, NULL);
+    gettimeofday(&t, nullptr);
     return nsecs_t(t.tv_sec)*1000000000LL + nsecs_t(t.tv_usec)*1000LL;
 }
 #endif
 
 int toMillisecondTimeoutDelay(nsecs_t referenceTime, nsecs_t timeoutTime)
 {
-    int timeoutDelayMillis;
+    nsecs_t timeoutDelayMillis;
     if (timeoutTime > referenceTime) {
         uint64_t timeoutDelay = uint64_t(timeoutTime - referenceTime);
         if (timeoutDelay > uint64_t((INT_MAX - 1) * 1000000LL)) {
@@ -64,5 +63,5 @@
     } else {
         timeoutDelayMillis = 0;
     }
-    return timeoutDelayMillis;
+    return (int)timeoutDelayMillis;
 }
diff --git a/libutils/Tokenizer.cpp b/libutils/Tokenizer.cpp
index 2d0e83d..f73d699 100644
--- a/libutils/Tokenizer.cpp
+++ b/libutils/Tokenizer.cpp
@@ -16,14 +16,10 @@
 
 #define LOG_TAG "Tokenizer"
 
-#include <stdlib.h>
-#include <unistd.h>
+#include <utils/Tokenizer.h>
 #include <fcntl.h>
-#include <errno.h>
-#include <sys/types.h>
 #include <sys/stat.h>
 #include <utils/Log.h>
-#include <utils/Tokenizer.h>
 
 // Enables debug output for the tokenizer.
 #define DEBUG_TOKENIZER 0
@@ -32,7 +28,7 @@
 namespace android {
 
 static inline bool isDelimiter(char ch, const char* delimiters) {
-    return strchr(delimiters, ch) != NULL;
+    return strchr(delimiters, ch) != nullptr;
 }
 
 Tokenizer::Tokenizer(const String8& filename, FileMap* fileMap, char* buffer,
@@ -50,7 +46,7 @@
 }
 
 status_t Tokenizer::open(const String8& filename, Tokenizer** outTokenizer) {
-    *outTokenizer = NULL;
+    *outTokenizer = nullptr;
 
     int result = NO_ERROR;
     int fd = ::open(filename.string(), O_RDONLY);
@@ -68,12 +64,12 @@
             FileMap* fileMap = new FileMap();
             bool ownBuffer = false;
             char* buffer;
-            if (fileMap->create(NULL, fd, 0, length, true)) {
+            if (fileMap->create(nullptr, fd, 0, length, true)) {
                 fileMap->advise(FileMap::SEQUENTIAL);
                 buffer = static_cast<char*>(fileMap->getDataPtr());
             } else {
                 delete fileMap;
-                fileMap = NULL;
+                fileMap = nullptr;
 
                 // Fall back to reading into a buffer since we can't mmap files in sysfs.
                 // The length we obtained from stat is wrong too (it will always be 4096)
@@ -85,7 +81,7 @@
                     result = -errno;
                     ALOGE("Error reading file '%s': %s", filename.string(), strerror(errno));
                     delete[] buffer;
-                    buffer = NULL;
+                    buffer = nullptr;
                 } else {
                     length = size_t(nrd);
                 }
@@ -102,7 +98,7 @@
 
 status_t Tokenizer::fromContents(const String8& filename,
         const char* contents, Tokenizer** outTokenizer) {
-    *outTokenizer = new Tokenizer(filename, NULL,
+    *outTokenizer = new Tokenizer(filename, nullptr,
             const_cast<char*>(contents), false, strlen(contents));
     return OK;
 }
diff --git a/libutils/Trace.cpp b/libutils/Trace.cpp
index 36fd802..8530fdc 100644
--- a/libutils/Trace.cpp
+++ b/libutils/Trace.cpp
@@ -14,12 +14,11 @@
  * limitations under the License.
  */
 
-#include <utils/misc.h>
 #include <utils/Trace.h>
+#include <utils/misc.h>
 
 static void traceInit() __attribute__((constructor));
 
-static void traceInit()
-{
+static void traceInit() {
     ::android::add_sysprop_change_callback(atrace_update_tags, 0);
 }
diff --git a/libutils/Unicode.cpp b/libutils/Unicode.cpp
index f1a41b9..e00fb81 100644
--- a/libutils/Unicode.cpp
+++ b/libutils/Unicode.cpp
@@ -16,11 +16,10 @@
 
 #define LOG_TAG "unicode"
 
+#include <utils/Unicode.h>
 #include <limits.h>
-#include <stddef.h>
 
 #include <log/log.h>
-#include <utils/Unicode.h>
 
 #if defined(_WIN32)
 # undef  nhtol
@@ -160,7 +159,7 @@
         return -1;
     }
     size_t dummy_index;
-    if (next_index == NULL) {
+    if (next_index == nullptr) {
         next_index = &dummy_index;
     }
     size_t num_read;
@@ -174,21 +173,29 @@
 
 ssize_t utf32_to_utf8_length(const char32_t *src, size_t src_len)
 {
-    if (src == NULL || src_len == 0) {
+    if (src == nullptr || src_len == 0) {
         return -1;
     }
 
     size_t ret = 0;
     const char32_t *end = src + src_len;
     while (src < end) {
-        ret += utf32_codepoint_utf8_length(*src++);
+        size_t char_len = utf32_codepoint_utf8_length(*src++);
+        if (SSIZE_MAX - char_len < ret) {
+            // If this happens, we would overflow the ssize_t type when
+            // returning from this function, so we cannot express how
+            // long this string is in an ssize_t.
+            android_errorWriteLog(0x534e4554, "37723026");
+            return -1;
+        }
+        ret += char_len;
     }
     return ret;
 }
 
 void utf32_to_utf8(const char32_t* src, size_t src_len, char* dst, size_t dst_len)
 {
-    if (src == NULL || src_len == 0 || dst == NULL) {
+    if (src == nullptr || src_len == 0 || dst == nullptr) {
         return;
     }
 
@@ -298,23 +305,22 @@
 
 char16_t* strstr16(const char16_t* src, const char16_t* target)
 {
-    const char16_t needle = *target++;
-    const size_t target_len = strlen16(target);
-    if (needle != '\0') {
-      do {
+    const char16_t needle = *target;
+    if (needle == '\0') return (char16_t*)src;
+
+    const size_t target_len = strlen16(++target);
+    do {
         do {
-          if (*src == '\0') {
-            return nullptr;
-          }
+            if (*src == '\0') {
+                return nullptr;
+            }
         } while (*src++ != needle);
-      } while (strncmp16(src, target, target_len) != 0);
-      src--;
-    }
+    } while (strncmp16(src, target, target_len) != 0);
+    src--;
 
     return (char16_t*)src;
 }
 
-
 int strzcmp16(const char16_t *s1, size_t n1, const char16_t *s2, size_t n2)
 {
     const char16_t* e1 = s1+n1;
@@ -357,7 +363,7 @@
 
 void utf16_to_utf8(const char16_t* src, size_t src_len, char* dst, size_t dst_len)
 {
-    if (src == NULL || src_len == 0 || dst == NULL) {
+    if (src == nullptr || src_len == 0 || dst == nullptr) {
         return;
     }
 
@@ -434,21 +440,30 @@
 
 ssize_t utf16_to_utf8_length(const char16_t *src, size_t src_len)
 {
-    if (src == NULL || src_len == 0) {
+    if (src == nullptr || src_len == 0) {
         return -1;
     }
 
     size_t ret = 0;
     const char16_t* const end = src + src_len;
     while (src < end) {
+        size_t char_len;
         if ((*src & 0xFC00) == 0xD800 && (src + 1) < end
                 && (*(src + 1) & 0xFC00) == 0xDC00) {
             // surrogate pairs are always 4 bytes.
-            ret += 4;
+            char_len = 4;
             src += 2;
         } else {
-            ret += utf32_codepoint_utf8_length((char32_t) *src++);
+            char_len = utf32_codepoint_utf8_length((char32_t)*src++);
         }
+        if (SSIZE_MAX - char_len < ret) {
+            // If this happens, we would overflow the ssize_t type when
+            // returning from this function, so we cannot express how
+            // long this string is in an ssize_t.
+            android_errorWriteLog(0x534e4554, "37723026");
+            return -1;
+        }
+        ret += char_len;
     }
     return ret;
 }
@@ -475,7 +490,7 @@
 
 size_t utf8_to_utf32_length(const char *src, size_t src_len)
 {
-    if (src == NULL || src_len == 0) {
+    if (src == nullptr || src_len == 0) {
         return 0;
     }
     size_t ret = 0;
@@ -500,7 +515,7 @@
 
 void utf8_to_utf32(const char* src, size_t src_len, char32_t* dst)
 {
-    if (src == NULL || src_len == 0 || dst == NULL) {
+    if (src == nullptr || src_len == 0 || dst == nullptr) {
         return;
     }
 
diff --git a/libutils/VectorImpl.cpp b/libutils/VectorImpl.cpp
index f7ca8f4..00a904d 100644
--- a/libutils/VectorImpl.cpp
+++ b/libutils/VectorImpl.cpp
@@ -16,13 +16,13 @@
 
 #define LOG_TAG "Vector"
 
+#include <utils/VectorImpl.h>
+
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 
 #include <log/log.h>
-#include <utils/Errors.h>
-#include <utils/VectorImpl.h>
 
 #include <safe_iop.h>
 
@@ -44,7 +44,7 @@
 // ----------------------------------------------------------------------------
 
 VectorImpl::VectorImpl(size_t itemSize, uint32_t flags)
-    : mStorage(0), mCount(0), mFlags(flags), mItemSize(itemSize)
+    : mStorage(nullptr), mCount(0), mFlags(flags), mItemSize(itemSize)
 {
 }
 
@@ -77,7 +77,7 @@
             mCount = rhs.mCount;
             SharedBuffer::bufferFromData(mStorage)->acquire();
         } else {
-            mStorage = 0;
+            mStorage = nullptr;
             mCount = 0;
         }
     }
@@ -89,14 +89,14 @@
     if (mStorage) {
         const SharedBuffer* sb = SharedBuffer::bufferFromData(mStorage);
         SharedBuffer* editable = sb->attemptEdit();
-        if (editable == 0) {
+        if (editable == nullptr) {
             // If we're here, we're not the only owner of the buffer.
             // We must make a copy of it.
             editable = SharedBuffer::alloc(sb->size());
             // Fail instead of returning a pointer to storage that's not
             // editable. Otherwise we'd be editing the contents of a buffer
             // for which we're not the only owner, which is undefined behaviour.
-            LOG_ALWAYS_FATAL_IF(editable == NULL);
+            LOG_ALWAYS_FATAL_IF(editable == nullptr);
             _do_copy(editable->data(), mStorage, mCount);
             release_storage();
             mStorage = editable->data();
@@ -141,7 +141,7 @@
 
 ssize_t VectorImpl::insertAt(size_t index, size_t numItems)
 {
-    return insertAt(0, index, numItems);
+    return insertAt(nullptr, index, numItems);
 }
 
 ssize_t VectorImpl::insertAt(const void* item, size_t index, size_t numItems)
@@ -177,7 +177,7 @@
     const ssize_t count = size();
     if (count > 1) {
         void* array = const_cast<void*>(arrayImpl());
-        void* temp = 0;
+        void* temp = nullptr;
         ssize_t i = 1;
         while (i < count) {
             void* item = reinterpret_cast<char*>(array) + mItemSize*(i);
@@ -205,7 +205,7 @@
                     _do_copy(next, curr, 1);
                     next = curr;
                     --j;
-                    curr = NULL;
+                    curr = nullptr;
                     if (j >= 0) {
                         curr = reinterpret_cast<char*>(array) + mItemSize*(j);
                     }
@@ -233,7 +233,7 @@
 
 void VectorImpl::push()
 {
-    push(0);
+    push(nullptr);
 }
 
 void VectorImpl::push(const void* item)
@@ -243,7 +243,7 @@
 
 ssize_t VectorImpl::add()
 {
-    return add(0);
+    return add(nullptr);
 }
 
 ssize_t VectorImpl::add(const void* item)
@@ -253,7 +253,7 @@
 
 ssize_t VectorImpl::replaceAt(size_t index)
 {
-    return replaceAt(0, index);
+    return replaceAt(nullptr, index);
 }
 
 ssize_t VectorImpl::replaceAt(const void* prototype, size_t index)
@@ -267,10 +267,10 @@
 
     void* item = editItemLocation(index);
     if (item != prototype) {
-        if (item == 0)
+        if (item == nullptr)
             return NO_MEMORY;
         _do_destroy(item, 1);
-        if (prototype == 0) {
+        if (prototype == nullptr) {
             _do_construct(item, 1);
         } else {
             _do_copy(item, prototype, 1);
@@ -294,7 +294,7 @@
 void VectorImpl::finish_vector()
 {
     release_storage();
-    mStorage = 0;
+    mStorage = nullptr;
     mCount = 0;
 }
 
@@ -315,7 +315,7 @@
             return reinterpret_cast<char*>(buffer) + index*mItemSize;
         }
     }
-    return 0;
+    return nullptr;
 }
 
 const void* VectorImpl::itemLocation(size_t index) const
@@ -330,7 +330,7 @@
             return reinterpret_cast<const char*>(buffer) + index*mItemSize;
         }
     }
-    return 0;
+    return nullptr;
 }
 
 ssize_t VectorImpl::setCapacity(size_t new_capacity)
@@ -418,7 +418,7 @@
             if (sb) {
                 mStorage = sb->data();
             } else {
-                return NULL;
+                return nullptr;
             }
         } else {
             SharedBuffer* sb = SharedBuffer::alloc(new_alloc_size);
@@ -435,7 +435,7 @@
                 release_storage();
                 mStorage = const_cast<void*>(array);
             } else {
-                return NULL;
+                return nullptr;
             }
         }
     } else {
diff --git a/libutils/include/utils/AndroidThreads.h b/libutils/include/utils/AndroidThreads.h
index 4c2dd49..a8d7851 100644
--- a/libutils/include/utils/AndroidThreads.h
+++ b/libutils/include/utils/AndroidThreads.h
@@ -106,7 +106,7 @@
                             const char* threadName = "android:unnamed_thread",
                             int32_t threadPriority = PRIORITY_DEFAULT,
                             size_t threadStackSize = 0,
-                            thread_id_t *threadId = 0)
+                            thread_id_t *threadId = nullptr)
 {
     return androidCreateThreadEtc(entryFunction, userData, threadName,
         threadPriority, threadStackSize, threadId) ? true : false;
@@ -118,7 +118,7 @@
 }
 
 // ----------------------------------------------------------------------------
-}; // namespace android
+}  // namespace android
 #endif  // __cplusplus
 // ----------------------------------------------------------------------------
 
diff --git a/libutils/include/utils/Atomic.h b/libutils/include/utils/Atomic.h
index 7eb476c..0f592fe 100644
--- a/libutils/include/utils/Atomic.h
+++ b/libutils/include/utils/Atomic.h
@@ -17,6 +17,8 @@
 #ifndef ANDROID_UTILS_ATOMIC_H
 #define ANDROID_UTILS_ATOMIC_H
 
+// DO NOT USE: Please instead use std::atomic
+
 #include <cutils/atomic.h>
 
 #endif // ANDROID_UTILS_ATOMIC_H
diff --git a/libutils/include/utils/BitSet.h b/libutils/include/utils/BitSet.h
index 8c61293..8abfb1a 100644
--- a/libutils/include/utils/BitSet.h
+++ b/libutils/include/utils/BitSet.h
@@ -22,6 +22,8 @@
 
 /*
  * Contains some bit manipulation helpers.
+ *
+ * DO NOT USE: std::bitset<32> or std::bitset<64> preferred
  */
 
 namespace android {
diff --git a/libutils/include/utils/BlobCache.h b/libutils/include/utils/BlobCache.h
deleted file mode 100644
index 65dca9f..0000000
--- a/libutils/include/utils/BlobCache.h
+++ /dev/null
@@ -1,249 +0,0 @@
-/*
- ** Copyright 2011, 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 ANDROID_BLOB_CACHE_H
-#define ANDROID_BLOB_CACHE_H
-
-#include <stddef.h>
-
-#include <utils/Flattenable.h>
-#include <utils/RefBase.h>
-#include <utils/SortedVector.h>
-#include <utils/threads.h>
-
-namespace android {
-
-// A BlobCache is an in-memory cache for binary key/value pairs.  A BlobCache
-// does NOT provide any thread-safety guarantees.
-//
-// The cache contents can be serialized to an in-memory buffer or mmap'd file
-// and then reloaded in a subsequent execution of the program.  This
-// serialization is non-portable and the data should only be used by the device
-// that generated it.
-class BlobCache : public RefBase {
-
-public:
-
-    // Create an empty blob cache. The blob cache will cache key/value pairs
-    // with key and value sizes less than or equal to maxKeySize and
-    // maxValueSize, respectively. The total combined size of ALL cache entries
-    // (key sizes plus value sizes) will not exceed maxTotalSize.
-    BlobCache(size_t maxKeySize, size_t maxValueSize, size_t maxTotalSize);
-
-    // set inserts a new binary value into the cache and associates it with the
-    // given binary key.  If the key or value are too large for the cache then
-    // the cache remains unchanged.  This includes the case where a different
-    // value was previously associated with the given key - the old value will
-    // remain in the cache.  If the given key and value are small enough to be
-    // put in the cache (based on the maxKeySize, maxValueSize, and maxTotalSize
-    // values specified to the BlobCache constructor), then the key/value pair
-    // will be in the cache after set returns.  Note, however, that a subsequent
-    // call to set may evict old key/value pairs from the cache.
-    //
-    // Preconditions:
-    //   key != NULL
-    //   0 < keySize
-    //   value != NULL
-    //   0 < valueSize
-    void set(const void* key, size_t keySize, const void* value,
-            size_t valueSize);
-
-    // get retrieves from the cache the binary value associated with a given
-    // binary key.  If the key is present in the cache then the length of the
-    // binary value associated with that key is returned.  If the value argument
-    // is non-NULL and the size of the cached value is less than valueSize bytes
-    // then the cached value is copied into the buffer pointed to by the value
-    // argument.  If the key is not present in the cache then 0 is returned and
-    // the buffer pointed to by the value argument is not modified.
-    //
-    // Note that when calling get multiple times with the same key, the later
-    // calls may fail, returning 0, even if earlier calls succeeded.  The return
-    // value must be checked for each call.
-    //
-    // Preconditions:
-    //   key != NULL
-    //   0 < keySize
-    //   0 <= valueSize
-    size_t get(const void* key, size_t keySize, void* value, size_t valueSize);
-
-
-    // getFlattenedSize returns the number of bytes needed to store the entire
-    // serialized cache.
-    size_t getFlattenedSize() const;
-
-    // flatten serializes the current contents of the cache into the memory
-    // pointed to by 'buffer'.  The serialized cache contents can later be
-    // loaded into a BlobCache object using the unflatten method.  The contents
-    // of the BlobCache object will not be modified.
-    //
-    // Preconditions:
-    //   size >= this.getFlattenedSize()
-    status_t flatten(void* buffer, size_t size) const;
-
-    // unflatten replaces the contents of the cache with the serialized cache
-    // contents in the memory pointed to by 'buffer'.  The previous contents of
-    // the BlobCache will be evicted from the cache.  If an error occurs while
-    // unflattening the serialized cache contents then the BlobCache will be
-    // left in an empty state.
-    //
-    status_t unflatten(void const* buffer, size_t size);
-
-private:
-    // Copying is disallowed.
-    BlobCache(const BlobCache&);
-    void operator=(const BlobCache&);
-
-    // A random function helper to get around MinGW not having nrand48()
-    long int blob_random();
-
-    // clean evicts a randomly chosen set of entries from the cache such that
-    // the total size of all remaining entries is less than mMaxTotalSize/2.
-    void clean();
-
-    // isCleanable returns true if the cache is full enough for the clean method
-    // to have some effect, and false otherwise.
-    bool isCleanable() const;
-
-    // A Blob is an immutable sized unstructured data blob.
-    class Blob : public RefBase {
-    public:
-        Blob(const void* data, size_t size, bool copyData);
-        ~Blob();
-
-        bool operator<(const Blob& rhs) const;
-
-        const void* getData() const;
-        size_t getSize() const;
-
-    private:
-        // Copying is not allowed.
-        Blob(const Blob&);
-        void operator=(const Blob&);
-
-        // mData points to the buffer containing the blob data.
-        const void* mData;
-
-        // mSize is the size of the blob data in bytes.
-        size_t mSize;
-
-        // mOwnsData indicates whether or not this Blob object should free the
-        // memory pointed to by mData when the Blob gets destructed.
-        bool mOwnsData;
-    };
-
-    // A CacheEntry is a single key/value pair in the cache.
-    class CacheEntry {
-    public:
-        CacheEntry();
-        CacheEntry(const sp<Blob>& key, const sp<Blob>& value);
-        CacheEntry(const CacheEntry& ce);
-
-        bool operator<(const CacheEntry& rhs) const;
-        const CacheEntry& operator=(const CacheEntry&);
-
-        sp<Blob> getKey() const;
-        sp<Blob> getValue() const;
-
-        void setValue(const sp<Blob>& value);
-
-    private:
-
-        // mKey is the key that identifies the cache entry.
-        sp<Blob> mKey;
-
-        // mValue is the cached data associated with the key.
-        sp<Blob> mValue;
-    };
-
-    // A Header is the header for the entire BlobCache serialization format. No
-    // need to make this portable, so we simply write the struct out.
-    struct Header {
-        // mMagicNumber is the magic number that identifies the data as
-        // serialized BlobCache contents.  It must always contain 'Blb$'.
-        uint32_t mMagicNumber;
-
-        // mBlobCacheVersion is the serialization format version.
-        uint32_t mBlobCacheVersion;
-
-        // mDeviceVersion is the device-specific version of the cache.  This can
-        // be used to invalidate the cache.
-        uint32_t mDeviceVersion;
-
-        // mNumEntries is number of cache entries following the header in the
-        // data.
-        size_t mNumEntries;
-
-        // mBuildId is the build id of the device when the cache was created.
-        // When an update to the build happens (via an OTA or other update) this
-        // is used to invalidate the cache.
-        int mBuildIdLength;
-        char mBuildId[];
-    };
-
-    // An EntryHeader is the header for a serialized cache entry.  No need to
-    // make this portable, so we simply write the struct out.  Each EntryHeader
-    // is followed imediately by the key data and then the value data.
-    //
-    // The beginning of each serialized EntryHeader is 4-byte aligned, so the
-    // number of bytes that a serialized cache entry will occupy is:
-    //
-    //   ((sizeof(EntryHeader) + keySize + valueSize) + 3) & ~3
-    //
-    struct EntryHeader {
-        // mKeySize is the size of the entry key in bytes.
-        size_t mKeySize;
-
-        // mValueSize is the size of the entry value in bytes.
-        size_t mValueSize;
-
-        // mData contains both the key and value data for the cache entry.  The
-        // key comes first followed immediately by the value.
-        uint8_t mData[];
-    };
-
-    // mMaxKeySize is the maximum key size that will be cached. Calls to
-    // BlobCache::set with a keySize parameter larger than mMaxKeySize will
-    // simply not add the key/value pair to the cache.
-    const size_t mMaxKeySize;
-
-    // mMaxValueSize is the maximum value size that will be cached. Calls to
-    // BlobCache::set with a valueSize parameter larger than mMaxValueSize will
-    // simply not add the key/value pair to the cache.
-    const size_t mMaxValueSize;
-
-    // mMaxTotalSize is the maximum size that all cache entries can occupy. This
-    // includes space for both keys and values. When a call to BlobCache::set
-    // would otherwise cause this limit to be exceeded, either the key/value
-    // pair passed to BlobCache::set will not be cached or other cache entries
-    // will be evicted from the cache to make room for the new entry.
-    const size_t mMaxTotalSize;
-
-    // mTotalSize is the total combined size of all keys and values currently in
-    // the cache.
-    size_t mTotalSize;
-
-    // mRandState is the pseudo-random number generator state. It is passed to
-    // nrand48 to generate random numbers when needed.
-    unsigned short mRandState[3];
-
-    // mCacheEntries stores all the cache entries that are resident in memory.
-    // Cache entries are added to it by the 'set' method.
-    SortedVector<CacheEntry> mCacheEntries;
-};
-
-}
-
-#endif // ANDROID_BLOB_CACHE_H
diff --git a/libutils/include/utils/CallStack.h b/libutils/include/utils/CallStack.h
index 27e89f4..0c1b875 100644
--- a/libutils/include/utils/CallStack.h
+++ b/libutils/include/utils/CallStack.h
@@ -49,13 +49,13 @@
     // Dump a stack trace to the log using the supplied logtag.
     void log(const char* logtag,
              android_LogPriority priority = ANDROID_LOG_DEBUG,
-             const char* prefix = 0) const;
+             const char* prefix = nullptr) const;
 
     // Dump a stack trace to the specified file descriptor.
-    void dump(int fd, int indent = 0, const char* prefix = 0) const;
+    void dump(int fd, int indent = 0, const char* prefix = nullptr) const;
 
     // Return a string (possibly very long) containing the complete stack trace.
-    String8 toString(const char* prefix = 0) const;
+    String8 toString(const char* prefix = nullptr) const;
 
     // Dump a serialized representation of the stack trace to the specified printer.
     void print(Printer& printer) const;
@@ -67,6 +67,6 @@
     Vector<String8> mFrameLines;
 };
 
-}; // namespace android
+}  // namespace android
 
 #endif // ANDROID_CALLSTACK_H
diff --git a/libutils/include/utils/Condition.h b/libutils/include/utils/Condition.h
index 3019a21..540ed82 100644
--- a/libutils/include/utils/Condition.h
+++ b/libutils/include/utils/Condition.h
@@ -34,6 +34,8 @@
 namespace android {
 // ---------------------------------------------------------------------------
 
+// DO NOT USE: please use std::condition_variable instead.
+
 /*
  * Condition variable class.  The implementation is system-dependent.
  *
@@ -122,7 +124,7 @@
 #else // __APPLE__
     // Apple doesn't support POSIX clocks.
     struct timeval t;
-    gettimeofday(&t, NULL);
+    gettimeofday(&t, nullptr);
     ts.tv_sec = t.tv_sec;
     ts.tv_nsec = t.tv_usec*1000;
 #endif
@@ -157,7 +159,7 @@
 #endif // !defined(_WIN32)
 
 // ---------------------------------------------------------------------------
-}; // namespace android
+}  // namespace android
 // ---------------------------------------------------------------------------
 
 #endif // _LIBS_UTILS_CONDITON_H
diff --git a/libutils/include/utils/Debug.h b/libutils/include/utils/Debug.h
index 08893bd..96bd70e 100644
--- a/libutils/include/utils/Debug.h
+++ b/libutils/include/utils/Debug.h
@@ -29,20 +29,12 @@
 #define COMPILE_TIME_ASSERT(_exp) \
     template class CompileTimeAssert< (_exp) >;
 #endif
+
+// DO NOT USE: Please use static_assert instead
 #define COMPILE_TIME_ASSERT_FUNCTION_SCOPE(_exp) \
     CompileTimeAssert<( _exp )>();
 
 // ---------------------------------------------------------------------------
-
-#ifdef __cplusplus
-template<bool C, typename LSH, typename RHS> struct CompileTimeIfElse;
-template<typename LHS, typename RHS> 
-struct CompileTimeIfElse<true,  LHS, RHS> { typedef LHS TYPE; };
-template<typename LHS, typename RHS> 
-struct CompileTimeIfElse<false, LHS, RHS> { typedef RHS TYPE; };
-#endif
-
-// ---------------------------------------------------------------------------
-}; // namespace android
+}  // namespace android
 
 #endif // ANDROID_UTILS_DEBUG_H
diff --git a/libutils/include/utils/Errors.h b/libutils/include/utils/Errors.h
index 16e1fa2..7093a20 100644
--- a/libutils/include/utils/Errors.h
+++ b/libutils/include/utils/Errors.h
@@ -81,8 +81,8 @@
 # define NO_ERROR 0L
 #endif
 
-}; // namespace android
-    
+}  // namespace android
+
 // ---------------------------------------------------------------------------
     
 #endif // ANDROID_ERRORS_H
diff --git a/libutils/include/utils/FileMap.h b/libutils/include/utils/FileMap.h
index 7d372e1..8d402a3 100644
--- a/libutils/include/utils/FileMap.h
+++ b/libutils/include/utils/FileMap.h
@@ -124,6 +124,6 @@
     static long mPageSize;
 };
 
-}; // namespace android
+}  // namespace android
 
 #endif // __LIBS_FILE_MAP_H
diff --git a/libutils/include/utils/Flattenable.h b/libutils/include/utils/Flattenable.h
index 22b811a..0a19019 100644
--- a/libutils/include/utils/Flattenable.h
+++ b/libutils/include/utils/Flattenable.h
@@ -33,13 +33,13 @@
 public:
     template<size_t N>
     static size_t align(size_t size) {
-        COMPILE_TIME_ASSERT_FUNCTION_SCOPE( !(N & (N-1)) );
+        static_assert(!(N & (N - 1)), "Can only align to a power of 2.");
         return (size + (N-1)) & ~(N-1);
     }
 
     template<size_t N>
     static size_t align(void const*& buffer) {
-        COMPILE_TIME_ASSERT_FUNCTION_SCOPE( !(N & (N-1)) );
+        static_assert(!(N & (N - 1)), "Can only align to a power of 2.");
         uintptr_t b = uintptr_t(buffer);
         buffer = reinterpret_cast<void*>((uintptr_t(buffer) + (N-1)) & ~(N-1));
         return size_t(uintptr_t(buffer) - b);
@@ -189,17 +189,15 @@
     }
     inline status_t flatten(void* buffer, size_t size) const {
         if (size < sizeof(T)) return NO_MEMORY;
-        *reinterpret_cast<T*>(buffer) = *static_cast<T const*>(this);
+        memcpy(buffer, static_cast<T const*>(this), sizeof(T));
         return NO_ERROR;
     }
     inline status_t unflatten(void const* buffer, size_t) {
-        *static_cast<T*>(this) = *reinterpret_cast<T const*>(buffer);
+        memcpy(static_cast<T*>(this), buffer, sizeof(T));
         return NO_ERROR;
     }
 };
 
-
-}; // namespace android
-
+}  // namespace android
 
 #endif /* ANDROID_UTILS_FLATTENABLE_H */
diff --git a/libutils/include/utils/Functor.h b/libutils/include/utils/Functor.h
index 09ea614..c0c8d57 100644
--- a/libutils/include/utils/Functor.h
+++ b/libutils/include/utils/Functor.h
@@ -21,6 +21,10 @@
 
 namespace  android {
 
+// DO NOT USE: please use
+// - C++ lambda
+// - class with well-defined and specific functionality and semantics
+
 class Functor {
 public:
     Functor() {}
@@ -28,6 +32,6 @@
     virtual status_t operator ()(int /*what*/, void* /*data*/) { return NO_ERROR; }
 };
 
-}; // namespace android
+}  // namespace android
 
 #endif // ANDROID_FUNCTOR_H
diff --git a/libutils/include/utils/KeyedVector.h b/libutils/include/utils/KeyedVector.h
index f93ad6e..7bda99b 100644
--- a/libutils/include/utils/KeyedVector.h
+++ b/libutils/include/utils/KeyedVector.h
@@ -30,6 +30,8 @@
 
 namespace android {
 
+// DO NOT USE: please use std::map
+
 template <typename KEY, typename VALUE>
 class KeyedVector
 {
@@ -209,7 +211,7 @@
     return i >= 0 ? KeyedVector<KEY,VALUE>::valueAt(i) : mDefault;
 }
 
-}; // namespace android
+}  // namespace android
 
 // ---------------------------------------------------------------------------
 
diff --git a/libutils/include/utils/LightRefBase.h b/libutils/include/utils/LightRefBase.h
new file mode 100644
index 0000000..e488e60
--- /dev/null
+++ b/libutils/include/utils/LightRefBase.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#pragma once
+
+/*
+ * See documentation in RefBase.h
+ */
+
+#include <atomic>
+
+#include <sys/types.h>
+
+namespace android {
+
+class ReferenceRenamer;
+
+template <class T>
+class LightRefBase
+{
+public:
+    inline LightRefBase() : mCount(0) { }
+    inline void incStrong(__attribute__((unused)) const void* id) const {
+        mCount.fetch_add(1, std::memory_order_relaxed);
+    }
+    inline void decStrong(__attribute__((unused)) const void* id) const {
+        if (mCount.fetch_sub(1, std::memory_order_release) == 1) {
+            std::atomic_thread_fence(std::memory_order_acquire);
+            delete static_cast<const T*>(this);
+        }
+    }
+    //! DEBUGGING ONLY: Get current strong ref count.
+    inline int32_t getStrongCount() const {
+        return mCount.load(std::memory_order_relaxed);
+    }
+
+    typedef LightRefBase<T> basetype;
+
+protected:
+    inline ~LightRefBase() { }
+
+private:
+    friend class ReferenceMover;
+    inline static void renameRefs(size_t /*n*/, const ReferenceRenamer& /*renamer*/) { }
+    inline static void renameRefId(T* /*ref*/, const void* /*old_id*/ , const void* /*new_id*/) { }
+
+private:
+    mutable std::atomic<int32_t> mCount;
+};
+
+
+// This is a wrapper around LightRefBase that simply enforces a virtual
+// destructor to eliminate the template requirement of LightRefBase
+class VirtualLightRefBase : public LightRefBase<VirtualLightRefBase> {
+public:
+    virtual ~VirtualLightRefBase() = default;
+};
+
+}  // namespace android
diff --git a/libutils/include/utils/LinearTransform.h b/libutils/include/utils/LinearTransform.h
deleted file mode 100644
index 04cb355..0000000
--- a/libutils/include/utils/LinearTransform.h
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Copyright (C) 2011 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 _LIBS_UTILS_LINEAR_TRANSFORM_H
-#define _LIBS_UTILS_LINEAR_TRANSFORM_H
-
-#include <stdint.h>
-
-namespace android {
-
-// LinearTransform defines a structure which hold the definition of a
-// transformation from single dimensional coordinate system A into coordinate
-// system B (and back again).  Values in A and in B are 64 bit, the linear
-// scale factor is expressed as a rational number using two 32 bit values.
-//
-// Specifically, let
-// f(a) = b
-// F(b) = f^-1(b) = a
-// then
-//
-// f(a) = (((a - a_zero) * a_to_b_numer) / a_to_b_denom) + b_zero;
-//
-// and
-//
-// F(b) = (((b - b_zero) * a_to_b_denom) / a_to_b_numer) + a_zero;
-//
-struct LinearTransform {
-  int64_t  a_zero;
-  int64_t  b_zero;
-  int32_t  a_to_b_numer;
-  uint32_t a_to_b_denom;
-
-  // Transform from A->B
-  // Returns true on success, or false in the case of a singularity or an
-  // overflow.
-  bool doForwardTransform(int64_t a_in, int64_t* b_out) const;
-
-  // Transform from B->A
-  // Returns true on success, or false in the case of a singularity or an
-  // overflow.
-  bool doReverseTransform(int64_t b_in, int64_t* a_out) const;
-
-  // Helpers which will reduce the fraction N/D using Euclid's method.
-  template <class T> static void reduce(T* N, T* D);
-  static void reduce(int32_t* N, uint32_t* D);
-};
-
-
-}
-
-#endif  // _LIBS_UTILS_LINEAR_TRANSFORM_H
diff --git a/libutils/include/utils/List.h b/libutils/include/utils/List.h
index 403cd7f..25b56fd 100644
--- a/libutils/include/utils/List.h
+++ b/libutils/include/utils/List.h
@@ -37,6 +37,8 @@
  *
  * Objects added to the list are copied using the assignment operator,
  * so this must be defined.
+ *
+ * DO NOT USE: please use std::list<T>
  */
 template<typename T> 
 class List 
@@ -327,6 +329,6 @@
     return *this;
 }
 
-}; // namespace android
+}  // namespace android
 
 #endif // _LIBS_UTILS_LIST_H
diff --git a/libutils/include/utils/Log.h b/libutils/include/utils/Log.h
index 5276a49..42e03e7 100644
--- a/libutils/include/utils/Log.h
+++ b/libutils/include/utils/Log.h
@@ -1,72 +1,7 @@
-/*
- * Copyright (C) 2005 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.
- */
+// DO NOT INCLUDE ANYTHING NEW IN THIS FILE.
 
-//
-// C/C++ logging functions.  See the logging documentation for API details.
-//
-// We'd like these to be available from C code (in case we import some from
-// somewhere), so this has a C interface.
-//
-// The output will be correct when the log file is shared between multiple
-// threads and/or multiple processes so long as the operating system
-// supports O_APPEND.  These calls have mutex-protected data structures
-// and so are NOT reentrant.  Do not use LOG in a signal handler.
-//
-#ifndef _LIBS_UTILS_LOG_H
-#define _LIBS_UTILS_LOG_H
-
-#include <sys/types.h>
+// <log/log.h> has replaced this file and all changes should go there instead.
+// This path remains strictly to include that header as there are thousands of
+// references to <utils/Log.h> in the tree.
 
 #include <log/log.h>
-
-#ifdef __cplusplus
-
-namespace android {
-
-/*
- * A very simple utility that yells in the log when an operation takes too long.
- */
-class LogIfSlow {
-public:
-    LogIfSlow(const char* tag, android_LogPriority priority,
-            int timeoutMillis, const char* message);
-    ~LogIfSlow();
-
-private:
-    const char* const mTag;
-    const android_LogPriority mPriority;
-    const int mTimeoutMillis;
-    const char* const mMessage;
-    const int64_t mStart;
-};
-
-/*
- * Writes the specified debug log message if this block takes longer than the
- * specified number of milliseconds to run.  Includes the time actually taken.
- *
- * {
- *     ALOGD_IF_SLOW(50, "Excessive delay doing something.");
- *     doSomething();
- * }
- */
-#define ALOGD_IF_SLOW(timeoutMillis, message) \
-    android::LogIfSlow _logIfSlow(LOG_TAG, ANDROID_LOG_DEBUG, timeoutMillis, message);
-
-} // namespace android
-
-#endif // __cplusplus
-
-#endif // _LIBS_UTILS_LOG_H
diff --git a/libutils/include/utils/Looper.h b/libutils/include/utils/Looper.h
index a62e67f..c439c5c 100644
--- a/libutils/include/utils/Looper.h
+++ b/libutils/include/utils/Looper.h
@@ -24,6 +24,8 @@
 
 #include <sys/epoll.h>
 
+#include <android-base/unique_fd.h>
+
 namespace android {
 
 /*
@@ -262,7 +264,7 @@
      */
     int pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outData);
     inline int pollOnce(int timeoutMillis) {
-        return pollOnce(timeoutMillis, NULL, NULL, NULL);
+        return pollOnce(timeoutMillis, nullptr, nullptr, nullptr);
     }
 
     /**
@@ -272,7 +274,7 @@
      */
     int pollAll(int timeoutMillis, int* outFd, int* outEvents, void** outData);
     inline int pollAll(int timeoutMillis) {
-        return pollAll(timeoutMillis, NULL, NULL, NULL);
+        return pollAll(timeoutMillis, nullptr, nullptr, nullptr);
     }
 
     /**
@@ -447,7 +449,7 @@
 
     const bool mAllowNonCallbacks; // immutable
 
-    int mWakeEventFd;  // immutable
+    android::base::unique_fd mWakeEventFd;  // immutable
     Mutex mLock;
 
     Vector<MessageEnvelope> mMessageEnvelopes; // guarded by mLock
@@ -457,7 +459,7 @@
     // any use of it is racy anyway.
     volatile bool mPolling;
 
-    int mEpollFd; // guarded by mLock but only modified on the looper thread
+    android::base::unique_fd mEpollFd;  // guarded by mLock but only modified on the looper thread
     bool mEpollRebuildRequired; // guarded by mLock
 
     // Locked list of file descriptor monitoring requests.
diff --git a/libutils/include/utils/LruCache.h b/libutils/include/utils/LruCache.h
index 89dccd6..36775d0 100644
--- a/libutils/include/utils/LruCache.h
+++ b/libutils/include/utils/LruCache.h
@@ -71,7 +71,7 @@
         Entry* parent;
         Entry* child;
 
-        Entry(TKey _key, TValue _value) : key(_key), value(_value), parent(NULL), child(NULL) {
+        Entry(TKey _key, TValue _value) : key(_key), value(_value), parent(nullptr), child(nullptr) {
         }
         const TKey& getKey() const final { return key; }
     };
@@ -162,9 +162,9 @@
 template <typename TKey, typename TValue>
 LruCache<TKey, TValue>::LruCache(uint32_t maxCapacity)
     : mSet(new LruCacheSet())
-    , mListener(NULL)
-    , mOldest(NULL)
-    , mYoungest(NULL)
+    , mListener(nullptr)
+    , mOldest(nullptr)
+    , mYoungest(nullptr)
     , mMaxCapacity(maxCapacity)
     , mNullValue(0) {
     mSet->max_load_factor(1.0);
@@ -236,7 +236,7 @@
 
 template <typename TKey, typename TValue>
 bool LruCache<TKey, TValue>::removeOldest() {
-    if (mOldest != NULL) {
+    if (mOldest != nullptr) {
         return remove(mOldest->key);
         // TODO: should probably abort if false
     }
@@ -254,12 +254,12 @@
 template <typename TKey, typename TValue>
 void LruCache<TKey, TValue>::clear() {
     if (mListener) {
-        for (Entry* p = mOldest; p != NULL; p = p->child) {
+        for (Entry* p = mOldest; p != nullptr; p = p->child) {
             (*mListener)(p->key, p->value);
         }
     }
-    mYoungest = NULL;
-    mOldest = NULL;
+    mYoungest = nullptr;
+    mOldest = nullptr;
     for (auto entry : *mSet.get()) {
         delete entry;
     }
@@ -268,7 +268,7 @@
 
 template <typename TKey, typename TValue>
 void LruCache<TKey, TValue>::attachToCache(Entry& entry) {
-    if (mYoungest == NULL) {
+    if (mYoungest == nullptr) {
         mYoungest = mOldest = &entry;
     } else {
         entry.parent = mYoungest;
@@ -279,19 +279,19 @@
 
 template <typename TKey, typename TValue>
 void LruCache<TKey, TValue>::detachFromCache(Entry& entry) {
-    if (entry.parent != NULL) {
+    if (entry.parent != nullptr) {
         entry.parent->child = entry.child;
     } else {
         mOldest = entry.child;
     }
-    if (entry.child != NULL) {
+    if (entry.child != nullptr) {
         entry.child->parent = entry.parent;
     } else {
         mYoungest = entry.parent;
     }
 
-    entry.parent = NULL;
-    entry.child = NULL;
+    entry.parent = nullptr;
+    entry.child = nullptr;
 }
 
 }
diff --git a/libutils/include/utils/Mutex.h b/libutils/include/utils/Mutex.h
index d106185..29c2e8c 100644
--- a/libutils/include/utils/Mutex.h
+++ b/libutils/include/utils/Mutex.h
@@ -28,6 +28,53 @@
 #include <utils/Errors.h>
 #include <utils/Timers.h>
 
+// Enable thread safety attributes only with clang.
+// The attributes can be safely erased when compiling with other compilers.
+#if defined(__clang__) && (!defined(SWIG))
+#define THREAD_ANNOTATION_ATTRIBUTE__(x) __attribute__((x))
+#else
+#define THREAD_ANNOTATION_ATTRIBUTE__(x)  // no-op
+#endif
+
+#define CAPABILITY(x) THREAD_ANNOTATION_ATTRIBUTE__(capability(x))
+
+#define SCOPED_CAPABILITY THREAD_ANNOTATION_ATTRIBUTE__(scoped_lockable)
+
+#define GUARDED_BY(x) THREAD_ANNOTATION_ATTRIBUTE__(guarded_by(x))
+
+#define PT_GUARDED_BY(x) THREAD_ANNOTATION_ATTRIBUTE__(pt_guarded_by(x))
+
+#define ACQUIRED_BEFORE(...) THREAD_ANNOTATION_ATTRIBUTE__(acquired_before(__VA_ARGS__))
+
+#define ACQUIRED_AFTER(...) THREAD_ANNOTATION_ATTRIBUTE__(acquired_after(__VA_ARGS__))
+
+#define REQUIRES(...) THREAD_ANNOTATION_ATTRIBUTE__(requires_capability(__VA_ARGS__))
+
+#define REQUIRES_SHARED(...) THREAD_ANNOTATION_ATTRIBUTE__(requires_shared_capability(__VA_ARGS__))
+
+#define ACQUIRE(...) THREAD_ANNOTATION_ATTRIBUTE__(acquire_capability(__VA_ARGS__))
+
+#define ACQUIRE_SHARED(...) THREAD_ANNOTATION_ATTRIBUTE__(acquire_shared_capability(__VA_ARGS__))
+
+#define RELEASE(...) THREAD_ANNOTATION_ATTRIBUTE__(release_capability(__VA_ARGS__))
+
+#define RELEASE_SHARED(...) THREAD_ANNOTATION_ATTRIBUTE__(release_shared_capability(__VA_ARGS__))
+
+#define TRY_ACQUIRE(...) THREAD_ANNOTATION_ATTRIBUTE__(try_acquire_capability(__VA_ARGS__))
+
+#define TRY_ACQUIRE_SHARED(...) \
+    THREAD_ANNOTATION_ATTRIBUTE__(try_acquire_shared_capability(__VA_ARGS__))
+
+#define EXCLUDES(...) THREAD_ANNOTATION_ATTRIBUTE__(locks_excluded(__VA_ARGS__))
+
+#define ASSERT_CAPABILITY(x) THREAD_ANNOTATION_ATTRIBUTE__(assert_capability(x))
+
+#define ASSERT_SHARED_CAPABILITY(x) THREAD_ANNOTATION_ATTRIBUTE__(assert_shared_capability(x))
+
+#define RETURN_CAPABILITY(x) THREAD_ANNOTATION_ATTRIBUTE__(lock_returned(x))
+
+#define NO_THREAD_SAFETY_ANALYSIS THREAD_ANNOTATION_ATTRIBUTE__(no_thread_safety_analysis)
+
 // ---------------------------------------------------------------------------
 namespace android {
 // ---------------------------------------------------------------------------
@@ -44,24 +91,24 @@
  * The mutex must be unlocked by the thread that locked it.  They are not
  * recursive, i.e. the same thread can't lock it multiple times.
  */
-class Mutex {
-public:
+class CAPABILITY("mutex") Mutex {
+  public:
     enum {
         PRIVATE = 0,
         SHARED = 1
     };
 
-                Mutex();
-    explicit    Mutex(const char* name);
-    explicit    Mutex(int type, const char* name = NULL);
-                ~Mutex();
+    Mutex();
+    explicit Mutex(const char* name);
+    explicit Mutex(int type, const char* name = nullptr);
+    ~Mutex();
 
     // lock or unlock the mutex
-    status_t    lock();
-    void        unlock();
+    status_t lock() ACQUIRE();
+    void unlock() RELEASE();
 
     // lock if possible; returns 0 on success, error otherwise
-    status_t    tryLock();
+    status_t tryLock() TRY_ACQUIRE(true);
 
 #if defined(__ANDROID__)
     // Lock the mutex, but don't wait longer than timeoutNs (relative time).
@@ -75,32 +122,36 @@
     // which is subject to NTP adjustments, and includes time during suspend,
     // so a timeout may occur even though no processes could run.
     // Not holding a partial wakelock may lead to a system suspend.
-    status_t    timedLock(nsecs_t timeoutNs);
+    status_t timedLock(nsecs_t timeoutNs) TRY_ACQUIRE(true);
 #endif
 
     // Manages the mutex automatically. It'll be locked when Autolock is
     // constructed and released when Autolock goes out of scope.
-    class Autolock {
-    public:
-        inline explicit Autolock(Mutex& mutex) : mLock(mutex)  { mLock.lock(); }
-        inline explicit Autolock(Mutex* mutex) : mLock(*mutex) { mLock.lock(); }
-        inline ~Autolock() { mLock.unlock(); }
-    private:
+    class SCOPED_CAPABILITY Autolock {
+      public:
+        inline explicit Autolock(Mutex& mutex) ACQUIRE(mutex) : mLock(mutex) { mLock.lock(); }
+        inline explicit Autolock(Mutex* mutex) ACQUIRE(mutex) : mLock(*mutex) { mLock.lock(); }
+        inline ~Autolock() RELEASE() { mLock.unlock(); }
+
+      private:
         Mutex& mLock;
+        // Cannot be copied or moved - declarations only
+        Autolock(const Autolock&);
+        Autolock& operator=(const Autolock&);
     };
 
-private:
+  private:
     friend class Condition;
 
     // A mutex cannot be copied
-                Mutex(const Mutex&);
-    Mutex&      operator = (const Mutex&);
+    Mutex(const Mutex&);
+    Mutex& operator=(const Mutex&);
 
 #if !defined(_WIN32)
     pthread_mutex_t mMutex;
 #else
-    void    _init();
-    void*   mState;
+    void _init();
+    void* mState;
 #endif
 };
 
@@ -109,10 +160,10 @@
 #if !defined(_WIN32)
 
 inline Mutex::Mutex() {
-    pthread_mutex_init(&mMutex, NULL);
+    pthread_mutex_init(&mMutex, nullptr);
 }
 inline Mutex::Mutex(__attribute__((unused)) const char* name) {
-    pthread_mutex_init(&mMutex, NULL);
+    pthread_mutex_init(&mMutex, nullptr);
 }
 inline Mutex::Mutex(int type, __attribute__((unused)) const char* name) {
     if (type == SHARED) {
@@ -122,7 +173,7 @@
         pthread_mutex_init(&mMutex, &attr);
         pthread_mutexattr_destroy(&attr);
     } else {
-        pthread_mutex_init(&mMutex, NULL);
+        pthread_mutex_init(&mMutex, nullptr);
     }
 }
 inline Mutex::~Mutex() {
@@ -161,7 +212,7 @@
 typedef Mutex::Autolock AutoMutex;
 
 // ---------------------------------------------------------------------------
-}; // namespace android
+}  // namespace android
 // ---------------------------------------------------------------------------
 
 #endif // _LIBS_UTILS_MUTEX_H
diff --git a/libutils/include/utils/NativeHandle.h b/libutils/include/utils/NativeHandle.h
index b825168..73fe804 100644
--- a/libutils/include/utils/NativeHandle.h
+++ b/libutils/include/utils/NativeHandle.h
@@ -24,7 +24,7 @@
 
 namespace android {
 
-class NativeHandle: public LightRefBase<NativeHandle> {
+class NativeHandle : public LightRefBase<NativeHandle> {
 public:
     // Create a refcounted wrapper around a native_handle_t, and declare
     // whether the wrapper owns the handle (so that it should clean up the
@@ -41,7 +41,7 @@
     friend class LightRefBase<NativeHandle>;
 
     NativeHandle(native_handle_t* handle, bool ownsHandle);
-    virtual ~NativeHandle();
+    ~NativeHandle();
 
     native_handle_t* mHandle;
     bool mOwnsHandle;
diff --git a/libutils/include/utils/Printer.h b/libutils/include/utils/Printer.h
index bb66287..7465927 100644
--- a/libutils/include/utils/Printer.h
+++ b/libutils/include/utils/Printer.h
@@ -45,7 +45,7 @@
     // (Note that the default ALOG behavior is to ignore blank lines)
     LogPrinter(const char* logtag,
                android_LogPriority priority = ANDROID_LOG_DEBUG,
-               const char* prefix = 0,
+               const char* prefix = nullptr,
                bool ignoreBlankLines = false);
 
     // Print the specified line to logcat. No \n at the end is necessary.
@@ -66,7 +66,7 @@
     // Create a printer using the specified file descriptor.
     // - Each line will be prefixed with 'indent' number of blank spaces.
     // - In addition, each line will be prefixed with the 'prefix' string.
-    FdPrinter(int fd, unsigned int indent = 0, const char* prefix = 0);
+    FdPrinter(int fd, unsigned int indent = 0, const char* prefix = nullptr);
 
     // Print the specified line to the file descriptor. \n is appended automatically.
     virtual void printLine(const char* string);
@@ -90,7 +90,7 @@
     // Create a printer using the specified String8 as the target.
     // - In addition, each line will be prefixed with the 'prefix' string.
     // - target's memory lifetime must be a superset of this String8Printer.
-    String8Printer(String8* target, const char* prefix = 0);
+    String8Printer(String8* target, const char* prefix = nullptr);
 
     // Append the specified line to the String8. \n is appended automatically.
     virtual void printLine(const char* string);
@@ -114,6 +114,6 @@
     const char* mPrefix;
 };
 
-}; // namespace android
+}  // namespace android
 
 #endif // ANDROID_PRINTER_H
diff --git a/libutils/include/utils/ProcessCallStack.h b/libutils/include/utils/ProcessCallStack.h
index 32458b8..7e06086 100644
--- a/libutils/include/utils/ProcessCallStack.h
+++ b/libutils/include/utils/ProcessCallStack.h
@@ -43,13 +43,13 @@
 
     // Print all stack traces to the log using the supplied logtag.
     void log(const char* logtag, android_LogPriority priority = ANDROID_LOG_DEBUG,
-             const char* prefix = 0) const;
+             const char* prefix = nullptr) const;
 
     // Dump all stack traces to the specified file descriptor.
-    void dump(int fd, int indent = 0, const char* prefix = 0) const;
+    void dump(int fd, int indent = 0, const char* prefix = nullptr) const;
 
     // Return a string (possibly very long) containing all the stack traces.
-    String8 toString(const char* prefix = 0) const;
+    String8 toString(const char* prefix = nullptr) const;
 
     // Dump a serialized representation of all the stack traces to the specified printer.
     void print(Printer& printer) const;
@@ -74,6 +74,6 @@
     struct tm mTimeUpdated;
 };
 
-}; // namespace android
+}  // namespace android
 
 #endif // ANDROID_PROCESS_CALLSTACK_H
diff --git a/libutils/include/utils/RWLock.h b/libutils/include/utils/RWLock.h
index d5b81d3..64e370e 100644
--- a/libutils/include/utils/RWLock.h
+++ b/libutils/include/utils/RWLock.h
@@ -48,7 +48,7 @@
 
                 RWLock();
     explicit    RWLock(const char* name);
-    explicit    RWLock(int type, const char* name = NULL);
+    explicit    RWLock(int type, const char* name = nullptr);
                 ~RWLock();
 
     status_t    readLock();
@@ -82,10 +82,10 @@
 };
 
 inline RWLock::RWLock() {
-    pthread_rwlock_init(&mRWLock, NULL);
+    pthread_rwlock_init(&mRWLock, nullptr);
 }
 inline RWLock::RWLock(__attribute__((unused)) const char* name) {
-    pthread_rwlock_init(&mRWLock, NULL);
+    pthread_rwlock_init(&mRWLock, nullptr);
 }
 inline RWLock::RWLock(int type, __attribute__((unused)) const char* name) {
     if (type == SHARED) {
@@ -95,7 +95,7 @@
         pthread_rwlock_init(&mRWLock, &attr);
         pthread_rwlockattr_destroy(&attr);
     } else {
-        pthread_rwlock_init(&mRWLock, NULL);
+        pthread_rwlock_init(&mRWLock, nullptr);
     }
 }
 inline RWLock::~RWLock() {
@@ -120,7 +120,7 @@
 #endif // !defined(_WIN32)
 
 // ---------------------------------------------------------------------------
-}; // namespace android
+}  // namespace android
 // ---------------------------------------------------------------------------
 
 #endif // _LIBS_UTILS_RWLOCK_H
diff --git a/libutils/include/utils/RefBase.h b/libutils/include/utils/RefBase.h
index 36016cd..1780cf2 100644
--- a/libutils/include/utils/RefBase.h
+++ b/libutils/include/utils/RefBase.h
@@ -177,6 +177,9 @@
 #include <stdlib.h>
 #include <string.h>
 
+// LightRefBase used to be declared in this header, so we have to include it
+#include <utils/LightRefBase.h>
+
 #include <utils/StrongPointer.h>
 #include <utils/TypeHelpers.h>
 
@@ -216,7 +219,7 @@
 
 class ReferenceRenamer {
 protected:
-    // destructor is purposedly not virtual so we avoid code overhead from
+    // destructor is purposely not virtual so we avoid code overhead from
     // subclasses; we have to make it protected to guarantee that it
     // cannot be called from this base class (and to make strict compilers
     // happy).
@@ -246,13 +249,13 @@
     {
     public:
         RefBase*            refBase() const;
-        
+
         void                incWeak(const void* id);
         void                decWeak(const void* id);
-        
+
         // acquires a strong reference if there is already one.
         bool                attemptIncStrong(const void* id);
-        
+
         // acquires a weak reference if there is already one.
         // This is not always safe. see ProcessState.cpp and BpBinder.cpp
         // for proper use.
@@ -268,12 +271,12 @@
         // enable -- enable/disable tracking
         // retain -- when tracking is enable, if true, then we save a stack trace
         //           for each reference and dereference; when retain == false, we
-        //           match up references and dereferences and keep only the 
+        //           match up references and dereferences and keep only the
         //           outstanding ones.
-        
+
         void                trackMe(bool enable, bool retain);
     };
-    
+
             weakref_type*   createWeak(const void* id) const;
             
             weakref_type*   getWeakRefs() const;
@@ -345,57 +348,13 @@
 
 // ---------------------------------------------------------------------------
 
-template <class T>
-class LightRefBase
-{
-public:
-    inline LightRefBase() : mCount(0) { }
-    inline void incStrong(__attribute__((unused)) const void* id) const {
-        mCount.fetch_add(1, std::memory_order_relaxed);
-    }
-    inline void decStrong(__attribute__((unused)) const void* id) const {
-        if (mCount.fetch_sub(1, std::memory_order_release) == 1) {
-            std::atomic_thread_fence(std::memory_order_acquire);
-            delete static_cast<const T*>(this);
-        }
-    }
-    //! DEBUGGING ONLY: Get current strong ref count.
-    inline int32_t getStrongCount() const {
-        return mCount.load(std::memory_order_relaxed);
-    }
-
-    typedef LightRefBase<T> basetype;
-
-protected:
-    inline ~LightRefBase() { }
-
-private:
-    friend class ReferenceMover;
-    inline static void renameRefs(size_t /*n*/,
-            const ReferenceRenamer& /*renamer*/) { }
-    inline static void renameRefId(T* /*ref*/,
-            const void* /*old_id*/ , const void* /*new_id*/) { }
-
-private:
-    mutable std::atomic<int32_t> mCount;
-};
-
-// This is a wrapper around LightRefBase that simply enforces a virtual
-// destructor to eliminate the template requirement of LightRefBase
-class VirtualLightRefBase : public LightRefBase<VirtualLightRefBase> {
-public:
-    virtual ~VirtualLightRefBase();
-};
-
-// ---------------------------------------------------------------------------
-
 template <typename T>
 class wp
 {
 public:
     typedef typename RefBase::weakref_type weakref_type;
-    
-    inline wp() : m_ptr(0) { }
+
+    inline wp() : m_ptr(nullptr) { }
 
     wp(T* other);  // NOLINT(implicit)
     wp(const wp<T>& other);
@@ -405,31 +364,31 @@
     template<typename U> wp(const wp<U>& other);  // NOLINT(implicit)
 
     ~wp();
-    
+
     // Assignment
 
     wp& operator = (T* other);
     wp& operator = (const wp<T>& other);
     wp& operator = (const sp<T>& other);
-    
+
     template<typename U> wp& operator = (U* other);
     template<typename U> wp& operator = (const wp<U>& other);
     template<typename U> wp& operator = (const sp<U>& other);
-    
+
     void set_object_and_refs(T* other, weakref_type* refs);
 
     // promotion to sp
-    
+
     sp<T> promote() const;
 
     // Reset
-    
+
     void clear();
 
     // Accessors
-    
+
     inline  weakref_type* get_refs() const { return m_refs; }
-    
+
     inline  T* unsafe_get() const { return m_ptr; }
 
     // Operators
@@ -546,7 +505,7 @@
 wp<T>& wp<T>::operator = (T* other)
 {
     weakref_type* newRefs =
-        other ? other->createWeak(this) : 0;
+        other ? other->createWeak(this) : nullptr;
     if (m_ptr) m_refs->decWeak(this);
     m_ptr = other;
     m_refs = newRefs;
@@ -569,7 +528,7 @@
 wp<T>& wp<T>::operator = (const sp<T>& other)
 {
     weakref_type* newRefs =
-        other != NULL ? other->createWeak(this) : 0;
+        other != nullptr ? other->createWeak(this) : nullptr;
     T* otherPtr(other.m_ptr);
     if (m_ptr) m_refs->decWeak(this);
     m_ptr = otherPtr;
@@ -604,7 +563,7 @@
 wp<T>& wp<T>::operator = (const sp<U>& other)
 {
     weakref_type* newRefs =
-        other != NULL ? other->createWeak(this) : 0;
+        other != nullptr ? other->createWeak(this) : 0;
     U* otherPtr(other.m_ptr);
     if (m_ptr) m_refs->decWeak(this);
     m_ptr = otherPtr;
@@ -724,7 +683,7 @@
     ReferenceMover::move_references(d, s, n);
 }
 
-}; // namespace android
+}  // namespace android
 
 // ---------------------------------------------------------------------------
 
diff --git a/libutils/include/utils/Singleton.h b/libutils/include/utils/Singleton.h
index a989a47..44d8ad7 100644
--- a/libutils/include/utils/Singleton.h
+++ b/libutils/include/utils/Singleton.h
@@ -18,9 +18,12 @@
 #define ANDROID_UTILS_SINGLETON_H
 
 #include <stdint.h>
+
+// some vendor code assumes they have atoi() after including this file.
+#include <stdlib.h>
+
 #include <sys/types.h>
 #include <utils/Mutex.h>
-#include <utils/threads.h>
 #include <cutils/compiler.h>
 
 namespace android {
@@ -36,6 +39,11 @@
 #pragma clang diagnostic ignored "-Wundefined-var-template"
 #endif
 
+// DO NOT USE: Please use scoped static initialization. For instance:
+//     MyClass& getInstance() {
+//         static MyClass gInstance(...);
+//         return gInstance;
+//     }
 template <typename TYPE>
 class ANDROID_API Singleton
 {
@@ -43,7 +51,7 @@
     static TYPE& getInstance() {
         Mutex::Autolock _l(sLock);
         TYPE* instance = sInstance;
-        if (instance == 0) {
+        if (instance == nullptr) {
             instance = new TYPE();
             sInstance = instance;
         }
@@ -52,7 +60,7 @@
 
     static bool hasInstance() {
         Mutex::Autolock _l(sLock);
-        return sInstance != 0;
+        return sInstance != nullptr;
     }
     
 protected:
@@ -82,12 +90,12 @@
 #define ANDROID_SINGLETON_STATIC_INSTANCE(TYPE)                 \
     template<> ::android::Mutex  \
         (::android::Singleton< TYPE >::sLock)(::android::Mutex::PRIVATE);  \
-    template<> TYPE* ::android::Singleton< TYPE >::sInstance(0);  \
+    template<> TYPE* ::android::Singleton< TYPE >::sInstance(nullptr);  /* NOLINT */ \
     template class ::android::Singleton< TYPE >;
 
 
 // ---------------------------------------------------------------------------
-}; // namespace android
+}  // namespace android
 
 #endif // ANDROID_UTILS_SINGLETON_H
 
diff --git a/libutils/include/utils/SortedVector.h b/libutils/include/utils/SortedVector.h
index 86f3496..394db12 100644
--- a/libutils/include/utils/SortedVector.h
+++ b/libutils/include/utils/SortedVector.h
@@ -30,6 +30,8 @@
 
 namespace android {
 
+// DO NOT USE: please use std::set
+
 template <class TYPE>
 class SortedVector : private SortedVectorImpl
 {
@@ -37,18 +39,18 @@
 
 public:
             typedef TYPE    value_type;
-    
-    /*! 
+
+    /*!
      * Constructors and destructors
      */
-    
+
                             SortedVector();
                             SortedVector(const SortedVector<TYPE>& rhs);
     virtual                 ~SortedVector();
 
     /*! copy operator */
-    const SortedVector<TYPE>&   operator = (const SortedVector<TYPE>& rhs) const;    
-    SortedVector<TYPE>&         operator = (const SortedVector<TYPE>& rhs);    
+    const SortedVector<TYPE>&   operator = (const SortedVector<TYPE>& rhs) const;
+    SortedVector<TYPE>&         operator = (const SortedVector<TYPE>& rhs);
 
     /*
      * empty the vector
@@ -56,7 +58,7 @@
 
     inline  void            clear()             { VectorImpl::clear(); }
 
-    /*! 
+    /*!
      * vector stats
      */
 
@@ -69,11 +71,11 @@
     //! sets the capacity. capacity can never be reduced less than size()
     inline  ssize_t         setCapacity(size_t size)    { return VectorImpl::setCapacity(size); }
 
-    /*! 
+    /*!
      * C-style array access
      */
-     
-    //! read-only C-style access 
+
+    //! read-only C-style access
     inline  const TYPE*     array() const;
 
     //! read-write C-style access. BE VERY CAREFUL when modifying the array
@@ -82,12 +84,12 @@
 
             //! finds the index of an item
             ssize_t         indexOf(const TYPE& item) const;
-            
+
             //! finds where this item should be inserted
             size_t          orderOf(const TYPE& item) const;
-            
-    
-    /*! 
+
+
+    /*!
      * accessors
      */
 
@@ -104,7 +106,7 @@
 
             //! add an item in the right place (and replace the one that is there)
             ssize_t         add(const TYPE& item);
-            
+
             //! editItemAt() MUST NOT change the order of this item
             TYPE&           editItemAt(size_t index) {
                 return *( static_cast<TYPE *>(VectorImpl::editItemLocation(index)) );
@@ -113,7 +115,7 @@
             //! merges a vector into this one
             ssize_t         merge(const Vector<TYPE>& vector);
             ssize_t         merge(const SortedVector<TYPE>& vector);
-            
+
             //! removes an item
             ssize_t         remove(const TYPE&);
 
@@ -121,7 +123,24 @@
     inline  ssize_t         removeItemsAt(size_t index, size_t count = 1);
     //! remove one item
     inline  ssize_t         removeAt(size_t index)  { return removeItemsAt(index); }
-            
+
+    /*
+     * these inlines add some level of compatibility with STL.
+     */
+    typedef TYPE* iterator;
+    typedef TYPE const* const_iterator;
+
+    inline iterator begin() { return editArray(); }
+    inline iterator end()   { return editArray() + size(); }
+    inline const_iterator begin() const { return array(); }
+    inline const_iterator end() const   { return array() + size(); }
+    inline void reserve(size_t n) { setCapacity(n); }
+    inline bool empty() const{ return isEmpty(); }
+    inline iterator erase(iterator pos) {
+        ssize_t index = removeItemsAt(pos-array());
+        return begin() + index;
+    }
+
 protected:
     virtual void    do_construct(void* storage, size_t num) const;
     virtual void    do_destroy(void* storage, size_t num) const;
@@ -159,13 +178,13 @@
 template<class TYPE> inline
 SortedVector<TYPE>& SortedVector<TYPE>::operator = (const SortedVector<TYPE>& rhs) {
     SortedVectorImpl::operator = (rhs);
-    return *this; 
+    return *this;
 }
 
 template<class TYPE> inline
 const SortedVector<TYPE>& SortedVector<TYPE>::operator = (const SortedVector<TYPE>& rhs) const {
     SortedVectorImpl::operator = (rhs);
-    return *this; 
+    return *this;
 }
 
 template<class TYPE> inline
@@ -235,7 +254,7 @@
 // ---------------------------------------------------------------------------
 
 template<class TYPE>
-void SortedVector<TYPE>::do_construct(void* storage, size_t num) const {
+UTILS_VECTOR_NO_CFI void SortedVector<TYPE>::do_construct(void* storage, size_t num) const {
     construct_type( reinterpret_cast<TYPE*>(storage), num );
 }
 
@@ -245,22 +264,22 @@
 }
 
 template<class TYPE>
-void SortedVector<TYPE>::do_copy(void* dest, const void* from, size_t num) const {
+UTILS_VECTOR_NO_CFI void SortedVector<TYPE>::do_copy(void* dest, const void* from, size_t num) const {
     copy_type( reinterpret_cast<TYPE*>(dest), reinterpret_cast<const TYPE*>(from), num );
 }
 
 template<class TYPE>
-void SortedVector<TYPE>::do_splat(void* dest, const void* item, size_t num) const {
+UTILS_VECTOR_NO_CFI void SortedVector<TYPE>::do_splat(void* dest, const void* item, size_t num) const {
     splat_type( reinterpret_cast<TYPE*>(dest), reinterpret_cast<const TYPE*>(item), num );
 }
 
 template<class TYPE>
-void SortedVector<TYPE>::do_move_forward(void* dest, const void* from, size_t num) const {
+UTILS_VECTOR_NO_CFI void SortedVector<TYPE>::do_move_forward(void* dest, const void* from, size_t num) const {
     move_forward_type( reinterpret_cast<TYPE*>(dest), reinterpret_cast<const TYPE*>(from), num );
 }
 
 template<class TYPE>
-void SortedVector<TYPE>::do_move_backward(void* dest, const void* from, size_t num) const {
+UTILS_VECTOR_NO_CFI void SortedVector<TYPE>::do_move_backward(void* dest, const void* from, size_t num) const {
     move_backward_type( reinterpret_cast<TYPE*>(dest), reinterpret_cast<const TYPE*>(from), num );
 }
 
@@ -269,8 +288,7 @@
     return compare_type( *reinterpret_cast<const TYPE*>(lhs), *reinterpret_cast<const TYPE*>(rhs) );
 }
 
-}; // namespace android
-
+}  // namespace android
 
 // ---------------------------------------------------------------------------
 
diff --git a/libutils/include/utils/StopWatch.h b/libutils/include/utils/StopWatch.h
index 693dd3c..9b14ac8 100644
--- a/libutils/include/utils/StopWatch.h
+++ b/libutils/include/utils/StopWatch.h
@@ -29,21 +29,18 @@
 class StopWatch
 {
 public:
-        StopWatch(  const char *name,
-                    int clock = SYSTEM_TIME_MONOTONIC,
-                    uint32_t flags = 0);
-        ~StopWatch();
-        
-        const char* name() const;
-        nsecs_t     lap();
-        nsecs_t     elapsedTime() const;
+  StopWatch(const char* name, int clock = SYSTEM_TIME_MONOTONIC);
+  ~StopWatch();
 
-        void        reset();
-        
+  const char* name() const;
+  nsecs_t lap();
+  nsecs_t elapsedTime() const;
+
+  void reset();
+
 private:
     const char*     mName;
     int             mClock;
-    uint32_t        mFlags;
     
     struct lap_t {
         nsecs_t     soFar;
@@ -55,9 +52,7 @@
     int             mNumLaps;
 };
 
-
-}; // namespace android
-
+}  // namespace android
 
 // ---------------------------------------------------------------------------
 
diff --git a/libutils/include/utils/String16.h b/libutils/include/utils/String16.h
index f6433a8..afbc2ed 100644
--- a/libutils/include/utils/String16.h
+++ b/libutils/include/utils/String16.h
@@ -35,9 +35,9 @@
 
 // ---------------------------------------------------------------------------
 
-class SharedBuffer;
 class String8;
-class TextOutput;
+
+// DO NOT USE: please use std::u16string
 
 //! This is a string holding UTF-16 characters.
 class String16
@@ -67,7 +67,6 @@
 
     inline  const char16_t*     string() const;
 
-//TODO(b/35363681): remove
 private:
     static inline std::string   std_string(const String16& str);
 public:
@@ -244,7 +243,7 @@
     return mString;
 }
 
-}; // namespace android
+}  // namespace android
 
 // ---------------------------------------------------------------------------
 
diff --git a/libutils/include/utils/String8.h b/libutils/include/utils/String8.h
index f5f9219..c8f584e 100644
--- a/libutils/include/utils/String8.h
+++ b/libutils/include/utils/String8.h
@@ -31,7 +31,8 @@
 namespace android {
 
 class String16;
-class TextOutput;
+
+// DO NOT USE: please use std::string
 
 //! This is a string holding UTF-8 characters. Does not allow the value more
 // than 0x10FFFF, which is not valid unicode codepoint.
@@ -67,7 +68,6 @@
     inline  const char*         c_str() const;
     inline  const char*         string() const;
 
-// TODO(b/35363681): remove
 private:
     static inline std::string   std_string(const String8& str);
 public:
@@ -187,7 +187,7 @@
      * "/tmp" --> "tmp" (remain = "")
      * "bar.c" --> "bar.c" (remain = "")
      */
-    String8 walkPath(String8* outRemains = NULL) const;
+    String8 walkPath(String8* outRemains = nullptr) const;
 
     /*
      * Return the filename extension.  This is the last '.' and any number
diff --git a/libutils/include/utils/StrongPointer.h b/libutils/include/utils/StrongPointer.h
index c2f7722..360fce5 100644
--- a/libutils/include/utils/StrongPointer.h
+++ b/libutils/include/utils/StrongPointer.h
@@ -17,12 +17,6 @@
 #ifndef ANDROID_STRONG_POINTER_H
 #define ANDROID_STRONG_POINTER_H
 
-#include <cutils/atomic.h>
-
-#include <stdint.h>
-#include <sys/types.h>
-#include <stdlib.h>
-
 // ---------------------------------------------------------------------------
 namespace android {
 
@@ -58,7 +52,7 @@
 template<typename T>
 class sp {
 public:
-    inline sp() : m_ptr(0) { }
+    inline sp() : m_ptr(nullptr) { }
 
     sp(T* other);  // NOLINT(implicit)
     sp(const sp<T>& other);
@@ -88,9 +82,10 @@
 
     // Accessors
 
-    inline  T&      operator* () const  { return *m_ptr; }
-    inline  T*      operator-> () const { return m_ptr;  }
-    inline  T*      get() const         { return m_ptr; }
+    inline T&       operator* () const     { return *m_ptr; }
+    inline T*       operator-> () const    { return m_ptr;  }
+    inline T*       get() const            { return m_ptr; }
+    inline explicit operator bool () const { return m_ptr != nullptr; }
 
     // Operators
 
@@ -235,7 +230,7 @@
 void sp<T>::clear() {
     if (m_ptr) {
         m_ptr->decStrong(this);
-        m_ptr = 0;
+        m_ptr = nullptr;
     }
 }
 
@@ -244,7 +239,7 @@
     m_ptr = ptr;
 }
 
-}; // namespace android
+}  // namespace android
 
 // ---------------------------------------------------------------------------
 
diff --git a/libutils/include/utils/SystemClock.h b/libutils/include/utils/SystemClock.h
index 01db340..f816fba 100644
--- a/libutils/include/utils/SystemClock.h
+++ b/libutils/include/utils/SystemClock.h
@@ -26,7 +26,7 @@
 int64_t elapsedRealtime();
 int64_t elapsedRealtimeNano();
 
-}; // namespace android
+}  // namespace android
 
 #endif // ANDROID_UTILS_SYSTEMCLOCK_H
 
diff --git a/libutils/include/utils/Thread.h b/libutils/include/utils/Thread.h
index a261fc8..3525138 100644
--- a/libutils/include/utils/Thread.h
+++ b/libutils/include/utils/Thread.h
@@ -36,6 +36,8 @@
 namespace android {
 // ---------------------------------------------------------------------------
 
+// DO NOT USE: please use std::thread
+
 class Thread : virtual public RefBase
 {
 public:
@@ -108,8 +110,7 @@
 #endif
 };
 
-
-}; // namespace android
+}  // namespace android
 
 // ---------------------------------------------------------------------------
 #endif // _LIBS_UTILS_THREAD_H
diff --git a/libutils/include/utils/ThreadDefs.h b/libutils/include/utils/ThreadDefs.h
index ae091e4..8eb3d1c 100644
--- a/libutils/include/utils/ThreadDefs.h
+++ b/libutils/include/utils/ThreadDefs.h
@@ -66,9 +66,8 @@
 };
 
 // ---------------------------------------------------------------------------
-}; // namespace android
+}  // namespace android
 #endif  // __cplusplus
 // ---------------------------------------------------------------------------
 
-
 #endif // _LIBS_UTILS_THREAD_DEFS_H
diff --git a/libutils/include/utils/Trace.h b/libutils/include/utils/Trace.h
index eeba40d..4b9c91e 100644
--- a/libutils/include/utils/Trace.h
+++ b/libutils/include/utils/Trace.h
@@ -19,16 +19,8 @@
 
 #if defined(__ANDROID__)
 
-#include <fcntl.h>
 #include <stdint.h>
-#include <stdio.h>
-#include <string.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
 
-#include <cutils/compiler.h>
-#include <utils/threads.h>
 #include <cutils/trace.h>
 
 // See <cutils/trace.h> for more ATRACE_* macros.
@@ -37,6 +29,7 @@
 #define _PASTE(x, y) x ## y
 #define PASTE(x, y) _PASTE(x,y)
 #define ATRACE_NAME(name) android::ScopedTrace PASTE(___tracer, __LINE__) (ATRACE_TAG, name)
+
 // ATRACE_CALL is an ATRACE_NAME that uses the current function name.
 #define ATRACE_CALL() ATRACE_NAME(__FUNCTION__)
 
@@ -44,20 +37,19 @@
 
 class ScopedTrace {
 public:
-inline ScopedTrace(uint64_t tag, const char* name)
-    : mTag(tag) {
-    atrace_begin(mTag,name);
-}
+    inline ScopedTrace(uint64_t tag, const char* name) : mTag(tag) {
+        atrace_begin(mTag, name);
+    }
 
-inline ~ScopedTrace() {
-    atrace_end(mTag);
-}
+    inline ~ScopedTrace() {
+        atrace_end(mTag);
+    }
 
 private:
     uint64_t mTag;
 };
 
-}; // namespace android
+}  // namespace android
 
 #else // !__ANDROID__
 
diff --git a/libutils/include/utils/TypeHelpers.h b/libutils/include/utils/TypeHelpers.h
index 2a25227..1554f52 100644
--- a/libutils/include/utils/TypeHelpers.h
+++ b/libutils/include/utils/TypeHelpers.h
@@ -36,7 +36,7 @@
 template <typename T> struct trait_trivial_dtor { enum { value = false }; };
 template <typename T> struct trait_trivial_copy { enum { value = false }; };
 template <typename T> struct trait_trivial_move { enum { value = false }; };
-template <typename T> struct trait_pointer      { enum { value = false }; };    
+template <typename T> struct trait_pointer      { enum { value = false }; };
 template <typename T> struct trait_pointer<T*>  { enum { value = true }; };
 
 template <typename TYPE>
@@ -59,13 +59,13 @@
 struct aggregate_traits {
     enum {
         is_pointer          = false,
-        has_trivial_ctor    = 
+        has_trivial_ctor    =
             traits<T>::has_trivial_ctor && traits<U>::has_trivial_ctor,
-        has_trivial_dtor    = 
+        has_trivial_dtor    =
             traits<T>::has_trivial_dtor && traits<U>::has_trivial_dtor,
-        has_trivial_copy    = 
+        has_trivial_copy    =
             traits<T>::has_trivial_copy && traits<U>::has_trivial_copy,
-        has_trivial_move    = 
+        has_trivial_move    =
             traits<T>::has_trivial_move && traits<U>::has_trivial_move
     };
 };
@@ -329,7 +329,7 @@
     return hash_type(uintptr_t(value));
 }
 
-}; // namespace android
+}  // namespace android
 
 // ---------------------------------------------------------------------------
 
diff --git a/libutils/include/utils/Vector.h b/libutils/include/utils/Vector.h
index 9a643f9..ddf71de 100644
--- a/libutils/include/utils/Vector.h
+++ b/libutils/include/utils/Vector.h
@@ -24,6 +24,20 @@
 #include <utils/TypeHelpers.h>
 #include <utils/VectorImpl.h>
 
+/*
+ * Used to blacklist some functions from CFI.
+ *
+ */
+#ifndef __has_attribute
+#define __has_attribute(x) 0
+#endif
+
+#if __has_attribute(no_sanitize)
+#define UTILS_VECTOR_NO_CFI __attribute__((no_sanitize("cfi")))
+#else
+#define UTILS_VECTOR_NO_CFI
+#endif
+
 // ---------------------------------------------------------------------------
 
 namespace android {
@@ -35,6 +49,8 @@
  * The main templated vector class ensuring type safety
  * while making use of VectorImpl.
  * This is the class users want to use.
+ *
+ * DO NOT USE: please use std::vector
  */
 
 template <class TYPE>
@@ -42,11 +58,11 @@
 {
 public:
             typedef TYPE    value_type;
-    
-    /*! 
+
+    /*!
      * Constructors and destructors
      */
-    
+
                             Vector();
                             Vector(const Vector<TYPE>& rhs);
     explicit                Vector(const SortedVector<TYPE>& rhs);
@@ -54,7 +70,7 @@
 
     /*! copy operator */
             const Vector<TYPE>&     operator = (const Vector<TYPE>& rhs) const;
-            Vector<TYPE>&           operator = (const Vector<TYPE>& rhs);    
+            Vector<TYPE>&           operator = (const Vector<TYPE>& rhs);
 
             const Vector<TYPE>&     operator = (const SortedVector<TYPE>& rhs) const;
             Vector<TYPE>&           operator = (const SortedVector<TYPE>& rhs);
@@ -65,7 +81,7 @@
 
     inline  void            clear()             { VectorImpl::clear(); }
 
-    /*! 
+    /*!
      * vector stats
      */
 
@@ -87,13 +103,13 @@
     /*!
      * C-style array access
      */
-     
-    //! read-only C-style access 
+
+    //! read-only C-style access
     inline  const TYPE*     array() const;
     //! read-write C-style access
             TYPE*           editArray();
-    
-    /*! 
+
+    /*!
      * accessors
      */
 
@@ -113,10 +129,10 @@
     //! grants right access to the top of the stack (last element)
             TYPE&           editTop();
 
-            /*! 
+            /*!
              * append/insert another vector
              */
-            
+
     //! insert another vector at a given index
             ssize_t         insertVectorAt(const Vector<TYPE>& vector, size_t index);
 
@@ -130,10 +146,10 @@
     //! append an array at the end of this vector
             ssize_t         appendArray(const TYPE* array, size_t length);
 
-            /*! 
+            /*!
              * add/insert/replace items
              */
-             
+
     //! insert one or several items initialized with their default constructor
     inline  ssize_t         insertAt(size_t index, size_t numItems = 1);
     //! insert one or several items initialized from a prototype item
@@ -147,7 +163,7 @@
     //! same as push() but returns the index the item was added at (or an error)
     inline  ssize_t         add();
     //! same as push() but returns the index the item was added at (or an error)
-            ssize_t         add(const TYPE& item);            
+            ssize_t         add(const TYPE& item);
     //! replace an item with a new one initialized with its default constructor
     inline  ssize_t         replaceAt(size_t index);
     //! replace an item with a new one
@@ -165,10 +181,10 @@
     /*!
      * sort (stable) the array
      */
-     
+
      typedef int (*compar_t)(const TYPE* lhs, const TYPE* rhs);
      typedef int (*compar_r_t)(const TYPE* lhs, const TYPE* rhs, void* state);
-     
+
      inline status_t        sort(compar_t cmp);
      inline status_t        sort(compar_r_t cmp, void* state);
 
@@ -237,7 +253,7 @@
 template<class TYPE> inline
 Vector<TYPE>& Vector<TYPE>::operator = (const Vector<TYPE>& rhs) {
     VectorImpl::operator = (rhs);
-    return *this; 
+    return *this;
 }
 
 template<class TYPE> inline
@@ -255,7 +271,7 @@
 template<class TYPE> inline
 const Vector<TYPE>& Vector<TYPE>::operator = (const SortedVector<TYPE>& rhs) const {
     VectorImpl::operator = (rhs);
-    return *this; 
+    return *this;
 }
 
 template<class TYPE> inline
@@ -380,7 +396,7 @@
 // ---------------------------------------------------------------------------
 
 template<class TYPE>
-void Vector<TYPE>::do_construct(void* storage, size_t num) const {
+UTILS_VECTOR_NO_CFI void Vector<TYPE>::do_construct(void* storage, size_t num) const {
     construct_type( reinterpret_cast<TYPE*>(storage), num );
 }
 
@@ -390,27 +406,26 @@
 }
 
 template<class TYPE>
-void Vector<TYPE>::do_copy(void* dest, const void* from, size_t num) const {
+UTILS_VECTOR_NO_CFI void Vector<TYPE>::do_copy(void* dest, const void* from, size_t num) const {
     copy_type( reinterpret_cast<TYPE*>(dest), reinterpret_cast<const TYPE*>(from), num );
 }
 
 template<class TYPE>
-void Vector<TYPE>::do_splat(void* dest, const void* item, size_t num) const {
+UTILS_VECTOR_NO_CFI void Vector<TYPE>::do_splat(void* dest, const void* item, size_t num) const {
     splat_type( reinterpret_cast<TYPE*>(dest), reinterpret_cast<const TYPE*>(item), num );
 }
 
 template<class TYPE>
-void Vector<TYPE>::do_move_forward(void* dest, const void* from, size_t num) const {
+UTILS_VECTOR_NO_CFI void Vector<TYPE>::do_move_forward(void* dest, const void* from, size_t num) const {
     move_forward_type( reinterpret_cast<TYPE*>(dest), reinterpret_cast<const TYPE*>(from), num );
 }
 
 template<class TYPE>
-void Vector<TYPE>::do_move_backward(void* dest, const void* from, size_t num) const {
+UTILS_VECTOR_NO_CFI void Vector<TYPE>::do_move_backward(void* dest, const void* from, size_t num) const {
     move_backward_type( reinterpret_cast<TYPE*>(dest), reinterpret_cast<const TYPE*>(from), num );
 }
 
-}; // namespace android
-
+}  // namespace android
 
 // ---------------------------------------------------------------------------
 
diff --git a/libutils/include/utils/VectorImpl.h b/libutils/include/utils/VectorImpl.h
index 4dd91fd..41b9f33 100644
--- a/libutils/include/utils/VectorImpl.h
+++ b/libutils/include/utils/VectorImpl.h
@@ -157,7 +157,7 @@
     virtual int             do_compare(const void* lhs, const void* rhs) const = 0;
 
 private:
-            ssize_t         _indexOrderOf(const void* item, size_t* order = 0) const;
+            ssize_t         _indexOrderOf(const void* item, size_t* order = nullptr) const;
 
             // these are made private, because they can't be used on a SortedVector
             // (they don't have an implementation either)
@@ -175,8 +175,7 @@
             ssize_t         replaceAt(const void* item, size_t index);
 };
 
-}; // namespace android
-
+}  // namespace android
 
 // ---------------------------------------------------------------------------
 
diff --git a/libutils/include/utils/misc.h b/libutils/include/utils/misc.h
index 6cccec3..32615b3 100644
--- a/libutils/include/utils/misc.h
+++ b/libutils/include/utils/misc.h
@@ -22,7 +22,9 @@
 
 #include <utils/Endian.h>
 
-/* get #of elements in a static array */
+/* get #of elements in a static array
+ * DO NOT USE: please use std::vector/std::array instead
+ */
 #ifndef NELEM
 # define NELEM(x) ((int) (sizeof(x) / sizeof((x)[0])))
 #endif
@@ -33,6 +35,6 @@
 void add_sysprop_change_callback(sysprop_change_callback cb, int priority);
 void report_sysprop_change();
 
-}; // namespace android
+}  // namespace android
 
 #endif // _LIBS_UTILS_MISC_H
diff --git a/libutils/misc.cpp b/libutils/misc.cpp
index 216dc14..f77e189 100644
--- a/libutils/misc.cpp
+++ b/libutils/misc.cpp
@@ -16,21 +16,19 @@
 
 #define LOG_TAG "misc"
 
-//
-// Miscellaneous utility functions.
-//
 #include <utils/misc.h>
+
+#include <pthread.h>
+
 #include <utils/Log.h>
+#include <utils/Vector.h>
 
-#include <sys/stat.h>
-#include <string.h>
-#include <stdio.h>
-
-#if !defined(_WIN32)
-# include <pthread.h>
+#if defined(__ANDROID__) && !defined(__ANDROID_RECOVERY__)
+#include <dlfcn.h>
+#include <vndksupport/linker.h>
 #endif
 
-#include <utils/Vector.h>
+extern "C" void do_report_sysprop_change();
 
 using namespace android;
 
@@ -43,13 +41,13 @@
 
 #if !defined(_WIN32)
 static pthread_mutex_t gSyspropMutex = PTHREAD_MUTEX_INITIALIZER;
-static Vector<sysprop_change_callback_info>* gSyspropList = NULL;
+static Vector<sysprop_change_callback_info>* gSyspropList = nullptr;
 #endif
 
-void add_sysprop_change_callback(sysprop_change_callback cb, int priority) {
 #if !defined(_WIN32)
+void add_sysprop_change_callback(sysprop_change_callback cb, int priority) {
     pthread_mutex_lock(&gSyspropMutex);
-    if (gSyspropList == NULL) {
+    if (gSyspropList == nullptr) {
         gSyspropList = new Vector<sysprop_change_callback_info>();
     }
     sysprop_change_callback_info info;
@@ -67,14 +65,45 @@
         gSyspropList->add(info);
     }
     pthread_mutex_unlock(&gSyspropMutex);
+}
+#else
+void add_sysprop_change_callback(sysprop_change_callback, int) {}
+#endif
+
+#if defined(__ANDROID__) && !defined(__ANDROID_RECOVERY__)
+void (*get_report_sysprop_change_func())() {
+    void (*func)() = nullptr;
+    void* handle = android_load_sphal_library("libutils.so", RTLD_NOW);
+    if (handle != nullptr) {
+        func = reinterpret_cast<decltype(func)>(dlsym(handle, "do_report_sysprop_change"));
+    }
+
+    return func;
+}
+#endif
+
+void report_sysprop_change() {
+    do_report_sysprop_change();
+
+#if defined(__ANDROID__) && !defined(__ANDROID_RECOVERY__)
+    // libutils.so is double loaded; from the default namespace and from the
+    // 'sphal' namespace. Redirect the sysprop change event to the other instance
+    // of libutils.so loaded in the 'sphal' namespace so that listeners attached
+    // to that instance is also notified with this event.
+    static auto func = get_report_sysprop_change_func();
+    if (func != nullptr) {
+        (*func)();
+    }
 #endif
 }
 
-void report_sysprop_change() {
+};  // namespace android
+
+void do_report_sysprop_change() {
 #if !defined(_WIN32)
     pthread_mutex_lock(&gSyspropMutex);
     Vector<sysprop_change_callback_info> listeners;
-    if (gSyspropList != NULL) {
+    if (gSyspropList != nullptr) {
         listeners = *gSyspropList;
     }
     pthread_mutex_unlock(&gSyspropMutex);
@@ -85,5 +114,3 @@
     }
 #endif
 }
-
-}; // namespace android
diff --git a/libutils/tests/Android.bp b/libutils/tests/Android.bp
index ea606a1..1390552 100644
--- a/libutils/tests/Android.bp
+++ b/libutils/tests/Android.bp
@@ -23,6 +23,7 @@
     srcs: [
         "BitSet_test.cpp",
         "LruCache_test.cpp",
+        "Mutex_test.cpp",
         "Singleton_test.cpp",
         "String8_test.cpp",
         "StrongPointer_test.cpp",
@@ -33,9 +34,6 @@
     target: {
         android: {
             srcs: [
-                "BlobCache_test.cpp",
-                "Looper_test.cpp",
-                "RefBase_test.cpp",
                 "SystemClock_test.cpp",
             ],
             shared_libs: [
@@ -44,7 +42,6 @@
                 "libcutils",
                 "libutils",
                 "libbase",
-                "libdl",
             ],
         },
         linux: {
@@ -59,7 +56,6 @@
                 "liblog",
                 "libbase",
             ],
-            host_ldlibs: ["-ldl"],
         },
     },
 
@@ -72,6 +68,7 @@
         "-Wall",
         "-Wextra",
         "-Werror",
+        "-Wthread-safety",
     ],
 }
 
@@ -80,6 +77,10 @@
     host_supported: true,
     relative_install_path: "libutils_tests",
     srcs: ["Singleton_test1.cpp"],
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
 }
 
 cc_test_library {
@@ -87,5 +88,9 @@
     host_supported: true,
     relative_install_path: "libutils_tests",
     srcs: ["Singleton_test2.cpp"],
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
     shared_libs: ["libutils_tests_singleton1"],
 }
diff --git a/libutils/tests/BlobCache_test.cpp b/libutils/tests/BlobCache_test.cpp
deleted file mode 100644
index 1e2ff98..0000000
--- a/libutils/tests/BlobCache_test.cpp
+++ /dev/null
@@ -1,425 +0,0 @@
-/*
- ** Copyright 2011, 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 <fcntl.h>
-#include <stdio.h>
-
-#include <gtest/gtest.h>
-
-#include <utils/BlobCache.h>
-#include <utils/Errors.h>
-
-namespace android {
-
-class BlobCacheTest : public ::testing::Test {
-protected:
-    enum {
-        MAX_KEY_SIZE = 6,
-        MAX_VALUE_SIZE = 8,
-        MAX_TOTAL_SIZE = 13,
-    };
-
-    virtual void SetUp() {
-        mBC = new BlobCache(MAX_KEY_SIZE, MAX_VALUE_SIZE, MAX_TOTAL_SIZE);
-    }
-
-    virtual void TearDown() {
-        mBC.clear();
-    }
-
-    sp<BlobCache> mBC;
-};
-
-TEST_F(BlobCacheTest, CacheSingleValueSucceeds) {
-    unsigned char buf[4] = { 0xee, 0xee, 0xee, 0xee };
-    mBC->set("abcd", 4, "efgh", 4);
-    ASSERT_EQ(size_t(4), mBC->get("abcd", 4, buf, 4));
-    ASSERT_EQ('e', buf[0]);
-    ASSERT_EQ('f', buf[1]);
-    ASSERT_EQ('g', buf[2]);
-    ASSERT_EQ('h', buf[3]);
-}
-
-TEST_F(BlobCacheTest, CacheTwoValuesSucceeds) {
-    unsigned char buf[2] = { 0xee, 0xee };
-    mBC->set("ab", 2, "cd", 2);
-    mBC->set("ef", 2, "gh", 2);
-    ASSERT_EQ(size_t(2), mBC->get("ab", 2, buf, 2));
-    ASSERT_EQ('c', buf[0]);
-    ASSERT_EQ('d', buf[1]);
-    ASSERT_EQ(size_t(2), mBC->get("ef", 2, buf, 2));
-    ASSERT_EQ('g', buf[0]);
-    ASSERT_EQ('h', buf[1]);
-}
-
-TEST_F(BlobCacheTest, GetOnlyWritesInsideBounds) {
-    unsigned char buf[6] = { 0xee, 0xee, 0xee, 0xee, 0xee, 0xee };
-    mBC->set("abcd", 4, "efgh", 4);
-    ASSERT_EQ(size_t(4), mBC->get("abcd", 4, buf+1, 4));
-    ASSERT_EQ(0xee, buf[0]);
-    ASSERT_EQ('e', buf[1]);
-    ASSERT_EQ('f', buf[2]);
-    ASSERT_EQ('g', buf[3]);
-    ASSERT_EQ('h', buf[4]);
-    ASSERT_EQ(0xee, buf[5]);
-}
-
-TEST_F(BlobCacheTest, GetOnlyWritesIfBufferIsLargeEnough) {
-    unsigned char buf[3] = { 0xee, 0xee, 0xee };
-    mBC->set("abcd", 4, "efgh", 4);
-    ASSERT_EQ(size_t(4), mBC->get("abcd", 4, buf, 3));
-    ASSERT_EQ(0xee, buf[0]);
-    ASSERT_EQ(0xee, buf[1]);
-    ASSERT_EQ(0xee, buf[2]);
-}
-
-TEST_F(BlobCacheTest, GetDoesntAccessNullBuffer) {
-    mBC->set("abcd", 4, "efgh", 4);
-    ASSERT_EQ(size_t(4), mBC->get("abcd", 4, NULL, 0));
-}
-
-TEST_F(BlobCacheTest, MultipleSetsCacheLatestValue) {
-    unsigned char buf[4] = { 0xee, 0xee, 0xee, 0xee };
-    mBC->set("abcd", 4, "efgh", 4);
-    mBC->set("abcd", 4, "ijkl", 4);
-    ASSERT_EQ(size_t(4), mBC->get("abcd", 4, buf, 4));
-    ASSERT_EQ('i', buf[0]);
-    ASSERT_EQ('j', buf[1]);
-    ASSERT_EQ('k', buf[2]);
-    ASSERT_EQ('l', buf[3]);
-}
-
-TEST_F(BlobCacheTest, SecondSetKeepsFirstValueIfTooLarge) {
-    unsigned char buf[MAX_VALUE_SIZE+1] = { 0xee, 0xee, 0xee, 0xee };
-    mBC->set("abcd", 4, "efgh", 4);
-    mBC->set("abcd", 4, buf, MAX_VALUE_SIZE+1);
-    ASSERT_EQ(size_t(4), mBC->get("abcd", 4, buf, 4));
-    ASSERT_EQ('e', buf[0]);
-    ASSERT_EQ('f', buf[1]);
-    ASSERT_EQ('g', buf[2]);
-    ASSERT_EQ('h', buf[3]);
-}
-
-TEST_F(BlobCacheTest, DoesntCacheIfKeyIsTooBig) {
-    char key[MAX_KEY_SIZE+1];
-    unsigned char buf[4] = { 0xee, 0xee, 0xee, 0xee };
-    for (int i = 0; i < MAX_KEY_SIZE+1; i++) {
-        key[i] = 'a';
-    }
-    mBC->set(key, MAX_KEY_SIZE+1, "bbbb", 4);
-    ASSERT_EQ(size_t(0), mBC->get(key, MAX_KEY_SIZE+1, buf, 4));
-    ASSERT_EQ(0xee, buf[0]);
-    ASSERT_EQ(0xee, buf[1]);
-    ASSERT_EQ(0xee, buf[2]);
-    ASSERT_EQ(0xee, buf[3]);
-}
-
-TEST_F(BlobCacheTest, DoesntCacheIfValueIsTooBig) {
-    char buf[MAX_VALUE_SIZE+1];
-    for (int i = 0; i < MAX_VALUE_SIZE+1; i++) {
-        buf[i] = 'b';
-    }
-    mBC->set("abcd", 4, buf, MAX_VALUE_SIZE+1);
-    for (int i = 0; i < MAX_VALUE_SIZE+1; i++) {
-        buf[i] = 0xee;
-    }
-    ASSERT_EQ(size_t(0), mBC->get("abcd", 4, buf, MAX_VALUE_SIZE+1));
-    for (int i = 0; i < MAX_VALUE_SIZE+1; i++) {
-        SCOPED_TRACE(i);
-        ASSERT_EQ(0xee, buf[i]);
-    }
-}
-
-TEST_F(BlobCacheTest, DoesntCacheIfKeyValuePairIsTooBig) {
-    // Check a testing assumptions
-    ASSERT_TRUE(MAX_TOTAL_SIZE < MAX_KEY_SIZE + MAX_VALUE_SIZE);
-    ASSERT_TRUE(MAX_KEY_SIZE < MAX_TOTAL_SIZE);
-
-    enum { bufSize = MAX_TOTAL_SIZE - MAX_KEY_SIZE + 1 };
-
-    char key[MAX_KEY_SIZE];
-    char buf[bufSize];
-    for (int i = 0; i < MAX_KEY_SIZE; i++) {
-        key[i] = 'a';
-    }
-    for (int i = 0; i < bufSize; i++) {
-        buf[i] = 'b';
-    }
-
-    mBC->set(key, MAX_KEY_SIZE, buf, MAX_VALUE_SIZE);
-    ASSERT_EQ(size_t(0), mBC->get(key, MAX_KEY_SIZE, NULL, 0));
-}
-
-TEST_F(BlobCacheTest, CacheMaxKeySizeSucceeds) {
-    char key[MAX_KEY_SIZE];
-    unsigned char buf[4] = { 0xee, 0xee, 0xee, 0xee };
-    for (int i = 0; i < MAX_KEY_SIZE; i++) {
-        key[i] = 'a';
-    }
-    mBC->set(key, MAX_KEY_SIZE, "wxyz", 4);
-    ASSERT_EQ(size_t(4), mBC->get(key, MAX_KEY_SIZE, buf, 4));
-    ASSERT_EQ('w', buf[0]);
-    ASSERT_EQ('x', buf[1]);
-    ASSERT_EQ('y', buf[2]);
-    ASSERT_EQ('z', buf[3]);
-}
-
-TEST_F(BlobCacheTest, CacheMaxValueSizeSucceeds) {
-    char buf[MAX_VALUE_SIZE];
-    for (int i = 0; i < MAX_VALUE_SIZE; i++) {
-        buf[i] = 'b';
-    }
-    mBC->set("abcd", 4, buf, MAX_VALUE_SIZE);
-    for (int i = 0; i < MAX_VALUE_SIZE; i++) {
-        buf[i] = 0xee;
-    }
-    ASSERT_EQ(size_t(MAX_VALUE_SIZE), mBC->get("abcd", 4, buf,
-            MAX_VALUE_SIZE));
-    for (int i = 0; i < MAX_VALUE_SIZE; i++) {
-        SCOPED_TRACE(i);
-        ASSERT_EQ('b', buf[i]);
-    }
-}
-
-TEST_F(BlobCacheTest, CacheMaxKeyValuePairSizeSucceeds) {
-    // Check a testing assumption
-    ASSERT_TRUE(MAX_KEY_SIZE < MAX_TOTAL_SIZE);
-
-    enum { bufSize = MAX_TOTAL_SIZE - MAX_KEY_SIZE };
-
-    char key[MAX_KEY_SIZE];
-    char buf[bufSize];
-    for (int i = 0; i < MAX_KEY_SIZE; i++) {
-        key[i] = 'a';
-    }
-    for (int i = 0; i < bufSize; i++) {
-        buf[i] = 'b';
-    }
-
-    mBC->set(key, MAX_KEY_SIZE, buf, bufSize);
-    ASSERT_EQ(size_t(bufSize), mBC->get(key, MAX_KEY_SIZE, NULL, 0));
-}
-
-TEST_F(BlobCacheTest, CacheMinKeyAndValueSizeSucceeds) {
-    unsigned char buf[1] = { 0xee };
-    mBC->set("x", 1, "y", 1);
-    ASSERT_EQ(size_t(1), mBC->get("x", 1, buf, 1));
-    ASSERT_EQ('y', buf[0]);
-}
-
-TEST_F(BlobCacheTest, CacheSizeDoesntExceedTotalLimit) {
-    for (int i = 0; i < 256; i++) {
-        uint8_t k = i;
-        mBC->set(&k, 1, "x", 1);
-    }
-    int numCached = 0;
-    for (int i = 0; i < 256; i++) {
-        uint8_t k = i;
-        if (mBC->get(&k, 1, NULL, 0) == 1) {
-            numCached++;
-        }
-    }
-    ASSERT_GE(MAX_TOTAL_SIZE / 2, numCached);
-}
-
-TEST_F(BlobCacheTest, ExceedingTotalLimitHalvesCacheSize) {
-    // Fill up the entire cache with 1 char key/value pairs.
-    const int maxEntries = MAX_TOTAL_SIZE / 2;
-    for (int i = 0; i < maxEntries; i++) {
-        uint8_t k = i;
-        mBC->set(&k, 1, "x", 1);
-    }
-    // Insert one more entry, causing a cache overflow.
-    {
-        uint8_t k = maxEntries;
-        mBC->set(&k, 1, "x", 1);
-    }
-    // Count the number of entries in the cache.
-    int numCached = 0;
-    for (int i = 0; i < maxEntries+1; i++) {
-        uint8_t k = i;
-        if (mBC->get(&k, 1, NULL, 0) == 1) {
-            numCached++;
-        }
-    }
-    ASSERT_EQ(maxEntries/2 + 1, numCached);
-}
-
-class BlobCacheFlattenTest : public BlobCacheTest {
-protected:
-    virtual void SetUp() {
-        BlobCacheTest::SetUp();
-        mBC2 = new BlobCache(MAX_KEY_SIZE, MAX_VALUE_SIZE, MAX_TOTAL_SIZE);
-    }
-
-    virtual void TearDown() {
-        mBC2.clear();
-        BlobCacheTest::TearDown();
-    }
-
-    void roundTrip() {
-        size_t size = mBC->getFlattenedSize();
-        uint8_t* flat = new uint8_t[size];
-        ASSERT_EQ(OK, mBC->flatten(flat, size));
-        ASSERT_EQ(OK, mBC2->unflatten(flat, size));
-        delete[] flat;
-    }
-
-    sp<BlobCache> mBC2;
-};
-
-TEST_F(BlobCacheFlattenTest, FlattenOneValue) {
-    unsigned char buf[4] = { 0xee, 0xee, 0xee, 0xee };
-    mBC->set("abcd", 4, "efgh", 4);
-    roundTrip();
-    ASSERT_EQ(size_t(4), mBC2->get("abcd", 4, buf, 4));
-    ASSERT_EQ('e', buf[0]);
-    ASSERT_EQ('f', buf[1]);
-    ASSERT_EQ('g', buf[2]);
-    ASSERT_EQ('h', buf[3]);
-}
-
-TEST_F(BlobCacheFlattenTest, FlattenFullCache) {
-    // Fill up the entire cache with 1 char key/value pairs.
-    const int maxEntries = MAX_TOTAL_SIZE / 2;
-    for (int i = 0; i < maxEntries; i++) {
-        uint8_t k = i;
-        mBC->set(&k, 1, &k, 1);
-    }
-
-    roundTrip();
-
-    // Verify the deserialized cache
-    for (int i = 0; i < maxEntries; i++) {
-        uint8_t k = i;
-        uint8_t v = 0xee;
-        ASSERT_EQ(size_t(1), mBC2->get(&k, 1, &v, 1));
-        ASSERT_EQ(k, v);
-    }
-}
-
-TEST_F(BlobCacheFlattenTest, FlattenDoesntChangeCache) {
-    // Fill up the entire cache with 1 char key/value pairs.
-    const int maxEntries = MAX_TOTAL_SIZE / 2;
-    for (int i = 0; i < maxEntries; i++) {
-        uint8_t k = i;
-        mBC->set(&k, 1, &k, 1);
-    }
-
-    size_t size = mBC->getFlattenedSize();
-    uint8_t* flat = new uint8_t[size];
-    ASSERT_EQ(OK, mBC->flatten(flat, size));
-    delete[] flat;
-
-    // Verify the cache that we just serialized
-    for (int i = 0; i < maxEntries; i++) {
-        uint8_t k = i;
-        uint8_t v = 0xee;
-        ASSERT_EQ(size_t(1), mBC->get(&k, 1, &v, 1));
-        ASSERT_EQ(k, v);
-    }
-}
-
-TEST_F(BlobCacheFlattenTest, FlattenCatchesBufferTooSmall) {
-    // Fill up the entire cache with 1 char key/value pairs.
-    const int maxEntries = MAX_TOTAL_SIZE / 2;
-    for (int i = 0; i < maxEntries; i++) {
-        uint8_t k = i;
-        mBC->set(&k, 1, &k, 1);
-    }
-
-    size_t size = mBC->getFlattenedSize() - 1;
-    uint8_t* flat = new uint8_t[size];
-    // ASSERT_EQ(BAD_VALUE, mBC->flatten(flat, size));
-    // TODO: The above fails. I expect this is so because getFlattenedSize()
-    // overstimates the size by using PROPERTY_VALUE_MAX.
-    delete[] flat;
-}
-
-TEST_F(BlobCacheFlattenTest, UnflattenCatchesBadMagic) {
-    unsigned char buf[4] = { 0xee, 0xee, 0xee, 0xee };
-    mBC->set("abcd", 4, "efgh", 4);
-
-    size_t size = mBC->getFlattenedSize();
-    uint8_t* flat = new uint8_t[size];
-    ASSERT_EQ(OK, mBC->flatten(flat, size));
-    flat[1] = ~flat[1];
-
-    // Bad magic should cause an error.
-    ASSERT_EQ(BAD_VALUE, mBC2->unflatten(flat, size));
-    delete[] flat;
-
-    // The error should cause the unflatten to result in an empty cache
-    ASSERT_EQ(size_t(0), mBC2->get("abcd", 4, buf, 4));
-}
-
-TEST_F(BlobCacheFlattenTest, UnflattenCatchesBadBlobCacheVersion) {
-    unsigned char buf[4] = { 0xee, 0xee, 0xee, 0xee };
-    mBC->set("abcd", 4, "efgh", 4);
-
-    size_t size = mBC->getFlattenedSize();
-    uint8_t* flat = new uint8_t[size];
-    ASSERT_EQ(OK, mBC->flatten(flat, size));
-    flat[5] = ~flat[5];
-
-    // Version mismatches shouldn't cause errors, but should not use the
-    // serialized entries
-    ASSERT_EQ(OK, mBC2->unflatten(flat, size));
-    delete[] flat;
-
-    // The version mismatch should cause the unflatten to result in an empty
-    // cache
-    ASSERT_EQ(size_t(0), mBC2->get("abcd", 4, buf, 4));
-}
-
-TEST_F(BlobCacheFlattenTest, UnflattenCatchesBadBlobCacheDeviceVersion) {
-    unsigned char buf[4] = { 0xee, 0xee, 0xee, 0xee };
-    mBC->set("abcd", 4, "efgh", 4);
-
-    size_t size = mBC->getFlattenedSize();
-    uint8_t* flat = new uint8_t[size];
-    ASSERT_EQ(OK, mBC->flatten(flat, size));
-    flat[10] = ~flat[10];
-
-    // Version mismatches shouldn't cause errors, but should not use the
-    // serialized entries
-    ASSERT_EQ(OK, mBC2->unflatten(flat, size));
-    delete[] flat;
-
-    // The version mismatch should cause the unflatten to result in an empty
-    // cache
-    ASSERT_EQ(size_t(0), mBC2->get("abcd", 4, buf, 4));
-}
-
-TEST_F(BlobCacheFlattenTest, UnflattenCatchesBufferTooSmall) {
-    unsigned char buf[4] = { 0xee, 0xee, 0xee, 0xee };
-    mBC->set("abcd", 4, "efgh", 4);
-
-    size_t size = mBC->getFlattenedSize();
-    uint8_t* flat = new uint8_t[size];
-    ASSERT_EQ(OK, mBC->flatten(flat, size));
-
-    // A buffer truncation shouldt cause an error
-    // ASSERT_EQ(BAD_VALUE, mBC2->unflatten(flat, size-1));
-    // TODO: The above appears to fail because getFlattenedSize() is
-    // conservative.
-    delete[] flat;
-
-    // The error should cause the unflatten to result in an empty cache
-    ASSERT_EQ(size_t(0), mBC2->get("abcd", 4, buf, 4));
-}
-
-} // namespace android
diff --git a/libutils/tests/Looper_test.cpp b/libutils/tests/Looper_test.cpp
index 8ebcfaf..2282ced 100644
--- a/libutils/tests/Looper_test.cpp
+++ b/libutils/tests/Looper_test.cpp
@@ -339,7 +339,7 @@
     Pipe pipe;
 
     pipe.writeSignal();
-    mLooper->addFd(pipe.receiveFd, expectedIdent, Looper::EVENT_INPUT, NULL, expectedData);
+    mLooper->addFd(pipe.receiveFd, expectedIdent, Looper::EVENT_INPUT, nullptr, expectedData);
 
     StopWatch stopWatch("pollOnce");
     int fd;
@@ -364,7 +364,7 @@
 
 TEST_F(LooperTest, AddFd_WhenCallbackAdded_ReturnsOne) {
     Pipe pipe;
-    int result = mLooper->addFd(pipe.receiveFd, 0, Looper::EVENT_INPUT, NULL, NULL);
+    int result = mLooper->addFd(pipe.receiveFd, 0, Looper::EVENT_INPUT, nullptr, nullptr);
 
     EXPECT_EQ(1, result)
             << "addFd should return 1 because FD was added";
@@ -372,7 +372,7 @@
 
 TEST_F(LooperTest, AddFd_WhenIdentIsNegativeAndCallbackIsNull_ReturnsError) {
     Pipe pipe;
-    int result = mLooper->addFd(pipe.receiveFd, -1, Looper::EVENT_INPUT, NULL, NULL);
+    int result = mLooper->addFd(pipe.receiveFd, -1, Looper::EVENT_INPUT, nullptr, nullptr);
 
     EXPECT_EQ(-1, result)
             << "addFd should return -1 because arguments were invalid";
@@ -381,7 +381,7 @@
 TEST_F(LooperTest, AddFd_WhenNoCallbackAndAllowNonCallbacksIsFalse_ReturnsError) {
     Pipe pipe;
     sp<Looper> looper = new Looper(false /*allowNonCallbacks*/);
-    int result = looper->addFd(pipe.receiveFd, 0, 0, NULL, NULL);
+    int result = looper->addFd(pipe.receiveFd, 0, 0, nullptr, nullptr);
 
     EXPECT_EQ(-1, result)
             << "addFd should return -1 because arguments were invalid";
diff --git a/libutils/tests/LruCache_test.cpp b/libutils/tests/LruCache_test.cpp
index 4e885bb..c4d917b 100644
--- a/libutils/tests/LruCache_test.cpp
+++ b/libutils/tests/LruCache_test.cpp
@@ -110,7 +110,7 @@
 
 class EntryRemovedCallback : public OnEntryRemoved<SimpleKey, StringValue> {
 public:
-    EntryRemovedCallback() : callbackCount(0), lastKey(-1), lastValue(NULL) { }
+    EntryRemovedCallback() : callbackCount(0), lastKey(-1), lastValue(nullptr) { }
     ~EntryRemovedCallback() {}
     void operator()(SimpleKey& k, StringValue& v) {
         callbackCount += 1;
@@ -153,7 +153,7 @@
 TEST_F(LruCacheTest, Empty) {
     LruCache<SimpleKey, StringValue> cache(100);
 
-    EXPECT_EQ(NULL, cache.get(0));
+    EXPECT_EQ(nullptr, cache.get(0));
     EXPECT_EQ(0u, cache.size());
 }
 
@@ -175,7 +175,7 @@
     cache.put(1, "one");
     cache.put(2, "two");
     cache.put(3, "three");
-    EXPECT_EQ(NULL, cache.get(1));
+    EXPECT_EQ(nullptr, cache.get(1));
     EXPECT_STREQ("two", cache.get(2));
     EXPECT_STREQ("three", cache.get(3));
     EXPECT_EQ(2u, cache.size());
@@ -188,7 +188,7 @@
     cache.put(2, "two");
     cache.put(3, "three");
     cache.removeOldest();
-    EXPECT_EQ(NULL, cache.get(1));
+    EXPECT_EQ(nullptr, cache.get(1));
     EXPECT_STREQ("two", cache.get(2));
     EXPECT_STREQ("three", cache.get(3));
     EXPECT_EQ(2u, cache.size());
@@ -203,7 +203,7 @@
     EXPECT_STREQ("one", cache.get(1));
     cache.removeOldest();
     EXPECT_STREQ("one", cache.get(1));
-    EXPECT_EQ(NULL, cache.get(2));
+    EXPECT_EQ(nullptr, cache.get(2));
     EXPECT_STREQ("three", cache.get(3));
     EXPECT_EQ(2u, cache.size());
 }
@@ -230,7 +230,7 @@
         int index = random() % kNumKeys;
         uint32_t key = hash_int(index);
         const char *val = cache.get(key);
-        if (val != NULL) {
+        if (val != nullptr) {
             EXPECT_EQ(strings[index], val);
             hitCount++;
         } else {
diff --git a/libutils/tests/Mutex_test.cpp b/libutils/tests/Mutex_test.cpp
new file mode 100644
index 0000000..8a1805f
--- /dev/null
+++ b/libutils/tests/Mutex_test.cpp
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2016 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 <utils/Mutex.h>
+
+#include <gtest/gtest.h>
+
+static android::Mutex mLock;
+static int i GUARDED_BY(mLock);
+
+void modifyLockedVariable() REQUIRES(mLock) {
+    i = 1;
+}
+
+TEST(Mutex, compile) {
+    android::Mutex::Autolock _l(mLock);
+    i = 0;
+    modifyLockedVariable();
+}
\ No newline at end of file
diff --git a/libutils/tests/Unicode_test.cpp b/libutils/tests/Unicode_test.cpp
index d23e43a..b92eef8 100644
--- a/libutils/tests/Unicode_test.cpp
+++ b/libutils/tests/Unicode_test.cpp
@@ -15,7 +15,11 @@
  */
 
 #define LOG_TAG "Unicode_test"
-#include <utils/Log.h>
+
+#include <sys/mman.h>
+#include <unistd.h>
+
+#include <log/log.h>
 #include <utils/Unicode.h>
 
 #include <gtest/gtest.h>
@@ -119,6 +123,31 @@
             << "should return the original pointer";
 }
 
+TEST_F(UnicodeTest, strstr16EmptyTarget_bug) {
+    // In the original code when target is an empty string strlen16() would
+    // start reading the memory until a "terminating null" (that is, zero)
+    // character is found.   This happens because "*target++" in the original
+    // code would increment the pointer beyond the actual string.
+    void* memptr;
+    const size_t alignment = sysconf(_SC_PAGESIZE);
+    const size_t size = 2 * alignment;
+    ASSERT_EQ(posix_memalign(&memptr, alignment, size), 0);
+    // Fill allocated memory.
+    memset(memptr, 'A', size);
+    // Create a pointer to an "empty" string on the first page.
+    char16_t* const emptyString = (char16_t* const)((char*)memptr + alignment - 4);
+    *emptyString = (char16_t)0;
+    // Protect the second page to show that strstr16() violates that.
+    ASSERT_EQ(mprotect((char*)memptr + alignment, alignment, PROT_NONE), 0);
+    // Test strstr16(): when bug is present a segmentation fault is raised.
+    ASSERT_EQ(strstr16((char16_t*)memptr, emptyString), (char16_t*)memptr)
+        << "should not read beyond the first char16_t.";
+    // Reset protection of the second page
+    ASSERT_EQ(mprotect((char*)memptr + alignment, alignment, PROT_READ | PROT_WRITE), 0);
+    // Free allocated memory.
+    free(memptr);
+}
+
 TEST_F(UnicodeTest, strstr16SameString) {
     const char16_t* result = strstr16(kSearchString, kSearchString);
     EXPECT_EQ(kSearchString, result)
diff --git a/libutils/tests/Vector_test.cpp b/libutils/tests/Vector_test.cpp
index e074a92..5336c40 100644
--- a/libutils/tests/Vector_test.cpp
+++ b/libutils/tests/Vector_test.cpp
@@ -98,7 +98,7 @@
 
   // Checks that the size calculation (not the capacity calculation) doesn't
   // overflow : the size here will be (1 + SIZE_MAX).
-  EXPECT_DEATH(vector.insertArrayAt(NULL, 0, SIZE_MAX), "new_size overflow");
+  EXPECT_DEATH(vector.insertArrayAt(nullptr, 0, SIZE_MAX), "new_size overflow");
 }
 
 TEST_F(VectorTest, _grow_OverflowCapacityDoubling) {
@@ -106,14 +106,14 @@
 
   // This should fail because the calculated capacity will overflow even though
   // the size of the vector doesn't.
-  EXPECT_DEATH(vector.insertArrayAt(NULL, 0, (SIZE_MAX - 1)), "new_capacity overflow");
+  EXPECT_DEATH(vector.insertArrayAt(nullptr, 0, (SIZE_MAX - 1)), "new_capacity overflow");
 }
 
 TEST_F(VectorTest, _grow_OverflowBufferAlloc) {
   Vector<int> vector;
   // This should fail because the capacity * sizeof(int) overflows, even
   // though the capacity itself doesn't.
-  EXPECT_DEATH(vector.insertArrayAt(NULL, 0, (SIZE_MAX / 2)), "new_alloc_size overflow");
+  EXPECT_DEATH(vector.insertArrayAt(nullptr, 0, (SIZE_MAX / 2)), "new_alloc_size overflow");
 }
 
 TEST_F(VectorTest, editArray_Shared) {
diff --git a/libvndksupport/Android.bp b/libvndksupport/Android.bp
new file mode 100644
index 0000000..e73b366
--- /dev/null
+++ b/libvndksupport/Android.bp
@@ -0,0 +1,19 @@
+subdirs = ["tests"]
+
+cc_library {
+    name: "libvndksupport",
+    srcs: ["linker.c"],
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
+    local_include_dirs: ["include/vndksupport"],
+    export_include_dirs: ["include"],
+    shared_libs: ["liblog"],
+}
+
+llndk_library {
+    name: "libvndksupport",
+    symbol_file: "libvndksupport.map.txt",
+    export_include_dirs: ["include"],
+}
diff --git a/libvndksupport/OWNERS b/libvndksupport/OWNERS
new file mode 100644
index 0000000..c7efc16
--- /dev/null
+++ b/libvndksupport/OWNERS
@@ -0,0 +1,2 @@
+jiyong@google.com
+smoreland@google.com
diff --git a/libvndksupport/include/vndksupport/linker.h b/libvndksupport/include/vndksupport/linker.h
new file mode 100644
index 0000000..f509564
--- /dev/null
+++ b/libvndksupport/include/vndksupport/linker.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2017 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 VNDKSUPPORT_LINKER_H_
+#define VNDKSUPPORT_LINKER_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void* android_load_sphal_library(const char* name, int flag);
+
+int android_unload_sphal_library(void* handle);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // VNDKSUPPORT_LINKER_H_
diff --git a/libvndksupport/libvndksupport.map.txt b/libvndksupport/libvndksupport.map.txt
new file mode 100644
index 0000000..16e38da
--- /dev/null
+++ b/libvndksupport/libvndksupport.map.txt
@@ -0,0 +1,7 @@
+LIBVNDKSUPPORT {
+  global:
+    android_load_sphal_library; # vndk
+    android_unload_sphal_library; # vndk
+  local:
+    *;
+};
diff --git a/libvndksupport/linker.c b/libvndksupport/linker.c
new file mode 100644
index 0000000..bc5620b
--- /dev/null
+++ b/libvndksupport/linker.c
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2017 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 "linker.h"
+
+#include <android/dlext.h>
+#include <dlfcn.h>
+
+#define LOG_TAG "vndksupport"
+#include <log/log.h>
+
+__attribute__((weak)) extern struct android_namespace_t* android_get_exported_namespace(const char*);
+__attribute__((weak)) extern void* android_dlopen_ext(const char*, int, const android_dlextinfo*);
+
+static const char* namespace_name = NULL;
+
+static struct android_namespace_t* get_vendor_namespace() {
+    const char* namespace_names[] = {"sphal", "default", NULL};
+    static struct android_namespace_t* vendor_namespace = NULL;
+    if (vendor_namespace == NULL) {
+        int name_idx = 0;
+        while (namespace_names[name_idx] != NULL) {
+            if (android_get_exported_namespace != NULL) {
+                vendor_namespace = android_get_exported_namespace(namespace_names[name_idx]);
+            }
+            if (vendor_namespace != NULL) {
+                namespace_name = namespace_names[name_idx];
+                break;
+            }
+            name_idx++;
+        }
+    }
+    return vendor_namespace;
+}
+
+void* android_load_sphal_library(const char* name, int flag) {
+    struct android_namespace_t* vendor_namespace = get_vendor_namespace();
+    if (vendor_namespace != NULL) {
+        const android_dlextinfo dlextinfo = {
+            .flags = ANDROID_DLEXT_USE_NAMESPACE, .library_namespace = vendor_namespace,
+        };
+        void* handle = NULL;
+        if (android_dlopen_ext != NULL) {
+            handle = android_dlopen_ext(name, flag, &dlextinfo);
+        }
+        if (!handle) {
+            ALOGE("Could not load %s from %s namespace: %s.", name, namespace_name, dlerror());
+        }
+        return handle;
+    } else {
+        ALOGD("Loading %s from current namespace instead of sphal namespace.", name);
+        return dlopen(name, flag);
+    }
+}
+
+int android_unload_sphal_library(void* handle) {
+    return dlclose(handle);
+}
diff --git a/libvndksupport/tests/Android.bp b/libvndksupport/tests/Android.bp
new file mode 100644
index 0000000..2570cce
--- /dev/null
+++ b/libvndksupport/tests/Android.bp
@@ -0,0 +1,30 @@
+// Copyright (C) 2017 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: "libvndksupport-tests",
+    srcs: [
+        "linker_test.cpp",
+    ],
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
+
+    host_supported: false,
+    shared_libs: [
+        "libvndksupport",
+        "libbase",
+    ],
+}
diff --git a/libvndksupport/tests/linker_test.cpp b/libvndksupport/tests/linker_test.cpp
new file mode 100644
index 0000000..7ce27d4
--- /dev/null
+++ b/libvndksupport/tests/linker_test.cpp
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2017 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 <gtest/gtest.h>
+
+#include <android-base/strings.h>
+#include <dirent.h>
+#include <dlfcn.h>
+#include <vndksupport/linker.h>
+#include <string>
+
+// Since the test executable will be in /data and ld.config.txt does not
+// configure sphal namespace for executables in /data, the call to
+// android_load_sphal_library will always fallback to the plain dlopen from the
+// default namespace.
+
+// Let's use libEGL_<chipset>.so as a SP-HAL in test
+static std::string find_sphal_lib() {
+    const char* path =
+#if defined(__LP64__)
+        "/vendor/lib64/egl";
+#else
+        "/vendor/lib/egl";
+#endif
+    std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(path), closedir);
+
+    dirent* dp;
+    while ((dp = readdir(dir.get())) != nullptr) {
+        std::string name = dp->d_name;
+        if (android::base::StartsWith(name, "libEGL_")) {
+            return std::string(path) + "/" + name;
+        }
+    }
+    return "";
+}
+
+TEST(linker, load_existing_lib) {
+    std::string name = find_sphal_lib();
+    ASSERT_NE("", name);
+    void* handle = android_load_sphal_library(name.c_str(), RTLD_NOW | RTLD_LOCAL);
+    ASSERT_NE(nullptr, handle);
+    android_unload_sphal_library(handle);
+}
+
+TEST(linker, load_nonexisting_lib) {
+    void* handle = android_load_sphal_library("libNeverUseThisName.so", RTLD_NOW | RTLD_LOCAL);
+    ASSERT_EQ(nullptr, handle);
+}
diff --git a/libziparchive/Android.bp b/libziparchive/Android.bp
index 333835c..2606aa9 100644
--- a/libziparchive/Android.bp
+++ b/libziparchive/Android.bp
@@ -58,6 +58,11 @@
     name: "libziparchive",
     host_supported: true,
     vendor_available: true,
+    recovery_available: true,
+    vndk: {
+        enabled: true,
+    },
+    double_loadable: true,
 
     defaults: [
         "libziparchive_defaults",
@@ -66,11 +71,11 @@
     shared_libs: [
         "liblog",
         "libbase",
+        "libz",
     ],
     target: {
         android: {
             shared_libs: [
-                "libz",
                 "libutils",
             ],
         },
@@ -78,40 +83,21 @@
             static_libs: ["libutils"],
         },
         linux_bionic: {
-            static_libs: ["libz"],
             enabled: true,
         },
-        linux: {
-            shared_libs: ["libz-host"],
-        },
-        darwin: {
-            shared_libs: ["libz-host"],
-        },
-        windows: {
-            shared_libs: ["libz-host"],
-        },
     },
 }
 
-// Also provide libziparchive-host until everything is switched over to using libziparchive
-cc_library {
-    name: "libziparchive-host",
-    host_supported: true,
-    device_supported: false,
-    defaults: [
-        "libziparchive_defaults",
-        "libziparchive_flags",
-    ],
-    shared_libs: ["libz-host"],
-    static_libs: ["libutils"],
-}
-
 // Tests.
 cc_test {
     name: "ziparchive-tests",
     host_supported: true,
     defaults: ["libziparchive_flags"],
 
+    data: [
+        "testdata/**/*",
+    ],
+
     srcs: [
         "entry_name_utils_test.cc",
         "zip_archive_test.cc",
diff --git a/libziparchive/OWNERS b/libziparchive/OWNERS
new file mode 100644
index 0000000..fcc567a
--- /dev/null
+++ b/libziparchive/OWNERS
@@ -0,0 +1 @@
+narayan@google.com
diff --git a/libziparchive/include/ziparchive/zip_archive.h b/libziparchive/include/ziparchive/zip_archive.h
index 73ae68d..018b1a9 100644
--- a/libziparchive/include/ziparchive/zip_archive.h
+++ b/libziparchive/include/ziparchive/zip_archive.h
@@ -230,4 +230,47 @@
                                 ProcessZipEntryFunction func, void* cookie);
 #endif
 
+namespace zip_archive {
+
+class Writer {
+ public:
+  virtual bool Append(uint8_t* buf, size_t buf_size) = 0;
+  virtual ~Writer();
+
+ protected:
+  Writer() = default;
+
+ private:
+  Writer(const Writer&) = delete;
+  void operator=(const Writer&) = delete;
+};
+
+class Reader {
+ public:
+  virtual bool ReadAtOffset(uint8_t* buf, size_t len, uint32_t offset) const = 0;
+  virtual ~Reader();
+
+ protected:
+  Reader() = default;
+
+ private:
+  Reader(const Reader&) = delete;
+  void operator=(const Reader&) = delete;
+};
+
+/*
+ * Inflates the first |compressed_length| bytes of |reader| to a given |writer|.
+ * |crc_out| is set to the CRC32 checksum of the uncompressed data.
+ *
+ * Returns 0 on success and negative values on failure, for example if |reader|
+ * cannot supply the right amount of data, or if the number of bytes written to
+ * data does not match |uncompressed_length|.
+ *
+ * If |crc_out| is not nullptr, it is set to the crc32 checksum of the
+ * uncompressed data.
+ */
+int32_t Inflate(const Reader& reader, const uint32_t compressed_length,
+                const uint32_t uncompressed_length, Writer* writer, uint64_t* crc_out);
+}  // namespace zip_archive
+
 #endif  // LIBZIPARCHIVE_ZIPARCHIVE_H_
diff --git a/libziparchive/include/ziparchive/zip_archive_stream_entry.h b/libziparchive/include/ziparchive/zip_archive_stream_entry.h
index a40b799..b4766f8 100644
--- a/libziparchive/include/ziparchive/zip_archive_stream_entry.h
+++ b/libziparchive/include/ziparchive/zip_archive_stream_entry.h
@@ -40,7 +40,8 @@
 
   ZipArchiveHandle handle_;
 
-  uint32_t crc32_;
+  off64_t offset_ = 0;
+  uint32_t crc32_ = 0u;
 };
 
 #endif  // LIBZIPARCHIVE_ZIPARCHIVESTREAMENTRY_H_
diff --git a/libziparchive/zip_archive.cc b/libziparchive/zip_archive.cc
index 17c268b..9536fc7 100644
--- a/libziparchive/zip_archive.cc
+++ b/libziparchive/zip_archive.cc
@@ -33,6 +33,10 @@
 #include <memory>
 #include <vector>
 
+#if defined(__BIONIC__)
+#include <android/fdsan.h>
+#endif
+
 #include <android-base/file.h>
 #include <android-base/logging.h>
 #include <android-base/macros.h>  // TEMP_FAILURE_RETRY may or may not be in unistd
@@ -100,6 +104,11 @@
 }
 
 static uint32_t ComputeHash(const ZipString& name) {
+#if !defined(_WIN32)
+  return std::hash<std::string_view>{}(
+      std::string_view(reinterpret_cast<const char*>(name.name), name.name_length));
+#else
+  // Remove this code path once the windows compiler knows how to compile the above statement.
   uint32_t hash = 0;
   uint16_t len = name.name_length;
   const uint8_t* str = name.name;
@@ -109,6 +118,7 @@
   }
 
   return hash;
+#endif
 }
 
 /*
@@ -159,6 +169,44 @@
   return 0;
 }
 
+ZipArchive::ZipArchive(const int fd, bool assume_ownership)
+    : mapped_zip(fd),
+      close_file(assume_ownership),
+      directory_offset(0),
+      central_directory(),
+      directory_map(new android::FileMap()),
+      num_entries(0),
+      hash_table_size(0),
+      hash_table(nullptr) {
+#if defined(__BIONIC__)
+  if (assume_ownership) {
+    android_fdsan_exchange_owner_tag(fd, 0, reinterpret_cast<uint64_t>(this));
+  }
+#endif
+}
+
+ZipArchive::ZipArchive(void* address, size_t length)
+    : mapped_zip(address, length),
+      close_file(false),
+      directory_offset(0),
+      central_directory(),
+      directory_map(new android::FileMap()),
+      num_entries(0),
+      hash_table_size(0),
+      hash_table(nullptr) {}
+
+ZipArchive::~ZipArchive() {
+  if (close_file && mapped_zip.GetFileDescriptor() >= 0) {
+#if defined(__BIONIC__)
+    android_fdsan_close_with_tag(mapped_zip.GetFileDescriptor(), reinterpret_cast<uint64_t>(this));
+#else
+    close(mapped_zip.GetFileDescriptor());
+#endif
+  }
+
+  free(hash_table);
+}
+
 static int32_t MapCentralDirectory0(const char* debug_file_name, ZipArchive* archive,
                                     off64_t file_length, off64_t read_amount, uint8_t* scan_buffer) {
   const off64_t search_start = file_length - read_amount;
@@ -218,7 +266,9 @@
     return kInvalidOffset;
   }
   if (eocd->num_records == 0) {
+#if defined(__ANDROID__)
     ALOGW("Zip: empty archive?");
+#endif
     return kEmptyArchive;
   }
 
@@ -379,6 +429,22 @@
       return -1;
     }
   }
+
+  uint32_t lfh_start_bytes;
+  if (!archive->mapped_zip.ReadAtOffset(reinterpret_cast<uint8_t*>(&lfh_start_bytes),
+                                        sizeof(uint32_t), 0)) {
+    ALOGW("Zip: Unable to read header for entry at offset == 0.");
+    return -1;
+  }
+
+  if (lfh_start_bytes != LocalFileHeader::kSignature) {
+    ALOGW("Zip: Entry at offset zero has invalid LFH signature %" PRIx32, lfh_start_bytes);
+#if defined(__ANDROID__)
+    android_errorWriteLog(0x534e4554, "64211847");
+#endif
+    return -1;
+  }
+
   ALOGV("+++ zip good scan %" PRIu16 " entries", num_entries);
 
   return 0;
@@ -435,13 +501,20 @@
 
 static int32_t ValidateDataDescriptor(MappedZipFile& mapped_zip, ZipEntry* entry) {
   uint8_t ddBuf[sizeof(DataDescriptor) + sizeof(DataDescriptor::kOptSignature)];
-  if (!mapped_zip.ReadData(ddBuf, sizeof(ddBuf))) {
+  off64_t offset = entry->offset;
+  if (entry->method != kCompressStored) {
+    offset += entry->compressed_length;
+  } else {
+    offset += entry->uncompressed_length;
+  }
+
+  if (!mapped_zip.ReadAtOffset(ddBuf, sizeof(ddBuf), offset)) {
     return kIoError;
   }
 
   const uint32_t ddSignature = *(reinterpret_cast<const uint32_t*>(ddBuf));
-  const uint16_t offset = (ddSignature == DataDescriptor::kOptSignature) ? 4 : 0;
-  const DataDescriptor* descriptor = reinterpret_cast<const DataDescriptor*>(ddBuf + offset);
+  const uint16_t ddOffset = (ddSignature == DataDescriptor::kOptSignature) ? 4 : 0;
+  const DataDescriptor* descriptor = reinterpret_cast<const DataDescriptor*>(ddBuf + ddOffset);
 
   // Validate that the values in the data descriptor match those in the central
   // directory.
@@ -718,22 +791,10 @@
   return kIterationEnd;
 }
 
-class Writer {
- public:
-  virtual bool Append(uint8_t* buf, size_t buf_size) = 0;
-  virtual ~Writer() {}
-
- protected:
-  Writer() = default;
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(Writer);
-};
-
 // A Writer that writes data to a fixed size memory region.
 // The size of the memory region must be equal to the total size of
 // the data appended to it.
-class MemoryWriter : public Writer {
+class MemoryWriter : public zip_archive::Writer {
  public:
   MemoryWriter(uint8_t* buf, size_t size) : Writer(), buf_(buf), size_(size), bytes_written_(0) {}
 
@@ -757,7 +818,7 @@
 
 // A Writer that appends data to a file |fd| at its current position.
 // The file will be truncated to the end of the written data.
-class FileWriter : public Writer {
+class FileWriter : public zip_archive::Writer {
  public:
   // Creates a FileWriter for |fd| and prepare to write |entry| to it,
   // guaranteeing that the file descriptor is valid and that there's enough
@@ -766,12 +827,12 @@
   // block device).
   //
   // Returns a valid FileWriter on success, |nullptr| if an error occurred.
-  static std::unique_ptr<FileWriter> Create(int fd, const ZipEntry* entry) {
+  static FileWriter Create(int fd, const ZipEntry* entry) {
     const uint32_t declared_length = entry->uncompressed_length;
     const off64_t current_offset = lseek64(fd, 0, SEEK_CUR);
     if (current_offset == -1) {
       ALOGW("Zip: unable to seek to current location on fd %d: %s", fd, strerror(errno));
-      return nullptr;
+      return FileWriter{};
     }
 
     int result = 0;
@@ -788,10 +849,10 @@
       // disk does not have enough space.
       result = TEMP_FAILURE_RETRY(fallocate(fd, 0, current_offset, declared_length));
       if (result == -1 && errno == ENOSPC) {
-        ALOGW("Zip: unable to allocate  %" PRId64 " bytes at offset %" PRId64 " : %s",
+        ALOGW("Zip: unable to allocate %" PRId64 " bytes at offset %" PRId64 ": %s",
               static_cast<int64_t>(declared_length), static_cast<int64_t>(current_offset),
               strerror(errno));
-        return std::unique_ptr<FileWriter>(nullptr);
+        return FileWriter{};
       }
     }
 #endif  // __linux__
@@ -799,7 +860,7 @@
     struct stat sb;
     if (fstat(fd, &sb) == -1) {
       ALOGW("Zip: unable to fstat file: %s", strerror(errno));
-      return std::unique_ptr<FileWriter>(nullptr);
+      return FileWriter{};
     }
 
     // Block device doesn't support ftruncate(2).
@@ -808,13 +869,22 @@
       if (result == -1) {
         ALOGW("Zip: unable to truncate file to %" PRId64 ": %s",
               static_cast<int64_t>(declared_length + current_offset), strerror(errno));
-        return std::unique_ptr<FileWriter>(nullptr);
+        return FileWriter{};
       }
     }
 
-    return std::unique_ptr<FileWriter>(new FileWriter(fd, declared_length));
+    return FileWriter(fd, declared_length);
   }
 
+  FileWriter(FileWriter&& other)
+      : fd_(other.fd_),
+        declared_length_(other.declared_length_),
+        total_bytes_written_(other.total_bytes_written_) {
+    other.fd_ = -1;
+  }
+
+  bool IsValid() const { return fd_ != -1; }
+
   virtual bool Append(uint8_t* buf, size_t buf_size) override {
     if (total_bytes_written_ + buf_size > declared_length_) {
       ALOGW("Zip: Unexpected size " ZD " (declared) vs " ZD " (actual)", declared_length_,
@@ -833,14 +903,30 @@
   }
 
  private:
-  FileWriter(const int fd, const size_t declared_length)
+  explicit FileWriter(const int fd = -1, const size_t declared_length = 0)
       : Writer(), fd_(fd), declared_length_(declared_length), total_bytes_written_(0) {}
 
-  const int fd_;
+  int fd_;
   const size_t declared_length_;
   size_t total_bytes_written_;
 };
 
+class EntryReader : public zip_archive::Reader {
+ public:
+  EntryReader(const MappedZipFile& zip_file, const ZipEntry* entry)
+      : Reader(), zip_file_(zip_file), entry_(entry) {}
+
+  virtual bool ReadAtOffset(uint8_t* buf, size_t len, uint32_t offset) const {
+    return zip_file_.ReadAtOffset(buf, len, entry_->offset + offset);
+  }
+
+  virtual ~EntryReader() {}
+
+ private:
+  const MappedZipFile& zip_file_;
+  const ZipEntry* entry_;
+};
+
 // This method is using libz macros with old-style-casts
 #pragma GCC diagnostic push
 #pragma GCC diagnostic ignored "-Wold-style-cast"
@@ -849,8 +935,14 @@
 }
 #pragma GCC diagnostic pop
 
-static int32_t InflateEntryToWriter(MappedZipFile& mapped_zip, const ZipEntry* entry,
-                                    Writer* writer, uint64_t* crc_out) {
+namespace zip_archive {
+
+// Moved out of line to avoid -Wweak-vtables.
+Reader::~Reader() {}
+Writer::~Writer() {}
+
+int32_t Inflate(const Reader& reader, const uint32_t compressed_length,
+                const uint32_t uncompressed_length, Writer* writer, uint64_t* crc_out) {
   const size_t kBufSize = 32768;
   std::vector<uint8_t> read_buf(kBufSize);
   std::vector<uint8_t> write_buf(kBufSize);
@@ -891,23 +983,24 @@
 
   std::unique_ptr<z_stream, decltype(zstream_deleter)> zstream_guard(&zstream, zstream_deleter);
 
-  const uint32_t uncompressed_length = entry->uncompressed_length;
-
+  const bool compute_crc = (crc_out != nullptr);
   uint64_t crc = 0;
-  uint32_t compressed_length = entry->compressed_length;
+  uint32_t remaining_bytes = compressed_length;
   do {
     /* read as much as we can */
     if (zstream.avail_in == 0) {
-      const size_t getSize = (compressed_length > kBufSize) ? kBufSize : compressed_length;
-      if (!mapped_zip.ReadData(read_buf.data(), getSize)) {
-        ALOGW("Zip: inflate read failed, getSize = %zu: %s", getSize, strerror(errno));
+      const size_t read_size = (remaining_bytes > kBufSize) ? kBufSize : remaining_bytes;
+      const uint32_t offset = (compressed_length - remaining_bytes);
+      // Make sure to read at offset to ensure concurrent access to the fd.
+      if (!reader.ReadAtOffset(read_buf.data(), read_size, offset)) {
+        ALOGW("Zip: inflate read failed, getSize = %zu: %s", read_size, strerror(errno));
         return kIoError;
       }
 
-      compressed_length -= getSize;
+      remaining_bytes -= read_size;
 
       zstream.next_in = &read_buf[0];
-      zstream.avail_in = getSize;
+      zstream.avail_in = read_size;
     }
 
     /* uncompress the data */
@@ -922,9 +1015,8 @@
     if (zstream.avail_out == 0 || (zerr == Z_STREAM_END && zstream.avail_out != kBufSize)) {
       const size_t write_size = zstream.next_out - &write_buf[0];
       if (!writer->Append(&write_buf[0], write_size)) {
-        // The file might have declared a bogus length.
-        return kInconsistentInformation;
-      } else {
+        return kIoError;
+      } else if (compute_crc) {
         crc = crc32(crc, &write_buf[0], write_size);
       }
 
@@ -941,9 +1033,11 @@
   // it ourselves above because there are no additional gains to be made by
   // having zlib calculate it for us, since they do it by calling crc32 in
   // the same manner that we have above.
-  *crc_out = crc;
+  if (compute_crc) {
+    *crc_out = crc;
+  }
 
-  if (zstream.total_out != uncompressed_length || compressed_length != 0) {
+  if (zstream.total_out != uncompressed_length || remaining_bytes != 0) {
     ALOGW("Zip: size mismatch on inflated file (%lu vs %" PRIu32 ")", zstream.total_out,
           uncompressed_length);
     return kInconsistentInformation;
@@ -951,9 +1045,18 @@
 
   return 0;
 }
+}  // namespace zip_archive
 
-static int32_t CopyEntryToWriter(MappedZipFile& mapped_zip, const ZipEntry* entry, Writer* writer,
-                                 uint64_t* crc_out) {
+static int32_t InflateEntryToWriter(MappedZipFile& mapped_zip, const ZipEntry* entry,
+                                    zip_archive::Writer* writer, uint64_t* crc_out) {
+  const EntryReader reader(mapped_zip, entry);
+
+  return zip_archive::Inflate(reader, entry->compressed_length, entry->uncompressed_length, writer,
+                              crc_out);
+}
+
+static int32_t CopyEntryToWriter(MappedZipFile& mapped_zip, const ZipEntry* entry,
+                                 zip_archive::Writer* writer, uint64_t* crc_out) {
   static const uint32_t kBufSize = 32768;
   std::vector<uint8_t> buf(kBufSize);
 
@@ -962,12 +1065,15 @@
   uint64_t crc = 0;
   while (count < length) {
     uint32_t remaining = length - count;
+    off64_t offset = entry->offset + count;
 
-    // Safe conversion because kBufSize is narrow enough for a 32 bit signed
-    // value.
+    // Safe conversion because kBufSize is narrow enough for a 32 bit signed value.
     const size_t block_size = (remaining > kBufSize) ? kBufSize : remaining;
-    if (!mapped_zip.ReadData(buf.data(), block_size)) {
-      ALOGW("CopyFileToFile: copy read failed, block_size = %zu: %s", block_size, strerror(errno));
+
+    // Make sure to read at offset to ensure concurrent access to the fd.
+    if (!mapped_zip.ReadAtOffset(buf.data(), block_size, offset)) {
+      ALOGW("CopyFileToFile: copy read failed, block_size = %zu, offset = %" PRId64 ": %s",
+            block_size, static_cast<int64_t>(offset), strerror(errno));
       return kIoError;
     }
 
@@ -983,15 +1089,9 @@
   return 0;
 }
 
-int32_t ExtractToWriter(ZipArchiveHandle handle, ZipEntry* entry, Writer* writer) {
+int32_t ExtractToWriter(ZipArchiveHandle handle, ZipEntry* entry, zip_archive::Writer* writer) {
   ZipArchive* archive = reinterpret_cast<ZipArchive*>(handle);
   const uint16_t method = entry->method;
-  off64_t data_offset = entry->offset;
-
-  if (!archive->mapped_zip.SeekToOffset(data_offset)) {
-    ALOGW("Zip: lseek to data at %" PRId64 " failed", static_cast<int64_t>(data_offset));
-    return kIoError;
-  }
 
   // this should default to kUnknownCompressionMethod.
   int32_t return_value = -1;
@@ -1019,17 +1119,17 @@
 }
 
 int32_t ExtractToMemory(ZipArchiveHandle handle, ZipEntry* entry, uint8_t* begin, uint32_t size) {
-  std::unique_ptr<Writer> writer(new MemoryWriter(begin, size));
-  return ExtractToWriter(handle, entry, writer.get());
+  MemoryWriter writer(begin, size);
+  return ExtractToWriter(handle, entry, &writer);
 }
 
 int32_t ExtractEntryToFile(ZipArchiveHandle handle, ZipEntry* entry, int fd) {
-  std::unique_ptr<Writer> writer(FileWriter::Create(fd, entry));
-  if (writer.get() == nullptr) {
+  auto writer = FileWriter::Create(fd, entry);
+  if (!writer.IsValid()) {
     return kIoError;
   }
 
-  return ExtractToWriter(handle, entry, writer.get());
+  return ExtractToWriter(handle, entry, &writer);
 }
 
 const char* ErrorCodeString(int32_t error_code) {
@@ -1057,7 +1157,7 @@
 }
 
 #if !defined(_WIN32)
-class ProcessWriter : public Writer {
+class ProcessWriter : public zip_archive::Writer {
  public:
   ProcessWriter(ProcessZipEntryFunction func, void* cookie)
       : Writer(), proc_function_(func), cookie_(cookie) {}
@@ -1111,52 +1211,21 @@
   }
 }
 
-bool MappedZipFile::SeekToOffset(off64_t offset) {
-  if (has_fd_) {
-    if (lseek64(fd_, offset, SEEK_SET) != offset) {
-      ALOGE("Zip: lseek to %" PRId64 " failed: %s\n", offset, strerror(errno));
-      return false;
-    }
-    return true;
-  } else {
-    if (offset < 0 || offset > static_cast<off64_t>(data_length_)) {
-      ALOGE("Zip: invalid offset: %" PRId64 ", data length: %" PRId64 "\n", offset, data_length_);
-      return false;
-    }
-
-    read_pos_ = offset;
-    return true;
-  }
-}
-
-bool MappedZipFile::ReadData(uint8_t* buffer, size_t read_amount) {
-  if (has_fd_) {
-    if (!android::base::ReadFully(fd_, buffer, read_amount)) {
-      ALOGE("Zip: read from %d failed\n", fd_);
-      return false;
-    }
-  } else {
-    memcpy(buffer, static_cast<uint8_t*>(base_ptr_) + read_pos_, read_amount);
-    read_pos_ += read_amount;
-  }
-  return true;
-}
-
 // Attempts to read |len| bytes into |buf| at offset |off|.
-bool MappedZipFile::ReadAtOffset(uint8_t* buf, size_t len, off64_t off) {
-#if !defined(_WIN32)
+bool MappedZipFile::ReadAtOffset(uint8_t* buf, size_t len, off64_t off) const {
   if (has_fd_) {
-    if (static_cast<size_t>(TEMP_FAILURE_RETRY(pread64(fd_, buf, len, off))) != len) {
+    if (!android::base::ReadFullyAtOffset(fd_, buf, len, off)) {
       ALOGE("Zip: failed to read at offset %" PRId64 "\n", off);
       return false;
     }
-    return true;
+  } else {
+    if (off < 0 || off > static_cast<off64_t>(data_length_)) {
+      ALOGE("Zip: invalid offset: %" PRId64 ", data length: %" PRId64 "\n", off, data_length_);
+      return false;
+    }
+    memcpy(buf, static_cast<uint8_t*>(base_ptr_) + off, len);
   }
-#endif
-  if (!SeekToOffset(off)) {
-    return false;
-  }
-  return ReadData(buf, len);
+  return true;
 }
 
 void CentralDirectory::Initialize(void* map_base_ptr, off64_t cd_start_offset, size_t cd_size) {
diff --git a/libziparchive/zip_archive_benchmark.cpp b/libziparchive/zip_archive_benchmark.cpp
index cd3e164..46aa5a6 100644
--- a/libziparchive/zip_archive_benchmark.cpp
+++ b/libziparchive/zip_archive_benchmark.cpp
@@ -84,4 +84,4 @@
 }
 BENCHMARK(Iterate_all_files);
 
-BENCHMARK_MAIN()
+BENCHMARK_MAIN();
diff --git a/libziparchive/zip_archive_private.h b/libziparchive/zip_archive_private.h
index 840f1af..0a73300 100644
--- a/libziparchive/zip_archive_private.h
+++ b/libziparchive/zip_archive_private.h
@@ -93,14 +93,10 @@
 class MappedZipFile {
  public:
   explicit MappedZipFile(const int fd)
-      : has_fd_(true), fd_(fd), base_ptr_(nullptr), data_length_(0), read_pos_(0) {}
+      : has_fd_(true), fd_(fd), base_ptr_(nullptr), data_length_(0) {}
 
   explicit MappedZipFile(void* address, size_t length)
-      : has_fd_(false),
-        fd_(-1),
-        base_ptr_(address),
-        data_length_(static_cast<off64_t>(length)),
-        read_pos_(0) {}
+      : has_fd_(false), fd_(-1), base_ptr_(address), data_length_(static_cast<off64_t>(length)) {}
 
   bool HasFd() const { return has_fd_; }
 
@@ -110,11 +106,7 @@
 
   off64_t GetFileLength() const;
 
-  bool SeekToOffset(off64_t offset);
-
-  bool ReadData(uint8_t* buffer, size_t read_amount);
-
-  bool ReadAtOffset(uint8_t* buf, size_t len, off64_t off);
+  bool ReadAtOffset(uint8_t* buf, size_t len, off64_t off) const;
 
  private:
   // If has_fd_ is true, fd is valid and we'll read contents of a zip archive
@@ -127,8 +119,6 @@
 
   void* const base_ptr_;
   const off64_t data_length_;
-  // read_pos_ is the offset to the base_ptr_ where we read data from.
-  size_t read_pos_;
 };
 
 class CentralDirectory {
@@ -166,33 +156,9 @@
   uint32_t hash_table_size;
   ZipString* hash_table;
 
-  ZipArchive(const int fd, bool assume_ownership)
-      : mapped_zip(fd),
-        close_file(assume_ownership),
-        directory_offset(0),
-        central_directory(),
-        directory_map(new android::FileMap()),
-        num_entries(0),
-        hash_table_size(0),
-        hash_table(nullptr) {}
-
-  ZipArchive(void* address, size_t length)
-      : mapped_zip(address, length),
-        close_file(false),
-        directory_offset(0),
-        central_directory(),
-        directory_map(new android::FileMap()),
-        num_entries(0),
-        hash_table_size(0),
-        hash_table(nullptr) {}
-
-  ~ZipArchive() {
-    if (close_file && mapped_zip.GetFileDescriptor() >= 0) {
-      close(mapped_zip.GetFileDescriptor());
-    }
-
-    free(hash_table);
-  }
+  ZipArchive(const int fd, bool assume_ownership);
+  ZipArchive(void* address, size_t length);
+  ~ZipArchive();
 
   bool InitializeCentralDirectory(const char* debug_file_name, off64_t cd_start_offset,
                                   size_t cd_size);
diff --git a/libziparchive/zip_archive_stream_entry.cc b/libziparchive/zip_archive_stream_entry.cc
index 50352ef..9ec89b1 100644
--- a/libziparchive/zip_archive_stream_entry.cc
+++ b/libziparchive/zip_archive_stream_entry.cc
@@ -38,13 +38,8 @@
 static constexpr size_t kBufSize = 65535;
 
 bool ZipArchiveStreamEntry::Init(const ZipEntry& entry) {
-  ZipArchive* archive = reinterpret_cast<ZipArchive*>(handle_);
-  off64_t data_offset = entry.offset;
-  if (!archive->mapped_zip.SeekToOffset(data_offset)) {
-    ALOGW("lseek to data at %" PRId64 " failed: %s", data_offset, strerror(errno));
-    return false;
-  }
   crc32_ = entry.crc32;
+  offset_ = entry.offset;
   return true;
 }
 
@@ -61,11 +56,11 @@
  protected:
   bool Init(const ZipEntry& entry) override;
 
-  uint32_t length_;
+  uint32_t length_ = 0u;
 
  private:
   std::vector<uint8_t> data_;
-  uint32_t computed_crc32_;
+  uint32_t computed_crc32_ = 0u;
 };
 
 bool ZipArchiveStreamEntryUncompressed::Init(const ZipEntry& entry) {
@@ -89,7 +84,7 @@
   size_t bytes = (length_ > data_.size()) ? data_.size() : length_;
   ZipArchive* archive = reinterpret_cast<ZipArchive*>(handle_);
   errno = 0;
-  if (!archive->mapped_zip.ReadData(data_.data(), bytes)) {
+  if (!archive->mapped_zip.ReadAtOffset(data_.data(), bytes, offset_)) {
     if (errno != 0) {
       ALOGE("Error reading from archive fd: %s", strerror(errno));
     } else {
@@ -104,6 +99,7 @@
   }
   computed_crc32_ = crc32(computed_crc32_, data_.data(), data_.size());
   length_ -= bytes;
+  offset_ += bytes;
   return &data_;
 }
 
@@ -129,9 +125,9 @@
   z_stream z_stream_;
   std::vector<uint8_t> in_;
   std::vector<uint8_t> out_;
-  uint32_t uncompressed_length_;
-  uint32_t compressed_length_;
-  uint32_t computed_crc32_;
+  uint32_t uncompressed_length_ = 0u;
+  uint32_t compressed_length_ = 0u;
+  uint32_t computed_crc32_ = 0u;
 };
 
 // This method is using libz macros with old-style-casts
@@ -210,7 +206,7 @@
       size_t bytes = (compressed_length_ > in_.size()) ? in_.size() : compressed_length_;
       ZipArchive* archive = reinterpret_cast<ZipArchive*>(handle_);
       errno = 0;
-      if (!archive->mapped_zip.ReadData(in_.data(), bytes)) {
+      if (!archive->mapped_zip.ReadAtOffset(in_.data(), bytes, offset_)) {
         if (errno != 0) {
           ALOGE("Error reading from archive fd: %s", strerror(errno));
         } else {
@@ -220,6 +216,7 @@
       }
 
       compressed_length_ -= bytes;
+      offset_ += bytes;
       z_stream_.next_in = in_.data();
       z_stream_.avail_in = bytes;
     }
diff --git a/libziparchive/zip_archive_test.cc b/libziparchive/zip_archive_test.cc
index dbc14f0..377479f 100644
--- a/libziparchive/zip_archive_test.cc
+++ b/libziparchive/zip_archive_test.cc
@@ -34,7 +34,7 @@
 #include <ziparchive/zip_archive.h>
 #include <ziparchive/zip_archive_stream_entry.h>
 
-static std::string test_data_dir;
+static std::string test_data_dir = android::base::GetExecutableDirectory() + "/testdata";
 
 static const std::string kMissingZip = "missing.zip";
 static const std::string kValidZip = "valid.zip";
@@ -64,11 +64,6 @@
   return OpenArchive(abs_path.c_str(), handle);
 }
 
-static void AssertNameEquals(const std::string& name_str, const ZipString& name) {
-  ASSERT_EQ(name_str.size(), name.name_length);
-  ASSERT_EQ(0, memcmp(name_str.c_str(), name.name, name.name_length));
-}
-
 static void SetZipString(ZipString* zip_str, const std::string& str) {
   zip_str->name = reinterpret_cast<const uint8_t*>(str.c_str());
   zip_str->name_length = str.size();
@@ -117,132 +112,60 @@
   close(fd);
 }
 
-TEST(ziparchive, Iteration) {
+static void AssertIterationOrder(const ZipString* prefix, const ZipString* suffix,
+                                 const std::vector<std::string>& expected_names_sorted) {
   ZipArchiveHandle handle;
   ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
 
   void* iteration_cookie;
-  ASSERT_EQ(0, StartIteration(handle, &iteration_cookie, nullptr, nullptr));
+  ASSERT_EQ(0, StartIteration(handle, &iteration_cookie, prefix, suffix));
 
   ZipEntry data;
+  std::vector<std::string> names;
+
   ZipString name;
-
-  // b/c.txt
-  ASSERT_EQ(0, Next(iteration_cookie, &data, &name));
-  AssertNameEquals("b/c.txt", name);
-
-  // b/d.txt
-  ASSERT_EQ(0, Next(iteration_cookie, &data, &name));
-  AssertNameEquals("b/d.txt", name);
-
-  // a.txt
-  ASSERT_EQ(0, Next(iteration_cookie, &data, &name));
-  AssertNameEquals("a.txt", name);
-
-  // b.txt
-  ASSERT_EQ(0, Next(iteration_cookie, &data, &name));
-  AssertNameEquals("b.txt", name);
-
-  // b/
-  ASSERT_EQ(0, Next(iteration_cookie, &data, &name));
-  AssertNameEquals("b/", name);
+  for (size_t i = 0; i < expected_names_sorted.size(); ++i) {
+    ASSERT_EQ(0, Next(iteration_cookie, &data, &name));
+    names.push_back(std::string(reinterpret_cast<const char*>(name.name), name.name_length));
+  }
 
   // End of iteration.
   ASSERT_EQ(-1, Next(iteration_cookie, &data, &name));
-
   CloseArchive(handle);
+
+  // Assert that the names are as expected.
+  std::sort(names.begin(), names.end());
+  ASSERT_EQ(expected_names_sorted, names);
+}
+
+TEST(ziparchive, Iteration) {
+  static const std::vector<std::string> kExpectedMatchesSorted = {"a.txt", "b.txt", "b/", "b/c.txt",
+                                                                  "b/d.txt"};
+
+  AssertIterationOrder(nullptr, nullptr, kExpectedMatchesSorted);
 }
 
 TEST(ziparchive, IterationWithPrefix) {
-  ZipArchiveHandle handle;
-  ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
-
-  void* iteration_cookie;
   ZipString prefix("b/");
-  ASSERT_EQ(0, StartIteration(handle, &iteration_cookie, &prefix, nullptr));
+  static const std::vector<std::string> kExpectedMatchesSorted = {"b/", "b/c.txt", "b/d.txt"};
 
-  ZipEntry data;
-  ZipString name;
-
-  // b/c.txt
-  ASSERT_EQ(0, Next(iteration_cookie, &data, &name));
-  AssertNameEquals("b/c.txt", name);
-
-  // b/d.txt
-  ASSERT_EQ(0, Next(iteration_cookie, &data, &name));
-  AssertNameEquals("b/d.txt", name);
-
-  // b/
-  ASSERT_EQ(0, Next(iteration_cookie, &data, &name));
-  AssertNameEquals("b/", name);
-
-  // End of iteration.
-  ASSERT_EQ(-1, Next(iteration_cookie, &data, &name));
-
-  CloseArchive(handle);
+  AssertIterationOrder(&prefix, nullptr, kExpectedMatchesSorted);
 }
 
 TEST(ziparchive, IterationWithSuffix) {
-  ZipArchiveHandle handle;
-  ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
-
-  void* iteration_cookie;
   ZipString suffix(".txt");
-  ASSERT_EQ(0, StartIteration(handle, &iteration_cookie, nullptr, &suffix));
+  static const std::vector<std::string> kExpectedMatchesSorted = {"a.txt", "b.txt", "b/c.txt",
+                                                                  "b/d.txt"};
 
-  ZipEntry data;
-  ZipString name;
-
-  // b/c.txt
-  ASSERT_EQ(0, Next(iteration_cookie, &data, &name));
-  AssertNameEquals("b/c.txt", name);
-
-  // b/d.txt
-  ASSERT_EQ(0, Next(iteration_cookie, &data, &name));
-  AssertNameEquals("b/d.txt", name);
-
-  // a.txt
-  ASSERT_EQ(0, Next(iteration_cookie, &data, &name));
-  AssertNameEquals("a.txt", name);
-
-  // b.txt
-  ASSERT_EQ(0, Next(iteration_cookie, &data, &name));
-  AssertNameEquals("b.txt", name);
-
-  // End of iteration.
-  ASSERT_EQ(-1, Next(iteration_cookie, &data, &name));
-
-  CloseArchive(handle);
+  AssertIterationOrder(nullptr, &suffix, kExpectedMatchesSorted);
 }
 
 TEST(ziparchive, IterationWithPrefixAndSuffix) {
-  ZipArchiveHandle handle;
-  ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
-
-  void* iteration_cookie;
   ZipString prefix("b");
   ZipString suffix(".txt");
-  ASSERT_EQ(0, StartIteration(handle, &iteration_cookie, &prefix, &suffix));
+  static const std::vector<std::string> kExpectedMatchesSorted = {"b.txt", "b/c.txt", "b/d.txt"};
 
-  ZipEntry data;
-  ZipString name;
-
-  // b/c.txt
-  ASSERT_EQ(0, Next(iteration_cookie, &data, &name));
-  AssertNameEquals("b/c.txt", name);
-
-  // b/d.txt
-  ASSERT_EQ(0, Next(iteration_cookie, &data, &name));
-  AssertNameEquals("b/d.txt", name);
-
-  // b.txt
-  ASSERT_EQ(0, Next(iteration_cookie, &data, &name));
-  AssertNameEquals("b.txt", name);
-
-  // End of iteration.
-  ASSERT_EQ(-1, Next(iteration_cookie, &data, &name));
-
-  CloseArchive(handle);
+  AssertIterationOrder(&prefix, &suffix, kExpectedMatchesSorted);
 }
 
 TEST(ziparchive, IterationWithBadPrefixAndSuffix) {
@@ -664,10 +587,11 @@
   // an entry whose name is "name" and whose size is 12 (contents =
   // "abdcdefghijk").
   ZipEntry entry;
-  ZipString empty_name;
-  SetZipString(&empty_name, "name");
+  ZipString name;
+  std::string name_str = "name";
+  SetZipString(&name, name_str);
 
-  ASSERT_EQ(0, FindEntry(handle, empty_name, &entry));
+  ASSERT_EQ(0, FindEntry(handle, name, &entry));
   ASSERT_EQ(static_cast<uint32_t>(12), entry.uncompressed_length);
 
   entry_out->resize(12);
@@ -687,7 +611,7 @@
   ASSERT_EQ('k', entry[11]);
 }
 
-TEST(ziparchive, InvalidDataDescriptors) {
+TEST(ziparchive, InvalidDataDescriptors_csize) {
   std::vector<uint8_t> invalid_csize = kDataDescriptorZipFile;
   invalid_csize[kCSizeOffset] = 0xfe;
 
@@ -696,13 +620,15 @@
   ExtractEntryToMemory(invalid_csize, &entry, &error_code);
 
   ASSERT_EQ(kInconsistentInformation, error_code);
+}
 
+TEST(ziparchive, InvalidDataDescriptors_size) {
   std::vector<uint8_t> invalid_size = kDataDescriptorZipFile;
-  invalid_csize[kSizeOffset] = 0xfe;
+  invalid_size[kSizeOffset] = 0xfe;
 
-  error_code = 0;
-  entry.clear();
-  ExtractEntryToMemory(invalid_csize, &entry, &error_code);
+  std::vector<uint8_t> entry;
+  int32_t error_code = 0;
+  ExtractEntryToMemory(invalid_size, &entry, &error_code);
 
   ASSERT_EQ(kInconsistentInformation, error_code);
 }
@@ -717,40 +643,89 @@
   ASSERT_STREQ("I/O error", ErrorCodeString(kIoError));
 }
 
-int main(int argc, char** argv) {
-  ::testing::InitGoogleTest(&argc, argv);
+class VectorReader : public zip_archive::Reader {
+ public:
+  VectorReader(const std::vector<uint8_t>& input) : Reader(), input_(input) {}
 
-  static struct option options[] = {{"test_data_dir", required_argument, nullptr, 't'},
-                                    {nullptr, 0, nullptr, 0}};
-
-  while (true) {
-    int option_index;
-    const int c = getopt_long_only(argc, argv, "", options, &option_index);
-    if (c == -1) {
-      break;
+  bool ReadAtOffset(uint8_t* buf, size_t len, uint32_t offset) const {
+    if ((offset + len) < input_.size()) {
+      return false;
     }
 
-    if (c == 't') {
-      test_data_dir = optarg;
-    }
+    memcpy(buf, &input_[offset], len);
+    return true;
   }
 
-  if (test_data_dir.size() == 0) {
-    printf("Test data flag (--test_data_dir) required\n\n");
-    return -1;
+ private:
+  const std::vector<uint8_t>& input_;
+};
+
+class VectorWriter : public zip_archive::Writer {
+ public:
+  VectorWriter() : Writer() {}
+
+  bool Append(uint8_t* buf, size_t size) {
+    output_.insert(output_.end(), buf, buf + size);
+    return true;
   }
 
-  if (test_data_dir[0] != '/') {
-    std::vector<char> cwd_buffer(1024);
-    const char* cwd = getcwd(cwd_buffer.data(), cwd_buffer.size() - 1);
-    if (cwd == nullptr) {
-      printf("Cannot get current working directory, use an absolute path instead, was %s\n\n",
-             test_data_dir.c_str());
-      return -2;
-    }
-    test_data_dir = '/' + test_data_dir;
-    test_data_dir = cwd + test_data_dir;
+  std::vector<uint8_t>& GetOutput() { return output_; }
+
+ private:
+  std::vector<uint8_t> output_;
+};
+
+class BadReader : public zip_archive::Reader {
+ public:
+  BadReader() : Reader() {}
+
+  bool ReadAtOffset(uint8_t*, size_t, uint32_t) const { return false; }
+};
+
+class BadWriter : public zip_archive::Writer {
+ public:
+  BadWriter() : Writer() {}
+
+  bool Append(uint8_t*, size_t) { return false; }
+};
+
+TEST(ziparchive, Inflate) {
+  const uint32_t compressed_length = kATxtContentsCompressed.size();
+  const uint32_t uncompressed_length = kATxtContents.size();
+
+  const VectorReader reader(kATxtContentsCompressed);
+  {
+    VectorWriter writer;
+    uint64_t crc_out = 0;
+
+    int32_t ret =
+        zip_archive::Inflate(reader, compressed_length, uncompressed_length, &writer, &crc_out);
+    ASSERT_EQ(0, ret);
+    ASSERT_EQ(kATxtContents, writer.GetOutput());
+    ASSERT_EQ(0x950821C5u, crc_out);
   }
 
-  return RUN_ALL_TESTS();
+  {
+    VectorWriter writer;
+    int32_t ret =
+        zip_archive::Inflate(reader, compressed_length, uncompressed_length, &writer, nullptr);
+    ASSERT_EQ(0, ret);
+    ASSERT_EQ(kATxtContents, writer.GetOutput());
+  }
+
+  {
+    BadWriter writer;
+    int32_t ret =
+        zip_archive::Inflate(reader, compressed_length, uncompressed_length, &writer, nullptr);
+    ASSERT_EQ(kIoError, ret);
+  }
+
+  {
+    BadReader reader;
+    VectorWriter writer;
+    int32_t ret =
+        zip_archive::Inflate(reader, compressed_length, uncompressed_length, &writer, nullptr);
+    ASSERT_EQ(kIoError, ret);
+    ASSERT_EQ(0u, writer.GetOutput().size());
+  }
 }
diff --git a/llkd/Android.bp b/llkd/Android.bp
new file mode 100644
index 0000000..a6edd26
--- /dev/null
+++ b/llkd/Android.bp
@@ -0,0 +1,42 @@
+cc_library_headers {
+    name: "llkd_headers",
+
+    export_include_dirs: ["include"],
+}
+
+cc_library_static {
+    name: "libllkd",
+
+    srcs: [
+        "libllkd.cpp",
+    ],
+
+    shared_libs: [
+        "libbase",
+        "libcutils",
+        "liblog",
+    ],
+
+    export_include_dirs: ["include"],
+
+    cflags: ["-Werror"],
+}
+
+cc_binary {
+    name: "llkd",
+
+    srcs: [
+        "llkd.cpp",
+    ],
+    shared_libs: [
+        "libbase",
+        "libcutils",
+        "liblog",
+    ],
+    static_libs: [
+        "libllkd",
+    ],
+    cflags: ["-Werror"],
+
+    init_rc: ["llkd.rc"],
+}
diff --git a/llkd/OWNERS b/llkd/OWNERS
new file mode 100644
index 0000000..b6af537
--- /dev/null
+++ b/llkd/OWNERS
@@ -0,0 +1,2 @@
+salyzyn@google.com
+surenb@google.com
diff --git a/llkd/README.md b/llkd/README.md
new file mode 100644
index 0000000..b2ba2a2
--- /dev/null
+++ b/llkd/README.md
@@ -0,0 +1,117 @@
+Android Live-LocK Daemon
+========================
+
+Introduction
+------------
+
+Android Live-LocK Daemon (llkd) is used to catch kernel deadlocks and mitigate.
+
+Code is structured to allow integration into another service as either as part
+of the main loop, or spun off as a thread should that be necessary.  A default
+standalone implementation is provided by llkd component.
+
+The 'C' interface from libllkd component is thus:
+
+    #include "llkd.h"
+    bool llkInit(const char* threadname) /* return true if enabled */
+    unsigned llkCheckMillseconds(void)   /* ms to sleep for next check */
+
+If a threadname is provided, a thread will be automatically spawned, otherwise
+caller must call llkCheckMilliseconds in its main loop.  Function will return
+the period of time before the next expected call to this handler.
+
+Operations
+----------
+
+If a thread is in D or Z state with no forward progress for longer than
+ro.llk.timeout_ms, or ro.llk.[D|Z].timeout_ms, kill the process or parent
+process respectively.  If another scan shows the same process continues to
+exist, then have a confirmed live-lock condition and need to panic.  Panic
+the kernel in a manner to provide the greatest bugreporting details as to the
+condition.  Add a alarm self watchdog should llkd ever get locked up that is
+double the expected time to flow through the mainloop.  Sampling is every
+ro.llk_sample_ms.
+
+Default will not monitor init, or [kthreadd] and all that [kthreadd] spawns.
+This reduces the effectiveness of llkd by limiting its coverage.  If there is
+value in covering [kthreadd] spawned threads, the requirement will be that
+the drivers not remain in a persistent 'D' state, or that they have mechanisms
+to recover the thread should it be killed externally (this is good driver
+coding hygiene, a common request to add such to publicly reviewed kernel.org
+maintained drivers).  For instance use wait_event_interruptible() instead of
+wait_event().  The blacklists can be adjusted accordingly if these
+conditions are met to cover kernel components.
+
+An accompanying gTest set have been added, and will setup a persistent D or Z
+process, with and without forward progress, but not in a live-lock state
+because that would require a buggy kernel, or a module or kernel modification
+to stimulate.  The test will check that llkd will mitigate first by killing
+the appropriate process.  D state is setup by vfork() waiting for exec() in
+child process.  Z state is setup by fork() and an un-waited for child process.
+Should be noted that both of these conditions should never happen on Android
+on purpose, and llkd effectively sweeps up processes that create these
+conditions.  If the test can, it will reconfigure llkd to expedite the test
+duration by adjusting the ro.llk.* Android properties.  Tests run the D state
+with some scheduling progress to ensure that ABA checking prevents false
+triggers. If 100% reliable ABA on platform, then ro.llk.killtest can be
+set to false; however this will result in some of the unit tests to panic
+kernel instead of deal with more graceful kill operation.
+
+Android Properties
+------------------
+
+Android Properties llkd respond to (<prop>_ms parms are in milliseconds):
+
+#### ro.config.low_ram
+default false, if true do not sysrq t (dump all threads).
+
+#### ro.llk.enable
+default false, allow live-lock daemon to be enabled.
+
+#### llk.enable
+default ro.llk.enable, and evaluated for eng.
+
+#### ro.khungtask.enable
+default false, allow [khungtask] daemon to be enabled.
+
+#### khungtask.enable
+default ro.khungtask.enable and evaluated for eng.
+
+#### ro.llk.mlockall
+default false, enable call to mlockall().
+
+#### ro.khungtask.timeout
+default value 12 minutes, [khungtask] maximum timelimit.
+
+#### ro.llk.timeout_ms
+default 10 minutes, D or Z maximum timelimit, double this value and it sets
+the alarm watchdog for llkd.
+
+#### ro.llk.D.timeout_ms
+default ro.llk.timeout_ms, D maximum timelimit.
+
+#### ro.llk.Z.timeout_ms
+default ro.llk.timeout_ms, Z maximum timelimit.
+
+#### ro.llk.check_ms
+default 2 minutes samples of threads for D or Z.
+
+#### ro.llk.blacklist.process
+default 0,1,2 (kernel, init and [kthreadd]) plus process names
+init,[kthreadd],[khungtaskd],lmkd,lmkd.llkd,llkd,watchdogd,
+[watchdogd],[watchdogd/0],...,[watchdogd/<get_nprocs-1>].
+
+#### ro.llk.blacklist.parent
+default 0,2 (kernel and [kthreadd]).
+
+#### ro.llk.blacklist.uid
+default <empty>, comma separated list of uid numbers or names.
+
+Architectural Concerns
+----------------------
+
+- Create kernel module and associated gTest to actually test panic.
+- Create gTest to test out blacklist (ro.llk.blacklist.<properties> generally
+  not be inputs).  Could require more test-only interfaces to libllkd.
+- Speed up gTest using something else than ro.llk.<properties>, which should
+  not be inputs.
diff --git a/llkd/include/llkd.h b/llkd/include/llkd.h
new file mode 100644
index 0000000..e3ae4bb
--- /dev/null
+++ b/llkd/include/llkd.h
@@ -0,0 +1,80 @@
+/*
+ * 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 _LLKD_H_
+#define _LLKD_H_
+
+#ifndef LOG_TAG
+#define LOG_TAG "livelock"
+#endif
+
+#include <stdbool.h>
+#include <sys/cdefs.h>
+
+__BEGIN_DECLS
+
+bool llkInit(const char* threadname); /* threadname NULL, not spawned */
+unsigned llkCheckMilliseconds(void);
+
+/* clang-format off */
+#define LLK_ENABLE_WRITEABLE_PROPERTY  "llk.enable"
+#define LLK_ENABLE_PROPERTY            "ro." LLK_ENABLE_WRITEABLE_PROPERTY
+#define LLK_ENABLE_DEFAULT             false /* "eng" and userdebug true */
+#define KHT_ENABLE_WRITEABLE_PROPERTY  "khungtask.enable"
+#define KHT_ENABLE_PROPERTY            "ro." KHT_ENABLE_WRITEABLE_PROPERTY
+#define LLK_MLOCKALL_PROPERTY          "ro.llk.mlockall"
+#define LLK_MLOCKALL_DEFAULT           true
+#define LLK_KILLTEST_PROPERTY          "ro.llk.killtest"
+#define LLK_KILLTEST_DEFAULT           true
+#define LLK_TIMEOUT_MS_PROPERTY        "ro.llk.timeout_ms"
+#define KHT_TIMEOUT_PROPERTY           "ro.khungtask.timeout"
+#define LLK_D_TIMEOUT_MS_PROPERTY      "ro.llk.D.timeout_ms"
+#define LLK_Z_TIMEOUT_MS_PROPERTY      "ro.llk.Z.timeout_ms"
+#define LLK_CHECK_MS_PROPERTY          "ro.llk.check_ms"
+/* LLK_CHECK_MS_DEFAULT = actual timeout_ms / LLK_CHECKS_PER_TIMEOUT_DEFAULT */
+#define LLK_CHECKS_PER_TIMEOUT_DEFAULT 5
+#define LLK_BLACKLIST_PROCESS_PROPERTY "ro.llk.blacklist.process"
+#define LLK_BLACKLIST_PROCESS_DEFAULT  \
+    "0,1,2,init,[kthreadd],[khungtaskd],lmkd,lmkd.llkd,llkd,watchdogd,[watchdogd],[watchdogd/0]"
+#define LLK_BLACKLIST_PARENT_PROPERTY  "ro.llk.blacklist.parent"
+#define LLK_BLACKLIST_PARENT_DEFAULT   "0,2,[kthreadd]"
+#define LLK_BLACKLIST_UID_PROPERTY     "ro.llk.blacklist.uid"
+#define LLK_BLACKLIST_UID_DEFAULT      ""
+/* clang-format on */
+
+__END_DECLS
+
+#ifdef __cplusplus
+extern "C++" { /* In case this included wrapped with __BEGIN_DECLS */
+
+#include <chrono>
+
+__BEGIN_DECLS
+/* C++ code allowed to not specify threadname argument for this C linkage */
+bool llkInit(const char* threadname = nullptr);
+__END_DECLS
+std::chrono::milliseconds llkCheck(bool checkRunning = false);
+
+/* clang-format off */
+#define LLK_TIMEOUT_MS_DEFAULT  std::chrono::duration_cast<milliseconds>(std::chrono::minutes(10))
+#define LLK_TIMEOUT_MS_MINIMUM  std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::seconds(10))
+#define LLK_CHECK_MS_MINIMUM    std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::seconds(1))
+/* clang-format on */
+
+} /* extern "C++" */
+#endif /* __cplusplus */
+
+#endif /* _LLKD_H_ */
diff --git a/llkd/libllkd.cpp b/llkd/libllkd.cpp
new file mode 100644
index 0000000..f357cc2
--- /dev/null
+++ b/llkd/libllkd.cpp
@@ -0,0 +1,1168 @@
+/*
+ * 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 "llkd.h"
+
+#include <ctype.h>
+#include <dirent.h>  // opendir() and readdir()
+#include <errno.h>
+#include <fcntl.h>
+#include <pthread.h>
+#include <pwd.h>  // getpwuid()
+#include <signal.h>
+#include <stdint.h>
+#include <sys/cdefs.h>  // ___STRING, __predict_true() and _predict_false()
+#include <sys/mman.h>   // mlockall()
+#include <sys/prctl.h>
+#include <sys/stat.h>     // lstat()
+#include <sys/syscall.h>  // __NR_getdents64
+#include <sys/sysinfo.h>  // get_nprocs_conf()
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <chrono>
+#include <ios>
+#include <sstream>
+#include <string>
+#include <unordered_map>
+#include <unordered_set>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/parseint.h>
+#include <android-base/properties.h>
+#include <android-base/strings.h>
+#include <cutils/android_get_control_file.h>
+#include <log/log_main.h>
+
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof(*(x)))
+
+#define TASK_COMM_LEN 16  // internal kernel, not uapi, from .../linux/include/linux/sched.h
+
+using namespace std::chrono_literals;
+using namespace std::chrono;
+
+namespace {
+
+constexpr pid_t kernelPid = 0;
+constexpr pid_t initPid = 1;
+constexpr pid_t kthreaddPid = 2;
+
+constexpr char procdir[] = "/proc/";
+
+// Configuration
+milliseconds llkUpdate;                              // last check ms signature
+milliseconds llkCycle;                               // ms to next thread check
+bool llkEnable = LLK_ENABLE_DEFAULT;                 // llk daemon enabled
+bool llkRunning = false;                             // thread is running
+bool llkMlockall = LLK_MLOCKALL_DEFAULT;             // run mlocked
+bool llkTestWithKill = LLK_KILLTEST_DEFAULT;         // issue test kills
+milliseconds llkTimeoutMs = LLK_TIMEOUT_MS_DEFAULT;  // default timeout
+enum { llkStateD, llkStateZ, llkNumStates };         // state indexes
+milliseconds llkStateTimeoutMs[llkNumStates];        // timeout override for each detection state
+milliseconds llkCheckMs;                             // checking interval to inspect any
+                                                     // persistent live-locked states
+bool llkLowRam;                                      // ro.config.low_ram
+bool khtEnable = LLK_ENABLE_DEFAULT;                 // [khungtaskd] panic
+// [khungtaskd] should have a timeout beyond the granularity of llkTimeoutMs.
+// Provides a wide angle of margin b/c khtTimeout is also its granularity.
+seconds khtTimeout = duration_cast<seconds>(llkTimeoutMs * (1 + LLK_CHECKS_PER_TIMEOUT_DEFAULT) /
+                                            LLK_CHECKS_PER_TIMEOUT_DEFAULT);
+
+// Blacklist variables, initialized with comma separated lists of high false
+// positive and/or dangerous references, e.g. without self restart, for pid,
+// ppid, name and uid:
+
+// list of pids, or tids or names to skip. kernel pid (0), init pid (1),
+// [kthreadd] pid (2), ourselves, "init", "[kthreadd]", "lmkd", "llkd" or
+// combinations of watchdogd in kernel and user space.
+std::unordered_set<std::string> llkBlacklistProcess;
+// list of parent pids, comm or cmdline names to skip. default:
+// kernel pid (0), [kthreadd] (2), or ourselves, enforced and implied
+std::unordered_set<std::string> llkBlacklistParent;
+// list of uids, and uid names, to skip, default nothing
+std::unordered_set<std::string> llkBlacklistUid;
+
+class dir {
+  public:
+    enum level { proc, task, numLevels };
+
+  private:
+    int fd;
+    size_t available_bytes;
+    dirent* next;
+    // each directory level picked to be just north of 4K in size
+    static constexpr size_t buffEntries = 15;
+    static dirent buff[numLevels][buffEntries];
+
+    bool fill(enum level index) {
+        if (index >= numLevels) return false;
+        if (available_bytes != 0) return true;
+        if (__predict_false(fd < 0)) return false;
+        // getdents64 has no libc wrapper
+        auto rc = TEMP_FAILURE_RETRY(syscall(__NR_getdents64, fd, buff[index], sizeof(buff[0]), 0));
+        if (rc <= 0) return false;
+        available_bytes = rc;
+        next = buff[index];
+        return true;
+    }
+
+  public:
+    dir() : fd(-1), available_bytes(0), next(nullptr) {}
+
+    explicit dir(const char* directory)
+        : fd(__predict_true(directory != nullptr)
+                 ? ::open(directory, O_CLOEXEC | O_DIRECTORY | O_RDONLY)
+                 : -1),
+          available_bytes(0),
+          next(nullptr) {}
+
+    explicit dir(const std::string&& directory)
+        : fd(::open(directory.c_str(), O_CLOEXEC | O_DIRECTORY | O_RDONLY)),
+          available_bytes(0),
+          next(nullptr) {}
+
+    explicit dir(const std::string& directory)
+        : fd(::open(directory.c_str(), O_CLOEXEC | O_DIRECTORY | O_RDONLY)),
+          available_bytes(0),
+          next(nullptr) {}
+
+    // Don't need any copy or move constructors.
+    explicit dir(const dir& c) = delete;
+    explicit dir(dir& c) = delete;
+    explicit dir(dir&& c) = delete;
+
+    ~dir() {
+        if (fd >= 0) {
+            ::close(fd);
+        }
+    }
+
+    operator bool() const { return fd >= 0; }
+
+    void reset(void) {
+        if (fd >= 0) {
+            ::close(fd);
+            fd = -1;
+            available_bytes = 0;
+            next = nullptr;
+        }
+    }
+
+    dir& reset(const char* directory) {
+        reset();
+        // available_bytes will _always_ be zero here as its value is
+        // intimately tied to fd < 0 or not.
+        fd = ::open(directory, O_CLOEXEC | O_DIRECTORY | O_RDONLY);
+        return *this;
+    }
+
+    void rewind(void) {
+        if (fd >= 0) {
+            ::lseek(fd, off_t(0), SEEK_SET);
+            available_bytes = 0;
+            next = nullptr;
+        }
+    }
+
+    dirent* read(enum level index = proc, dirent* def = nullptr) {
+        if (!fill(index)) return def;
+        auto ret = next;
+        available_bytes -= next->d_reclen;
+        next = reinterpret_cast<dirent*>(reinterpret_cast<char*>(next) + next->d_reclen);
+        return ret;
+    }
+} llkTopDirectory;
+
+dirent dir::buff[dir::numLevels][dir::buffEntries];
+
+// helper functions
+
+bool llkIsMissingExeLink(pid_t tid) {
+    char c;
+    // CAP_SYS_PTRACE is required to prevent ret == -1, but ENOENT is signal
+    auto ret = ::readlink((procdir + std::to_string(tid) + "/exe").c_str(), &c, sizeof(c));
+    return (ret == -1) && (errno == ENOENT);
+}
+
+// Common routine where caller accepts empty content as error/passthrough.
+// Reduces the churn of reporting read errors in the callers.
+std::string ReadFile(std::string&& path) {
+    std::string content;
+    if (!android::base::ReadFileToString(path, &content)) {
+        PLOG(DEBUG) << "Read " << path << " failed";
+        content = "";
+    }
+    return content;
+}
+
+std::string llkProcGetName(pid_t tid, const char* node = "/cmdline") {
+    std::string content = ReadFile(procdir + std::to_string(tid) + node);
+    static constexpr char needles[] = " \t\r\n";  // including trailing nul
+    auto pos = content.find_first_of(needles, 0, sizeof(needles));
+    if (pos != std::string::npos) {
+        content.erase(pos);
+    }
+    return content;
+}
+
+uid_t llkProcGetUid(pid_t tid) {
+    // Get the process' uid.  The following read from /status is admittedly
+    // racy, prone to corruption due to shape-changes.  The consequences are
+    // not catastrophic as we sample a few times before taking action.
+    //
+    // If /loginuid worked on reliably, or on Android (all tasks report -1)...
+    // Android lmkd causes /cgroup to contain memory:/<dom>/uid_<uid>/pid_<pid>
+    // which is tighter, but also not reliable.
+    std::string content = ReadFile(procdir + std::to_string(tid) + "/status");
+    static constexpr char Uid[] = "\nUid:";
+    auto pos = content.find(Uid);
+    if (pos == std::string::npos) {
+        return -1;
+    }
+    pos += ::strlen(Uid);
+    while ((pos < content.size()) && ::isblank(content[pos])) {
+        ++pos;
+    }
+    content.erase(0, pos);
+    for (pos = 0; (pos < content.size()) && ::isdigit(content[pos]); ++pos) {
+        ;
+    }
+    // Content of form 'Uid:	0	0	0	0', newline is error
+    if ((pos >= content.size()) || !::isblank(content[pos])) {
+        return -1;
+    }
+    content.erase(pos);
+    uid_t ret;
+    if (!android::base::ParseInt(content, &ret, uid_t(0))) {
+        return -1;
+    }
+    return ret;
+}
+
+struct proc {
+    pid_t tid;                     // monitored thread id (in Z or D state).
+    nanoseconds schedUpdate;       // /proc/<tid>/sched "se.avg.lastUpdateTime",
+    uint64_t nrSwitches;           // /proc/<tid>/sched "nr_switches" for
+                                   // refined ABA problem detection, determine
+                                   // forward scheduling progress.
+    milliseconds update;           // llkUpdate millisecond signature of last.
+    milliseconds count;            // duration in state.
+    pid_t pid;                     // /proc/<pid> before iterating through
+                                   // /proc/<pid>/task/<tid> for threads.
+    pid_t ppid;                    // /proc/<tid>/stat field 4 parent pid.
+    uid_t uid;                     // /proc/<tid>/status Uid: field.
+    unsigned time;                 // sum of /proc/<tid>/stat field 14 utime &
+                                   // 15 stime for coarse ABA problem detection.
+    std::string cmdline;           // cached /cmdline content
+    char state;                    // /proc/<tid>/stat field 3: Z or D
+                                   // (others we do not monitor: S, R, T or ?)
+    char comm[TASK_COMM_LEN + 3];  // space for adding '[' and ']'
+    bool exeMissingValid;          // exeMissing has been cached
+    bool cmdlineValid;             // cmdline has been cached
+    bool updated;                  // cleared before monitoring pass.
+    bool killed;                   // sent a kill to this thread, next panic...
+
+    void setComm(const char* _comm) { strncpy(comm + 1, _comm, sizeof(comm) - 2); }
+
+    proc(pid_t tid, pid_t pid, pid_t ppid, const char* _comm, int time, char state)
+        : tid(tid),
+          schedUpdate(0),
+          nrSwitches(0),
+          update(llkUpdate),
+          count(0),
+          pid(pid),
+          ppid(ppid),
+          uid(-1),
+          time(time),
+          state(state),
+          exeMissingValid(false),
+          cmdlineValid(false),
+          updated(true),
+          killed(!llkTestWithKill) {
+        memset(comm, '\0', sizeof(comm));
+        setComm(_comm);
+    }
+
+    const char* getComm(void) {
+        if (comm[1] == '\0') {  // comm Valid?
+            strncpy(comm + 1, llkProcGetName(tid, "/comm").c_str(), sizeof(comm) - 2);
+        }
+        if (!exeMissingValid) {
+            if (llkIsMissingExeLink(tid)) {
+                comm[0] = '[';
+            }
+            exeMissingValid = true;
+        }
+        size_t len = strlen(comm + 1);
+        if (__predict_true(len < (sizeof(comm) - 1))) {
+            if (comm[0] == '[') {
+                if ((comm[len] != ']') && __predict_true(len < (sizeof(comm) - 2))) {
+                    comm[++len] = ']';
+                    comm[++len] = '\0';
+                }
+            } else {
+                if (comm[len] == ']') {
+                    comm[len] = '\0';
+                }
+            }
+        }
+        return &comm[comm[0] != '['];
+    }
+
+    const char* getCmdline(void) {
+        if (!cmdlineValid) {
+            cmdline = llkProcGetName(tid);
+            cmdlineValid = true;
+        }
+        return cmdline.c_str();
+    }
+
+    uid_t getUid(void) {
+        if (uid <= 0) {  // Churn on root user, because most likely to setuid()
+            uid = llkProcGetUid(tid);
+        }
+        return uid;
+    }
+
+    void reset(void) {  // reset cache, if we detected pid rollover
+        uid = -1;
+        state = '?';
+        cmdline = "";
+        comm[0] = '\0';
+        exeMissingValid = false;
+        cmdlineValid = false;
+    }
+};
+
+std::unordered_map<pid_t, proc> tids;
+
+// Check range and setup defaults, in order of propagation:
+//     llkTimeoutMs
+//     llkCheckMs
+//     ...
+// KISS to keep it all self-contained, and called multiple times as parameters
+// are interpreted so that defaults, llkCheckMs and llkCycle make sense.
+void llkValidate() {
+    if (llkTimeoutMs == 0ms) {
+        llkTimeoutMs = LLK_TIMEOUT_MS_DEFAULT;
+    }
+    llkTimeoutMs = std::max(llkTimeoutMs, LLK_TIMEOUT_MS_MINIMUM);
+    if (llkCheckMs == 0ms) {
+        llkCheckMs = llkTimeoutMs / LLK_CHECKS_PER_TIMEOUT_DEFAULT;
+    }
+    llkCheckMs = std::min(llkCheckMs, llkTimeoutMs);
+
+    for (size_t state = 0; state < ARRAY_SIZE(llkStateTimeoutMs); ++state) {
+        if (llkStateTimeoutMs[state] == 0ms) {
+            llkStateTimeoutMs[state] = llkTimeoutMs;
+        }
+        llkStateTimeoutMs[state] =
+            std::min(std::max(llkStateTimeoutMs[state], LLK_TIMEOUT_MS_MINIMUM), llkTimeoutMs);
+        llkCheckMs = std::min(llkCheckMs, llkStateTimeoutMs[state]);
+    }
+
+    llkCheckMs = std::max(llkCheckMs, LLK_CHECK_MS_MINIMUM);
+    if (llkCycle == 0ms) {
+        llkCycle = llkCheckMs;
+    }
+    llkCycle = std::min(llkCycle, llkCheckMs);
+}
+
+milliseconds llkGetTimespecDiffMs(timespec* from, timespec* to) {
+    return duration_cast<milliseconds>(seconds(to->tv_sec - from->tv_sec)) +
+           duration_cast<milliseconds>(nanoseconds(to->tv_nsec - from->tv_nsec));
+}
+
+std::string llkProcGetName(pid_t tid, const char* comm, const char* cmdline) {
+    if ((cmdline != nullptr) && (*cmdline != '\0')) {
+        return cmdline;
+    }
+    if ((comm != nullptr) && (*comm != '\0')) {
+        return comm;
+    }
+
+    // UNLIKELY! Here because killed before we kill it?
+    // Assume change is afoot, do not call llkTidAlloc
+
+    // cmdline ?
+    std::string content = llkProcGetName(tid);
+    if (content.size() != 0) {
+        return content;
+    }
+    // Comm instead?
+    content = llkProcGetName(tid, "/comm");
+    if (llkIsMissingExeLink(tid) && (content.size() != 0)) {
+        return '[' + content + ']';
+    }
+    return content;
+}
+
+int llkKillOneProcess(pid_t pid, char state, pid_t tid, const char* tcomm = nullptr,
+                      const char* tcmdline = nullptr, const char* pcomm = nullptr,
+                      const char* pcmdline = nullptr) {
+    std::string forTid;
+    if (tid != pid) {
+        forTid = " for '" + llkProcGetName(tid, tcomm, tcmdline) + "' (" + std::to_string(tid) + ")";
+    }
+    LOG(INFO) << "Killing '" << llkProcGetName(pid, pcomm, pcmdline) << "' (" << pid
+              << ") to check forward scheduling progress in " << state << " state" << forTid;
+    // CAP_KILL required
+    errno = 0;
+    auto r = ::kill(pid, SIGKILL);
+    if (r) {
+        PLOG(ERROR) << "kill(" << pid << ")=" << r << ' ';
+    }
+
+    return r;
+}
+
+// Kill one process
+int llkKillOneProcess(pid_t pid, proc* tprocp) {
+    return llkKillOneProcess(pid, tprocp->state, tprocp->tid, tprocp->getComm(),
+                             tprocp->getCmdline());
+}
+
+// Kill one process specified by kprocp
+int llkKillOneProcess(proc* kprocp, proc* tprocp) {
+    if (kprocp == nullptr) {
+        return -2;
+    }
+
+    return llkKillOneProcess(kprocp->tid, tprocp->state, tprocp->tid, tprocp->getComm(),
+                             tprocp->getCmdline(), kprocp->getComm(), kprocp->getCmdline());
+}
+
+// Acquire file descriptor from environment, or open and cache it.
+// NB: cache is unnecessary in our current context, pedantically
+//     required to prevent leakage of file descriptors in the future.
+int llkFileToWriteFd(const std::string& file) {
+    static std::unordered_map<std::string, int> cache;
+    auto search = cache.find(file);
+    if (search != cache.end()) return search->second;
+    auto fd = android_get_control_file(file.c_str());
+    if (fd >= 0) return fd;
+    fd = TEMP_FAILURE_RETRY(::open(file.c_str(), O_WRONLY | O_CLOEXEC));
+    if (fd >= 0) cache.emplace(std::make_pair(file, fd));
+    return fd;
+}
+
+// Wrap android::base::WriteStringToFile to use android_get_control_file.
+bool llkWriteStringToFile(const std::string& string, const std::string& file) {
+    auto fd = llkFileToWriteFd(file);
+    if (fd < 0) return false;
+    return android::base::WriteStringToFd(string, fd);
+}
+
+bool llkWriteStringToFileConfirm(const std::string& string, const std::string& file) {
+    auto fd = llkFileToWriteFd(file);
+    auto ret = (fd < 0) ? false : android::base::WriteStringToFd(string, fd);
+    std::string content;
+    if (!android::base::ReadFileToString(file, &content)) return ret;
+    return android::base::Trim(content) == string;
+}
+
+void llkPanicKernel(bool dump, pid_t tid, const char* state) __noreturn;
+void llkPanicKernel(bool dump, pid_t tid, const char* state) {
+    auto sysrqTriggerFd = llkFileToWriteFd("/proc/sysrq-trigger");
+    if (sysrqTriggerFd < 0) {
+        // DYB
+        llkKillOneProcess(initPid, 'R', tid);
+        // The answer to life, the universe and everything
+        ::exit(42);
+        // NOTREACHED
+    }
+    ::sync();
+    if (dump) {
+        // Show all locks that are held
+        android::base::WriteStringToFd("d", sysrqTriggerFd);
+        // This can trigger hardware watchdog, that is somewhat _ok_.
+        // But useless if pstore configured for <256KB, low ram devices ...
+        if (!llkLowRam) {
+            android::base::WriteStringToFd("t", sysrqTriggerFd);
+        }
+        ::usleep(200000);  // let everything settle
+    }
+    llkWriteStringToFile(std::string("SysRq : Trigger a crash : 'livelock,") + state + "'\n",
+                         "/dev/kmsg");
+    android::base::WriteStringToFd("c", sysrqTriggerFd);
+    // NOTREACHED
+    // DYB
+    llkKillOneProcess(initPid, 'R', tid);
+    // I sat at my desk, stared into the garden and thought '42 will do'.
+    // I typed it out. End of story
+    ::exit(42);
+    // NOTREACHED
+}
+
+void llkAlarmHandler(int) {
+    llkPanicKernel(false, ::getpid(), "alarm");
+}
+
+milliseconds GetUintProperty(const std::string& key, milliseconds def) {
+    return milliseconds(android::base::GetUintProperty(key, static_cast<uint64_t>(def.count()),
+                                                       static_cast<uint64_t>(def.max().count())));
+}
+
+seconds GetUintProperty(const std::string& key, seconds def) {
+    return seconds(android::base::GetUintProperty(key, static_cast<uint64_t>(def.count()),
+                                                  static_cast<uint64_t>(def.max().count())));
+}
+
+proc* llkTidLookup(pid_t tid) {
+    auto search = tids.find(tid);
+    if (search == tids.end()) {
+        return nullptr;
+    }
+    return &search->second;
+}
+
+void llkTidRemove(pid_t tid) {
+    tids.erase(tid);
+}
+
+proc* llkTidAlloc(pid_t tid, pid_t pid, pid_t ppid, const char* comm, int time, char state) {
+    auto it = tids.emplace(std::make_pair(tid, proc(tid, pid, ppid, comm, time, state)));
+    return &it.first->second;
+}
+
+std::string llkFormat(milliseconds ms) {
+    auto sec = duration_cast<seconds>(ms);
+    std::ostringstream s;
+    s << sec.count() << '.';
+    auto f = s.fill('0');
+    auto w = s.width(3);
+    s << std::right << (ms - sec).count();
+    s.width(w);
+    s.fill(f);
+    s << 's';
+    return s.str();
+}
+
+std::string llkFormat(seconds s) {
+    return std::to_string(s.count()) + 's';
+}
+
+std::string llkFormat(bool flag) {
+    return flag ? "true" : "false";
+}
+
+std::string llkFormat(const std::unordered_set<std::string>& blacklist) {
+    std::string ret;
+    for (auto entry : blacklist) {
+        if (ret.size()) {
+            ret += ",";
+        }
+        ret += entry;
+    }
+    return ret;
+}
+
+// We only officially support comma separators, but wetware being what they
+// are will take some liberty and I do not believe they should be punished.
+std::unordered_set<std::string> llkSplit(const std::string& s,
+                                         const std::string& delimiters = ", \t:") {
+    std::unordered_set<std::string> result;
+
+    size_t base = 0;
+    size_t found;
+    while (true) {
+        found = s.find_first_of(delimiters, base);
+        result.emplace(s.substr(base, found - base));
+        if (found == s.npos) break;
+        base = found + 1;
+    }
+    return result;
+}
+
+bool llkSkipName(const std::string& name,
+                 const std::unordered_set<std::string>& blacklist = llkBlacklistProcess) {
+    if ((name.size() == 0) || (blacklist.size() == 0)) {
+        return false;
+    }
+
+    return blacklist.find(name) != blacklist.end();
+}
+
+bool llkSkipPid(pid_t pid) {
+    return llkSkipName(std::to_string(pid), llkBlacklistProcess);
+}
+
+bool llkSkipPpid(pid_t ppid) {
+    return llkSkipName(std::to_string(ppid), llkBlacklistParent);
+}
+
+bool llkSkipUid(uid_t uid) {
+    // Match by number?
+    if (llkSkipName(std::to_string(uid), llkBlacklistUid)) {
+        return true;
+    }
+
+    // Match by name?
+    auto pwd = ::getpwuid(uid);
+    return (pwd != nullptr) && __predict_true(pwd->pw_name != nullptr) &&
+           __predict_true(pwd->pw_name[0] != '\0') && llkSkipName(pwd->pw_name, llkBlacklistUid);
+}
+
+bool getValidTidDir(dirent* dp, std::string* piddir) {
+    if (!::isdigit(dp->d_name[0])) {
+        return false;
+    }
+
+    // Corner case can not happen in reality b/c of above ::isdigit check
+    if (__predict_false(dp->d_type != DT_DIR)) {
+        if (__predict_false(dp->d_type == DT_UNKNOWN)) {  // can't b/c procfs
+            struct stat st;
+            *piddir = procdir;
+            *piddir += dp->d_name;
+            return (lstat(piddir->c_str(), &st) == 0) && (st.st_mode & S_IFDIR);
+        }
+        return false;
+    }
+
+    *piddir = procdir;
+    *piddir += dp->d_name;
+    return true;
+}
+
+bool llkIsMonitorState(char state) {
+    return (state == 'Z') || (state == 'D');
+}
+
+// returns -1 if not found
+long long getSchedValue(const std::string& schedString, const char* key) {
+    auto pos = schedString.find(key);
+    if (pos == std::string::npos) {
+        return -1;
+    }
+    pos = schedString.find(':', pos);
+    if (__predict_false(pos == std::string::npos)) {
+        return -1;
+    }
+    while ((++pos < schedString.size()) && ::isblank(schedString[pos])) {
+        ;
+    }
+    long long ret;
+    if (!android::base::ParseInt(schedString.substr(pos), &ret, static_cast<long long>(0))) {
+        return -1;
+    }
+    return ret;
+}
+
+// Primary ABA mitigation watching last time schedule activity happened
+void llkCheckSchedUpdate(proc* procp, const std::string& piddir) {
+    // Audit finds /proc/<tid>/sched is just over 1K, and
+    // is rarely larger than 2K, even less on Android.
+    // For example, the "se.avg.lastUpdateTime" field we are
+    // interested in typically within the primary set in
+    // the first 1K.
+    //
+    // Proc entries can not be read >1K atomically via libbase,
+    // but if there are problems we assume at least a few
+    // samples of reads occur before we take any real action.
+    std::string schedString = ReadFile(piddir + "/sched");
+    if (schedString.size() == 0) {
+        // /schedstat is not as standardized, but in 3.1+
+        // Android devices, the third field is nr_switches
+        // from /sched:
+        schedString = ReadFile(piddir + "/schedstat");
+        if (schedString.size() == 0) {
+            return;
+        }
+        auto val = static_cast<unsigned long long>(-1);
+        if (((::sscanf(schedString.c_str(), "%*d %*d %llu", &val)) == 1) &&
+            (val != static_cast<unsigned long long>(-1)) && (val != 0) &&
+            (val != procp->nrSwitches)) {
+            procp->nrSwitches = val;
+            procp->count = 0ms;
+            procp->killed = !llkTestWithKill;
+        }
+        return;
+    }
+
+    auto val = getSchedValue(schedString, "\nse.avg.lastUpdateTime");
+    if (val == -1) {
+        val = getSchedValue(schedString, "\nse.svg.last_update_time");
+    }
+    if (val != -1) {
+        auto schedUpdate = nanoseconds(val);
+        if (schedUpdate != procp->schedUpdate) {
+            procp->schedUpdate = schedUpdate;
+            procp->count = 0ms;
+            procp->killed = !llkTestWithKill;
+        }
+    }
+
+    val = getSchedValue(schedString, "\nnr_switches");
+    if (val != -1) {
+        if (static_cast<uint64_t>(val) != procp->nrSwitches) {
+            procp->nrSwitches = val;
+            procp->count = 0ms;
+            procp->killed = !llkTestWithKill;
+        }
+    }
+}
+
+void llkLogConfig(void) {
+    LOG(INFO) << "ro.config.low_ram=" << llkFormat(llkLowRam) << "\n"
+              << LLK_ENABLE_PROPERTY "=" << llkFormat(llkEnable) << "\n"
+              << KHT_ENABLE_PROPERTY "=" << llkFormat(khtEnable) << "\n"
+              << LLK_MLOCKALL_PROPERTY "=" << llkFormat(llkMlockall) << "\n"
+              << LLK_KILLTEST_PROPERTY "=" << llkFormat(llkTestWithKill) << "\n"
+              << KHT_TIMEOUT_PROPERTY "=" << llkFormat(khtTimeout) << "\n"
+              << LLK_TIMEOUT_MS_PROPERTY "=" << llkFormat(llkTimeoutMs) << "\n"
+              << LLK_D_TIMEOUT_MS_PROPERTY "=" << llkFormat(llkStateTimeoutMs[llkStateD]) << "\n"
+              << LLK_Z_TIMEOUT_MS_PROPERTY "=" << llkFormat(llkStateTimeoutMs[llkStateZ]) << "\n"
+              << LLK_CHECK_MS_PROPERTY "=" << llkFormat(llkCheckMs) << "\n"
+              << LLK_BLACKLIST_PROCESS_PROPERTY "=" << llkFormat(llkBlacklistProcess) << "\n"
+              << LLK_BLACKLIST_PARENT_PROPERTY "=" << llkFormat(llkBlacklistParent) << "\n"
+              << LLK_BLACKLIST_UID_PROPERTY "=" << llkFormat(llkBlacklistUid);
+}
+
+void* llkThread(void* obj) {
+    LOG(INFO) << "started";
+
+    std::string name = std::to_string(::gettid());
+    if (!llkSkipName(name)) {
+        llkBlacklistProcess.emplace(name);
+    }
+    name = static_cast<const char*>(obj);
+    prctl(PR_SET_NAME, name.c_str());
+    if (__predict_false(!llkSkipName(name))) {
+        llkBlacklistProcess.insert(name);
+    }
+    // No longer modifying llkBlacklistProcess.
+    llkRunning = true;
+    llkLogConfig();
+    while (llkRunning) {
+        ::usleep(duration_cast<microseconds>(llkCheck(true)).count());
+    }
+    // NOTREACHED
+    LOG(INFO) << "exiting";
+    return nullptr;
+}
+
+}  // namespace
+
+milliseconds llkCheck(bool checkRunning) {
+    if (!llkEnable || (checkRunning != llkRunning)) {
+        return milliseconds::max();
+    }
+
+    // Reset internal watchdog, which is a healthy engineering margin of
+    // double the maximum wait or cycle time for the mainloop that calls us.
+    //
+    // This alarm is effectively the live lock detection of llkd, as
+    // we understandably can not monitor ourselves otherwise.
+    ::alarm(duration_cast<seconds>(llkTimeoutMs * 2).count());
+
+    // kernel jiffy precision fastest acquisition
+    static timespec last;
+    timespec now;
+    ::clock_gettime(CLOCK_MONOTONIC_COARSE, &now);
+    auto ms = llkGetTimespecDiffMs(&last, &now);
+    if (ms < llkCycle) {
+        return llkCycle - ms;
+    }
+    last = now;
+
+    LOG(VERBOSE) << "opendir(\"" << procdir << "\")";
+    if (__predict_false(!llkTopDirectory)) {
+        // gid containing AID_READPROC required
+        llkTopDirectory.reset(procdir);
+        if (__predict_false(!llkTopDirectory)) {
+            // Most likely reason we could be here is a resource limit.
+            // Keep our processing down to a minimum, but not so low that
+            // we do not recover in a timely manner should the issue be
+            // transitory.
+            LOG(DEBUG) << "opendir(\"" << procdir << "\") failed";
+            return llkTimeoutMs;
+        }
+    }
+
+    for (auto& it : tids) {
+        it.second.updated = false;
+    }
+
+    auto prevUpdate = llkUpdate;
+    llkUpdate += ms;
+    ms -= llkCycle;
+    auto myPid = ::getpid();
+    auto myTid = ::gettid();
+    for (auto dp = llkTopDirectory.read(); dp != nullptr; dp = llkTopDirectory.read()) {
+        std::string piddir;
+
+        if (!getValidTidDir(dp, &piddir)) {
+            continue;
+        }
+
+        // Get the process tasks
+        std::string taskdir = piddir + "/task/";
+        int pid = -1;
+        LOG(VERBOSE) << "+opendir(\"" << taskdir << "\")";
+        dir taskDirectory(taskdir);
+        if (__predict_false(!taskDirectory)) {
+            LOG(DEBUG) << "+opendir(\"" << taskdir << "\") failed";
+        }
+        for (auto tp = taskDirectory.read(dir::task, dp); tp != nullptr;
+             tp = taskDirectory.read(dir::task)) {
+            if (!getValidTidDir(tp, &piddir)) {
+                continue;
+            }
+
+            // Get the process stat
+            std::string stat = ReadFile(piddir + "/stat");
+            if (stat.size() == 0) {
+                continue;
+            }
+            unsigned tid = -1;
+            char pdir[TASK_COMM_LEN + 1];
+            char state = '?';
+            unsigned ppid = -1;
+            unsigned utime = -1;
+            unsigned stime = -1;
+            int dummy;
+            pdir[0] = '\0';
+            // tid should not change value
+            auto match = ::sscanf(
+                stat.c_str(),
+                "%u (%" ___STRING(
+                    TASK_COMM_LEN) "[^)]) %c %u %*d %*d %*d %*d %*d %*d %*d %*d %*d %u %u %d",
+                &tid, pdir, &state, &ppid, &utime, &stime, &dummy);
+            if (pid == -1) {
+                pid = tid;
+            }
+            LOG(VERBOSE) << "match " << match << ' ' << tid << " (" << pdir << ") " << state << ' '
+                         << ppid << " ... " << utime << ' ' << stime << ' ' << dummy;
+            if (match != 7) {
+                continue;
+            }
+
+            auto procp = llkTidLookup(tid);
+            if (procp == nullptr) {
+                procp = llkTidAlloc(tid, pid, ppid, pdir, utime + stime, state);
+            } else {
+                // comm can change ...
+                procp->setComm(pdir);
+                procp->updated = true;
+                // pid/ppid/tid wrap?
+                if (((procp->update != prevUpdate) && (procp->update != llkUpdate)) ||
+                    (procp->ppid != ppid) || (procp->pid != pid)) {
+                    procp->reset();
+                } else if (procp->time != (utime + stime)) {  // secondary ABA.
+                    // watching utime+stime granularity jiffy
+                    procp->state = '?';
+                }
+                procp->update = llkUpdate;
+                procp->pid = pid;
+                procp->ppid = ppid;
+                procp->time = utime + stime;
+                if (procp->state != state) {
+                    procp->count = 0ms;
+                    procp->killed = !llkTestWithKill;
+                    procp->state = state;
+                } else {
+                    procp->count += llkCycle;
+                }
+            }
+
+            // Filter checks in intuitive order of CPU cost to evaluate
+            // If tid unique continue, if ppid or pid unique break
+
+            if (pid == myPid) {
+                break;
+            }
+            if (!llkIsMonitorState(state)) {
+                continue;
+            }
+            if ((tid == myTid) || llkSkipPid(tid)) {
+                continue;
+            }
+            if (llkSkipPpid(ppid)) {
+                break;
+            }
+
+            if (llkSkipName(procp->getComm())) {
+                continue;
+            }
+            if (llkSkipName(procp->getCmdline())) {
+                break;
+            }
+
+            auto pprocp = llkTidLookup(ppid);
+            if (pprocp == nullptr) {
+                pprocp = llkTidAlloc(ppid, ppid, 0, "", 0, '?');
+            }
+            if ((pprocp != nullptr) && (llkSkipName(pprocp->getComm(), llkBlacklistParent) ||
+                                        llkSkipName(pprocp->getCmdline(), llkBlacklistParent))) {
+                break;
+            }
+
+            if ((llkBlacklistUid.size() != 0) && llkSkipUid(procp->getUid())) {
+                continue;
+            }
+
+            // ABA mitigation watching last time schedule activity happened
+            llkCheckSchedUpdate(procp, piddir);
+
+            // Can only fall through to here if registered D or Z state !!!
+            if (procp->count < llkStateTimeoutMs[(state == 'Z') ? llkStateZ : llkStateD]) {
+                LOG(VERBOSE) << state << ' ' << llkFormat(procp->count) << ' ' << ppid << "->"
+                             << pid << "->" << tid << ' ' << procp->getComm();
+                continue;
+            }
+
+            // We have to kill it to determine difference between live lock
+            // and persistent state blocked on a resource.  Is there something
+            // wrong with a process that has no forward scheduling progress in
+            // Z or D?  Yes, generally means improper accounting in the
+            // process, but not always ...
+            //
+            // Whomever we hit with a test kill must accept the Android
+            // Aphorism that everything can be burned to the ground and
+            // must survive.
+            if (procp->killed == false) {
+                procp->killed = true;
+                // confirm: re-read uid before committing to a panic.
+                procp->uid = -1;
+                switch (state) {
+                    case 'Z':  // kill ppid to free up a Zombie
+                        // Killing init will kernel panic without diagnostics
+                        // so skip right to controlled kernel panic with
+                        // diagnostics.
+                        if (ppid == initPid) {
+                            break;
+                        }
+                        LOG(WARNING) << "Z " << llkFormat(procp->count) << ' ' << ppid << "->"
+                                     << pid << "->" << tid << ' ' << procp->getComm() << " [kill]";
+                        if ((llkKillOneProcess(pprocp, procp) >= 0) ||
+                            (llkKillOneProcess(ppid, procp) >= 0)) {
+                            continue;
+                        }
+                        break;
+
+                    case 'D':  // kill tid to free up an uninterruptible D
+                        // If ABA is doing its job, we would not need or
+                        // want the following.  Test kill is a Hail Mary
+                        // to make absolutely sure there is no forward
+                        // scheduling progress.  The cost when ABA is
+                        // not working is we kill a process that likes to
+                        // stay in 'D' state, instead of panicing the
+                        // kernel (worse).
+                        LOG(WARNING) << "D " << llkFormat(procp->count) << ' ' << pid << "->" << tid
+                                     << ' ' << procp->getComm() << " [kill]";
+                        if ((llkKillOneProcess(llkTidLookup(pid), procp) >= 0) ||
+                            (llkKillOneProcess(pid, 'D', tid) >= 0) ||
+                            (llkKillOneProcess(procp, procp) >= 0) ||
+                            (llkKillOneProcess(tid, 'D', tid) >= 0)) {
+                            continue;
+                        }
+                        break;
+                }
+            }
+            // We are here because we have confirmed kernel live-lock
+            LOG(ERROR) << state << ' ' << llkFormat(procp->count) << ' ' << ppid << "->" << pid
+                       << "->" << tid << ' ' << procp->getComm() << " [panic]";
+            llkPanicKernel(true, tid, (state == 'Z') ? "zombie" : "driver");
+        }
+        LOG(VERBOSE) << "+closedir()";
+    }
+    llkTopDirectory.rewind();
+    LOG(VERBOSE) << "closedir()";
+
+    // garbage collection of old process references
+    for (auto p = tids.begin(); p != tids.end();) {
+        if (!p->second.updated) {
+            IF_ALOG(LOG_VERBOSE, LOG_TAG) {
+                std::string ppidCmdline = llkProcGetName(p->second.ppid, nullptr, nullptr);
+                if (ppidCmdline.size()) {
+                    ppidCmdline = "(" + ppidCmdline + ")";
+                }
+                std::string pidCmdline;
+                if (p->second.pid != p->second.tid) {
+                    pidCmdline = llkProcGetName(p->second.pid, nullptr, p->second.getCmdline());
+                    if (pidCmdline.size()) {
+                        pidCmdline = "(" + pidCmdline + ")";
+                    }
+                }
+                std::string tidCmdline =
+                    llkProcGetName(p->second.tid, p->second.getComm(), p->second.getCmdline());
+                if (tidCmdline.size()) {
+                    tidCmdline = "(" + tidCmdline + ")";
+                }
+                LOG(VERBOSE) << "thread " << p->second.ppid << ppidCmdline << "->" << p->second.pid
+                             << pidCmdline << "->" << p->second.tid << tidCmdline << " removed";
+            }
+            p = tids.erase(p);
+        } else {
+            ++p;
+        }
+    }
+    if (__predict_false(tids.empty())) {
+        llkTopDirectory.reset();
+    }
+
+    llkCycle = llkCheckMs;
+
+    timespec end;
+    ::clock_gettime(CLOCK_MONOTONIC_COARSE, &end);
+    auto milli = llkGetTimespecDiffMs(&now, &end);
+    LOG((milli > 10s) ? ERROR : (milli > 1s) ? WARNING : VERBOSE) << "sample " << llkFormat(milli);
+
+    // cap to minimum sleep for 1 second since last cycle
+    if (llkCycle < (ms + 1s)) {
+        return 1s;
+    }
+    return llkCycle - ms;
+}
+
+unsigned llkCheckMilliseconds() {
+    return duration_cast<milliseconds>(llkCheck()).count();
+}
+
+bool llkInit(const char* threadname) {
+    llkLowRam = android::base::GetBoolProperty("ro.config.low_ram", false);
+    if (!LLK_ENABLE_DEFAULT && android::base::GetBoolProperty("ro.debuggable", false)) {
+        llkEnable = android::base::GetProperty(LLK_ENABLE_PROPERTY, "eng") == "eng";
+        khtEnable = android::base::GetProperty(KHT_ENABLE_PROPERTY, "eng") == "eng";
+    }
+    llkEnable = android::base::GetBoolProperty(LLK_ENABLE_PROPERTY, llkEnable);
+    if (llkEnable && !llkTopDirectory.reset(procdir)) {
+        // Most likely reason we could be here is llkd was started
+        // incorrectly without the readproc permissions.  Keep our
+        // processing down to a minimum.
+        llkEnable = false;
+    }
+    khtEnable = android::base::GetBoolProperty(KHT_ENABLE_PROPERTY, khtEnable);
+    llkMlockall = android::base::GetBoolProperty(LLK_MLOCKALL_PROPERTY, llkMlockall);
+    llkTestWithKill = android::base::GetBoolProperty(LLK_KILLTEST_PROPERTY, llkTestWithKill);
+    // if LLK_TIMOUT_MS_PROPERTY was not set, we will use a set
+    // KHT_TIMEOUT_PROPERTY as co-operative guidance for the default value.
+    khtTimeout = GetUintProperty(KHT_TIMEOUT_PROPERTY, khtTimeout);
+    if (khtTimeout == 0s) {
+        khtTimeout = duration_cast<seconds>(llkTimeoutMs * (1 + LLK_CHECKS_PER_TIMEOUT_DEFAULT) /
+                                            LLK_CHECKS_PER_TIMEOUT_DEFAULT);
+    }
+    llkTimeoutMs =
+        khtTimeout * LLK_CHECKS_PER_TIMEOUT_DEFAULT / (1 + LLK_CHECKS_PER_TIMEOUT_DEFAULT);
+    llkTimeoutMs = GetUintProperty(LLK_TIMEOUT_MS_PROPERTY, llkTimeoutMs);
+    llkValidate();  // validate llkTimeoutMs, llkCheckMs and llkCycle
+    llkStateTimeoutMs[llkStateD] = GetUintProperty(LLK_D_TIMEOUT_MS_PROPERTY, llkTimeoutMs);
+    llkStateTimeoutMs[llkStateZ] = GetUintProperty(LLK_Z_TIMEOUT_MS_PROPERTY, llkTimeoutMs);
+    llkCheckMs = GetUintProperty(LLK_CHECK_MS_PROPERTY, llkCheckMs);
+    llkValidate();  // validate all (effectively minus llkTimeoutMs)
+    std::string defaultBlacklistProcess(
+        std::to_string(kernelPid) + "," + std::to_string(initPid) + "," +
+        std::to_string(kthreaddPid) + "," + std::to_string(::getpid()) + "," +
+        std::to_string(::gettid()) + "," LLK_BLACKLIST_PROCESS_DEFAULT);
+    if (threadname) {
+        defaultBlacklistProcess += std::string(",") + threadname;
+    }
+    for (int cpu = 1; cpu < get_nprocs_conf(); ++cpu) {
+        defaultBlacklistProcess += ",[watchdog/" + std::to_string(cpu) + "]";
+    }
+    defaultBlacklistProcess =
+        android::base::GetProperty(LLK_BLACKLIST_PROCESS_PROPERTY, defaultBlacklistProcess);
+    llkBlacklistProcess = llkSplit(defaultBlacklistProcess);
+    if (!llkSkipName("[khungtaskd]")) {  // ALWAYS ignore as special
+        llkBlacklistProcess.emplace("[khungtaskd]");
+    }
+    llkBlacklistParent = llkSplit(android::base::GetProperty(
+        LLK_BLACKLIST_PARENT_PROPERTY, std::to_string(kernelPid) + "," + std::to_string(kthreaddPid) +
+                                           "," LLK_BLACKLIST_PARENT_DEFAULT));
+    llkBlacklistUid =
+        llkSplit(android::base::GetProperty(LLK_BLACKLIST_UID_PROPERTY, LLK_BLACKLIST_UID_DEFAULT));
+
+    // internal watchdog
+    ::signal(SIGALRM, llkAlarmHandler);
+
+    // kernel hung task configuration? Otherwise leave it as-is
+    if (khtEnable) {
+        // EUID must be AID_ROOT to write to /proc/sys/kernel/ nodes, there
+        // are no capability overrides.  For security reasons we do not want
+        // to run as AID_ROOT.  We may not be able to write them successfully,
+        // we will try, but the least we can do is read the values back to
+        // confirm expectations and report whether configured or not.
+        auto configured = llkWriteStringToFileConfirm(std::to_string(khtTimeout.count()),
+                                                      "/proc/sys/kernel/hung_task_timeout_secs");
+        if (configured) {
+            llkWriteStringToFile("65535", "/proc/sys/kernel/hung_task_warnings");
+            llkWriteStringToFile("65535", "/proc/sys/kernel/hung_task_check_count");
+            configured = llkWriteStringToFileConfirm("1", "/proc/sys/kernel/hung_task_panic");
+        }
+        if (configured) {
+            LOG(INFO) << "[khungtaskd] configured";
+        } else {
+            LOG(WARNING) << "[khungtaskd] not configurable";
+        }
+    }
+
+    bool logConfig = true;
+    if (llkEnable) {
+        if (llkMlockall &&
+            // MCL_ONFAULT pins pages as they fault instead of loading
+            // everything immediately all at once. (Which would be bad,
+            // because as of this writing, we have a lot of mapped pages we
+            // never use.) Old kernels will see MCL_ONFAULT and fail with
+            // EINVAL; we ignore this failure.
+            //
+            // N.B. read the man page for mlockall. MCL_CURRENT | MCL_ONFAULT
+            // pins ⊆ MCL_CURRENT, converging to just MCL_CURRENT as we fault
+            // in pages.
+
+            // CAP_IPC_LOCK required
+            mlockall(MCL_CURRENT | MCL_FUTURE | MCL_ONFAULT) && (errno != EINVAL)) {
+            PLOG(WARNING) << "mlockall failed ";
+        }
+
+        if (threadname) {
+            pthread_attr_t attr;
+
+            if (!pthread_attr_init(&attr)) {
+                sched_param param;
+
+                memset(&param, 0, sizeof(param));
+                pthread_attr_setschedparam(&attr, &param);
+                pthread_attr_setschedpolicy(&attr, SCHED_BATCH);
+                if (!pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED)) {
+                    pthread_t thread;
+                    if (!pthread_create(&thread, &attr, llkThread, const_cast<char*>(threadname))) {
+                        // wait a second for thread to start
+                        for (auto retry = 50; retry && !llkRunning; --retry) {
+                            ::usleep(20000);
+                        }
+                        logConfig = !llkRunning;  // printed in llkd context?
+                    } else {
+                        LOG(ERROR) << "failed to spawn llkd thread";
+                    }
+                } else {
+                    LOG(ERROR) << "failed to detach llkd thread";
+                }
+                pthread_attr_destroy(&attr);
+            } else {
+                LOG(ERROR) << "failed to allocate attibutes for llkd thread";
+            }
+        }
+    } else {
+        LOG(DEBUG) << "[khungtaskd] left unconfigured";
+    }
+    if (logConfig) {
+        llkLogConfig();
+    }
+
+    return llkEnable;
+}
diff --git a/llkd/llkd.cpp b/llkd/llkd.cpp
new file mode 100644
index 0000000..f10253d
--- /dev/null
+++ b/llkd/llkd.cpp
@@ -0,0 +1,50 @@
+/*
+ * 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 "llkd.h"
+
+#include <sched.h>
+#include <unistd.h>
+
+#include <chrono>
+
+#include <android-base/logging.h>
+
+using namespace std::chrono;
+
+int main(int, char**) {
+    LOG(INFO) << "started";
+
+    bool enabled = llkInit();
+
+    // Would like this policy to be automatic as part of libllkd,
+    // but that would be presumptuous and bad side-effect.
+    struct sched_param param;
+    memset(&param, 0, sizeof(param));
+    sched_setscheduler(0, SCHED_BATCH, &param);
+
+    while (true) {
+        if (enabled) {
+            ::usleep(duration_cast<microseconds>(llkCheck()).count());
+        } else {
+            ::pause();
+        }
+    }
+    // NOTREACHED
+
+    LOG(INFO) << "exiting";
+    return 0;
+}
diff --git a/llkd/llkd.rc b/llkd/llkd.rc
new file mode 100644
index 0000000..e538cdb
--- /dev/null
+++ b/llkd/llkd.rc
@@ -0,0 +1,49 @@
+# eng default for ro.llk.enable and ro.khungtask.enable
+on property:ro.debuggable=*
+    setprop llk.enable ${ro.llk.enable:-0}
+    setprop khungtask.enable ${ro.khungtask.enable:-0}
+
+on property:ro.debuggable=1
+    setprop llk.enable ${ro.llk.enable:-1}
+    setprop khungtask.enable ${ro.khungtask.enable:-1}
+
+on property:ro.llk.enable=eng
+    setprop llk.enable ${ro.debuggable:-0}
+
+on property:ro.khungtask.enable=eng
+    setprop khungtask.enable ${ro.debuggable:-0}
+
+on property:llk.enable=1
+    setprop llk.enable true
+
+on property:llk.enable=0
+    setprop llk.enable false
+
+on property:khungtask.enable=1
+    setprop khungtask.enable true
+
+on property:khungtask.enable=0
+    setprop khungtask.enable false
+
+# Configure [khungtaskd]
+on property:khungtask.enable=true
+    write /proc/sys/kernel/hung_task_timeout_secs ${ro.khungtask.timeout:-720}
+    write /proc/sys/kernel/hung_task_warnings 65535
+    write /proc/sys/kernel/hung_task_check_count 65535
+    write /proc/sys/kernel/hung_task_panic 1
+
+on property:khungtask.enable=false
+    write /proc/sys/kernel/hung_task_panic 0
+
+on property:llk.enable=true
+    start llkd
+
+service llkd /system/bin/llkd
+    class late_start
+    disabled
+    user llkd
+    group llkd readproc
+    capabilities KILL IPC_LOCK
+    file /dev/kmsg w
+    file /proc/sysrq-trigger w
+    writepid /dev/cpuset/system-background/tasks
diff --git a/llkd/tests/Android.bp b/llkd/tests/Android.bp
new file mode 100644
index 0000000..6dd5938
--- /dev/null
+++ b/llkd/tests/Android.bp
@@ -0,0 +1,41 @@
+// 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: "llkd_unit_test",
+
+    shared_libs: [
+        "libbase",
+        "liblog",
+    ],
+    header_libs: [
+        "llkd_headers",
+    ],
+
+    target: {
+        android: {
+            srcs: [
+                "llkd_test.cpp",
+            ],
+        },
+    },
+
+    cflags: [
+        "-Wall",
+        "-Wextra",
+        "-Werror",
+    ],
+
+    compile_multilib: "first",
+}
diff --git a/llkd/tests/llkd_test.cpp b/llkd/tests/llkd_test.cpp
new file mode 100644
index 0000000..3a15ff1
--- /dev/null
+++ b/llkd/tests/llkd_test.cpp
@@ -0,0 +1,287 @@
+/*
+ * 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 <signal.h>
+#include <stdint.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <chrono>
+#include <iostream>
+#include <string>
+
+#include <android-base/properties.h>
+#include <gtest/gtest.h>
+#include <log/log_time.h>  // for MS_PER_SEC and US_PER_SEC
+
+#include "llkd.h"
+
+using namespace std::chrono;
+using namespace std::chrono_literals;
+
+namespace {
+
+milliseconds GetUintProperty(const std::string& key, milliseconds def) {
+    return milliseconds(android::base::GetUintProperty(key, static_cast<uint64_t>(def.count()),
+                                                       static_cast<uint64_t>(def.max().count())));
+}
+
+seconds GetUintProperty(const std::string& key, seconds def) {
+    return seconds(android::base::GetUintProperty(key, static_cast<uint64_t>(def.count()),
+                                                  static_cast<uint64_t>(def.max().count())));
+}
+
+// GTEST_LOG_(WARNING) output is fugly, this has much less noise
+// ToDo: look into fixing googletest to produce output that matches style of
+//       all the other status messages, and can switch off __line__ and
+//       __function__ noise
+#define GTEST_LOG_WARNING std::cerr << "[ WARNING  ] "
+#define GTEST_LOG_INFO std::cerr << "[   INFO   ] "
+
+// Properties is _not_ a high performance ABI!
+void rest() {
+    usleep(200000);
+}
+
+void execute(const char* command) {
+    if (getuid() || system(command)) {
+        system((std::string("su root ") + command).c_str());
+    }
+}
+
+seconds llkdSleepPeriod(char state) {
+    auto default_eng = android::base::GetProperty(LLK_ENABLE_PROPERTY, "eng") == "eng";
+    auto default_enable = LLK_ENABLE_DEFAULT;
+    if (!LLK_ENABLE_DEFAULT && default_eng &&
+        android::base::GetBoolProperty("ro.debuggable", false)) {
+        default_enable = true;
+    }
+    default_enable = android::base::GetBoolProperty(LLK_ENABLE_PROPERTY, default_enable);
+    if (default_eng) {
+        GTEST_LOG_INFO << LLK_ENABLE_PROPERTY " defaults to \"eng\" thus "
+                       << (default_enable ? "true" : "false") << "\n";
+    }
+    // Hail Mary hope is unconfigured.
+    if ((GetUintProperty(LLK_TIMEOUT_MS_PROPERTY, LLK_TIMEOUT_MS_DEFAULT) !=
+         duration_cast<milliseconds>(120s)) ||
+        (GetUintProperty(LLK_CHECK_MS_PROPERTY,
+                         LLK_TIMEOUT_MS_DEFAULT / LLK_CHECKS_PER_TIMEOUT_DEFAULT) !=
+         duration_cast<milliseconds>(10s))) {
+        execute("stop llkd");
+        rest();
+        std::string setprop("setprop ");
+        execute((setprop + LLK_ENABLE_WRITEABLE_PROPERTY + " false").c_str());
+        rest();
+        execute((setprop + LLK_TIMEOUT_MS_PROPERTY + " 120000").c_str());
+        rest();
+        execute((setprop + KHT_TIMEOUT_PROPERTY + " 130").c_str());
+        rest();
+        execute((setprop + LLK_CHECK_MS_PROPERTY + " 10000").c_str());
+        rest();
+        execute((setprop + LLK_ENABLE_PROPERTY + " true").c_str());
+        rest();
+        execute((setprop + LLK_ENABLE_WRITEABLE_PROPERTY + " true").c_str());
+        rest();
+    }
+    default_enable = LLK_ENABLE_DEFAULT;
+    if (!LLK_ENABLE_DEFAULT && (android::base::GetProperty(LLK_ENABLE_PROPERTY, "eng") == "eng") &&
+        android::base::GetBoolProperty("ro.debuggable", false)) {
+        default_enable = true;
+    }
+    default_enable = android::base::GetBoolProperty(LLK_ENABLE_PROPERTY, default_enable);
+    if (default_enable) {
+        execute("start llkd");
+        rest();
+        GTEST_LOG_INFO << "llkd enabled\n";
+    } else {
+        GTEST_LOG_WARNING << "llkd disabled\n";
+    }
+
+    /* KISS follows llk_init() */
+    milliseconds llkTimeoutMs = LLK_TIMEOUT_MS_DEFAULT;
+    seconds khtTimeout = duration_cast<seconds>(
+        llkTimeoutMs * (1 + LLK_CHECKS_PER_TIMEOUT_DEFAULT) / LLK_CHECKS_PER_TIMEOUT_DEFAULT);
+    khtTimeout = GetUintProperty(KHT_TIMEOUT_PROPERTY, khtTimeout);
+    llkTimeoutMs =
+        khtTimeout * LLK_CHECKS_PER_TIMEOUT_DEFAULT / (1 + LLK_CHECKS_PER_TIMEOUT_DEFAULT);
+    llkTimeoutMs = GetUintProperty(LLK_TIMEOUT_MS_PROPERTY, llkTimeoutMs);
+    if (llkTimeoutMs < LLK_TIMEOUT_MS_MINIMUM) {
+        llkTimeoutMs = LLK_TIMEOUT_MS_MINIMUM;
+    }
+    milliseconds llkCheckMs = llkTimeoutMs / LLK_CHECKS_PER_TIMEOUT_DEFAULT;
+    auto timeout = GetUintProperty(
+        (state == 'Z') ? LLK_Z_TIMEOUT_MS_PROPERTY : LLK_D_TIMEOUT_MS_PROPERTY, llkTimeoutMs);
+    if (timeout < LLK_TIMEOUT_MS_MINIMUM) {
+        timeout = LLK_TIMEOUT_MS_MINIMUM;
+    }
+
+    if (llkCheckMs > timeout) {
+        llkCheckMs = timeout;
+    }
+    llkCheckMs = GetUintProperty(LLK_CHECK_MS_PROPERTY, llkCheckMs);
+    timeout += llkCheckMs;
+    auto sec = duration_cast<seconds>(timeout);
+    if (sec == 0s) {
+        ++sec;
+    } else if (sec > 59s) {
+        GTEST_LOG_WARNING << "llkd is configured for about " << duration_cast<minutes>(sec).count()
+                          << " minutes to react\n";
+    }
+
+    // 33% margin for the test to naturally timeout waiting for llkd to respond
+    return (sec * 4 + 2s) / 3;
+}
+
+inline void waitForPid(pid_t child_pid) {
+    int wstatus;
+    ASSERT_LE(0, waitpid(child_pid, &wstatus, 0));
+    EXPECT_FALSE(WIFEXITED(wstatus)) << "[   INFO   ] exit=" << WEXITSTATUS(wstatus);
+    ASSERT_TRUE(WIFSIGNALED(wstatus));
+    ASSERT_EQ(WTERMSIG(wstatus), SIGKILL);
+}
+
+bool checkKill(const char* reason) {
+    if (android::base::GetBoolProperty(LLK_KILLTEST_PROPERTY, LLK_KILLTEST_DEFAULT)) {
+        return false;
+    }
+    auto bootreason = android::base::GetProperty("sys.boot.reason", "nothing");
+    if (bootreason == reason) {
+        GTEST_LOG_INFO << "Expected test result confirmed " << reason << "\n";
+        return true;
+    }
+    GTEST_LOG_WARNING << "Expected test result is " << reason << "\n";
+
+    // apct adjustment if needed (set LLK_KILLTEST_PROPERTY to "off" to allow test)
+    //
+    // if (android::base::GetProperty(LLK_KILLTEST_PROPERTY, "") == "false") {
+    //     GTEST_LOG_WARNING << "Bypassing test\n";
+    //     return true;
+    // }
+
+    return false;
+}
+
+}  // namespace
+
+// The tests that use this helper are to simulate processes stuck in 'D'
+// state that are experiencing forward scheduled progress. As such the
+// expectation is that llkd will _not_ perform any mitigations. The sleepfor
+// argument helps us set the amount of forward scheduler progress.
+static void llkd_driver_ABA(const microseconds sleepfor) {
+    const auto period = llkdSleepPeriod('D');
+    if (period <= sleepfor) {
+        GTEST_LOG_WARNING << "llkd configuration too short for "
+                          << duration_cast<milliseconds>(sleepfor).count() << "ms work cycle\n";
+        return;
+    }
+
+    auto child_pid = fork();
+    ASSERT_LE(0, child_pid);
+    int wstatus;
+    if (!child_pid) {
+        auto ratio = period / sleepfor;
+        ASSERT_LT(0, ratio);
+        // vfork() parent is uninterruptable D state waiting for child to exec()
+        while (--ratio > 0) {
+            auto driver_pid = vfork();
+            ASSERT_LE(0, driver_pid);
+            if (driver_pid) {  // parent
+                waitpid(driver_pid, &wstatus, 0);
+                if (!WIFEXITED(wstatus)) {
+                    exit(42);
+                }
+                if (WEXITSTATUS(wstatus) != 42) {
+                    exit(42);
+                }
+            } else {
+                usleep(sleepfor.count());
+                exit(42);
+            }
+        }
+        exit(0);
+    }
+    ASSERT_LE(0, waitpid(child_pid, &wstatus, 0));
+    EXPECT_TRUE(WIFEXITED(wstatus));
+    if (WIFEXITED(wstatus)) {
+        EXPECT_EQ(0, WEXITSTATUS(wstatus));
+    }
+    ASSERT_FALSE(WIFSIGNALED(wstatus)) << "[   INFO   ] signo=" << WTERMSIG(wstatus);
+}
+
+TEST(llkd, driver_ABA_fast) {
+    llkd_driver_ABA(5ms);
+}
+
+TEST(llkd, driver_ABA_slow) {
+    llkd_driver_ABA(1s);
+}
+
+TEST(llkd, driver_ABA_glacial) {
+    llkd_driver_ABA(1min);
+}
+
+// Following tests must be last in this file to capture possible errant
+// kernel_panic mitigation failure.
+
+// The following tests simulate processes stick in 'Z' or 'D' state with
+// no forward scheduling progress, but interruptible. As such the expectation
+// is that llkd will perform kill mitigation and not progress to kernel_panic.
+
+TEST(llkd, zombie) {
+    if (checkKill("kernel_panic,sysrq,livelock,zombie")) {
+        return;
+    }
+
+    const auto period = llkdSleepPeriod('Z');
+
+    /* Create a Persistent Zombie Process */
+    pid_t child_pid = fork();
+    ASSERT_LE(0, child_pid);
+    if (!child_pid) {
+        auto zombie_pid = fork();
+        ASSERT_LE(0, zombie_pid);
+        if (!zombie_pid) {
+            sleep(1);
+            exit(0);
+        }
+        sleep(period.count());
+        exit(42);
+    }
+
+    waitForPid(child_pid);
+}
+
+TEST(llkd, driver) {
+    if (checkKill("kernel_panic,sysrq,livelock,driver")) {
+        return;
+    }
+
+    const auto period = llkdSleepPeriod('D');
+
+    /* Create a Persistent Device Process */
+    auto child_pid = fork();
+    ASSERT_LE(0, child_pid);
+    if (!child_pid) {
+        // vfork() parent is uninterruptable D state waiting for child to exec()
+        auto driver_pid = vfork();
+        ASSERT_LE(0, driver_pid);
+        sleep(period.count());
+        exit(driver_pid ? 42 : 0);
+    }
+
+    waitForPid(child_pid);
+}
diff --git a/lmkd/Android.bp b/lmkd/Android.bp
index 3f8a503..2efda86 100644
--- a/lmkd/Android.bp
+++ b/lmkd/Android.bp
@@ -3,11 +3,33 @@
 
     srcs: ["lmkd.c"],
     shared_libs: [
-        "liblog",
-        "libprocessgroup",
         "libcutils",
+        "liblog",
     ],
+    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/OWNERS b/lmkd/OWNERS
new file mode 100644
index 0000000..b15bb48
--- /dev/null
+++ b/lmkd/OWNERS
@@ -0,0 +1 @@
+surenb@google.com
diff --git a/lmkd/README.md b/lmkd/README.md
new file mode 100644
index 0000000..656a6ea
--- /dev/null
+++ b/lmkd/README.md
@@ -0,0 +1,65 @@
+Android Low Memory Killer Daemon
+================================
+
+
+Introduction
+------------
+
+Android Low Memory Killer Daemon (lmkd) is a process monitoring memory
+state of a running Android system and reacting to high memory pressure
+by killing the least essential process(es) to keep system performing
+at acceptable levels.
+
+
+Background
+----------
+
+Historically on Android systems memory monitoring and killing of
+non-essential processes was handled by a kernel lowmemorykiller driver.
+Since Linux Kernel 4.12 the lowmemorykiller driver has been removed and
+instead userspace lmkd daemon performs these tasks.
+
+
+Android Properties
+------------------
+
+lmkd can be configured on a particular system using the following Android
+properties:
+
+  ro.config.low_ram:         choose between low-memory vs high-performance
+                             device. Default = false.
+
+  ro.lmk.use_minfree_levels: use free memory and file cache thresholds for
+                             making decisions when to kill. This mode works
+                             the same way kernel lowmemorykiller driver used
+                             to work. Default = false
+
+  ro.lmk.low:                min oom_adj score for processes eligible to be
+                             killed at low vmpressure level. Default = 1001
+                             (disabled)
+
+  ro.lmk.medium:             min oom_adj score for processes eligible to be
+                             killed at medium vmpressure level. Default = 800
+                             (non-essential processes)
+
+  ro.lmk.critical:           min oom_adj score for processes eligible to be
+                             killed at critical vmpressure level. Default = 0
+                             (all processes)
+
+  ro.lmk.critical_upgrade:   enables upgrade to critical level. Default = false
+
+  ro.lmk.upgrade_pressure:   max mem_pressure at which level will be upgraded
+                             because system is swapping too much. Default = 100
+                             (disabled)
+
+  ro.lmk.downgrade_pressure: min mem_pressure at which vmpressure event will
+                             be ignored because enough free memory is still
+                             available. Default = 100 (disabled)
+
+  ro.lmk.kill_heaviest_task: kill heaviest eligible task (best decision) vs.
+                             any eligible task (fast decision). Default = false
+
+  ro.lmk.kill_timeout_ms:    duration in ms after a kill when no additional
+                             kill will be done, Default = 0 (disabled)
+
+  ro.lmk.debug:              enable lmkd debug logs, Default = false
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 8a6168c..c2487d6 100644
--- a/lmkd/lmkd.c
+++ b/lmkd/lmkd.c
@@ -16,65 +16,126 @@
 
 #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 <time.h>
 #include <sys/cdefs.h>
 #include <sys/epoll.h>
 #include <sys/eventfd.h>
 #include <sys/mman.h>
 #include <sys/socket.h>
+#include <sys/sysinfo.h>
 #include <sys/types.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__))
 #endif
 
 #define MEMCG_SYSFS_PATH "/dev/memcg/"
-#define MEMPRESSURE_WATCH_LEVEL "low"
+#define MEMCG_MEMORY_USAGE "/dev/memcg/memory.usage_in_bytes"
+#define MEMCG_MEMORYSW_USAGE "/dev/memcg/memory.memsw.usage_in_bytes"
 #define ZONEINFO_PATH "/proc/zoneinfo"
+#define MEMINFO_PATH "/proc/meminfo"
 #define LINE_MAX 128
 
+/* gid containing AID_SYSTEM required */
 #define INKERNEL_MINFREE_PATH "/sys/module/lowmemorykiller/parameters/minfree"
 #define INKERNEL_ADJ_PATH "/sys/module/lowmemorykiller/parameters/adj"
 
 #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 use_inkernel_interface = true;
+static bool has_inkernel_module;
 
-/* memory pressure level medium event */
-static int mpevfd;
+/* memory pressure levels */
+enum vmpressure_level {
+    VMPRESS_LEVEL_LOW = 0,
+    VMPRESS_LEVEL_MEDIUM,
+    VMPRESS_LEVEL_CRITICAL,
+    VMPRESS_LEVEL_COUNT
+};
 
-/* 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? */
+static const char *level_name[] = {
+    "low",
+    "medium",
+    "critical"
+};
 
-/* 1 memory pressure level, 1 ctrl listen socket, 1 ctrl data socket */
-#define MAX_EPOLL_EVENTS 3
+struct {
+    int64_t min_nr_free_pages; /* recorded but not used yet */
+    int64_t max_nr_free_pages;
+} 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 low_ram_device;
+static bool kill_heaviest_task;
+static unsigned long kill_timeout_ms;
+static bool use_minfree_levels;
+
+/* data required to handle events */
+struct event_handler_info {
+    int data;
+    void (*handler)(int data, uint32_t events);
+};
+
+/* 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;
 
@@ -86,11 +147,84 @@
 static int lowmem_minfree[MAX_TARGETS];
 static int lowmem_targets_size;
 
-struct sysmeminfo {
-    int nr_free_pages;
-    int nr_file_pages;
-    int nr_shmem;
-    int totalreserve_pages;
+/* Fields to parse in /proc/zoneinfo */
+enum zoneinfo_field {
+    ZI_NR_FREE_PAGES = 0,
+    ZI_NR_FILE_PAGES,
+    ZI_NR_SHMEM,
+    ZI_NR_UNEVICTABLE,
+    ZI_WORKINGSET_REFAULT,
+    ZI_HIGH,
+    ZI_FIELD_COUNT
+};
+
+static const char* const zoneinfo_field_names[ZI_FIELD_COUNT] = {
+    "nr_free_pages",
+    "nr_file_pages",
+    "nr_shmem",
+    "nr_unevictable",
+    "workingset_refault",
+    "high",
+};
+
+union zoneinfo {
+    struct {
+        int64_t nr_free_pages;
+        int64_t nr_file_pages;
+        int64_t nr_shmem;
+        int64_t nr_unevictable;
+        int64_t workingset_refault;
+        int64_t high;
+        /* fields below are calculated rather than read from the file */
+        int64_t totalreserve_pages;
+    } field;
+    int64_t arr[ZI_FIELD_COUNT];
+};
+
+/* Fields to parse in /proc/meminfo */
+enum meminfo_field {
+    MI_NR_FREE_PAGES = 0,
+    MI_CACHED,
+    MI_SWAP_CACHED,
+    MI_BUFFERS,
+    MI_SHMEM,
+    MI_UNEVICTABLE,
+    MI_FREE_SWAP,
+    MI_DIRTY,
+    MI_FIELD_COUNT
+};
+
+static const char* const meminfo_field_names[MI_FIELD_COUNT] = {
+    "MemFree:",
+    "Cached:",
+    "SwapCached:",
+    "Buffers:",
+    "Shmem:",
+    "Unevictable:",
+    "SwapFree:",
+    "Dirty:",
+};
+
+union meminfo {
+    struct {
+        int64_t nr_free_pages;
+        int64_t cached;
+        int64_t swap_cached;
+        int64_t buffers;
+        int64_t shmem;
+        int64_t unevictable;
+        int64_t free_swap;
+        int64_t dirty;
+        /* fields below are calculated rather than read from the file */
+        int64_t nr_file_pages;
+    } field;
+    int64_t arr[MI_FIELD_COUNT];
+};
+
+enum field_match_result {
+    NO_MATCH,
+    PARSE_FAIL,
+    PARSE_SUCCESS
 };
 
 struct adjslot_list {
@@ -106,6 +240,11 @@
     struct proc *pidhash_next;
 };
 
+struct reread_data {
+    const char* const filename;
+    int fd;
+};
+
 #define PIDHASH_SZ 1024
 static struct proc *pidhash[PIDHASH_SZ];
 #define pid_hashfn(x) ((((x) >> 8) ^ (x)) & (PIDHASH_SZ - 1))
@@ -113,23 +252,46 @@
 #define ADJTOSLOT(adj) ((adj) + -OOM_SCORE_ADJ_MIN)
 static struct adjslot_list procadjslot_list[ADJTOSLOT(OOM_SCORE_ADJ_MAX) + 1];
 
-/*
- * Wait 1-2 seconds for the death report of a killed process prior to
- * considering killing more processes.
- */
-#define KILL_TIMEOUT 2
-/* Time of last process kill we initiated, stop me before I kill again */
-static time_t kill_lasttime;
-
 /* PAGE_SIZE / 1024 */
 static long page_k;
 
+static bool parse_int64(const char* str, int64_t* ret) {
+    char* endptr;
+    long long val = strtoll(str, &endptr, 10);
+    if (str == endptr || val > INT64_MAX) {
+        return false;
+    }
+    *ret = (int64_t)val;
+    return true;
+}
+
+static enum field_match_result match_field(const char* cp, const char* ap,
+                                   const char* const field_names[],
+                                   int field_count, int64_t* field,
+                                   int *field_idx) {
+    int64_t val;
+    int i;
+
+    for (i = 0; i < field_count; i++) {
+        if (!strcmp(cp, field_names[i])) {
+            *field_idx = i;
+            return parse_int64(ap, field) ? PARSE_SUCCESS : PARSE_FAIL;
+        }
+    }
+    return NO_MATCH;
+}
+
+/*
+ * Read file content from the beginning up to max_len bytes or EOF
+ * whichever happens first.
+ */
 static ssize_t read_all(int fd, char *buf, size_t max_len)
 {
     ssize_t ret = 0;
+    off_t offset = 0;
 
     while (max_len > 0) {
-        ssize_t r = read(fd, buf, max_len);
+        ssize_t r = TEMP_FAILURE_RETRY(pread(fd, buf, max_len, offset));
         if (r == 0) {
             break;
         }
@@ -138,12 +300,44 @@
         }
         ret += r;
         buf += r;
+        offset += r;
         max_len -= r;
     }
 
     return ret;
 }
 
+/*
+ * Read a new or already opened file from the beginning.
+ * If the file has not been opened yet data->fd should be set to -1.
+ * To be used with files which are read often and possibly during high
+ * memory pressure to minimize file opening which by itself requires kernel
+ * memory allocation and might result in a stall on memory stressed system.
+ */
+static int reread_file(struct reread_data *data, char *buf, size_t buf_size) {
+    ssize_t size;
+
+    if (data->fd == -1) {
+        data->fd = open(data->filename, O_RDONLY | O_CLOEXEC);
+        if (data->fd == -1) {
+            ALOGE("%s open: %s", data->filename, strerror(errno));
+            return -1;
+        }
+    }
+
+    size = read_all(data->fd, buf, buf_size - 1);
+    if (size < 0) {
+        ALOGE("%s read: %s", data->filename, strerror(errno));
+        close(data->fd);
+        data->fd = -1;
+        return -1;
+    }
+    ALOG_ASSERT((size_t)size < buf_size - 1, "%s too large", data->filename);
+    buf[size] = 0;
+
+    return 0;
+}
+
 static struct proc *pid_lookup(int pid) {
     struct proc *procp;
 
@@ -217,44 +411,112 @@
     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);
-
-    if (use_inkernel_interface)
+    /* gid containing AID_READPROC required */
+    /* CAP_SYS_RESOURCE required */
+    /* CAP_DAC_OVERRIDE required */
+    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;
+    }
 
-    procp = pid_lookup(pid);
+    if (use_inkernel_interface) {
+        return;
+    }
+
+    if (low_ram_device) {
+        if (params.oomadj >= 900) {
+            soft_limit_mult = 0;
+        } else if (params.oomadj >= 800) {
+            soft_limit_mult = 0;
+        } else if (params.oomadj >= 700) {
+            soft_limit_mult = 0;
+        } else if (params.oomadj >= 600) {
+            // Launcher should be perceptible, don't kill it.
+            params.oomadj = 200;
+            soft_limit_mult = 1;
+        } else if (params.oomadj >= 500) {
+            soft_limit_mult = 0;
+        } else if (params.oomadj >= 400) {
+            soft_limit_mult = 0;
+        } else if (params.oomadj >= 300) {
+            soft_limit_mult = 1;
+        } else if (params.oomadj >= 200) {
+            soft_limit_mult = 2;
+        } else if (params.oomadj >= 100) {
+            soft_limit_mult = 10;
+        } else if (params.oomadj >=   0) {
+            soft_limit_mult = 20;
+        } else {
+            // Persistent processes will have a large
+            // soft limit 512MB.
+            soft_limit_mult = 64;
+        }
+
+        snprintf(path, sizeof(path), MEMCG_SYSFS_PATH
+                 "apps/uid_%d/pid_%d/memory.soft_limit_in_bytes",
+                 params.uid, params.pid);
+        snprintf(val, sizeof(val), "%d", soft_limit_mult * EIGHT_MEGA);
+
+        /*
+         * 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) {
@@ -262,39 +524,44 @@
                 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) {
-    if (use_inkernel_interface)
-        return;
+static void cmd_procremove(LMKD_CTRL_PACKET packet) {
+    struct lmk_procremove params;
 
-    pid_remove(pid);
-    kill_lasttime = 0;
+    if (use_inkernel_interface) {
+        return;
+    }
+
+    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;
 
-    if (use_inkernel_interface) {
+    if (has_inkernel_module) {
         char minfreestr[128];
         char killpriostr[128];
 
@@ -309,28 +576,35 @@
                 strlcat(killpriostr, ",", sizeof(killpriostr));
             }
 
-            snprintf(val, sizeof(val), "%d", lowmem_minfree[i]);
+            snprintf(val, sizeof(val), "%d", use_inkernel_interface ? lowmem_minfree[i] : 0);
             strlcat(minfreestr, val, sizeof(minfreestr));
-            snprintf(val, sizeof(val), "%d", lowmem_adj[i]);
+            snprintf(val, sizeof(val), "%d", use_inkernel_interface ? lowmem_adj[i] : 0);
             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 = TEMP_FAILURE_RETRY(read(data_sock[dsock_idx].sock, buf, bufsz));
 
     if (ret == -1) {
         ALOGE("control data socket read failed; errno=%d", errno);
@@ -342,39 +616,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);
@@ -387,109 +665,195 @@
     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) {
-    int max = 0;
-    int zoneval;
+/* /prop/zoneinfo parsing routines */
+static int64_t zoneinfo_parse_protection(char *cp) {
+    int64_t max = 0;
+    long long zoneval;
     char *save_ptr;
 
-    for (cp = strtok_r(cp, "(), ", &save_ptr); cp; cp = strtok_r(NULL, "), ", &save_ptr)) {
-        zoneval = strtol(cp, &cp, 0);
-        if (zoneval > max)
-            max = zoneval;
+    for (cp = strtok_r(cp, "(), ", &save_ptr); cp;
+         cp = strtok_r(NULL, "), ", &save_ptr)) {
+        zoneval = strtoll(cp, &cp, 0);
+        if (zoneval > max) {
+            max = (zoneval > INT64_MAX) ? INT64_MAX : zoneval;
+        }
     }
 
     return max;
 }
 
-static void zoneinfo_parse_line(char *line, struct sysmeminfo *mip) {
+static bool zoneinfo_parse_line(char *line, union zoneinfo *zi) {
     char *cp = line;
     char *ap;
     char *save_ptr;
+    int64_t val;
+    int field_idx;
 
     cp = strtok_r(line, " ", &save_ptr);
-    if (!cp)
-        return;
+    if (!cp) {
+        return true;
+    }
 
-    ap = strtok_r(NULL, " ", &save_ptr);
-    if (!ap)
-        return;
+    if (!strcmp(cp, "protection:")) {
+        ap = strtok_r(NULL, ")", &save_ptr);
+    } else {
+        ap = strtok_r(NULL, " ", &save_ptr);
+    }
 
-    if (!strcmp(cp, "nr_free_pages"))
-        mip->nr_free_pages += strtol(ap, NULL, 0);
-    else if (!strcmp(cp, "nr_file_pages"))
-        mip->nr_file_pages += strtol(ap, NULL, 0);
-    else if (!strcmp(cp, "nr_shmem"))
-        mip->nr_shmem += strtol(ap, NULL, 0);
-    else if (!strcmp(cp, "high"))
-        mip->totalreserve_pages += strtol(ap, NULL, 0);
-    else if (!strcmp(cp, "protection:"))
-        mip->totalreserve_pages += zoneinfo_parse_protection(ap);
+    if (!ap) {
+        return true;
+    }
+
+    switch (match_field(cp, ap, zoneinfo_field_names,
+                        ZI_FIELD_COUNT, &val, &field_idx)) {
+    case (PARSE_SUCCESS):
+        zi->arr[field_idx] += val;
+        break;
+    case (NO_MATCH):
+        if (!strcmp(cp, "protection:")) {
+            zi->field.totalreserve_pages +=
+                zoneinfo_parse_protection(ap);
+        }
+        break;
+    case (PARSE_FAIL):
+    default:
+        return false;
+    }
+    return true;
 }
 
-static int zoneinfo_parse(struct sysmeminfo *mip) {
-    int fd;
-    ssize_t size;
+static int zoneinfo_parse(union zoneinfo *zi) {
+    static struct reread_data file_data = {
+        .filename = ZONEINFO_PATH,
+        .fd = -1,
+    };
     char buf[PAGE_SIZE];
     char *save_ptr;
     char *line;
 
-    memset(mip, 0, sizeof(struct sysmeminfo));
+    memset(zi, 0, sizeof(union zoneinfo));
 
-    fd = open(ZONEINFO_PATH, O_RDONLY | O_CLOEXEC);
-    if (fd == -1) {
-        ALOGE("%s open: errno=%d", ZONEINFO_PATH, errno);
+    if (reread_file(&file_data, buf, sizeof(buf)) < 0) {
         return -1;
     }
 
-    size = read_all(fd, buf, sizeof(buf) - 1);
-    if (size < 0) {
-        ALOGE("%s read: errno=%d", ZONEINFO_PATH, errno);
-        close(fd);
+    for (line = strtok_r(buf, "\n", &save_ptr); line;
+         line = strtok_r(NULL, "\n", &save_ptr)) {
+        if (!zoneinfo_parse_line(line, zi)) {
+            ALOGE("%s parse error", file_data.filename);
+            return -1;
+        }
+    }
+    zi->field.totalreserve_pages += zi->field.high;
+
+    return 0;
+}
+
+/* /prop/meminfo parsing routines */
+static bool meminfo_parse_line(char *line, union meminfo *mi) {
+    char *cp = line;
+    char *ap;
+    char *save_ptr;
+    int64_t val;
+    int field_idx;
+    enum field_match_result match_res;
+
+    cp = strtok_r(line, " ", &save_ptr);
+    if (!cp) {
+        return false;
+    }
+
+    ap = strtok_r(NULL, " ", &save_ptr);
+    if (!ap) {
+        return false;
+    }
+
+    match_res = match_field(cp, ap, meminfo_field_names, MI_FIELD_COUNT,
+        &val, &field_idx);
+    if (match_res == PARSE_SUCCESS) {
+        mi->arr[field_idx] = val / page_k;
+    }
+    return (match_res != PARSE_FAIL);
+}
+
+static int meminfo_parse(union meminfo *mi) {
+    static struct reread_data file_data = {
+        .filename = MEMINFO_PATH,
+        .fd = -1,
+    };
+    char buf[PAGE_SIZE];
+    char *save_ptr;
+    char *line;
+
+    memset(mi, 0, sizeof(union meminfo));
+
+    if (reread_file(&file_data, buf, sizeof(buf)) < 0) {
         return -1;
     }
-    ALOG_ASSERT((size_t)size < sizeof(buf) - 1, "/proc/zoneinfo too large");
-    buf[size] = 0;
 
-    for (line = strtok_r(buf, "\n", &save_ptr); line; line = strtok_r(NULL, "\n", &save_ptr))
-            zoneinfo_parse_line(line, mip);
+    for (line = strtok_r(buf, "\n", &save_ptr); line;
+         line = strtok_r(NULL, "\n", &save_ptr)) {
+        if (!meminfo_parse_line(line, mi)) {
+            ALOGE("%s parse error", file_data.filename);
+            return -1;
+        }
+    }
+    mi->field.nr_file_pages = mi->field.cached + mi->field.swap_cached +
+        mi->field.buffers;
 
-    close(fd);
     return 0;
 }
 
@@ -501,6 +865,7 @@
     int total;
     ssize_t ret;
 
+    /* gid containing AID_READPROC required */
     snprintf(path, PATH_MAX, "/proc/%d/statm", pid);
     fd = open(path, O_RDONLY | O_CLOEXEC);
     if (fd == -1)
@@ -524,6 +889,7 @@
     char *cp;
     ssize_t ret;
 
+    /* gid containing AID_READPROC required */
     snprintf(path, PATH_MAX, "/proc/%d/cmdline", pid);
     fd = open(path, O_RDONLY | O_CLOEXEC);
     if (fd == -1)
@@ -545,10 +911,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 other_free, int other_file,
-        int minfree, int min_score_adj, bool first)
-{
+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;
@@ -567,109 +955,335 @@
         return -1;
     }
 
-    ALOGI("Killing '%s' (%d), uid %d, adj %d\n"
-          "   to free %ldkB because cache %s%ldkB is below limit %ldkB for oom_adj %d\n"
-          "   Free memory is %s%ldkB %s reserved",
-          taskname, pid, uid, procp->oomadj, tasksize * page_k,
-          first ? "" : "~", other_file * page_k, minfree * page_k, min_score_adj,
-          first ? "" : "~", other_free * page_k, other_free >= 0 ? "above" : "below");
+    TRACE_KILL_START(pid);
+
+    /* CAP_KILL required */
     r = kill(pid, SIGKILL);
-    killProcessGroup(uid, pid, SIGKILL);
+    ALOGI(
+        "Killing '%s' (%d), uid %d, adj %d\n"
+        "   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", procp->pid, errno);
+        ALOGE("kill(%d): errno=%d", pid, errno);
         return -1;
-    } else {
-        return tasksize;
     }
+
+    return tasksize;
 }
 
 /*
- * 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(int other_free, int other_file, bool first)
-{
+static int find_and_kill_processes(enum vmpressure_level level,
+                                   int min_score_adj, int pages_to_free) {
     int i;
-    int min_score_adj = OOM_SCORE_ADJ_MAX + 1;
-    int minfree = 0;
-    int killed_size = 0;
-
-    for (i = 0; i < lowmem_targets_size; i++) {
-        minfree = lowmem_minfree[i];
-        if (other_free < minfree && other_file < minfree) {
-            min_score_adj = lowmem_adj[i];
-            break;
-        }
-    }
-
-    if (min_score_adj == OOM_SCORE_ADJ_MAX + 1)
-        return 0;
+    int killed_size;
+    int pages_freed = 0;
 
     for (i = OOM_SCORE_ADJ_MAX; i >= min_score_adj; i--) {
         struct proc *procp;
 
-retry:
-        procp = proc_adj_lru(i);
+        while (true) {
+            procp = kill_heaviest_task ?
+                proc_get_heaviest(i) : proc_adj_lru(i);
 
-        if (procp) {
-            killed_size = kill_one_process(procp, other_free, other_file, minfree, min_score_adj, first);
-            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 void mp_event(uint32_t events __unused) {
+static int64_t get_memory_usage(struct reread_data *file_data) {
     int ret;
-    unsigned long long evcount;
-    struct sysmeminfo mi;
-    int other_free;
-    int other_file;
-    int killed_size;
-    bool first = true;
+    int64_t mem_usage;
+    char buf[32];
 
-    ret = read(mpevfd, &evcount, sizeof(evcount));
-    if (ret < 0)
-        ALOGE("Error reading memory pressure event fd; errno=%d",
-              errno);
-
-    if (time(NULL) - kill_lasttime < KILL_TIMEOUT)
-        return;
-
-    while (zoneinfo_parse(&mi) < 0) {
-        // Failed to read /proc/zoneinfo, assume ENOMEM and kill something
-        find_and_kill_process(0, 0, true);
+    if (reread_file(file_data, buf, sizeof(buf)) < 0) {
+        return -1;
     }
 
-    other_free = mi.nr_free_pages - mi.totalreserve_pages;
-    other_file = mi.nr_file_pages - mi.nr_shmem;
-
-    do {
-        killed_size = find_and_kill_process(other_free, other_file, first);
-        if (killed_size > 0) {
-            first = false;
-            other_free += killed_size;
-            other_file += killed_size;
-        }
-    } while (killed_size > 0);
+    if (!parse_int64(buf, &mem_usage)) {
+        ALOGE("%s parse error", file_data->filename);
+        return -1;
+    }
+    if (mem_usage == 0) {
+        ALOGE("No memory!");
+        return -1;
+    }
+    return mem_usage;
 }
 
-static int init_mp(char *levelstr, void *event_handler)
-{
+void record_low_pressure_levels(union meminfo *mi) {
+    if (low_pressure_mem.min_nr_free_pages == -1 ||
+        low_pressure_mem.min_nr_free_pages > mi->field.nr_free_pages) {
+        if (debug_process_killing) {
+            ALOGI("Low pressure min memory update from %" PRId64 " to %" PRId64,
+                low_pressure_mem.min_nr_free_pages, mi->field.nr_free_pages);
+        }
+        low_pressure_mem.min_nr_free_pages = mi->field.nr_free_pages;
+    }
+    /*
+     * 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_nr_free_pages that would mess up our stats.
+     */
+    if (low_pressure_mem.max_nr_free_pages == -1 ||
+        (low_pressure_mem.max_nr_free_pages < mi->field.nr_free_pages &&
+         mi->field.nr_free_pages - low_pressure_mem.max_nr_free_pages <
+         low_pressure_mem.max_nr_free_pages * 0.1)) {
+        if (debug_process_killing) {
+            ALOGI("Low pressure max memory update from %" PRId64 " to %" PRId64,
+                low_pressure_mem.max_nr_free_pages, mi->field.nr_free_pages);
+        }
+        low_pressure_mem.max_nr_free_pages = mi->field.nr_free_pages;
+    }
+}
+
+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;
+    int64_t mem_usage, memsw_usage;
+    int64_t mem_pressure;
+    enum vmpressure_level lvl;
+    union meminfo mi;
+    union zoneinfo zi;
+    static struct timeval last_report_tm;
+    static unsigned long skip_count = 0;
+    enum vmpressure_level level = (enum vmpressure_level)data;
+    long other_free = 0, other_file = 0;
+    int min_score_adj;
+    int pages_to_free = 0;
+    int minfree = 0;
+    static struct reread_data mem_usage_file_data = {
+        .filename = MEMCG_MEMORY_USAGE,
+        .fd = -1,
+    };
+    static struct reread_data memsw_usage_file_data = {
+        .filename = MEMCG_MEMORYSW_USAGE,
+        .fd = -1,
+    };
+
+    /*
+     * 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 &&
+            TEMP_FAILURE_RETRY(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 (meminfo_parse(&mi) < 0 || zoneinfo_parse(&zi) < 0) {
+        ALOGE("Failed to get free memory!");
+        return;
+    }
+
+    if (use_minfree_levels) {
+        int i;
+
+        other_free = mi.field.nr_free_pages - zi.field.totalreserve_pages;
+        if (mi.field.nr_file_pages > (mi.field.shmem + mi.field.unevictable + mi.field.swap_cached)) {
+            other_file = (mi.field.nr_file_pages - mi.field.shmem -
+                          mi.field.unevictable - mi.field.swap_cached);
+        } else {
+            other_file = 0;
+        }
+
+        min_score_adj = OOM_SCORE_ADJ_MAX + 1;
+        for (i = 0; i < lowmem_targets_size; i++) {
+            minfree = lowmem_minfree[i];
+            if (other_free < minfree && other_file < minfree) {
+                min_score_adj = lowmem_adj[i];
+                break;
+            }
+        }
+
+        if (min_score_adj == OOM_SCORE_ADJ_MAX + 1) {
+            if (debug_process_killing) {
+                ALOGI("Ignore %s memory pressure event "
+                      "(free memory=%ldkB, cache=%ldkB, limit=%ldkB)",
+                      level_name[level], other_free * page_k, other_file * page_k,
+                      (long)lowmem_minfree[lowmem_targets_size - 1] * page_k);
+            }
+            return;
+        }
+
+        /* Free up enough pages to push over the highest minfree level */
+        pages_to_free = lowmem_minfree[lowmem_targets_size - 1] -
+            ((other_free < other_file) ? other_free : other_file);
+        goto do_kill;
+    }
+
+    if (level == VMPRESS_LEVEL_LOW) {
+        record_low_pressure_levels(&mi);
+    }
+
+    if (level_oomadj[level] > OOM_SCORE_ADJ_MAX) {
+        /* Do not monitor this pressure level */
+        return;
+    }
+
+    if ((mem_usage = get_memory_usage(&mem_usage_file_data)) < 0) {
+        goto do_kill;
+    }
+    if ((memsw_usage = get_memory_usage(&memsw_usage_file_data)) < 0) {
+        goto do_kill;
+    }
+
+    // Calculate percent for swappinness.
+    mem_pressure = (mem_usage * 100) / memsw_usage;
+
+    if (enable_pressure_upgrade && level != VMPRESS_LEVEL_CRITICAL) {
+        // We are swapping too much.
+        if (mem_pressure < upgrade_pressure) {
+            level = upgrade_level(level);
+            if (debug_process_killing) {
+                ALOGI("Event upgraded to %s", level_name[level]);
+            }
+        }
+    }
+
+    // If the pressure is larger than downgrade_pressure lmk will not
+    // kill any process, since enough memory is available.
+    if (mem_pressure > downgrade_pressure) {
+        if (debug_process_killing) {
+            ALOGI("Ignore %s memory pressure", level_name[level]);
+        }
+        return;
+    } else if (level == VMPRESS_LEVEL_CRITICAL &&
+               mem_pressure > upgrade_pressure) {
+        if (debug_process_killing) {
+            ALOGI("Downgrade critical memory pressure");
+        }
+        // Downgrade event, since enough memory available.
+        level = downgrade_level(level);
+    }
+
+do_kill:
+    if (low_ram_device) {
+        /* For Go devices kill only one task */
+        if (find_and_kill_processes(level, level_oomadj[level], 0) == 0) {
+            if (debug_process_killing) {
+                ALOGI("Nothing to kill");
+            }
+        }
+    } else {
+        int pages_freed;
+
+        if (!use_minfree_levels) {
+            /* If pressure level is less than critical and enough free swap then ignore */
+            if (level < VMPRESS_LEVEL_CRITICAL &&
+                mi.field.free_swap > low_pressure_mem.max_nr_free_pages) {
+                if (debug_process_killing) {
+                    ALOGI("Ignoring pressure since %" PRId64
+                          " swap pages are available ",
+                          mi.field.free_swap);
+                }
+                return;
+            }
+            /* Free up enough memory to downgrate the memory pressure to low level */
+            if (mi.field.nr_free_pages < low_pressure_mem.max_nr_free_pages) {
+                pages_to_free = low_pressure_mem.max_nr_free_pages -
+                    mi.field.nr_free_pages;
+            } else {
+                if (debug_process_killing) {
+                    ALOGI("Ignoring pressure since more memory is "
+                        "available (%" PRId64 ") than watermark (%" PRId64 ")",
+                        mi.field.nr_free_pages, low_pressure_mem.max_nr_free_pages);
+                }
+                return;
+            }
+            min_score_adj = level_oomadj[level];
+        } else {
+            if (debug_process_killing) {
+                ALOGI("Killing because cache %ldkB is below "
+                      "limit %ldkB for oom_adj %d\n"
+                      "   Free memory is %ldkB %s reserved",
+                      other_file * page_k, minfree * page_k, min_score_adj,
+                      other_free * page_k, other_free >= 0 ? "above" : "below");
+            }
+        }
+
+        if (debug_process_killing) {
+            ALOGI("Trying to free %d pages", pages_to_free);
+        }
+        pages_freed = find_and_kill_processes(level, min_score_adj, 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 bool init_mp_common(enum vmpressure_level level) {
     int mpfd;
     int evfd;
     int evctlfd;
     char buf[256];
     struct epoll_event epev;
     int ret;
+    int level_idx = (int)level;
+    const char *levelstr = level_name[level_idx];
 
+    /* gid containing AID_SYSTEM required */
     mpfd = open(MEMCG_SYSFS_PATH "memory.pressure_level", O_RDONLY | O_CLOEXEC);
     if (mpfd < 0) {
         ALOGI("No kernel memory.pressure_level support (errno=%d)", errno);
@@ -694,7 +1308,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);
@@ -702,15 +1316,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 = evfd;
-    return 0;
+    mpevfd[level] = evfd;
+    close(evctlfd);
+    return true;
 
 err:
     close(evfd);
@@ -719,7 +1337,7 @@
 err_open_evctlfd:
     close(mpfd);
 err_open_mpfd:
-    return -1;
+    return false;
 }
 
 static int init(void) {
@@ -738,34 +1356,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++;
 
-    use_inkernel_interface = !access(INKERNEL_MINFREE_PATH, W_OK);
+    has_inkernel_module = !access(INKERNEL_MINFREE_PATH, W_OK);
+    use_inkernel_interface = has_inkernel_module;
 
     if (use_inkernel_interface) {
         ALOGI("Using in-kernel low memory killer interface");
     } else {
-        ret = init_mp(MEMPRESSURE_WATCH_LEVEL, (void *)&mp_event);
-        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++) {
@@ -777,12 +1405,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) {
@@ -792,11 +1422,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);
+            }
         }
     }
 }
@@ -806,10 +1458,56 @@
             .sched_priority = 1,
     };
 
-    mlockall(MCL_FUTURE);
-    sched_setscheduler(0, SCHED_FIFO, &param);
-    if (!init())
+    /* 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);
+
+    /* 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", false);
+    low_ram_device = property_get_bool("ro.config.low_ram", false);
+    kill_timeout_ms =
+        (unsigned long)property_get_int32("ro.lmk.kill_timeout_ms", 0);
+    use_minfree_levels =
+        property_get_bool("ro.lmk.use_minfree_levels", false);
+
+    if (!init()) {
+        if (!use_inkernel_interface) {
+            /*
+             * MCL_ONFAULT pins pages as they fault instead of loading
+             * everything immediately all at once. (Which would be bad,
+             * because as of this writing, we have a lot of mapped pages we
+             * never use.) Old kernels will see MCL_ONFAULT and fail with
+             * EINVAL; we ignore this failure.
+             *
+             * N.B. read the man page for mlockall. MCL_CURRENT | MCL_ONFAULT
+             * pins ⊆ MCL_CURRENT, converging to just MCL_CURRENT as we fault
+             * in pages.
+             */
+            /* CAP_IPC_LOCK required */
+            if (mlockall(MCL_CURRENT | MCL_FUTURE | MCL_ONFAULT) && (errno != EINVAL)) {
+                ALOGW("mlockall failed %s", strerror(errno));
+            }
+
+            /* CAP_NICE required */
+            if (sched_setscheduler(0, SCHED_FIFO, &param)) {
+                ALOGW("set SCHED_FIFO failed %s", strerror(errno));
+            }
+        }
+
         mainloop();
+    }
 
     ALOGI("exiting");
     return 0;
diff --git a/lmkd/lmkd.rc b/lmkd/lmkd.rc
index 3bb84ab..76b6055 100644
--- a/lmkd/lmkd.rc
+++ b/lmkd/lmkd.rc
@@ -1,6 +1,8 @@
 service lmkd /system/bin/lmkd
     class core
-    group root readproc
+    user lmkd
+    group lmkd system readproc
+    capabilities DAC_OVERRIDE KILL IPC_LOCK SYS_NICE SYS_RESOURCE
     critical
     socket lmkd seqpacket 0660 system system
     writepid /dev/cpuset/system-background/tasks
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..8c7a75f
--- /dev/null
+++ b/lmkd/tests/lmkd_test.cpp
@@ -0,0 +1,369 @@
+/*
+ * 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();
+
+    // 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;
+    }
+
+    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;
+    }
+
+    // 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/logcat/.clang-format b/logcat/.clang-format
deleted file mode 100644
index 393c309..0000000
--- a/logcat/.clang-format
+++ /dev/null
@@ -1,11 +0,0 @@
-BasedOnStyle: Google
-AllowShortFunctionsOnASingleLine: false
-
-CommentPragmas: NOLINT:.*
-DerivePointerAlignment: false
-IndentWidth: 4
-PointerAlignment: Left
-TabWidth: 4
-PenaltyExcessCharacter: 32
-
-Cpp11BracedListStyle: false
diff --git a/logcat/Android.bp b/logcat/Android.bp
new file mode 100644
index 0000000..b0563a6
--- /dev/null
+++ b/logcat/Android.bp
@@ -0,0 +1,66 @@
+//
+// Copyright (C) 2006 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_defaults {
+    name: "logcat_defaults",
+
+    cflags: [
+        "-Wall",
+        "-Wextra",
+        "-Werror",
+    ],
+    shared_libs: [
+        "libbase",
+        "libcutils",
+        "liblog",
+        "libpcrecpp",
+    ],
+    logtags: ["event.logtags"],
+}
+
+cc_binary {
+    name: "logcat",
+
+    defaults: ["logcat_defaults"],
+    srcs: [
+        "logcat_main.cpp",
+        "logcat.cpp",
+    ],
+}
+
+cc_binary {
+    name: "logcatd",
+
+    defaults: ["logcat_defaults"],
+    srcs: [
+        "logcatd_main.cpp",
+        "logcat.cpp",
+    ],
+}
+
+cc_prebuilt_binary {
+    name: "logpersist.start",
+    srcs: ["logpersist"],
+    init_rc: ["logcatd.rc"],
+    required: ["logcatd"],
+    symlinks: [
+        "logpersist.stop",
+        "logpersist.cat",
+    ],
+    strip: {
+        none: true,
+    },
+}
diff --git a/logcat/Android.mk b/logcat/Android.mk
index 4e11ca9..a716993 100644
--- a/logcat/Android.mk
+++ b/logcat/Android.mk
@@ -2,48 +2,4 @@
 
 LOCAL_PATH := $(call my-dir)
 
-logcatLibs := liblog libbase libcutils libpcrecpp
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := logcat
-LOCAL_SRC_FILES := logcat_main.cpp event.logtags
-LOCAL_SHARED_LIBRARIES := liblogcat $(logcatLibs)
-LOCAL_CFLAGS := -Werror
-
-include $(BUILD_EXECUTABLE)
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := logcatd
-LOCAL_MODULE_TAGS := debug
-LOCAL_SRC_FILES := logcatd_main.cpp event.logtags
-LOCAL_SHARED_LIBRARIES := liblogcat $(logcatLibs)
-LOCAL_CFLAGS := -Werror
-
-include $(BUILD_EXECUTABLE)
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := liblogcat
-LOCAL_SRC_FILES := logcat.cpp getopt_long.cpp logcat_system.cpp
-LOCAL_SHARED_LIBRARIES := $(logcatLibs)
-LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
-LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
-LOCAL_CFLAGS := -Werror
-
-include $(BUILD_SHARED_LIBRARY)
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := logpersist.start
-LOCAL_MODULE_TAGS := debug
-LOCAL_MODULE_CLASS := EXECUTABLES
-LOCAL_INIT_RC := logcatd.rc
-LOCAL_MODULE_PATH := $(bin_dir)
-LOCAL_SRC_FILES := logpersist
-ALL_TOOLS := logpersist.start logpersist.stop logpersist.cat
-LOCAL_POST_INSTALL_CMD := $(hide) $(foreach t,$(filter-out $(LOCAL_MODULE),$(ALL_TOOLS)),ln -sf $(LOCAL_MODULE) $(TARGET_OUT)/bin/$(t);)
-include $(BUILD_PREBUILT)
-
 include $(call first-makefiles-under,$(LOCAL_PATH))
diff --git a/logcat/OWNERS b/logcat/OWNERS
new file mode 100644
index 0000000..babbe4d
--- /dev/null
+++ b/logcat/OWNERS
@@ -0,0 +1 @@
+tomcherry@google.com
diff --git a/logcat/event.logtags b/logcat/event.logtags
index efcc817..da8d2d4 100644
--- a/logcat/event.logtags
+++ b/logcat/event.logtags
@@ -67,8 +67,9 @@
 # ZygoteInit class preloading ends:
 3030 boot_progress_preload_end (time|2|3)
 
-# Dalvik VM
+# Dalvik VM / ART
 20003 dvm_lock_sample (process|3),(main|1|5),(thread|3),(time|1|3),(file|3),(line|1|5),(ownerfile|3),(ownerline|1|5),(sample_percent|1|6)
+20004 art_hidden_api_access (access_method|1),(flags|1),(class|3),(member|3),(type_signature|3)
 
 75000 sqlite_mem_alarm_current (current|1|2)
 75001 sqlite_mem_alarm_max (max|1|2)
@@ -112,6 +113,9 @@
 # graphics timestamp
 # 60100 - 60199 reserved for surfaceflinger
 
+# audio
+# 61000 - 61199 reserved for audioserver
+
 # 0 for screen off, 1 for screen on, 2 for key-guard done
 70000 screen_toggled (screen_state|1|5)
 
@@ -119,6 +123,9 @@
 70200 aggregation (aggregation time|2|3)
 70201 aggregation_test (field1|1|2),(field2|1|2),(field3|1|2),(field4|1|2),(field5|1|2)
 
+# gms refuses to register this log tag, b/30156345
+70220 gms_unknown
+
 # libc failure logging
 80100 bionic_event_memcpy_buffer_overflow (uid|1)
 80105 bionic_event_strcat_buffer_overflow (uid|1)
@@ -140,5 +147,8 @@
 
 1397638484 snet_event_log (subtag|3) (uid|1) (message|3)
 
+# for events that go to stats log buffer
+1937006964 stats_log (atom_id|1|5),(data|4)
+
 # NOTE - the range 1000000-2000000 is reserved for partners and others who
 # want to define their own log tags without conflicting with the core platform.
diff --git a/logcat/getopt_long.cpp b/logcat/getopt_long.cpp
deleted file mode 100644
index 5f8dd66..0000000
--- a/logcat/getopt_long.cpp
+++ /dev/null
@@ -1,402 +0,0 @@
-/* $OpenBSD: getopt_long.c,v 1.26 2013/06/08 22:47:56 millert Exp $ */
-/* $NetBSD: getopt_long.c,v 1.15 2002/01/31 22:43:40 tv Exp $       */
-
-/*
- * Copyright (c) 2002 Todd C. Miller <Todd.Miller@courtesan.com>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- *
- * Sponsored in part by the Defense Advanced Research Projects
- * Agency (DARPA) and Air Force Research Laboratory, Air Force
- * Materiel Command, USAF, under agreement number F39502-99-1-0512.
- */
-/*-
- * Copyright (c) 2000 The NetBSD Foundation, Inc.
- * All rights reserved.
- *
- * This code is derived from software contributed to The NetBSD Foundation
- * by Dieter Baron and Thomas Klausner.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
- * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
- * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
- * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include <stdbool.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/cdefs.h>
-
-#include <log/getopt.h>
-
-#define PRINT_ERROR ((context->opterr) && (*options != ':'))
-
-#define FLAG_PERMUTE 0x01  // permute non-options to the end of argv
-#define FLAG_ALLARGS 0x02  // treat non-options as args to option "-1"
-
-// return values
-#define BADCH (int)'?'
-#define BADARG ((*options == ':') ? (int)':' : (int)'?')
-#define INORDER (int)1
-
-#define D_PREFIX 0
-#define DD_PREFIX 1
-#define W_PREFIX 2
-
-// Compute the greatest common divisor of a and b.
-static int gcd(int a, int b) {
-    int c = a % b;
-    while (c) {
-        a = b;
-        b = c;
-        c = a % b;
-    }
-    return b;
-}
-
-// Exchange the block from nonopt_start to nonopt_end with the block from
-// nonopt_end to opt_end (keeping the same order of arguments in each block).
-// Returns optind - (nonopt_end - nonopt_start) for convenience.
-static int permute_args(getopt_context* context, char* const* nargv) {
-    // compute lengths of blocks and number and size of cycles
-    int nnonopts = context->nonopt_end - context->nonopt_start;
-    int nopts = context->optind - context->nonopt_end;
-    int ncycle = gcd(nnonopts, nopts);
-    int cyclelen = (context->optind - context->nonopt_start) / ncycle;
-
-    for (int i = 0; i < ncycle; i++) {
-        int cstart = context->nonopt_end + i;
-        int pos = cstart;
-        for (int j = 0; j < cyclelen; j++) {
-            if (pos >= context->nonopt_end) {
-                pos -= nnonopts;
-            } else {
-                pos += nopts;
-            }
-            char* swap = nargv[pos];
-            const_cast<char**>(nargv)[pos] = nargv[cstart];
-            const_cast<char**>(nargv)[cstart] = swap;
-        }
-    }
-    return context->optind - (context->nonopt_end - context->nonopt_start);
-}
-
-// parse_long_options_r --
-//    Parse long options in argc/argv argument vector.
-// Returns -1 if short_too is set and the option does not match long_options.
-static int parse_long_options_r(char* const* nargv, const char* options,
-                                const struct option* long_options, int* idx,
-                                bool short_too, struct getopt_context* context) {
-    const char* current_argv = context->place;
-    const char* current_dash;
-    switch (context->dash_prefix) {
-        case D_PREFIX:
-            current_dash = "-";
-            break;
-        case DD_PREFIX:
-            current_dash = "--";
-            break;
-        case W_PREFIX:
-            current_dash = "-W ";
-            break;
-        default:
-            current_dash = "";
-            break;
-    }
-    context->optind++;
-
-    const char* has_equal;
-    size_t current_argv_len;
-    if (!!(has_equal = strchr(current_argv, '='))) {
-        // argument found (--option=arg)
-        current_argv_len = has_equal - current_argv;
-        has_equal++;
-    } else {
-        current_argv_len = strlen(current_argv);
-    }
-
-    int match = -1;
-    bool exact_match = false;
-    bool second_partial_match = false;
-    for (int i = 0; long_options[i].name; i++) {
-        // find matching long option
-        if (strncmp(current_argv, long_options[i].name, current_argv_len)) {
-            continue;
-        }
-
-        if (strlen(long_options[i].name) == current_argv_len) {
-            // exact match
-            match = i;
-            exact_match = true;
-            break;
-        }
-        // If this is a known short option, don't allow
-        // a partial match of a single character.
-        if (short_too && current_argv_len == 1) continue;
-
-        if (match == -1) {  // first partial match
-            match = i;
-        } else if (long_options[i].has_arg != long_options[match].has_arg ||
-                   long_options[i].flag != long_options[match].flag ||
-                   long_options[i].val != long_options[match].val) {
-            second_partial_match = true;
-        }
-    }
-    if (!exact_match && second_partial_match) {
-        // ambiguous abbreviation
-        if (PRINT_ERROR) {
-            fprintf(context->optstderr ?: stderr,
-                    "option `%s%.*s' is ambiguous", current_dash,
-                    (int)current_argv_len, current_argv);
-        }
-        context->optopt = 0;
-        return BADCH;
-    }
-    if (match != -1) {  // option found
-        if (long_options[match].has_arg == no_argument && has_equal) {
-            if (PRINT_ERROR) {
-                fprintf(context->optstderr ?: stderr,
-                        "option `%s%.*s' doesn't allow an argument",
-                        current_dash, (int)current_argv_len, current_argv);
-            }
-            // XXX: GNU sets optopt to val regardless of flag
-            context->optopt =
-                long_options[match].flag ? 0 : long_options[match].val;
-            return BADCH;
-        }
-        if (long_options[match].has_arg == required_argument ||
-            long_options[match].has_arg == optional_argument) {
-            if (has_equal) {
-                context->optarg = has_equal;
-            } else if (long_options[match].has_arg == required_argument) {
-                // optional argument doesn't use next nargv
-                context->optarg = nargv[context->optind++];
-            }
-        }
-        if ((long_options[match].has_arg == required_argument) &&
-            !context->optarg) {
-            // Missing argument; leading ':' indicates no error
-            // should be generated.
-            if (PRINT_ERROR) {
-                fprintf(context->optstderr ?: stderr,
-                        "option `%s%s' requires an argument", current_dash,
-                        current_argv);
-            }
-            // XXX: GNU sets optopt to val regardless of flag
-            context->optopt =
-                long_options[match].flag ? 0 : long_options[match].val;
-            context->optind--;
-            return BADARG;
-        }
-    } else {  // unknown option
-        if (short_too) {
-            context->optind--;
-            return -1;
-        }
-        if (PRINT_ERROR) {
-            fprintf(context->optstderr ?: stderr, "unrecognized option `%s%s'",
-                    current_dash, current_argv);
-        }
-        context->optopt = 0;
-        return BADCH;
-    }
-    if (idx) *idx = match;
-    if (long_options[match].flag) {
-        *long_options[match].flag = long_options[match].val;
-        return 0;
-    }
-    return long_options[match].val;
-}
-
-// getopt_long_r --
-//    Parse argc/argv argument vector.
-int getopt_long_r(int nargc, char* const* nargv, const char* options,
-                  const struct option* long_options, int* idx,
-                  struct getopt_context* context) {
-    if (!options) return -1;
-
-    // XXX Some GNU programs (like cvs) set optind to 0 instead of
-    // XXX using optreset.  Work around this braindamage.
-    if (!context->optind) context->optind = context->optreset = 1;
-
-    // Disable GNU extensions if options string begins with a '+'.
-    int flags = FLAG_PERMUTE;
-    if (*options == '-') {
-        flags |= FLAG_ALLARGS;
-    } else if (*options == '+') {
-        flags &= ~FLAG_PERMUTE;
-    }
-    if (*options == '+' || *options == '-') options++;
-
-    context->optarg = nullptr;
-    if (context->optreset) context->nonopt_start = context->nonopt_end = -1;
-start:
-    if (context->optreset || !*context->place) {  // update scanning pointer
-        context->optreset = 0;
-        if (context->optind >= nargc) {  // end of argument vector
-            context->place = EMSG;
-            if (context->nonopt_end != -1) {
-                // do permutation, if we have to
-                context->optind = permute_args(context, nargv);
-            } else if (context->nonopt_start != -1) {
-                // If we skipped non-options, set optind to the first of them.
-                context->optind = context->nonopt_start;
-            }
-            context->nonopt_start = context->nonopt_end = -1;
-            return -1;
-        }
-        if (*(context->place = nargv[context->optind]) != '-' ||
-            context->place[1] == '\0') {
-            context->place = EMSG;  // found non-option
-            if (flags & FLAG_ALLARGS) {
-                // GNU extension: return non-option as argument to option 1
-                context->optarg = nargv[context->optind++];
-                return INORDER;
-            }
-            if (!(flags & FLAG_PERMUTE)) {
-                // If no permutation wanted, stop parsing at first non-option.
-                return -1;
-            }
-            // do permutation
-            if (context->nonopt_start == -1) {
-                context->nonopt_start = context->optind;
-            } else if (context->nonopt_end != -1) {
-                context->nonopt_start = permute_args(context, nargv);
-                context->nonopt_end = -1;
-            }
-            context->optind++;
-            // process next argument
-            goto start;
-        }
-        if (context->nonopt_start != -1 && context->nonopt_end == -1) {
-            context->nonopt_end = context->optind;
-        }
-
-        // If we have "-" do nothing, if "--" we are done.
-        if (context->place[1] != '\0' && *++(context->place) == '-' &&
-            context->place[1] == '\0') {
-            context->optind++;
-            context->place = EMSG;
-            // We found an option (--), so if we skipped
-            // non-options, we have to permute.
-            if (context->nonopt_end != -1) {
-                context->optind = permute_args(context, nargv);
-            }
-            context->nonopt_start = context->nonopt_end = -1;
-            return -1;
-        }
-    }
-
-    int optchar;
-    // Check long options if:
-    //  1) we were passed some
-    //  2) the arg is not just "-"
-    //  3) either the arg starts with -- we are getopt_long_only()
-    if (long_options && context->place != nargv[context->optind] &&
-        (*context->place == '-')) {
-        bool short_too = false;
-        context->dash_prefix = D_PREFIX;
-        if (*context->place == '-') {
-            context->place++;  // --foo long option
-            context->dash_prefix = DD_PREFIX;
-        } else if (*context->place != ':' && strchr(options, *context->place)) {
-            short_too = true;  // could be short option too
-        }
-
-        optchar = parse_long_options_r(nargv, options, long_options, idx,
-                                       short_too, context);
-        if (optchar != -1) {
-            context->place = EMSG;
-            return optchar;
-        }
-    }
-
-    const char* oli;  // option letter list index
-    if ((optchar = (int)*(context->place)++) == (int)':' ||
-        (optchar == (int)'-' && *context->place != '\0') ||
-        !(oli = strchr(options, optchar))) {
-        // If the user specified "-" and  '-' isn't listed in
-        // options, return -1 (non-option) as per POSIX.
-        // Otherwise, it is an unknown option character (or ':').
-        if (optchar == (int)'-' && *context->place == '\0') return -1;
-        if (!*context->place) context->optind++;
-        if (PRINT_ERROR) {
-            fprintf(context->optstderr ?: stderr, "invalid option -- %c",
-                    optchar);
-        }
-        context->optopt = optchar;
-        return BADCH;
-    }
-
-    static const char recargchar[] = "option requires an argument -- %c";
-    if (long_options && optchar == 'W' && oli[1] == ';') {
-        // -W long-option
-        if (*context->place) {                      // no space
-            ;                                       // NOTHING
-        } else if (++(context->optind) >= nargc) {  // no arg
-            context->place = EMSG;
-            if (PRINT_ERROR) {
-                fprintf(context->optstderr ?: stderr, recargchar, optchar);
-            }
-            context->optopt = optchar;
-            return BADARG;
-        } else {  // white space
-            context->place = nargv[context->optind];
-        }
-        context->dash_prefix = W_PREFIX;
-        optchar = parse_long_options_r(nargv, options, long_options, idx, false,
-                                       context);
-        context->place = EMSG;
-        return optchar;
-    }
-    if (*++oli != ':') {  // doesn't take argument
-        if (!*context->place) context->optind++;
-    } else {  // takes (optional) argument
-        context->optarg = nullptr;
-        if (*context->place) {  // no white space
-            context->optarg = context->place;
-        } else if (oli[1] != ':') {              // arg not optional
-            if (++(context->optind) >= nargc) {  // no arg
-                context->place = EMSG;
-                if (PRINT_ERROR) {
-                    fprintf(context->optstderr ?: stderr, recargchar, optchar);
-                }
-                context->optopt = optchar;
-                return BADARG;
-            }
-            context->optarg = nargv[context->optind];
-        }
-        context->place = EMSG;
-        context->optind++;
-    }
-    // dump back option letter
-    return optchar;
-}
diff --git a/logcat/include/log/getopt.h b/logcat/include/log/getopt.h
deleted file mode 100644
index 0da2b10..0000000
--- a/logcat/include/log/getopt.h
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Copyright (C) 2017 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 _LOG_GETOPT_H_
-#define _LOG_GETOPT_H_
-
-#ifndef __ANDROID_USE_LIBLOG_LOGCAT_INTERFACE
-#ifndef __ANDROID_API__
-#define __ANDROID_USE_LIBLOG_LOGCAT_INTERFACE 1
-#elif __ANDROID_API__ > 24 /* > Nougat */
-#define __ANDROID_USE_LIBLOG_LOGCAT_INTERFACE 1
-#else
-#define __ANDROID_USE_LIBLOG_LOGCAT_INTERFACE 0
-#endif
-#endif
-
-#if __ANDROID_USE_LIBLOG_LOGCAT_INTERFACE
-
-#include <getopt.h>
-#include <sys/cdefs.h>
-
-struct getopt_context {
-    int opterr;
-    int optind;
-    int optopt;
-    int optreset;
-    const char* optarg;
-    FILE* optstderr; /* NULL defaults to stderr */
-    /* private */
-    const char* place;
-    int nonopt_start;
-    int nonopt_end;
-    int dash_prefix;
-    /* expansion space */
-    int __extra__;
-    void* __stuff__;
-};
-
-#define EMSG ""
-#define NO_PREFIX (-1)
-
-#define INIT_GETOPT_CONTEXT(context) \
-    context = { 1, 1, '?', 0, NULL, NULL, EMSG, -1, -1, NO_PREFIX, 0, NULL }
-
-__BEGIN_DECLS
-int getopt_long_r(int nargc, char* const* nargv, const char* options,
-                  const struct option* long_options, int* idx,
-                  struct getopt_context* context);
-
-__END_DECLS
-
-#endif /* __ANDROID_USE_LIBLOG_LOGCAT_INTERFACE */
-
-#endif /* !_LOG_GETOPT_H_ */
diff --git a/logcat/include/log/logcat.h b/logcat/include/log/logcat.h
deleted file mode 100644
index 009672c..0000000
--- a/logcat/include/log/logcat.h
+++ /dev/null
@@ -1,122 +0,0 @@
-/*
- * Copyright (C) 2005-2017 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 _LIBS_LOGCAT_H /* header boilerplate */
-#define _LIBS_LOGCAT_H
-
-#include <stdio.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#ifndef __ANDROID_USE_LIBLOG_LOGCAT_INTERFACE
-#ifndef __ANDROID_API__
-#define __ANDROID_USE_LIBLOG_LOGCAT_INTERFACE 1
-#elif __ANDROID_API__ > 24 /* > Nougat */
-#define __ANDROID_USE_LIBLOG_LOGCAT_INTERFACE 1
-#else
-#define __ANDROID_USE_LIBLOG_LOGCAT_INTERFACE 0
-#endif
-#endif
-
-#if __ANDROID_USE_LIBLOG_LOGCAT_INTERFACE
-
-/* For managing an in-process logcat function, rather than forking/execing
- *
- * It also serves as the basis for the logcat command.
- *
- * The following C API allows a logcat instance to be created, run
- * to completion, and then release all the associated resources.
- */
-
-/*
- * The opaque context
- */
-#ifndef __android_logcat_context_defined /* typedef boilerplate */
-#define __android_logcat_context_defined
-typedef struct android_logcat_context_internal* android_logcat_context;
-#endif
-
-/* Creates a context associated with this logcat instance
- *
- * Returns a pointer to the context, or a NULL on error.
- */
-android_logcat_context create_android_logcat();
-
-/* Collects and outputs the logcat data to output and error file descriptors
- *
- * Will block, performed in-thread and in-process
- *
- * The output file descriptor variable, if greater than or equal to 0, is
- * where the output (ie: stdout) will be sent. The file descriptor is closed
- * on android_logcat_destroy which terminates the instance, or when an -f flag
- * (output redirect to a file) is present in the command.  The error file
- * descriptor variable, if greater than or equal to 0, is where the error
- * stream (ie: stderr) will be sent, also closed on android_logcat_destroy.
- * The error file descriptor can be set to equal to the output file descriptor,
- * which will mix output and error stream content, and will defer closure of
- * the file descriptor on -f flag redirection.  Negative values for the file
- * descriptors will use stdout and stderr FILE references respectively
- * internally, and will not close the references as noted above.
- *
- * Return value is 0 for success, non-zero for errors.
- */
-int android_logcat_run_command(android_logcat_context ctx, int output, int error,
-                               int argc, char* const* argv, char* const* envp);
-
-/* Will not block, performed in-process
- *
- * Starts a thread, opens a pipe, returns reading end fd, saves off argv.
- * The command supports 2>&1 (mix content) and 2>/dev/null (drop content) for
- * scripted error (stderr) redirection.
- */
-int android_logcat_run_command_thread(android_logcat_context ctx, int argc,
-                                      char* const* argv, char* const* envp);
-int android_logcat_run_command_thread_running(android_logcat_context ctx);
-
-/* Finished with context
- *
- * Kill the command thread ASAP (if any), and free up all associated resources.
- *
- * Return value is the result of the android_logcat_run_command, or
- * non-zero for any errors.
- */
-int android_logcat_destroy(android_logcat_context* ctx);
-
-/* derived helpers */
-
-/*
- * In-process thread that acts like somewhat like libc-like system and popen
- * respectively.  Can not handle shell scripting, only pure calls to the
- * logcat operations. The android_logcat_system is a wrapper for the
- * create_android_logcat, android_logcat_run_command and android_logcat_destroy
- * API above.  The android_logcat_popen is a wrapper for the
- * android_logcat_run_command_thread API above.  The android_logcat_pclose is
- * a wrapper for a reasonable wait until output has subsided for command
- * completion, fclose on the FILE pointer and the android_logcat_destroy API.
- */
-int android_logcat_system(const char* command);
-FILE* android_logcat_popen(android_logcat_context* ctx, const char* command);
-int android_logcat_pclose(android_logcat_context* ctx, FILE* output);
-
-#endif /* __ANDROID_USE_LIBLOG_LOGCAT_INTERFACE */
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* _LIBS_LOGCAT_H */
diff --git a/logcat/logcat.cpp b/logcat/logcat.cpp
index e9ef9cc..0f56337 100644
--- a/logcat/logcat.cpp
+++ b/logcat/logcat.cpp
@@ -14,12 +14,15 @@
  * limitations under the License.
  */
 
+#include "logcat.h"
+
 #include <arpa/inet.h>
 #include <assert.h>
 #include <ctype.h>
 #include <dirent.h>
 #include <errno.h>
 #include <fcntl.h>
+#include <getopt.h>
 #include <math.h>
 #include <pthread.h>
 #include <sched.h>
@@ -47,8 +50,6 @@
 #include <cutils/sched_policy.h>
 #include <cutils/sockets.h>
 #include <log/event_tag_map.h>
-#include <log/getopt.h>
-#include <log/logcat.h>
 #include <log/logprint.h>
 #include <private/android_logger.h>
 #include <system/thread_defs.h>
@@ -457,7 +458,7 @@
                     "  -d              Dump the log and then exit (don't block)\n"
                     "  -e <expr>, --regex=<expr>\n"
                     "                  Only print lines where the log message matches <expr>\n"
-                    "                  where <expr> is a regular expression\n"
+                    "                  where <expr> is a Perl-compatible regular expression\n"
                     // Leave --head undocumented as alias for -m
                     "  -m <count>, --max-count=<count>\n"
                     "                  Quit after printing <count> lines. This is meant to be\n"
@@ -854,14 +855,8 @@
     // net for stability dealing with possible mistaken inputs.
     static const char delimiters[] = ",:; \t\n\r\f";
 
-    struct getopt_context optctx;
-    INIT_GETOPT_CONTEXT(optctx);
-    optctx.opterr = !!context->error;
-    optctx.optstderr = context->error;
-
-    for (;;) {
-        int ret;
-
+    optind = 0;
+    while (true) {
         int option_index = 0;
         // list of long-argument only strings for later comparison
         static const char pid_str[] = "pid";
@@ -902,19 +897,18 @@
         };
         // clang-format on
 
-        ret = getopt_long_r(argc, argv, ":cdDhLt:T:gG:sQf:r:n:v:b:BSpP:m:e:",
-                            long_options, &option_index, &optctx);
-        if (ret < 0) break;
+        int c = getopt_long(argc, argv, ":cdDhLt:T:gG:sQf:r:n:v:b:BSpP:m:e:", long_options,
+                            &option_index);
+        if (c == -1) break;
 
-        switch (ret) {
+        switch (c) {
             case 0:
                 // only long options
                 if (long_options[option_index].name == pid_str) {
                     // ToDo: determine runtime PID_MAX?
-                    if (!getSizeTArg(optctx.optarg, &pid, 1)) {
+                    if (!getSizeTArg(optarg, &pid, 1)) {
                         logcat_panic(context, HELP_TRUE, "%s %s out of range\n",
-                                     long_options[option_index].name,
-                                     optctx.optarg);
+                                     long_options[option_index].name, optarg);
                         goto exit;
                     }
                     break;
@@ -924,11 +918,9 @@
                             ANDROID_LOG_NONBLOCK;
                     // ToDo: implement API that supports setting a wrap timeout
                     size_t dummy = ANDROID_LOG_WRAP_DEFAULT_TIMEOUT;
-                    if (optctx.optarg &&
-                        !getSizeTArg(optctx.optarg, &dummy, 1)) {
+                    if (optarg && !getSizeTArg(optarg, &dummy, 1)) {
                         logcat_panic(context, HELP_TRUE, "%s %s out of range\n",
-                                     long_options[option_index].name,
-                                     optctx.optarg);
+                                     long_options[option_index].name, optarg);
                         goto exit;
                     }
                     if ((dummy != ANDROID_LOG_WRAP_DEFAULT_TIMEOUT) &&
@@ -949,8 +941,7 @@
                     break;
                 }
                 if (long_options[option_index].name == id_str) {
-                    setId = (optctx.optarg && optctx.optarg[0]) ? optctx.optarg
-                                                                : nullptr;
+                    setId = (optarg && optarg[0]) ? optarg : nullptr;
                 }
                 break;
 
@@ -978,32 +969,27 @@
                 mode |= ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK;
             // FALLTHRU
             case 'T':
-                if (strspn(optctx.optarg, "0123456789") !=
-                    strlen(optctx.optarg)) {
-                    char* cp = parseTime(tail_time, optctx.optarg);
+                if (strspn(optarg, "0123456789") != strlen(optarg)) {
+                    char* cp = parseTime(tail_time, optarg);
                     if (!cp) {
-                        logcat_panic(context, HELP_FALSE,
-                                     "-%c \"%s\" not in time format\n", ret,
-                                     optctx.optarg);
+                        logcat_panic(context, HELP_FALSE, "-%c \"%s\" not in time format\n", c,
+                                     optarg);
                         goto exit;
                     }
                     if (*cp) {
-                        char c = *cp;
+                        char ch = *cp;
                         *cp = '\0';
                         if (context->error) {
-                            fprintf(
-                                context->error,
-                                "WARNING: -%c \"%s\"\"%c%s\" time truncated\n",
-                                ret, optctx.optarg, c, cp + 1);
+                            fprintf(context->error, "WARNING: -%c \"%s\"\"%c%s\" time truncated\n",
+                                    c, optarg, ch, cp + 1);
                         }
-                        *cp = c;
+                        *cp = ch;
                     }
                 } else {
-                    if (!getSizeTArg(optctx.optarg, &tail_lines, 1)) {
+                    if (!getSizeTArg(optarg, &tail_lines, 1)) {
                         if (context->error) {
-                            fprintf(context->error,
-                                    "WARNING: -%c %s invalid, setting to 1\n",
-                                    ret, optctx.optarg);
+                            fprintf(context->error, "WARNING: -%c %s invalid, setting to 1\n", c,
+                                    optarg);
                         }
                         tail_lines = 1;
                     }
@@ -1015,22 +1001,19 @@
                 break;
 
             case 'e':
-                context->regex = new pcrecpp::RE(optctx.optarg);
+                context->regex = new pcrecpp::RE(optarg);
                 break;
 
             case 'm': {
-                char* end = nullptr;
-                if (!getSizeTArg(optctx.optarg, &context->maxCount)) {
+                if (!getSizeTArg(optarg, &context->maxCount)) {
                     logcat_panic(context, HELP_FALSE,
-                                 "-%c \"%s\" isn't an "
-                                 "integer greater than zero\n",
-                                 ret, optctx.optarg);
+                                 "-%c \"%s\" isn't an integer greater than zero\n", c, optarg);
                     goto exit;
                 }
             } break;
 
             case 'g':
-                if (!optctx.optarg) {
+                if (!optarg) {
                     getLogSize = true;
                     break;
                 }
@@ -1038,8 +1021,8 @@
 
             case 'G': {
                 char* cp;
-                if (strtoll(optctx.optarg, &cp, 0) > 0) {
-                    setLogSize = strtoll(optctx.optarg, &cp, 0);
+                if (strtoll(optarg, &cp, 0) > 0) {
+                    setLogSize = strtoll(optarg, &cp, 0);
                 } else {
                     setLogSize = 0;
                 }
@@ -1072,19 +1055,18 @@
             } break;
 
             case 'p':
-                if (!optctx.optarg) {
+                if (!optarg) {
                     getPruneList = true;
                     break;
                 }
             // FALLTHRU
 
             case 'P':
-                setPruneList = optctx.optarg;
+                setPruneList = optarg;
                 break;
 
             case 'b': {
-                std::unique_ptr<char, void (*)(void*)> buffers(
-                    strdup(optctx.optarg), free);
+                std::unique_ptr<char, void (*)(void*)> buffers(strdup(optarg), free);
                 char* arg = buffers.get();
                 unsigned idMask = 0;
                 char* sv = nullptr;  // protect against -ENOMEM above
@@ -1127,8 +1109,9 @@
                     }
                     if (found) continue;
 
-                    bool binary =
-                        !strcmp(name, "events") || !strcmp(name, "security");
+                    bool binary = !strcmp(name, "events") ||
+                                  !strcmp(name, "security") ||
+                                  !strcmp(name, "stats");
                     log_device_t* d = new log_device_t(name, binary);
 
                     if (dev) {
@@ -1147,42 +1130,34 @@
 
             case 'f':
                 if ((tail_time == log_time::EPOCH) && !tail_lines) {
-                    tail_time = lastLogTime(optctx.optarg);
+                    tail_time = lastLogTime(optarg);
                 }
                 // redirect output to a file
-                context->outputFileName = optctx.optarg;
+                context->outputFileName = optarg;
                 break;
 
             case 'r':
-                if (!getSizeTArg(optctx.optarg, &context->logRotateSizeKBytes,
-                                 1)) {
-                    logcat_panic(context, HELP_TRUE,
-                                 "Invalid parameter \"%s\" to -r\n",
-                                 optctx.optarg);
+                if (!getSizeTArg(optarg, &context->logRotateSizeKBytes, 1)) {
+                    logcat_panic(context, HELP_TRUE, "Invalid parameter \"%s\" to -r\n", optarg);
                     goto exit;
                 }
                 break;
 
             case 'n':
-                if (!getSizeTArg(optctx.optarg, &context->maxRotatedLogs, 1)) {
-                    logcat_panic(context, HELP_TRUE,
-                                 "Invalid parameter \"%s\" to -n\n",
-                                 optctx.optarg);
+                if (!getSizeTArg(optarg, &context->maxRotatedLogs, 1)) {
+                    logcat_panic(context, HELP_TRUE, "Invalid parameter \"%s\" to -n\n", optarg);
                     goto exit;
                 }
                 break;
 
             case 'v': {
-                if (!strcmp(optctx.optarg, "help") ||
-                    !strcmp(optctx.optarg, "--help")) {
+                if (!strcmp(optarg, "help") || !strcmp(optarg, "--help")) {
                     show_format_help(context);
                     context->retval = EXIT_SUCCESS;
                     goto exit;
                 }
-                std::unique_ptr<char, void (*)(void*)> formats(
-                    strdup(optctx.optarg), free);
+                std::unique_ptr<char, void (*)(void*)> formats(strdup(optarg), free);
                 char* arg = formats.get();
-                unsigned idMask = 0;
                 char* sv = nullptr;  // protect against -ENOMEM above
                 while (!!(arg = strtok_r(arg, delimiters, &sv))) {
                     err = setLogFormat(context, arg);
@@ -1256,7 +1231,7 @@
                         // example: "qemu_pipe,pipe:logcat"
                         // upon opening of /dev/qemu_pipe, the "pipe:logcat"
                         // string with trailing '\0' should be written to the fd
-                        size_t pos = devname.find(",");
+                        size_t pos = devname.find(',');
                         if (pos != std::string::npos) {
                             pipePurpose = devname.substr(pos + 1);
                             devname = devname.substr(0, pos);
@@ -1301,8 +1276,7 @@
                 break;
 
             case ':':
-                logcat_panic(context, HELP_TRUE,
-                             "Option -%c needs an argument\n", optctx.optopt);
+                logcat_panic(context, HELP_TRUE, "Option -%c needs an argument\n", optopt);
                 goto exit;
 
             case 'h':
@@ -1311,8 +1285,7 @@
                 goto exit;
 
             default:
-                logcat_panic(context, HELP_TRUE, "Unrecognized Option %c\n",
-                             optctx.optopt);
+                logcat_panic(context, HELP_TRUE, "Unrecognized Option %c\n", optopt);
                 goto exit;
         }
     }
@@ -1401,7 +1374,7 @@
                          "Invalid filter expression in logcat args\n");
             goto exit;
         }
-    } else if (argc == optctx.optind) {
+    } else if (argc == optind) {
         // Add from environment variable
         const char* env_tags_orig = android::getenv(context, "ANDROID_LOG_TAGS");
 
@@ -1417,7 +1390,7 @@
         }
     } else {
         // Add from commandline
-        for (int i = optctx.optind ; i < argc ; i++) {
+        for (int i = optind ; i < argc ; i++) {
             // skip stderr redirections of _all_ kinds
             if ((argv[i][0] == '2') && (argv[i][1] == '>')) continue;
             // skip stdout redirections of _all_ kinds
@@ -1638,7 +1611,7 @@
                 logcat_panic(context, HELP_FALSE, "read: unexpected length.\n");
                 break;
             }
-            logcat_panic(context, HELP_FALSE, "logcat read failure");
+            logcat_panic(context, HELP_FALSE, "logcat read failure\n");
             break;
         }
 
@@ -1708,105 +1681,6 @@
     return __logcat(context);
 }
 
-// starts a thread, opens a pipe, returns reading end.
-int android_logcat_run_command_thread(android_logcat_context ctx,
-                                      int argc, char* const* argv,
-                                      char* const* envp) {
-    android_logcat_context_internal* context = ctx;
-
-    int save_errno = EBUSY;
-    if ((context->fds[0] >= 0) || (context->fds[1] >= 0)) goto exit;
-
-    if (pipe(context->fds) < 0) {
-        save_errno = errno;
-        goto exit;
-    }
-
-    pthread_attr_t attr;
-    if (pthread_attr_init(&attr)) {
-        save_errno = errno;
-        goto close_exit;
-    }
-
-    struct sched_param param;
-    memset(&param, 0, sizeof(param));
-    pthread_attr_setschedparam(&attr, &param);
-    pthread_attr_setschedpolicy(&attr, SCHED_BATCH);
-    if (pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED)) {
-        int save_errno = errno;
-        goto pthread_attr_exit;
-    }
-
-    context->stop = false;
-    context->thread_stopped = false;
-    context->output_fd = context->fds[1];
-    // save off arguments so they remain while thread is active.
-    for (int i = 0; i < argc; ++i) {
-        context->args.push_back(std::string(argv[i]));
-    }
-    // save off environment so they remain while thread is active.
-    if (envp) for (size_t i = 0; envp[i]; ++i) {
-        context->envs.push_back(std::string(envp[i]));
-    }
-
-    for (auto& str : context->args) {
-        context->argv_hold.push_back(str.c_str());
-    }
-    context->argv_hold.push_back(nullptr);
-    for (auto& str : context->envs) {
-        context->envp_hold.push_back(str.c_str());
-    }
-    context->envp_hold.push_back(nullptr);
-
-    context->argc = context->argv_hold.size() - 1;
-    context->argv = (char* const*)&context->argv_hold[0];
-    context->envp = (char* const*)&context->envp_hold[0];
-
-#ifdef DEBUG
-    fprintf(stderr, "argv[%d] = {", context->argc);
-    for (auto str : context->argv_hold) {
-        fprintf(stderr, " \"%s\"", str ?: "nullptr");
-    }
-    fprintf(stderr, " }\n");
-    fflush(stderr);
-#endif
-    context->retval = EXIT_SUCCESS;
-    if (pthread_create(&context->thr, &attr,
-                       (void*(*)(void*))__logcat, context)) {
-        int save_errno = errno;
-        goto argv_exit;
-    }
-    pthread_attr_destroy(&attr);
-
-    return context->fds[0];
-
-argv_exit:
-    context->argv_hold.clear();
-    context->args.clear();
-    context->envp_hold.clear();
-    context->envs.clear();
-pthread_attr_exit:
-    pthread_attr_destroy(&attr);
-close_exit:
-    close(context->fds[0]);
-    context->fds[0] = -1;
-    close(context->fds[1]);
-    context->fds[1] = -1;
-exit:
-    errno = save_errno;
-    context->stop = true;
-    context->thread_stopped = true;
-    context->retval = EXIT_FAILURE;
-    return -1;
-}
-
-// test if the thread is still doing 'stuff'
-int android_logcat_run_command_thread_running(android_logcat_context ctx) {
-    android_logcat_context_internal* context = ctx;
-
-    return context->thread_stopped == false;
-}
-
 // Finished with context
 int android_logcat_destroy(android_logcat_context* ctx) {
     android_logcat_context_internal* context = *ctx;
@@ -1833,11 +1707,10 @@
     }
     android::close_output(context);
     android::close_error(context);
+
     if (context->fds[1] >= 0) {
-        // NB: could be closed by the above fclose(s), ignore error.
-        int save_errno = errno;
+        // NB: this should be closed by close_output, but just in case...
         close(context->fds[1]);
-        errno = save_errno;
         context->fds[1] = -1;
     }
 
diff --git a/logcat/logcat.h b/logcat/logcat.h
new file mode 100644
index 0000000..85ed7da
--- /dev/null
+++ b/logcat/logcat.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2005-2017 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.
+ */
+
+#pragma once
+
+#include <stdio.h>
+
+/*
+ * The opaque context
+ */
+typedef struct android_logcat_context_internal* android_logcat_context;
+
+/* Creates a context associated with this logcat instance
+ *
+ * Returns a pointer to the context, or a NULL on error.
+ */
+android_logcat_context create_android_logcat();
+
+/* Collects and outputs the logcat data to output and error file descriptors
+ *
+ * Will block, performed in-thread and in-process
+ *
+ * The output file descriptor variable, if greater than or equal to 0, is
+ * where the output (ie: stdout) will be sent. The file descriptor is closed
+ * on android_logcat_destroy which terminates the instance, or when an -f flag
+ * (output redirect to a file) is present in the command.  The error file
+ * descriptor variable, if greater than or equal to 0, is where the error
+ * stream (ie: stderr) will be sent, also closed on android_logcat_destroy.
+ * The error file descriptor can be set to equal to the output file descriptor,
+ * which will mix output and error stream content, and will defer closure of
+ * the file descriptor on -f flag redirection.  Negative values for the file
+ * descriptors will use stdout and stderr FILE references respectively
+ * internally, and will not close the references as noted above.
+ *
+ * Return value is 0 for success, non-zero for errors.
+ */
+int android_logcat_run_command(android_logcat_context ctx, int output, int error, int argc,
+                               char* const* argv, char* const* envp);
+
+/* Finished with context
+ *
+ * Kill the command thread ASAP (if any), and free up all associated resources.
+ *
+ * Return value is the result of the android_logcat_run_command, or
+ * non-zero for any errors.
+ */
+int android_logcat_destroy(android_logcat_context* ctx);
diff --git a/logcat/logcat_main.cpp b/logcat/logcat_main.cpp
index 9477e79..ecfa2ba 100644
--- a/logcat/logcat_main.cpp
+++ b/logcat/logcat_main.cpp
@@ -17,7 +17,7 @@
 #include <signal.h>
 #include <stdlib.h>
 
-#include <log/logcat.h>
+#include "logcat.h"
 
 int main(int argc, char** argv, char** envp) {
     android_logcat_context ctx = create_android_logcat();
diff --git a/logcat/logcat_system.cpp b/logcat/logcat_system.cpp
deleted file mode 100644
index ea393bd..0000000
--- a/logcat/logcat_system.cpp
+++ /dev/null
@@ -1,148 +0,0 @@
-/*
- * Copyright (C) 2017 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 <ctype.h>
-#include <stdio.h>
-#include <string.h>
-#include <unistd.h>
-
-#include <string>
-#include <vector>
-
-#include <log/logcat.h>
-
-static std::string unquote(const char*& cp, const char*& delim) {
-    if ((*cp == '\'') || (*cp == '"')) {
-        // KISS: Simple quotes. Do not handle the case
-        //       of concatenation like "blah"foo'bar'
-        char quote = *cp++;
-        delim = strchr(cp, quote);
-        if (!delim) delim = cp + strlen(cp);
-        std::string str(cp, delim);
-        if (*delim) ++delim;
-        return str;
-    }
-    delim = strpbrk(cp, " \t\f\r\n");
-    if (!delim) delim = cp + strlen(cp);
-    return std::string(cp, delim);
-}
-
-static bool __android_logcat_parse(const char* command,
-                                   std::vector<std::string>& args,
-                                   std::vector<std::string>& envs) {
-    for (const char *delim, *cp = command; cp && *cp; cp = delim) {
-        while (isspace(*cp)) ++cp;
-        if ((args.size() == 0) && (*cp != '=') && !isdigit(*cp)) {
-            const char* env = cp;
-            while (isalnum(*cp) || (*cp == '_')) ++cp;
-            if (cp && (*cp == '=')) {
-                std::string str(env, ++cp);
-                str += unquote(cp, delim);
-                envs.push_back(str);
-                continue;
-            }
-            cp = env;
-        }
-        args.push_back(unquote(cp, delim));
-        if ((args.size() == 1) && (args[0] != "logcat") &&
-            (args[0] != "/system/bin/logcat")) {
-            return false;
-        }
-    }
-    return args.size() != 0;
-}
-
-FILE* android_logcat_popen(android_logcat_context* ctx, const char* command) {
-    *ctx = NULL;
-
-    std::vector<std::string> args;
-    std::vector<std::string> envs;
-    if (!__android_logcat_parse(command, args, envs)) return NULL;
-
-    std::vector<const char*> argv;
-    for (auto& str : args) {
-        argv.push_back(str.c_str());
-    }
-    argv.push_back(NULL);
-
-    std::vector<const char*> envp;
-    for (auto& str : envs) {
-        envp.push_back(str.c_str());
-    }
-    envp.push_back(NULL);
-
-    *ctx = create_android_logcat();
-    if (!*ctx) return NULL;
-
-    int fd = android_logcat_run_command_thread(
-        *ctx, argv.size() - 1, (char* const*)&argv[0], (char* const*)&envp[0]);
-    argv.clear();
-    args.clear();
-    envp.clear();
-    envs.clear();
-    if (fd < 0) {
-        android_logcat_destroy(ctx);
-        return NULL;
-    }
-
-    FILE* retval = fdopen(fd, "reb");
-    if (!retval) android_logcat_destroy(ctx);
-    return retval;
-}
-
-int android_logcat_pclose(android_logcat_context* ctx, FILE* output) {
-    if (*ctx) {
-        static const useconds_t wait_sample = 20000;
-        // Wait two seconds maximum
-        for (size_t retry = ((2 * 1000000) + wait_sample - 1) / wait_sample;
-             android_logcat_run_command_thread_running(*ctx) && retry; --retry) {
-            usleep(wait_sample);
-        }
-    }
-
-    if (output) fclose(output);
-    return android_logcat_destroy(ctx);
-}
-
-int android_logcat_system(const char* command) {
-    std::vector<std::string> args;
-    std::vector<std::string> envs;
-    if (!__android_logcat_parse(command, args, envs)) return -1;
-
-    std::vector<const char*> argv;
-    for (auto& str : args) {
-        argv.push_back(str.c_str());
-    }
-    argv.push_back(NULL);
-
-    std::vector<const char*> envp;
-    for (auto& str : envs) {
-        envp.push_back(str.c_str());
-    }
-    envp.push_back(NULL);
-
-    android_logcat_context ctx = create_android_logcat();
-    if (!ctx) return -1;
-    /* Command return value */
-    int retval = android_logcat_run_command(ctx, -1, -1, argv.size() - 1,
-                                            (char* const*)&argv[0],
-                                            (char* const*)&envp[0]);
-    /* destroy return value */
-    int ret = android_logcat_destroy(&ctx);
-    /* Paranoia merging any discrepancies between the two return values */
-    if (!ret) ret = retval;
-    return ret;
-}
diff --git a/logcat/logcatd_main.cpp b/logcat/logcatd_main.cpp
index 9109eb1..c131846 100644
--- a/logcat/logcatd_main.cpp
+++ b/logcat/logcatd_main.cpp
@@ -21,7 +21,7 @@
 #include <string>
 #include <vector>
 
-#include <log/logcat.h>
+#include "logcat.h"
 
 int main(int argc, char** argv, char** envp) {
     android_logcat_context ctx = create_android_logcat();
diff --git a/logcat/tests/Android.mk b/logcat/tests/Android.mk
index defd3c4..66f6724 100644
--- a/logcat/tests/Android.mk
+++ b/logcat/tests/Android.mk
@@ -32,7 +32,6 @@
 
 benchmark_src_files := \
     logcat_benchmark.cpp \
-    exec_benchmark.cpp \
 
 # Build benchmarks for the device. Run with:
 #   adb shell /data/nativetest/logcat-benchmarks/logcat-benchmarks
@@ -41,7 +40,7 @@
 LOCAL_MODULE_TAGS := $(test_tags)
 LOCAL_CFLAGS += $(test_c_flags)
 LOCAL_SRC_FILES := $(benchmark_src_files)
-LOCAL_SHARED_LIBRARIES := libbase liblogcat
+LOCAL_SHARED_LIBRARIES := libbase
 include $(BUILD_NATIVE_BENCHMARK)
 
 # -----------------------------------------------------------------------------
@@ -51,7 +50,6 @@
 test_src_files := \
     logcat_test.cpp \
     logcatd_test.cpp \
-    liblogcat_test.cpp \
 
 # Build tests for the device (with .so). Run with:
 #   adb shell /data/nativetest/logcat-unit-tests/logcat-unit-tests
@@ -59,6 +57,6 @@
 LOCAL_MODULE := $(test_module_prefix)unit-tests
 LOCAL_MODULE_TAGS := $(test_tags)
 LOCAL_CFLAGS += $(test_c_flags)
-LOCAL_SHARED_LIBRARIES := liblog libbase liblogcat
+LOCAL_SHARED_LIBRARIES := liblog libbase
 LOCAL_SRC_FILES := $(test_src_files)
 include $(BUILD_NATIVE_TEST)
diff --git a/logcat/tests/exec_benchmark.cpp b/logcat/tests/exec_benchmark.cpp
deleted file mode 100644
index c30a5f5..0000000
--- a/logcat/tests/exec_benchmark.cpp
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * Copyright (C) 2017 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 <stdio.h>
-
-#include <android-base/file.h>
-#include <benchmark/benchmark.h>
-#include <log/logcat.h>
-
-// Dump the statistics and report results
-
-static void logcat_popen_libc(benchmark::State& state, const char* cmd) {
-    while (state.KeepRunning()) {
-        FILE* fp = popen(cmd, "r");
-        std::string ret;
-        android::base::ReadFdToString(fileno(fp), &ret);
-        pclose(fp);
-    }
-}
-
-static void BM_logcat_stat_popen_libc(benchmark::State& state) {
-    logcat_popen_libc(state, "logcat -b all -S");
-}
-BENCHMARK(BM_logcat_stat_popen_libc);
-
-static void logcat_popen_liblogcat(benchmark::State& state, const char* cmd) {
-    while (state.KeepRunning()) {
-        android_logcat_context ctx;
-        FILE* fp = android_logcat_popen(&ctx, cmd);
-        std::string ret;
-        android::base::ReadFdToString(fileno(fp), &ret);
-        android_logcat_pclose(&ctx, fp);
-    }
-}
-
-static void BM_logcat_stat_popen_liblogcat(benchmark::State& state) {
-    logcat_popen_liblogcat(state, "logcat -b all -S");
-}
-BENCHMARK(BM_logcat_stat_popen_liblogcat);
-
-static void logcat_system_libc(benchmark::State& state, const char* cmd) {
-    while (state.KeepRunning()) {
-        system(cmd);
-    }
-}
-
-static void BM_logcat_stat_system_libc(benchmark::State& state) {
-    logcat_system_libc(state, "logcat -b all -S >/dev/null 2>/dev/null");
-}
-BENCHMARK(BM_logcat_stat_system_libc);
-
-static void logcat_system_liblogcat(benchmark::State& state, const char* cmd) {
-    while (state.KeepRunning()) {
-        android_logcat_system(cmd);
-    }
-}
-
-static void BM_logcat_stat_system_liblogcat(benchmark::State& state) {
-    logcat_system_liblogcat(state, "logcat -b all -S >/dev/null 2>/dev/null");
-}
-BENCHMARK(BM_logcat_stat_system_liblogcat);
-
-// Dump the logs and report results
-
-static void BM_logcat_dump_popen_libc(benchmark::State& state) {
-    logcat_popen_libc(state, "logcat -b all -d");
-}
-BENCHMARK(BM_logcat_dump_popen_libc);
-
-static void BM_logcat_dump_popen_liblogcat(benchmark::State& state) {
-    logcat_popen_liblogcat(state, "logcat -b all -d");
-}
-BENCHMARK(BM_logcat_dump_popen_liblogcat);
-
-static void BM_logcat_dump_system_libc(benchmark::State& state) {
-    logcat_system_libc(state, "logcat -b all -d >/dev/null 2>/dev/null");
-}
-BENCHMARK(BM_logcat_dump_system_libc);
-
-static void BM_logcat_dump_system_liblogcat(benchmark::State& state) {
-    logcat_system_liblogcat(state, "logcat -b all -d >/dev/null 2>/dev/null");
-}
-BENCHMARK(BM_logcat_dump_system_liblogcat);
diff --git a/logcat/tests/liblogcat_test.cpp b/logcat/tests/liblogcat_test.cpp
deleted file mode 100644
index 9e9a2c2..0000000
--- a/logcat/tests/liblogcat_test.cpp
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * Copyright (C) 2017 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 <log/logcat.h>
-
-#define logcat_define(context) android_logcat_context context
-#define logcat_popen(context, command) android_logcat_popen(&context, command)
-#define logcat_pclose(context, fp) android_logcat_pclose(&context, fp)
-#define logcat_system(command) android_logcat_system(command)
-#define logcat liblogcat
-
-#include "logcat_test.cpp"
diff --git a/logcat/tests/logcat_test.cpp b/logcat/tests/logcat_test.cpp
index 786fb14..cc1632a 100644
--- a/logcat/tests/logcat_test.cpp
+++ b/logcat/tests/logcat_test.cpp
@@ -37,12 +37,6 @@
 #include <log/log.h>
 #include <log/log_event_list.h>
 
-#ifndef logcat_popen
-#define logcat_define(context)
-#define logcat_popen(context, command) popen((command), "r")
-#define logcat_pclose(context, fp) pclose(fp)
-#define logcat_system(command) system(command)
-#endif
 #ifndef logcat_executable
 #define USING_LOGCAT_EXECUTABLE_DEFAULT
 #define logcat_executable "logcat"
@@ -78,7 +72,6 @@
 
 TEST(logcat, buckets) {
     FILE* fp;
-    logcat_define(ctx);
 
 #undef LOG_TAG
 #define LOG_TAG "inject.buckets"
@@ -90,10 +83,9 @@
     __android_log_bswrite(0, logcat_executable ".inject.buckets");
     rest();
 
-    ASSERT_TRUE(NULL !=
-                (fp = logcat_popen(
-                     ctx, logcat_executable
-                     " -b radio -b events -b system -b main -d 2>/dev/null")));
+    ASSERT_TRUE(NULL != (fp = popen(logcat_executable
+                                    " -b radio -b events -b system -b main -d 2>/dev/null",
+                                    "r")));
 
     char buffer[BIG_BUFFER];
 
@@ -111,7 +103,7 @@
         }
     }
 
-    logcat_pclose(ctx, fp);
+    pclose(fp);
 
     EXPECT_EQ(ids, 15);
 
@@ -120,7 +112,6 @@
 
 TEST(logcat, event_tag_filter) {
     FILE* fp;
-    logcat_define(ctx);
 
 #undef LOG_TAG
 #define LOG_TAG "inject.filter"
@@ -135,7 +126,7 @@
         logcat_executable
         " -b radio -b system -b main --pid=%d -d -s inject.filter 2>/dev/null",
         getpid());
-    ASSERT_TRUE(NULL != (fp = logcat_popen(ctx, command.c_str())));
+    ASSERT_TRUE(NULL != (fp = popen(command.c_str(), "r")));
 
     char buffer[BIG_BUFFER];
 
@@ -145,7 +136,7 @@
         if (strncmp(begin, buffer, sizeof(begin) - 1)) ++count;
     }
 
-    logcat_pclose(ctx, fp);
+    pclose(fp);
 
     // logcat, liblogcat and logcatd test instances result in the progression
     // of 3, 6 and 9 for our counts as each round is performed.
@@ -191,7 +182,6 @@
 
     do {
         FILE* fp;
-        logcat_define(ctx);
 
         char needle[32];
         time_t now;
@@ -205,9 +195,8 @@
 #endif
         strftime(needle, sizeof(needle), "[ %Y-", ptm);
 
-        ASSERT_TRUE(NULL != (fp = logcat_popen(
-                                 ctx, logcat_executable
-                                 " -v long -v year -b all -t 3 2>/dev/null")));
+        ASSERT_TRUE(NULL !=
+                    (fp = popen(logcat_executable " -v long -v year -b all -t 3 2>/dev/null", "r")));
 
         char buffer[BIG_BUFFER];
 
@@ -218,7 +207,7 @@
                 ++count;
             }
         }
-        logcat_pclose(ctx, fp);
+        pclose(fp);
 
     } while ((count < 3) && --tries && inject(3 - count));
 
@@ -268,12 +257,10 @@
 
     do {
         FILE* fp;
-        logcat_define(ctx);
 
-        ASSERT_TRUE(NULL !=
-                    (fp = logcat_popen(ctx, logcat_executable
-                                       " -v long -v America/Los_Angeles "
-                                       "-b all -t 3 2>/dev/null")));
+        ASSERT_TRUE(NULL != (fp = popen(logcat_executable
+                                        " -v long -v America/Los_Angeles -b all -t 3 2>/dev/null",
+                                        "r")));
 
         char buffer[BIG_BUFFER];
 
@@ -287,7 +274,7 @@
             }
         }
 
-        logcat_pclose(ctx, fp);
+        pclose(fp);
 
     } while ((count < 3) && --tries && inject(3 - count));
 
@@ -296,11 +283,11 @@
 
 TEST(logcat, ntz) {
     FILE* fp;
-    logcat_define(ctx);
 
-    ASSERT_TRUE(NULL != (fp = logcat_popen(ctx, logcat_executable
-                                           " -v long -v America/Los_Angeles -v "
-                                           "zone -b all -t 3 2>/dev/null")));
+    ASSERT_TRUE(NULL !=
+                (fp = popen(logcat_executable
+                            " -v long -v America/Los_Angeles -v zone -b all -t 3 2>/dev/null",
+                            "r")));
 
     char buffer[BIG_BUFFER];
 
@@ -312,7 +299,7 @@
         }
     }
 
-    logcat_pclose(ctx, fp);
+    pclose(fp);
 
     ASSERT_EQ(0, count);
 }
@@ -330,8 +317,7 @@
                  "ANDROID_PRINTF_LOG=long logcat -b all -t %d 2>/dev/null", num);
 
         FILE* fp;
-        logcat_define(ctx);
-        ASSERT_TRUE(NULL != (fp = logcat_popen(ctx, buffer)));
+        ASSERT_TRUE(NULL != (fp = popen(buffer, "r")));
 
         count = 0;
 
@@ -339,7 +325,7 @@
             ++count;
         }
 
-        logcat_pclose(ctx, fp);
+        pclose(fp);
 
     } while ((count < num) && --tries && inject(num - count));
 
@@ -377,8 +363,7 @@
 
     do {
         snprintf(buffer, sizeof(buffer), "%s -t 10 2>&1", cmd);
-        logcat_define(ctx);
-        ASSERT_TRUE(NULL != (fp = logcat_popen(ctx, buffer)));
+        ASSERT_TRUE(NULL != (fp = popen(buffer, "r")));
         count = 0;
 
         while ((input = fgetLongTime(buffer, sizeof(buffer), fp))) {
@@ -391,7 +376,7 @@
             free(last_timestamp);
             last_timestamp = strdup(input);
         }
-        logcat_pclose(ctx, fp);
+        pclose(fp);
 
     } while ((count < 10) && --tries && inject(10 - count));
 
@@ -401,8 +386,7 @@
     EXPECT_TRUE(second_timestamp != NULL);
 
     snprintf(buffer, sizeof(buffer), "%s -t '%s' 2>&1", cmd, first_timestamp);
-    logcat_define(ctx);
-    ASSERT_TRUE(NULL != (fp = logcat_popen(ctx, buffer)));
+    ASSERT_TRUE(NULL != (fp = popen(buffer, "r")));
 
     int second_count = 0;
     int last_timestamp_count = -1;
@@ -442,7 +426,7 @@
             last_timestamp_count = second_count;
         }
     }
-    logcat_pclose(ctx, fp);
+    pclose(fp);
 
     EXPECT_TRUE(found);
     if (!found) {
@@ -483,10 +467,8 @@
     ASSERT_LT(0, __android_log_btwrite(0, EVENT_TYPE_LONG, &ts, sizeof(ts)));
 
     FILE* fp;
-    logcat_define(ctx);
     ASSERT_TRUE(NULL !=
-                (fp = logcat_popen(ctx, logcat_executable
-                                   " -v brief -b events -t 100 2>/dev/null")));
+                (fp = popen(logcat_executable " -v brief -b events -t 100 2>/dev/null", "r")));
 
     char buffer[BIG_BUFFER];
 
@@ -507,7 +489,7 @@
         }
     }
 
-    logcat_pclose(ctx, fp);
+    pclose(fp);
 
     ASSERT_EQ(1, count);
 }
@@ -521,12 +503,9 @@
 
     FILE* fp[256];  // does this count as a multitude!
     memset(fp, 0, sizeof(fp));
-    logcat_define(ctx[sizeof(fp) / sizeof(fp[0])]);
     size_t num = 0;
     do {
-        EXPECT_TRUE(NULL !=
-                    (fp[num] = logcat_popen(ctx[num], logcat_executable
-                                            " -v brief -b events -t 100")));
+        EXPECT_TRUE(NULL != (fp[num] = popen(logcat_executable " -v brief -b events -t 100", "r")));
         if (!fp[num]) {
             fprintf(stderr,
                     "WARNING: limiting to %zu simultaneous logcat operations\n",
@@ -556,7 +535,7 @@
             }
         }
 
-        logcat_pclose(ctx[idx], fp[idx]);
+        pclose(fp[idx]);
     }
 
     ASSERT_EQ(num, count);
@@ -564,10 +543,9 @@
 
 static int get_groups(const char* cmd) {
     FILE* fp;
-    logcat_define(ctx);
 
     // NB: crash log only available in user space
-    EXPECT_TRUE(NULL != (fp = logcat_popen(ctx, cmd)));
+    EXPECT_TRUE(NULL != (fp = popen(cmd, "r")));
 
     if (fp == NULL) {
         return 0;
@@ -631,7 +609,7 @@
         }
     }
 
-    logcat_pclose(ctx, fp);
+    pclose(fp);
 
     return count;
 }
@@ -815,7 +793,7 @@
     snprintf(command, sizeof(command), comm, buf);
 
     int ret;
-    EXPECT_FALSE(IsFalse(ret = logcat_system(command), command));
+    EXPECT_FALSE(IsFalse(ret = system(command), command));
     if (!ret) {
         snprintf(command, sizeof(command), "ls -s %s 2>/dev/null", buf);
 
@@ -861,7 +839,7 @@
     snprintf(command, sizeof(command), logcat_cmd, tmp_out_dir);
 
     int ret;
-    EXPECT_FALSE(IsFalse(ret = logcat_system(command), command));
+    EXPECT_FALSE(IsFalse(ret = system(command), command));
     if (!ret) {
         snprintf(command, sizeof(command), "ls %s 2>/dev/null", tmp_out_dir);
 
@@ -920,7 +898,7 @@
     snprintf(command, sizeof(command), logcat_cmd, tmp_out_dir, log_filename);
 
     int ret;
-    EXPECT_FALSE(IsFalse(ret = logcat_system(command), command));
+    EXPECT_FALSE(IsFalse(ret = system(command), command));
     if (ret) {
         snprintf(command, sizeof(command), cleanup_cmd, tmp_out_dir);
         EXPECT_FALSE(IsFalse(system(command), command));
@@ -969,7 +947,7 @@
     // re-run the command, it should only add a few lines more content if it
     // continues where it left off.
     snprintf(command, sizeof(command), logcat_cmd, tmp_out_dir, log_filename);
-    EXPECT_FALSE(IsFalse(ret = logcat_system(command), command));
+    EXPECT_FALSE(IsFalse(ret = system(command), command));
     if (ret) {
         snprintf(command, sizeof(command), cleanup_cmd, tmp_out_dir);
         EXPECT_FALSE(IsFalse(system(command), command));
@@ -1052,7 +1030,7 @@
                  tmp_out_dir, log_filename, num_val);
 
         int ret;
-        EXPECT_FALSE(IsFalse(ret = logcat_system(command), command));
+        EXPECT_FALSE(IsFalse(ret = system(command), command));
         if (ret) {
             snprintf(command, sizeof(command), cleanup_cmd, tmp_out_dir);
             EXPECT_FALSE(IsFalse(system(command), command));
@@ -1082,7 +1060,7 @@
         strcat(command, clear_cmd);
 
         int ret;
-        EXPECT_FALSE(IsFalse(ret = logcat_system(command), command));
+        EXPECT_FALSE(IsFalse(ret = system(command), command));
         if (ret) {
             snprintf(command, sizeof(command), cleanup_cmd, tmp_out_dir);
             EXPECT_FALSE(system(command));
@@ -1120,7 +1098,7 @@
 
     snprintf(command, sizeof(command), logcat_cmd, tmp_out_dir, log_filename);
 
-    int ret = logcat_system(command);
+    int ret = system(command);
     if (ret) {
         fprintf(stderr, "system(\"%s\")=%d", command, ret);
         return -1;
@@ -1194,7 +1172,7 @@
         " -b all -d"
         " -f /das/nein/gerfingerpoken/logcat/log.txt"
         " -n 256 -r 1024";
-    EXPECT_FALSE(IsFalse(0 == logcat_system(command), command));
+    EXPECT_FALSE(IsFalse(0 == system(command), command));
 }
 
 #ifndef logcat
@@ -1329,10 +1307,7 @@
 #endif
 
 static bool get_white_black(char** list) {
-    FILE* fp;
-    logcat_define(ctx);
-
-    fp = logcat_popen(ctx, logcat_executable " -p 2>/dev/null");
+    FILE* fp = popen(logcat_executable " -p 2>/dev/null", "r");
     if (fp == NULL) {
         fprintf(stderr, "ERROR: logcat -p 2>/dev/null\n");
         return false;
@@ -1360,19 +1335,15 @@
             asprintf(list, "%s", buf);
         }
     }
-    logcat_pclose(ctx, fp);
+    pclose(fp);
     return *list != NULL;
 }
 
 static bool set_white_black(const char* list) {
-    FILE* fp;
-    logcat_define(ctx);
-
     char buffer[BIG_BUFFER];
-
     snprintf(buffer, sizeof(buffer), logcat_executable " -P '%s' 2>&1",
              list ? list : "");
-    fp = logcat_popen(ctx, buffer);
+    FILE* fp = popen(buffer, "r");
     if (fp == NULL) {
         fprintf(stderr, "ERROR: %s\n", buffer);
         return false;
@@ -1391,10 +1362,10 @@
             continue;
         }
         fprintf(stderr, "%s\n", buf);
-        logcat_pclose(ctx, fp);
+        pclose(fp);
         return false;
     }
-    return logcat_pclose(ctx, fp) == 0;
+    return pclose(fp) == 0;
 }
 
 TEST(logcat, white_black_adjust) {
@@ -1429,7 +1400,6 @@
 
 TEST(logcat, regex) {
     FILE* fp;
-    logcat_define(ctx);
     int count = 0;
 
     char buffer[BIG_BUFFER];
@@ -1450,7 +1420,7 @@
     // Let the logs settle
     rest();
 
-    ASSERT_TRUE(NULL != (fp = logcat_popen(ctx, buffer)));
+    ASSERT_TRUE(NULL != (fp = popen(buffer, "r")));
 
     while (fgets(buffer, sizeof(buffer), fp)) {
         if (!strncmp(begin, buffer, sizeof(begin) - 1)) {
@@ -1462,14 +1432,13 @@
         count++;
     }
 
-    logcat_pclose(ctx, fp);
+    pclose(fp);
 
     ASSERT_EQ(2, count);
 }
 
 TEST(logcat, maxcount) {
     FILE* fp;
-    logcat_define(ctx);
     int count = 0;
 
     char buffer[BIG_BUFFER];
@@ -1488,7 +1457,7 @@
 
     rest();
 
-    ASSERT_TRUE(NULL != (fp = logcat_popen(ctx, buffer)));
+    ASSERT_TRUE(NULL != (fp = popen(buffer, "r")));
 
     while (fgets(buffer, sizeof(buffer), fp)) {
         if (!strncmp(begin, buffer, sizeof(begin) - 1)) {
@@ -1498,7 +1467,7 @@
         count++;
     }
 
-    logcat_pclose(ctx, fp);
+    pclose(fp);
 
     ASSERT_EQ(3, count);
 }
@@ -1510,13 +1479,7 @@
     ;
 
 static bool End_to_End(const char* tag, const char* fmt, ...) {
-    logcat_define(ctx);
-    FILE* fp = logcat_popen(ctx, logcat_executable
-                            " -v brief"
-                            " -b events"
-                            " -v descriptive"
-                            " -t 100"
-                            " 2>/dev/null");
+    FILE* fp = popen(logcat_executable " -v brief -b events -v descriptive -t 100 2>/dev/null", "r");
     if (!fp) {
         fprintf(stderr, "End_to_End: popen failed");
         return false;
@@ -1551,7 +1514,7 @@
         }
     }
 
-    logcat_pclose(ctx, fp);
+    pclose(fp);
 
     if ((count == 0) && (lastMatch.length() > 0)) {
         // Help us pinpoint where things went wrong ...
@@ -1741,13 +1704,12 @@
 }
 
 static bool reportedSecurity(const char* command) {
-    logcat_define(ctx);
-    FILE* fp = logcat_popen(ctx, command);
+    FILE* fp = popen(command, "r");
     if (!fp) return true;
 
     std::string ret;
     bool val = android::base::ReadFdToString(fileno(fp), &ret);
-    logcat_pclose(ctx, fp);
+    pclose(fp);
 
     if (!val) return true;
     return std::string::npos != ret.find("'security'");
@@ -1762,13 +1724,12 @@
 }
 
 static size_t commandOutputSize(const char* command) {
-    logcat_define(ctx);
-    FILE* fp = logcat_popen(ctx, command);
+    FILE* fp = popen(command, "r");
     if (!fp) return 0;
 
     std::string ret;
     if (!android::base::ReadFdToString(fileno(fp), &ret)) return 0;
-    if (logcat_pclose(ctx, fp) != 0) return 0;
+    if (pclose(fp) != 0) return 0;
 
     return ret.size();
 }
diff --git a/logd/Android.bp b/logd/Android.bp
index 68b79d3..5c79976 100644
--- a/logd/Android.bp
+++ b/logd/Android.bp
@@ -25,7 +25,7 @@
     "-DAUDITD_LOG_TAG=1003",
     "-DCHATTY_LOG_TAG=1004",
     "-DTAG_DEF_LOG_TAG=1005",
-    "-DLIBLOG_LOG_TAG=1006"
+    "-DLIBLOG_LOG_TAG=1006",
 ]
 
 cc_library_static {
diff --git a/logd/CommandListener.cpp b/logd/CommandListener.cpp
index 06c0ab5..7a843d8 100644
--- a/logd/CommandListener.cpp
+++ b/logd/CommandListener.cpp
@@ -288,9 +288,9 @@
         uid = AID_ROOT;
     }
 
-    const char* name = NULL;
-    const char* format = NULL;
-    const char* id = NULL;
+    const char* name = nullptr;
+    const char* format = nullptr;
+    const char* id = nullptr;
     for (int i = 1; i < argc; ++i) {
         static const char _name[] = "name=";
         if (!strncmp(argv[i], _name, strlen(_name))) {
diff --git a/logd/FlushCommand.cpp b/logd/FlushCommand.cpp
index a9edc3e..658e079 100644
--- a/logd/FlushCommand.cpp
+++ b/logd/FlushCommand.cpp
@@ -26,18 +26,6 @@
 #include "LogTimes.h"
 #include "LogUtils.h"
 
-FlushCommand::FlushCommand(LogReader& reader, bool nonBlock, unsigned long tail,
-                           unsigned int logMask, pid_t pid, log_time start,
-                           uint64_t timeout)
-    : mReader(reader),
-      mNonBlock(nonBlock),
-      mTail(tail),
-      mLogMask(logMask),
-      mPid(pid),
-      mStart(start),
-      mTimeout((start != log_time::EPOCH) ? timeout : 0) {
-}
-
 // runSocketCommand is called once for every open client on the
 // log reader socket. Here we manage and associated the reader
 // client tracking and log region locks LastLogTimes list of
@@ -48,7 +36,7 @@
 // reference counts are used to ensure that individual
 // LogTimeEntry lifetime is managed when not protected.
 void FlushCommand::runSocketCommand(SocketClient* client) {
-    LogTimeEntry* entry = NULL;
+    LogTimeEntry* entry = nullptr;
     LastLogTimes& times = mReader.logbuf().mTimes;
 
     LogTimeEntry::wrlock();
@@ -56,6 +44,10 @@
     while (it != times.end()) {
         entry = (*it);
         if (entry->mClient == client) {
+            if (!entry->isWatchingMultiple(mLogMask)) {
+                LogTimeEntry::unlock();
+                return;
+            }
             if (entry->mTimeout.tv_sec || entry->mTimeout.tv_nsec) {
                 if (mReader.logbuf().isMonotonic()) {
                     LogTimeEntry::unlock();
diff --git a/logd/FlushCommand.h b/logd/FlushCommand.h
index 7cdd03f..543dfc3 100644
--- a/logd/FlushCommand.h
+++ b/logd/FlushCommand.h
@@ -29,16 +29,36 @@
     LogReader& mReader;
     bool mNonBlock;
     unsigned long mTail;
-    unsigned int mLogMask;
+    log_mask_t mLogMask;
     pid_t mPid;
     log_time mStart;
     uint64_t mTimeout;
 
    public:
-    explicit FlushCommand(LogReader& mReader, bool nonBlock = false,
-                          unsigned long tail = -1, unsigned int logMask = -1,
-                          pid_t pid = 0, log_time start = log_time::EPOCH,
-                          uint64_t timeout = 0);
+    // for opening a reader
+    explicit FlushCommand(LogReader& reader, bool nonBlock, unsigned long tail,
+                          log_mask_t logMask, pid_t pid, log_time start,
+                          uint64_t timeout)
+        : mReader(reader),
+          mNonBlock(nonBlock),
+          mTail(tail),
+          mLogMask(logMask),
+          mPid(pid),
+          mStart(start),
+          mTimeout((start != log_time::EPOCH) ? timeout : 0) {
+    }
+
+    // for notification of an update
+    explicit FlushCommand(LogReader& reader, log_mask_t logMask)
+        : mReader(reader),
+          mNonBlock(false),
+          mTail(-1),
+          mLogMask(logMask),
+          mPid(0),
+          mStart(log_time::EPOCH),
+          mTimeout(0) {
+    }
+
     virtual void runSocketCommand(SocketClient* client);
 
     static bool hasReadLogs(SocketClient* client);
diff --git a/logd/LogAudit.cpp b/logd/LogAudit.cpp
index ee38d1c..4ea7877 100644
--- a/logd/LogAudit.cpp
+++ b/logd/LogAudit.cpp
@@ -25,7 +25,11 @@
 #include <sys/uio.h>
 #include <syslog.h>
 
+#include <fstream>
+#include <sstream>
+
 #include <android-base/macros.h>
+#include <log/log_properties.h>
 #include <private/android_filesystem_config.h>
 #include <private/android_logger.h>
 
@@ -41,7 +45,7 @@
         '0' + LOG_MAKEPRI(LOG_AUTH, LOG_PRI(PRI)) % 10, '>'
 
 LogAudit::LogAudit(LogBuffer* buf, LogReader* reader, int fdDmesg)
-    : SocketListener(mSock = getLogSocket(), false),
+    : SocketListener(getLogSocket(), false),
       logbuf(buf),
       reader(reader),
       fdDmesg(fdDmesg),
@@ -49,8 +53,7 @@
                                               BOOL_DEFAULT_TRUE)),
       events(__android_logger_property_get_bool("ro.logd.auditd.events",
                                                 BOOL_DEFAULT_TRUE)),
-      initialized(false),
-      tooFast(false) {
+      initialized(false) {
     static const char auditd_message[] = { KMSG_PRIORITY(LOG_INFO),
                                            'l',
                                            'o',
@@ -74,54 +77,12 @@
     write(fdDmesg, auditd_message, sizeof(auditd_message));
 }
 
-void LogAudit::checkRateLimit() {
-    // trim list for AUDIT_RATE_LIMIT_BURST_DURATION of history
-    log_time oldest(AUDIT_RATE_LIMIT_BURST_DURATION, 0);
-    bucket.emplace(android_log_clockid());
-    oldest = bucket.back() - oldest;
-    while (bucket.front() < oldest) bucket.pop();
-
-    static const size_t upperThreshold =
-        ((AUDIT_RATE_LIMIT_BURST_DURATION *
-          (AUDIT_RATE_LIMIT_DEFAULT + AUDIT_RATE_LIMIT_MAX)) +
-         1) /
-        2;
-    if (bucket.size() >= upperThreshold) {
-        // Hit peak, slow down source
-        if (!tooFast) {
-            tooFast = true;
-            audit_rate_limit(mSock, AUDIT_RATE_LIMIT_MAX);
-        }
-
-        // We do not need to hold on to the full set of timing data history,
-        // let's ensure it does not grow without bounds.  This also ensures
-        // that std::dequeue underneath behaves almost like a ring buffer.
-        do {
-            bucket.pop();
-        } while (bucket.size() >= upperThreshold);
-        return;
-    }
-
-    if (!tooFast) return;
-
-    static const size_t lowerThreshold =
-        AUDIT_RATE_LIMIT_BURST_DURATION * AUDIT_RATE_LIMIT_MAX;
-
-    if (bucket.size() >= lowerThreshold) return;
-
-    tooFast = false;
-    // Went below max sustained rate, allow source to speed up
-    audit_rate_limit(mSock, AUDIT_RATE_LIMIT_DEFAULT);
-}
-
 bool LogAudit::onDataAvailable(SocketClient* cli) {
     if (!initialized) {
         prctl(PR_SET_NAME, "logd.auditd");
         initialized = true;
     }
 
-    checkRateLimit();
-
     struct audit_message rep;
 
     rep.nlh.nlmsg_type = 0;
@@ -138,14 +99,85 @@
     return true;
 }
 
+static inline bool hasMetadata(char* str, int str_len) {
+    // need to check and see if str already contains bug metadata from
+    // possibility of stuttering if log audit crashes and then reloads kernel
+    // messages. Kernel denials that contain metadata will either end in
+    // "b/[0-9]+$" or "b/[0-9]+  duplicate messages suppressed$" which will put
+    // a '/' character at either 9 or 39 indices away from the end of the str.
+    return str_len >= 39 &&
+           (str[str_len - 9] == '/' || str[str_len - 39] == '/');
+}
+
+std::map<std::string, std::string> LogAudit::populateDenialMap() {
+    std::ifstream bug_file("/system/etc/selinux/selinux_denial_metadata");
+    std::string line;
+    // allocate a map for the static map pointer in auditParse to keep track of,
+    // this function only runs once
+    std::map<std::string, std::string> denial_to_bug;
+    if (bug_file.good()) {
+        std::string scontext;
+        std::string tcontext;
+        std::string tclass;
+        std::string bug_num;
+        while (std::getline(bug_file, line)) {
+            std::stringstream split_line(line);
+            split_line >> scontext >> tcontext >> tclass >> bug_num;
+            denial_to_bug.emplace(scontext + tcontext + tclass, bug_num);
+        }
+    }
+    return denial_to_bug;
+}
+
+std::string LogAudit::denialParse(const std::string& denial, char terminator,
+                                  const std::string& search_term) {
+    size_t start_index = denial.find(search_term);
+    if (start_index != std::string::npos) {
+        start_index += search_term.length();
+        return denial.substr(
+            start_index, denial.find(terminator, start_index) - start_index);
+    }
+    return "";
+}
+
+void LogAudit::auditParse(const std::string& string, uid_t uid,
+                          std::string* bug_num) {
+    if (!__android_log_is_debuggable()) {
+        bug_num->assign("");
+        return;
+    }
+    static std::map<std::string, std::string> denial_to_bug =
+        populateDenialMap();
+    std::string scontext = denialParse(string, ':', "scontext=u:object_r:");
+    std::string tcontext = denialParse(string, ':', "tcontext=u:object_r:");
+    std::string tclass = denialParse(string, ' ', "tclass=");
+    if (scontext.empty()) {
+        scontext = denialParse(string, ':', "scontext=u:r:");
+    }
+    if (tcontext.empty()) {
+        tcontext = denialParse(string, ':', "tcontext=u:r:");
+    }
+    auto search = denial_to_bug.find(scontext + tcontext + tclass);
+    if (search != denial_to_bug.end()) {
+        bug_num->assign(" b/" + search->second);
+    } else {
+        bug_num->assign("");
+    }
+
+    if (uid >= AID_APP_START && uid <= AID_APP_END) {
+        bug_num->append(" app=");
+        bug_num->append(android::uidToName(uid));
+    }
+}
+
 int LogAudit::logPrint(const char* fmt, ...) {
-    if (fmt == NULL) {
+    if (fmt == nullptr) {
         return -EINVAL;
     }
 
     va_list args;
 
-    char* str = NULL;
+    char* str = nullptr;
     va_start(args, fmt);
     int rc = vasprintf(&str, fmt, args);
     va_end(args);
@@ -153,7 +185,6 @@
     if (rc < 0) {
         return rc;
     }
-
     char* cp;
     // Work around kernels missing
     // https://github.com/torvalds/linux/commit/b8f89caafeb55fba75b74bea25adc4e4cd91be67
@@ -165,10 +196,29 @@
     while ((cp = strstr(str, "  "))) {
         memmove(cp, cp + 1, strlen(cp + 1) + 1);
     }
+    pid_t pid = getpid();
+    pid_t tid = gettid();
+    uid_t uid = AID_LOGD;
+    static const char pid_str[] = " pid=";
+    char* pidptr = strstr(str, pid_str);
+    if (pidptr && isdigit(pidptr[sizeof(pid_str) - 1])) {
+        cp = pidptr + sizeof(pid_str) - 1;
+        pid = 0;
+        while (isdigit(*cp)) {
+            pid = (pid * 10) + (*cp - '0');
+            ++cp;
+        }
+        tid = pid;
+        logbuf->wrlock();
+        uid = logbuf->pidToUid(pid);
+        logbuf->unlock();
+        memmove(pidptr, cp, strlen(cp) + 1);
+    }
 
     bool info = strstr(str, " permissive=1") || strstr(str, " policy loaded ");
+    static std::string denial_metadata;
     if ((fdDmesg >= 0) && initialized) {
-        struct iovec iov[3];
+        struct iovec iov[4];
         static const char log_info[] = { KMSG_PRIORITY(LOG_INFO) };
         static const char log_warning[] = { KMSG_PRIORITY(LOG_WARNING) };
         static const char newline[] = "\n";
@@ -178,7 +228,7 @@
         static char* last_str;
         static bool last_info;
 
-        if (last_str != NULL) {
+        if (last_str != nullptr) {
             static const char avc[] = "): avc: ";
             char* avcl = strstr(last_str, avc);
             bool skip = false;
@@ -197,39 +247,43 @@
             }
             if (!skip) {
                 static const char resume[] = " duplicate messages suppressed\n";
-
                 iov[0].iov_base = last_info ? const_cast<char*>(log_info)
                                             : const_cast<char*>(log_warning);
                 iov[0].iov_len =
                     last_info ? sizeof(log_info) : sizeof(log_warning);
                 iov[1].iov_base = last_str;
                 iov[1].iov_len = strlen(last_str);
+                iov[2].iov_base = const_cast<char*>(denial_metadata.c_str());
+                iov[2].iov_len = denial_metadata.length();
                 if (count > 1) {
-                    iov[2].iov_base = const_cast<char*>(resume);
-                    iov[2].iov_len = strlen(resume);
+                    iov[3].iov_base = const_cast<char*>(resume);
+                    iov[3].iov_len = strlen(resume);
                 } else {
-                    iov[2].iov_base = const_cast<char*>(newline);
-                    iov[2].iov_len = strlen(newline);
+                    iov[3].iov_base = const_cast<char*>(newline);
+                    iov[3].iov_len = strlen(newline);
                 }
 
                 writev(fdDmesg, iov, arraysize(iov));
                 free(last_str);
-                last_str = NULL;
+                last_str = nullptr;
             }
         }
-        if (last_str == NULL) {
+        if (last_str == nullptr) {
             count = 0;
             last_str = strdup(str);
             last_info = info;
         }
         if (count == 0) {
+            auditParse(str, uid, &denial_metadata);
             iov[0].iov_base = info ? const_cast<char*>(log_info)
                                    : const_cast<char*>(log_warning);
             iov[0].iov_len = info ? sizeof(log_info) : sizeof(log_warning);
             iov[1].iov_base = str;
             iov[1].iov_len = strlen(str);
-            iov[2].iov_base = const_cast<char*>(newline);
-            iov[2].iov_len = strlen(newline);
+            iov[2].iov_base = const_cast<char*>(denial_metadata.c_str());
+            iov[2].iov_len = denial_metadata.length();
+            iov[3].iov_base = const_cast<char*>(newline);
+            iov[3].iov_len = strlen(newline);
 
             writev(fdDmesg, iov, arraysize(iov));
         }
@@ -240,9 +294,6 @@
         return 0;
     }
 
-    pid_t pid = getpid();
-    pid_t tid = gettid();
-    uid_t uid = AID_LOGD;
     log_time now;
 
     static const char audit_str[] = " audit(";
@@ -267,44 +318,36 @@
         now = log_time(CLOCK_REALTIME);
     }
 
-    static const char pid_str[] = " pid=";
-    char* pidptr = strstr(str, pid_str);
-    if (pidptr && isdigit(pidptr[sizeof(pid_str) - 1])) {
-        cp = pidptr + sizeof(pid_str) - 1;
-        pid = 0;
-        while (isdigit(*cp)) {
-            pid = (pid * 10) + (*cp - '0');
-            ++cp;
-        }
-        tid = pid;
-        logbuf->wrlock();
-        uid = logbuf->pidToUid(pid);
-        logbuf->unlock();
-        memmove(pidptr, cp, strlen(cp) + 1);
-    }
-
     // log to events
 
-    size_t l = strnlen(str, LOGGER_ENTRY_MAX_PAYLOAD);
-    size_t n = l + sizeof(android_log_event_string_t);
+    size_t str_len = strnlen(str, LOGGER_ENTRY_MAX_PAYLOAD);
+    if (((fdDmesg < 0) || !initialized) && !hasMetadata(str, str_len))
+        auditParse(str, uid, &denial_metadata);
+    str_len = (str_len + denial_metadata.length() <= LOGGER_ENTRY_MAX_PAYLOAD)
+                  ? str_len + denial_metadata.length()
+                  : LOGGER_ENTRY_MAX_PAYLOAD;
+    size_t message_len = str_len + sizeof(android_log_event_string_t);
 
-    bool notify = false;
+    log_mask_t notify = 0;
 
     if (events) {  // begin scope for event buffer
-        uint32_t buffer[(n + sizeof(uint32_t) - 1) / sizeof(uint32_t)];
+        uint32_t buffer[(message_len + sizeof(uint32_t) - 1) / sizeof(uint32_t)];
 
         android_log_event_string_t* event =
             reinterpret_cast<android_log_event_string_t*>(buffer);
         event->header.tag = htole32(AUDITD_LOG_TAG);
         event->type = EVENT_TYPE_STRING;
-        event->length = htole32(l);
-        memcpy(event->data, str, l);
+        event->length = htole32(str_len);
+        memcpy(event->data, str, str_len - denial_metadata.length());
+        memcpy(event->data + str_len - denial_metadata.length(),
+               denial_metadata.c_str(), denial_metadata.length());
 
-        rc = logbuf->log(LOG_ID_EVENTS, now, uid, pid, tid,
-                         reinterpret_cast<char*>(event),
-                         (n <= USHRT_MAX) ? (unsigned short)n : USHRT_MAX);
+        rc = logbuf->log(
+            LOG_ID_EVENTS, now, uid, pid, tid, reinterpret_cast<char*>(event),
+            (message_len <= USHRT_MAX) ? (unsigned short)message_len
+                                       : USHRT_MAX);
         if (rc >= 0) {
-            notify = true;
+            notify |= 1 << LOG_ID_EVENTS;
         }
         // end scope for event buffer
     }
@@ -314,7 +357,7 @@
     static const char comm_str[] = " comm=\"";
     const char* comm = strstr(str, comm_str);
     const char* estr = str + strlen(str);
-    const char* commfree = NULL;
+    const char* commfree = nullptr;
     if (comm) {
         estr = comm;
         comm += sizeof(comm_str) - 1;
@@ -333,31 +376,35 @@
     const char* ecomm = strchr(comm, '"');
     if (ecomm) {
         ++ecomm;
-        l = ecomm - comm;
+        str_len = ecomm - comm;
     } else {
-        l = strlen(comm) + 1;
+        str_len = strlen(comm) + 1;
         ecomm = "";
     }
-    size_t b = estr - str;
-    if (b > LOGGER_ENTRY_MAX_PAYLOAD) {
-        b = LOGGER_ENTRY_MAX_PAYLOAD;
+    size_t prefix_len = estr - str;
+    if (prefix_len > LOGGER_ENTRY_MAX_PAYLOAD) {
+        prefix_len = LOGGER_ENTRY_MAX_PAYLOAD;
     }
-    size_t e = strnlen(ecomm, LOGGER_ENTRY_MAX_PAYLOAD - b);
-    n = b + e + l + 2;
+    size_t suffix_len = strnlen(ecomm, LOGGER_ENTRY_MAX_PAYLOAD - prefix_len);
+    message_len =
+        str_len + prefix_len + suffix_len + denial_metadata.length() + 2;
 
     if (main) {  // begin scope for main buffer
-        char newstr[n];
+        char newstr[message_len];
 
         *newstr = info ? ANDROID_LOG_INFO : ANDROID_LOG_WARN;
-        strlcpy(newstr + 1, comm, l);
-        strncpy(newstr + 1 + l, str, b);
-        strncpy(newstr + 1 + l + b, ecomm, e);
+        strlcpy(newstr + 1, comm, str_len);
+        strncpy(newstr + 1 + str_len, str, prefix_len);
+        strncpy(newstr + 1 + str_len + prefix_len, ecomm, suffix_len);
+        strncpy(newstr + 1 + str_len + prefix_len + suffix_len,
+                denial_metadata.c_str(), denial_metadata.length());
 
         rc = logbuf->log(LOG_ID_MAIN, now, uid, pid, tid, newstr,
-                         (n <= USHRT_MAX) ? (unsigned short)n : USHRT_MAX);
+                         (message_len <= USHRT_MAX) ? (unsigned short)message_len
+                                                    : USHRT_MAX);
 
         if (rc >= 0) {
-            notify = true;
+            notify |= 1 << LOG_ID_MAIN;
         }
         // end scope for main buffer
     }
@@ -366,9 +413,9 @@
     free(str);
 
     if (notify) {
-        reader->notifyNewLog();
+        reader->notifyNewLog(notify);
         if (rc < 0) {
-            rc = n;
+            rc = message_len;
         }
     }
 
@@ -403,6 +450,5 @@
         audit_close(fd);
         fd = -1;
     }
-    (void)audit_rate_limit(fd, AUDIT_RATE_LIMIT_DEFAULT);
     return fd;
 }
diff --git a/logd/LogAudit.h b/logd/LogAudit.h
index 5947819..c3d7a3e 100644
--- a/logd/LogAudit.h
+++ b/logd/LogAudit.h
@@ -17,7 +17,7 @@
 #ifndef _LOGD_LOG_AUDIT_H__
 #define _LOGD_LOG_AUDIT_H__
 
-#include <queue>
+#include <map>
 
 #include <sysutils/SocketListener.h>
 
@@ -33,11 +33,6 @@
     bool events;
     bool initialized;
 
-    bool tooFast;
-    int mSock;
-    std::queue<log_time> bucket;
-    void checkRateLimit();
-
    public:
     LogAudit(LogBuffer* buf, LogReader* reader, int fdDmesg);
     int log(char* buf, size_t len);
@@ -50,6 +45,10 @@
 
    private:
     static int getLogSocket();
+    std::map<std::string, std::string> populateDenialMap();
+    std::string denialParse(const std::string& denial, char terminator,
+                            const std::string& search_term);
+    void auditParse(const std::string& string, uid_t uid, std::string* bug_num);
     int logPrint(const char* fmt, ...)
         __attribute__((__format__(__printf__, 2, 3)));
 };
diff --git a/logd/LogBuffer.cpp b/logd/LogBuffer.cpp
index 76a4aff..a78319f 100644
--- a/logd/LogBuffer.cpp
+++ b/logd/LogBuffer.cpp
@@ -165,15 +165,19 @@
             (lenl == sizeof(android_log_event_int_t)) &&
             !fastcmp<memcmp>(msgl, msgr, sizeof(android_log_event_int_t) -
                                              sizeof(int32_t)) &&
-            (elem->getTag() == LIBLOG_LOG_TAG))
+            (elem->getTag() == LIBLOG_LOG_TAG)) {
             return SAME_LIBLOG;
+        }
     }
 
     // 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)))
+                                            sizeof(int32_t))) {
             return DIFFERENT;
+        }
         msgl += sizeof(android_log_event_string_t);
         lenl -= sizeof(android_log_event_string_t);
         msgr += sizeof(android_log_event_string_t);
@@ -196,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;
     }
 
@@ -210,13 +214,19 @@
     if (log_id != LOG_ID_SECURITY) {
         int prio = ANDROID_LOG_INFO;
         const char* tag = nullptr;
-        if (log_id == LOG_ID_EVENTS) {
+        size_t tag_len = 0;
+        if (log_id == LOG_ID_EVENTS || log_id == LOG_ID_STATS) {
             tag = tagToName(elem->getTag());
+            if (tag) {
+                tag_len = strlen(tag);
+            }
         } else {
             prio = *msg;
             tag = msg + 1;
+            tag_len = strnlen(tag, len - 1);
         }
-        if (!__android_log_is_loggable(prio, tag, ANDROID_LOG_VERBOSE)) {
+        if (!__android_log_is_loggable_len(prio, tag, tag_len,
+                                           ANDROID_LOG_VERBOSE)) {
             // Log traffic received to total
             wrlock();
             stats.addTotal(elem);
diff --git a/logd/LogBufferElement.cpp b/logd/LogBufferElement.cpp
index 381c974..2d627b9 100644
--- a/logd/LogBufferElement.cpp
+++ b/logd/LogBufferElement.cpp
@@ -41,22 +41,20 @@
       mTid(tid),
       mRealTime(realtime),
       mMsgLen(len),
-      mLogId(log_id) {
+      mLogId(log_id),
+      mDropped(false) {
     mMsg = new char[len];
     memcpy(mMsg, msg, len);
-    mTag = (isBinary() && (mMsgLen >= sizeof(uint32_t)))
-               ? le32toh(reinterpret_cast<android_event_header_t*>(mMsg)->tag)
-               : 0;
 }
 
 LogBufferElement::LogBufferElement(const LogBufferElement& elem)
-    : mTag(elem.mTag),
-      mUid(elem.mUid),
+    : mUid(elem.mUid),
       mPid(elem.mPid),
       mTid(elem.mTid),
       mRealTime(elem.mRealTime),
       mMsgLen(elem.mMsgLen),
-      mLogId(elem.mLogId) {
+      mLogId(elem.mLogId),
+      mDropped(elem.mDropped) {
     mMsg = new char[mMsgLen];
     memcpy(mMsg, elem.mMsg, mMsgLen);
 }
@@ -65,9 +63,35 @@
     delete[] mMsg;
 }
 
+uint32_t LogBufferElement::getTag() const {
+    return (isBinary() &&
+            ((mDropped && mMsg != nullptr) ||
+             (!mDropped && mMsgLen >= sizeof(android_event_header_t))))
+               ? reinterpret_cast<const android_event_header_t*>(mMsg)->tag
+               : 0;
+}
+
+unsigned short LogBufferElement::setDropped(unsigned short value) {
+    // The tag information is saved in mMsg data, if the tag is non-zero
+    // save only the information needed to get the tag.
+    if (getTag() != 0) {
+        if (mMsgLen > sizeof(android_event_header_t)) {
+            char* truncated_msg = new char[sizeof(android_event_header_t)];
+            memcpy(truncated_msg, mMsg, sizeof(android_event_header_t));
+            delete[] mMsg;
+            mMsg = truncated_msg;
+        }  // mMsgLen == sizeof(android_event_header_t), already at minimum.
+    } else {
+        delete[] mMsg;
+        mMsg = nullptr;
+    }
+    mDropped = true;
+    return mDroppedCount = value;
+}
+
 // caller must own and free character string
 char* android::tidToName(pid_t tid) {
-    char* retval = NULL;
+    char* retval = nullptr;
     char buffer[256];
     snprintf(buffer, sizeof(buffer), "/proc/%u/comm", tid);
     int fd = open(buffer, O_RDONLY);
@@ -90,7 +114,7 @@
     char* name = android::pidToName(tid);
     if (!retval) {
         retval = name;
-        name = NULL;
+        name = nullptr;
     }
 
     // check if comm is truncated, see if cmdline has full representation
@@ -138,15 +162,15 @@
         if (!strncmp(name + 1, commName + 1, len)) {
             if (commName[len + 1] == '\0') {
                 free(const_cast<char*>(commName));
-                commName = NULL;
+                commName = nullptr;
             } else {
                 free(const_cast<char*>(name));
-                name = NULL;
+                name = nullptr;
             }
         }
     }
     if (name) {
-        char* buf = NULL;
+        char* buf = nullptr;
         asprintf(&buf, "(%s)", name);
         if (buf) {
             free(const_cast<char*>(name));
@@ -154,7 +178,7 @@
         }
     }
     if (commName) {
-        char* buf = NULL;
+        char* buf = nullptr;
         asprintf(&buf, " %s", commName);
         if (buf) {
             free(const_cast<char*>(commName));
@@ -163,9 +187,9 @@
     }
     // identical to below to calculate the buffer size required
     const char* type = lastSame ? "identical" : "expire";
-    size_t len = snprintf(NULL, 0, format_uid, mUid, name ? name : "",
-                          commName ? commName : "", type, mDropped,
-                          (mDropped > 1) ? "s" : "");
+    size_t len = snprintf(nullptr, 0, format_uid, mUid, name ? name : "",
+                          commName ? commName : "", type, getDropped(),
+                          (getDropped() > 1) ? "s" : "");
 
     size_t hdrLen;
     if (isBinary()) {
@@ -196,8 +220,8 @@
     }
 
     snprintf(buffer + hdrLen, len + 1, format_uid, mUid, name ? name : "",
-             commName ? commName : "", type, mDropped,
-             (mDropped > 1) ? "s" : "");
+             commName ? commName : "", type, getDropped(),
+             (getDropped() > 1) ? "s" : "");
     free(const_cast<char*>(name));
     free(const_cast<char*>(commName));
 
@@ -223,9 +247,9 @@
     iovec[0].iov_base = &entry;
     iovec[0].iov_len = entry.hdr_size;
 
-    char* buffer = NULL;
+    char* buffer = nullptr;
 
-    if (!mMsg) {
+    if (mDropped) {
         entry.len = populateDroppedMessage(buffer, parent, lastSame);
         if (!entry.len) return mRealTime;
         iovec[1].iov_base = buffer;
diff --git a/logd/LogBufferElement.h b/logd/LogBufferElement.h
index 814ec87..b168645 100644
--- a/logd/LogBufferElement.h
+++ b/logd/LogBufferElement.h
@@ -32,25 +32,25 @@
                                   // chatty for the temporal expire messages
 #define EXPIRE_RATELIMIT 10  // maximum rate in seconds to report expiration
 
-class LogBufferElement {
+class __attribute__((packed)) LogBufferElement {
     friend LogBuffer;
 
     // sized to match reality of incoming log packets
-    uint32_t mTag;  // only valid for isBinary()
     const uint32_t mUid;
     const uint32_t mPid;
     const uint32_t mTid;
     log_time mRealTime;
     char* mMsg;
     union {
-        const uint16_t mMsgLen;  // mMSg != NULL
-        uint16_t mDropped;       // mMsg == NULL
+        const uint16_t mMsgLen;  // mDropped == false
+        uint16_t mDroppedCount;  // mDropped == true
     };
     const uint8_t mLogId;
+    bool mDropped;
 
     static atomic_int_fast64_t sequence;
 
-    // assumption: mMsg == NULL
+    // assumption: mDropped == true
     size_t populateDroppedMessage(char*& buffer, LogBuffer* parent,
                                   bool lastSame);
 
@@ -58,7 +58,7 @@
     LogBufferElement(log_id_t log_id, log_time realtime, uid_t uid, pid_t pid,
                      pid_t tid, const char* msg, unsigned short len);
     LogBufferElement(const LogBufferElement& elem);
-    virtual ~LogBufferElement();
+    ~LogBufferElement();
 
     bool isBinary(void) const {
         return (mLogId == LOG_ID_EVENTS) || (mLogId == LOG_ID_SECURITY);
@@ -76,24 +76,16 @@
     pid_t getTid(void) const {
         return mTid;
     }
-    uint32_t getTag() const {
-        return mTag;
-    }
+    uint32_t getTag() const;
     unsigned short getDropped(void) const {
-        return mMsg ? 0 : mDropped;
+        return mDropped ? mDroppedCount : 0;
     }
-    unsigned short setDropped(unsigned short value) {
-        if (mMsg) {
-            delete[] mMsg;
-            mMsg = NULL;
-        }
-        return mDropped = value;
-    }
+    unsigned short setDropped(unsigned short value);
     unsigned short getMsgLen() const {
-        return mMsg ? mMsgLen : 0;
+        return mDropped ? 0 : mMsgLen;
     }
     const char* getMsg() const {
-        return mMsg;
+        return mDropped ? nullptr : mMsg;
     }
     log_time getRealTime(void) const {
         return mRealTime;
diff --git a/logd/LogCommand.cpp b/logd/LogCommand.cpp
index 6d7c0a5..8bff9da 100644
--- a/logd/LogCommand.cpp
+++ b/logd/LogCommand.cpp
@@ -44,9 +44,9 @@
     char* ptr;
     static const char ws[] = " \n";
 
-    for (buf = strtok_r(buf, ws, &ptr); buf; buf = strtok_r(NULL, ws, &ptr)) {
+    for (buf = strtok_r(buf, ws, &ptr); buf; buf = strtok_r(nullptr, ws, &ptr)) {
         errno = 0;
-        gid_t Gid = strtol(buf, NULL, 10);
+        gid_t Gid = strtol(buf, nullptr, 10);
         if (errno != 0) {
             return false;
         }
@@ -98,7 +98,7 @@
             continue;
         }
 
-        char* line = NULL;
+        char* line = nullptr;
         size_t len = 0;
         while (getline(&line, &len, file) > 0) {
             static const char groups_string[] = "Groups:\t";
diff --git a/logd/LogKlog.cpp b/logd/LogKlog.cpp
index a7e7208..ab980ac 100644
--- a/logd/LogKlog.cpp
+++ b/logd/LogKlog.cpp
@@ -825,8 +825,8 @@
                          (unsigned short)n);
 
     // notify readers
-    if (!rc) {
-        reader->notifyNewLog();
+    if (rc > 0) {
+        reader->notifyNewLog(static_cast<log_mask_t>(1 << LOG_ID_KERNEL));
     }
 
     return rc;
diff --git a/logd/LogListener.cpp b/logd/LogListener.cpp
index d2df68e..e568ddc 100644
--- a/logd/LogListener.cpp
+++ b/logd/LogListener.cpp
@@ -43,29 +43,33 @@
         name_set = true;
     }
 
+    // + 1 to ensure null terminator if MAX_PAYLOAD buffer is received
     char buffer[sizeof_log_id_t + sizeof(uint16_t) + sizeof(log_time) +
-                LOGGER_ENTRY_MAX_PAYLOAD];
-    struct iovec iov = { buffer, sizeof(buffer) };
+                LOGGER_ENTRY_MAX_PAYLOAD + 1];
+    struct iovec iov = { buffer, sizeof(buffer) - 1 };
 
     alignas(4) char control[CMSG_SPACE(sizeof(struct ucred))];
     struct msghdr hdr = {
-        NULL, 0, &iov, 1, control, sizeof(control), 0,
+        nullptr, 0, &iov, 1, control, sizeof(control), 0,
     };
 
     int socket = cli->getSocket();
 
     // To clear the entire buffer is secure/safe, but this contributes to 1.68%
-    // overhead under logging load. We are safe because we check counts.
+    // overhead under logging load. We are safe because we check counts, but
+    // still need to clear null terminator
     // memset(buffer, 0, sizeof(buffer));
     ssize_t n = recvmsg(socket, &hdr, 0);
     if (n <= (ssize_t)(sizeof(android_log_header_t))) {
         return false;
     }
 
-    struct ucred* cred = NULL;
+    buffer[n] = 0;
+
+    struct ucred* cred = nullptr;
 
     struct cmsghdr* cmsg = CMSG_FIRSTHDR(&hdr);
-    while (cmsg != NULL) {
+    while (cmsg != nullptr) {
         if (cmsg->cmsg_level == SOL_SOCKET &&
             cmsg->cmsg_type == SCM_CREDENTIALS) {
             cred = (struct ucred*)CMSG_DATA(cmsg);
@@ -75,7 +79,7 @@
     }
 
     struct ucred fake_cred;
-    if (cred == NULL) {
+    if (cred == nullptr) {
         cred = &fake_cred;
         cred->pid = 0;
         cred->uid = DEFAULT_OVERFLOWUID;
@@ -90,12 +94,13 @@
 
     android_log_header_t* header =
         reinterpret_cast<android_log_header_t*>(buffer);
-    if (/* header->id < LOG_ID_MIN || */ header->id >= LOG_ID_MAX ||
-        header->id == LOG_ID_KERNEL) {
+    log_id_t logId = static_cast<log_id_t>(header->id);
+    if (/* logId < LOG_ID_MIN || */ logId >= LOG_ID_MAX ||
+        logId == LOG_ID_KERNEL) {
         return false;
     }
 
-    if ((header->id == LOG_ID_SECURITY) &&
+    if ((logId == LOG_ID_SECURITY) &&
         (!__android_log_security() ||
          !clientHasLogCredentials(cred->uid, cred->gid, cred->pid))) {
         return false;
@@ -130,11 +135,10 @@
 
     if (logbuf != nullptr) {
         int res = logbuf->log(
-            (log_id_t)header->id, header->realtime, cred->uid, cred->pid,
-            header->tid, msg,
+            logId, header->realtime, cred->uid, cred->pid, header->tid, msg,
             ((size_t)n <= USHRT_MAX) ? (unsigned short)n : USHRT_MAX);
         if (res > 0 && reader != nullptr) {
-            reader->notifyNewLog();
+            reader->notifyNewLog(static_cast<log_mask_t>(1 << logId));
         }
     }
 
diff --git a/logd/LogReader.cpp b/logd/LogReader.cpp
index 6d69316..2b6556d 100644
--- a/logd/LogReader.cpp
+++ b/logd/LogReader.cpp
@@ -35,9 +35,9 @@
 }
 
 // When we are notified a new log entry is available, inform
-// all of our listening sockets.
-void LogReader::notifyNewLog() {
-    FlushCommand command(*this);
+// listening sockets who are watching this entry's log id.
+void LogReader::notifyNewLog(log_mask_t logMask) {
+    FlushCommand command(*this, logMask);
     runOnEachSocket(&command);
 }
 
diff --git a/logd/LogReader.h b/logd/LogReader.h
index 271e08c..b5312b6 100644
--- a/logd/LogReader.h
+++ b/logd/LogReader.h
@@ -19,6 +19,8 @@
 
 #include <sysutils/SocketListener.h>
 
+#include "LogTimes.h"
+
 #define LOGD_SNDTIMEO 32
 
 class LogBuffer;
@@ -28,7 +30,7 @@
 
    public:
     explicit LogReader(LogBuffer* logbuf);
-    void notifyNewLog();
+    void notifyNewLog(log_mask_t logMask);
 
     LogBuffer& logbuf(void) const {
         return mLogbuf;
diff --git a/logd/LogStatistics.cpp b/logd/LogStatistics.cpp
index af59ddc..cefacf7 100644
--- a/logd/LogStatistics.cpp
+++ b/logd/LogStatistics.cpp
@@ -56,7 +56,7 @@
 
 // caller must own and free character string
 char* pidToName(pid_t pid) {
-    char* retval = NULL;
+    char* retval = nullptr;
     if (pid == 0) {  // special case from auditd/klogd for kernel
         retval = strdup("logd");
     } else {
@@ -286,7 +286,7 @@
                     name = strdup(nameTmp);
                 } else if (fastcmp<strcmp>(name, nameTmp)) {
                     free(const_cast<char*>(name));
-                    name = NULL;
+                    name = nullptr;
                     break;
                 }
             }
@@ -872,7 +872,7 @@
     pidTable_t& writablePidTable = const_cast<pidTable_t&>(pidTable);
     const char* name = writablePidTable.add(pid)->second.getName();
     if (!name) {
-        return NULL;
+        return nullptr;
     }
     return strdup(name);
 }
diff --git a/logd/LogStatistics.h b/logd/LogStatistics.h
index 8808aac..ac3cf9a 100644
--- a/logd/LogStatistics.h
+++ b/logd/LogStatistics.h
@@ -25,9 +25,9 @@
 #include <sys/types.h>
 
 #include <algorithm>  // std::max
-#include <experimental/string_view>
 #include <memory>
-#include <string>  // std::string
+#include <string>
+#include <string_view>
 #include <unordered_map>
 
 #include <android-base/stringprintf.h>
@@ -495,7 +495,7 @@
 
 struct TagNameKey {
     std::string* alloc;
-    std::experimental::string_view name;  // Saves space if const char*
+    std::string_view name;  // Saves space if const char*
 
     explicit TagNameKey(const LogBufferElement* element)
         : alloc(nullptr), name("", strlen("")) {
@@ -504,31 +504,31 @@
             if (tag) {
                 const char* cp = android::tagToName(tag);
                 if (cp) {
-                    name = std::experimental::string_view(cp, strlen(cp));
+                    name = std::string_view(cp, strlen(cp));
                     return;
                 }
             }
             alloc = new std::string(
                 android::base::StringPrintf("[%" PRIu32 "]", tag));
             if (!alloc) return;
-            name = std::experimental::string_view(alloc->c_str(), alloc->size());
+            name = std::string_view(alloc->c_str(), alloc->size());
             return;
         }
         const char* msg = element->getMsg();
         if (!msg) {
-            name = std::experimental::string_view("chatty", strlen("chatty"));
+            name = std::string_view("chatty", strlen("chatty"));
             return;
         }
         ++msg;
         unsigned short len = element->getMsgLen();
         len = (len <= 1) ? 0 : strnlen(msg, len - 1);
         if (!len) {
-            name = std::experimental::string_view("<NULL>", strlen("<NULL>"));
+            name = std::string_view("<NULL>", strlen("<NULL>"));
             return;
         }
         alloc = new std::string(msg, len);
         if (!alloc) return;
-        name = std::experimental::string_view(alloc->c_str(), alloc->size());
+        name = std::string_view(alloc->c_str(), alloc->size());
     }
 
     explicit TagNameKey(TagNameKey&& rval)
@@ -545,7 +545,7 @@
         if (alloc) delete alloc;
     }
 
-    operator const std::experimental::string_view() const {
+    operator const std::string_view() const {
         return name;
     }
 
@@ -576,8 +576,7 @@
     : public std::unary_function<const TagNameKey&, size_t> {
     size_t operator()(const TagNameKey& __t) const noexcept {
         if (!__t.length()) return 0;
-        return std::hash<std::experimental::string_view>()(
-            std::experimental::string_view(__t));
+        return std::hash<std::string_view>()(std::string_view(__t));
     }
 };
 
diff --git a/logd/LogTags.cpp b/logd/LogTags.cpp
index fcd45bd..1ab9dd1 100644
--- a/logd/LogTags.cpp
+++ b/logd/LogTags.cpp
@@ -91,7 +91,7 @@
         fd = TEMP_FAILURE_RETRY(open(
             filename, O_WRONLY | O_TRUNC | O_CLOEXEC | O_NOFOLLOW | O_BINARY));
         if (fd >= 0) {
-            time_t now = time(NULL);
+            time_t now = time(nullptr);
             struct tm tm;
             localtime_r(&now, &tm);
             char timebuf[20];
@@ -105,7 +105,7 @@
                 android::base::WriteStringToFd(
                     formatEntry_locked(it.first, AID_ROOT), fd);
             }
-            TEMP_FAILURE_RETRY(close(fd));
+            close(fd);
         }
     }
 
@@ -208,7 +208,7 @@
             } else if (lineStart) {
                 if (*cp == '#') {
                     /* comment; just scan to end */
-                    lineStart = NULL;
+                    lineStart = nullptr;
                 } else if (isdigit(*cp)) {
                     unsigned long Tag = strtoul(cp, &cp, 10);
                     if (warn && (Tag > emptyTag)) {
@@ -235,7 +235,7 @@
                     if (hasAlpha &&
                         ((cp >= endp) || (*cp == '#') || isspace(*cp))) {
                         if (Tag > emptyTag) {
-                            if (*cp != '\n') lineStart = NULL;
+                            if (*cp != '\n') lineStart = nullptr;
                             continue;
                         }
                         while ((cp < endp) && (*cp != '\n') && isspace(*cp))
@@ -245,14 +245,14 @@
                         while ((cp < endp) && (*cp != '\n')) {
                             if (*cp == '#') {
                                 uid = sniffUid(cp, endp);
-                                lineStart = NULL;
+                                lineStart = nullptr;
                                 break;
                             }
                             ++cp;
                         }
                         while ((cp > format) && isspace(cp[-1])) {
                             --cp;
-                            lineStart = NULL;
+                            lineStart = nullptr;
                         }
                         std::string Format(format, cp - format);
 
@@ -263,7 +263,7 @@
                             android::prdebug("tag name invalid %.*s",
                                              (int)(cp - name + 1), name);
                         }
-                        lineStart = NULL;
+                        lineStart = nullptr;
                     }
                 } else if (!isspace(*cp)) {
                     break;
@@ -364,7 +364,7 @@
     android::RWLock::AutoRLock readLock(const_cast<android::RWLock&>(rwlock));
 
     it = tag2name.find(tag);
-    if ((it == tag2name.end()) || (it->second.length() == 0)) return NULL;
+    if ((it == tag2name.end()) || (it->second.length() == 0)) return nullptr;
 
     return it->second.c_str();
 }
@@ -383,7 +383,7 @@
 const char* android::tagToName(uint32_t tag) {
     LogTags* me = logtags;
 
-    if (!me) return NULL;
+    if (!me) return nullptr;
     me->WritePmsgEventLogTags(tag);
     return me->tagToName(tag);
 }
@@ -412,7 +412,7 @@
     android::RWLock::AutoRLock readLock(const_cast<android::RWLock&>(rwlock));
 
     iform = tag2format.find(tag);
-    if (iform == tag2format.end()) return NULL;
+    if (iform == tag2format.end()) return nullptr;
 
     return iform->second.c_str();
 }
@@ -441,7 +441,7 @@
                                    bool& unique) {
     key2tag_const_iterator ik;
 
-    bool write = format != NULL;
+    bool write = format != nullptr;
     unique = write;
 
     if (!write) {
@@ -601,7 +601,7 @@
 
     std::string ret = formatEntry_locked(tag, uid, false);
     android::base::WriteStringToFd(ret, fd);
-    TEMP_FAILURE_RETRY(close(fd));
+    close(fd);
 
     size_t size = 0;
     file2watermark_const_iterator iwater;
@@ -625,7 +625,7 @@
 
     std::string ret = formatEntry_locked(tag, uid, false);
     android::base::WriteStringToFd(ret, fd);
-    TEMP_FAILURE_RETRY(close(fd));
+    close(fd);
 
     size_t size = 0;
     file2watermark_const_iterator iwater;
@@ -679,7 +679,7 @@
 // are in readonly mode.
 uint32_t LogTags::nameToTag(uid_t uid, const char* name, const char* format) {
     std::string Name = std::string(name);
-    bool write = format != NULL;
+    bool write = format != nullptr;
     bool updateUid = uid != AID_ROOT;
     bool updateFormat = format && *format;
     bool unique;
@@ -848,7 +848,7 @@
 
     if (!list) {
         // switch to read entry only if format == "*"
-        if (format && (format[0] == '*') && !format[1]) format = NULL;
+        if (format && (format[0] == '*') && !format[1]) format = nullptr;
 
         // WAI: for null format, only works for a single entry, we can have
         // multiple entries, one for each format, so we find first entry
diff --git a/logd/LogTags.h b/logd/LogTags.h
index 203318d..e4d165a 100644
--- a/logd/LogTags.h
+++ b/logd/LogTags.h
@@ -87,14 +87,14 @@
     bool RebuildFileEventLogTags(const char* filename, bool warn = true);
 
     void AddEventLogTags(uint32_t tag, uid_t uid, const std::string& Name,
-                         const std::string& Format, const char* source = NULL,
+                         const std::string& Format, const char* source = nullptr,
                          bool warn = false);
 
     void WriteDynamicEventLogTags(uint32_t tag, uid_t uid);
     void WriteDebugEventLogTags(uint32_t tag, uid_t uid);
     // push tag details to persistent storage
     void WritePersistEventLogTags(uint32_t tag, uid_t uid = AID_ROOT,
-                                  const char* source = NULL);
+                                  const char* source = nullptr);
 
     static const uint32_t emptyTag = uint32_t(-1);
 
diff --git a/logd/LogTimes.cpp b/logd/LogTimes.cpp
index 25c2ad2..7a6f84b 100644
--- a/logd/LogTimes.cpp
+++ b/logd/LogTimes.cpp
@@ -28,9 +28,8 @@
 pthread_mutex_t LogTimeEntry::timesLock = PTHREAD_MUTEX_INITIALIZER;
 
 LogTimeEntry::LogTimeEntry(LogReader& reader, SocketClient* client,
-                           bool nonBlock, unsigned long tail,
-                           unsigned int logMask, pid_t pid, log_time start,
-                           uint64_t timeout)
+                           bool nonBlock, unsigned long tail, log_mask_t logMask,
+                           pid_t pid, log_time start, uint64_t timeout)
     : mRefCount(1),
       mRelease(false),
       mError(false),
diff --git a/logd/LogTimes.h b/logd/LogTimes.h
index 9ca2aea..76d016c 100644
--- a/logd/LogTimes.h
+++ b/logd/LogTimes.h
@@ -26,6 +26,8 @@
 #include <log/log.h>
 #include <sysutils/SocketClient.h>
 
+typedef unsigned int log_mask_t;
+
 class LogReader;
 class LogBufferElement;
 
@@ -41,7 +43,7 @@
     LogReader& mReader;
     static void* threadStart(void* me);
     static void threadStop(void* me);
-    const unsigned int mLogMask;
+    const log_mask_t mLogMask;
     const pid_t mPid;
     unsigned int skipAhead[LOG_ID_MAX];
     pid_t mLastTid[LOG_ID_MAX];
@@ -51,7 +53,7 @@
 
    public:
     LogTimeEntry(LogReader& reader, SocketClient* client, bool nonBlock,
-                 unsigned long tail, unsigned int logMask, pid_t pid,
+                 unsigned long tail, log_mask_t logMask, pid_t pid,
                  log_time start, uint64_t timeout);
 
     SocketClient* mClient;
@@ -133,8 +135,11 @@
         // No one else is holding a reference to this
         delete this;
     }
-    bool isWatching(log_id_t id) {
-        return (mLogMask & (1 << id)) != 0;
+    bool isWatching(log_id_t id) const {
+        return mLogMask & (1 << id);
+    }
+    bool isWatchingMultiple(log_mask_t logMask) const {
+        return mLogMask & logMask;
     }
     // flushTo filter callbacks
     static int FilterFirstPass(const LogBufferElement* element, void* me);
diff --git a/logd/LogWhiteBlackList.cpp b/logd/LogWhiteBlackList.cpp
index 4b8b080..9d762dc 100644
--- a/logd/LogWhiteBlackList.cpp
+++ b/logd/LogWhiteBlackList.cpp
@@ -51,7 +51,7 @@
 }
 
 PruneList::PruneList() {
-    init(NULL);
+    init(nullptr);
 }
 
 PruneList::~PruneList() {
@@ -79,7 +79,7 @@
     // default here means take ro.logd.filter, persist.logd.filter then
     // internal default in that order.
     if (str && !strcmp(str, _default)) {
-        str = NULL;
+        str = nullptr;
     }
     static const char _disable[] = "disable";
     if (str && !strcmp(str, _disable)) {
diff --git a/logd/OWNERS b/logd/OWNERS
new file mode 100644
index 0000000..2394e32
--- /dev/null
+++ b/logd/OWNERS
@@ -0,0 +1,2 @@
+cferris@google.com
+tomcherry@google.com
diff --git a/logd/README.property b/logd/README.property
index de6767a..da5f96f 100644
--- a/logd/README.property
+++ b/logd/README.property
@@ -31,9 +31,9 @@
                                          resist increasing the log buffer.
 persist.logd.size.<buffer> number  ro    Size of the buffer for <buffer> log
 ro.logd.size.<buffer>      number svelte default for persist.logd.size.<buffer>
-ro.config.low_ram          bool   false  if true, logd.statistics, logd.kernel
-                                         default false, logd.size 64K instead
-                                         of 256K.
+ro.config.low_ram          bool   false  if true, logd.statistics,
+                                         ro.logd.kernel default false,
+                                         logd.size 64K instead of 256K.
 persist.logd.filter        string        Pruning filter to optimize content.
                                          At runtime use: logcat -P "<string>"
 ro.logd.filter       string "~! ~1000/!" default for persist.logd.filter.
diff --git a/logd/libaudit.c b/logd/libaudit.c
index dfd56f2..9d9a857 100644
--- a/logd/libaudit.c
+++ b/logd/libaudit.c
@@ -160,7 +160,8 @@
      * and the the mask set to AUDIT_STATUS_PID
      */
     status.pid = pid;
-    status.mask = AUDIT_STATUS_PID;
+    status.mask = AUDIT_STATUS_PID | AUDIT_STATUS_RATE_LIMIT;
+    status.rate_limit = AUDIT_RATE_LIMIT; /* audit entries per second */
 
     /* Let the kernel know this pid will be registering for audit events */
     rc = audit_send(fd, AUDIT_SET, &status, sizeof(status));
@@ -183,26 +184,6 @@
     return 0;
 }
 
-int audit_rate_limit(int fd, unsigned rate_limit) {
-    int rc;
-    struct audit_message rep;
-    struct audit_status status;
-
-    memset(&status, 0, sizeof(status));
-
-    status.mask = AUDIT_STATUS_RATE_LIMIT;
-    status.rate_limit = rate_limit; /* audit entries per second */
-
-    rc = audit_send(fd, AUDIT_SET, &status, sizeof(status));
-    if (rc < 0) {
-        return rc;
-    }
-
-    audit_get_reply(fd, &rep, GET_REPLY_NONBLOCKING, 0);
-
-    return 0;
-}
-
 int audit_open() {
     return socket(PF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, NETLINK_AUDIT);
 }
diff --git a/logd/libaudit.h b/logd/libaudit.h
index a2afe47..2a93ea3 100644
--- a/logd/libaudit.h
+++ b/logd/libaudit.h
@@ -89,22 +89,8 @@
  */
 extern int audit_setup(int fd, pid_t pid);
 
-/**
- * Sets the rate limit to receive audit netlink events from the kernel
- * @param fd
- *  The fd returned by a call to audit_open()
- * @param max_rate
- *  The cap of the maximum number of audit messages a second
- * @return
- *  This function returns 0 on success, -errno on error.
- */
-
-/* Guidelines to follow for dynamic rate_limit */
-#define AUDIT_RATE_LIMIT_DEFAULT 20        /* acceptable burst rate      */
-#define AUDIT_RATE_LIMIT_BURST_DURATION 10 /* number of seconds of burst */
-#define AUDIT_RATE_LIMIT_MAX 5             /* acceptable sustained rate  */
-
-extern int audit_rate_limit(int fd, unsigned rate_limit);
+/* Max audit messages per second  */
+#define AUDIT_RATE_LIMIT 5
 
 __END_DECLS
 
diff --git a/logd/logd.rc b/logd/logd.rc
index 8804246..c740ecf 100644
--- a/logd/logd.rc
+++ b/logd/logd.rc
@@ -6,6 +6,7 @@
     file /dev/kmsg w
     user logd
     group logd system package_info readproc
+    capabilities SYSLOG AUDIT_CONTROL SETGID
     writepid /dev/cpuset/system-background/tasks
 
 service logd-reinit /system/bin/logd --reinit
@@ -20,4 +21,3 @@
 "
     chown logd logd /dev/event-log-tags
     chmod 0644 /dev/event-log-tags
-    restorecon /dev/event-log-tags
diff --git a/logd/main.cpp b/logd/main.cpp
index c8183f0..b697d44 100644
--- a/logd/main.cpp
+++ b/logd/main.cpp
@@ -33,7 +33,6 @@
 #include <syslog.h>
 #include <unistd.h>
 
-#include <cstdbool>
 #include <memory>
 
 #include <android-base/macros.h>
@@ -88,37 +87,27 @@
 //
 
 static int drop_privs(bool klogd, bool auditd) {
-    // Tricky, if ro.build.type is "eng" then this is true because of the
-    // side effect that ro.debuggable == 1 as well, else it is false.
-    bool eng =
-        __android_logger_property_get_bool("ro.build.type", BOOL_DEFAULT_FALSE);
-
-    struct sched_param param;
-    memset(&param, 0, sizeof(param));
+    sched_param param = {};
 
     if (set_sched_policy(0, SP_BACKGROUND) < 0) {
         android::prdebug("failed to set background scheduling policy");
-        if (!eng) return -1;
+        return -1;
     }
 
     if (sched_setscheduler((pid_t)0, SCHED_BATCH, &param) < 0) {
         android::prdebug("failed to set batch scheduler");
-        if (!eng) return -1;
+        return -1;
     }
 
     if (setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_BACKGROUND) < 0) {
         android::prdebug("failed to set background cgroup");
-        if (!eng) return -1;
-    }
-
-    if (!eng && (prctl(PR_SET_DUMPABLE, 0) < 0)) {
-        android::prdebug("failed to clear PR_SET_DUMPABLE");
         return -1;
     }
 
-    if (prctl(PR_SET_KEEPCAPS, 1) < 0) {
-        android::prdebug("failed to set PR_SET_KEEPCAPS");
-        if (!eng) return -1;
+    if (__android_logger_property_get_bool("ro.debuggable", BOOL_DEFAULT_FALSE) &&
+        prctl(PR_SET_DUMPABLE, 0) == -1) {
+        android::prdebug("failed to clear PR_SET_DUMPABLE");
+        return -1;
     }
 
     std::unique_ptr<struct _cap_struct, int (*)(void*)> caps(cap_init(),
@@ -139,24 +128,24 @@
         android::prdebug(
             "failed to set CAP_SETGID, CAP_SYSLOG or CAP_AUDIT_CONTROL (%d)",
             errno);
-        if (!eng) return -1;
+        return -1;
     }
 
     gid_t groups[] = { AID_READPROC };
 
     if (setgroups(arraysize(groups), groups) == -1) {
         android::prdebug("failed to set AID_READPROC groups");
-        if (!eng) return -1;
+        return -1;
     }
 
     if (setgid(AID_LOGD) != 0) {
         android::prdebug("failed to set AID_LOGD gid");
-        if (!eng) return -1;
+        return -1;
     }
 
     if (setuid(AID_LOGD) != 0) {
         android::prdebug("failed to set AID_LOGD uid");
-        if (!eng) return -1;
+        return -1;
     }
 
     if (cap_set_flag(caps.get(), CAP_PERMITTED, 1, cap_value, CAP_CLEAR) < 0) {
@@ -167,7 +156,7 @@
     }
     if (cap_set_proc(caps.get()) < 0) {
         android::prdebug("failed to clear CAP_SETGID (%d)", errno);
-        if (!eng) return -1;
+        return -1;
     }
 
     return 0;
@@ -438,8 +427,8 @@
 
     int fdPmesg = -1;
     bool klogd = __android_logger_property_get_bool(
-        "logd.kernel", BOOL_DEFAULT_TRUE | BOOL_DEFAULT_FLAG_PERSIST |
-                           BOOL_DEFAULT_FLAG_ENG | BOOL_DEFAULT_FLAG_SVELTE);
+        "ro.logd.kernel",
+        BOOL_DEFAULT_TRUE | BOOL_DEFAULT_FLAG_ENG | BOOL_DEFAULT_FLAG_SVELTE);
     if (klogd) {
         static const char proc_kmsg[] = "/proc/kmsg";
         fdPmesg = android_get_control_file(proc_kmsg);
@@ -474,7 +463,7 @@
     bool auditd =
         __android_logger_property_get_bool("ro.logd.auditd", BOOL_DEFAULT_TRUE);
     if (drop_privs(klogd, auditd) != 0) {
-        return -1;
+        return EXIT_FAILURE;
     }
 
     // Serves the purpose of managing the last logs times read on a
@@ -502,7 +491,7 @@
 
     LogReader* reader = new LogReader(logBuf);
     if (reader->startListener()) {
-        exit(1);
+        return EXIT_FAILURE;
     }
 
     // LogListener listens on /dev/socket/logdw for client
@@ -512,7 +501,7 @@
     LogListener* swl = new LogListener(logBuf, reader);
     // Backlog and /proc/sys/net/unix/max_dgram_qlen set to large value
     if (swl->startListener(600)) {
-        exit(1);
+        return EXIT_FAILURE;
     }
 
     // Command listener listens on /dev/socket/logd for incoming logd
@@ -520,7 +509,7 @@
 
     CommandListener* cl = new CommandListener(logBuf, reader, swl);
     if (cl->startListener()) {
-        exit(1);
+        return EXIT_FAILURE;
     }
 
     // LogAudit listens on NETLINK_AUDIT socket for selinux
@@ -555,5 +544,5 @@
 
     TEMP_FAILURE_RETRY(pause());
 
-    exit(0);
+    return EXIT_SUCCESS;
 }
diff --git a/logd/tests/Android.mk b/logd/tests/Android.mk
index 1915677..a0875ea 100644
--- a/logd/tests/Android.mk
+++ b/logd/tests/Android.mk
@@ -63,7 +63,7 @@
 LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
 LOCAL_SHARED_LIBRARIES := libbase libcutils liblog libselinux
 LOCAL_STATIC_LIBRARIES := libgtest libgtest_main
-LOCAL_COMPATIBILITY_SUITE := cts
+LOCAL_COMPATIBILITY_SUITE := cts vts
 LOCAL_CTS_TEST_PACKAGE := android.core.logd
 include $(BUILD_CTS_EXECUTABLE)
 
diff --git a/logd/tests/AndroidTest.xml b/logd/tests/AndroidTest.xml
index b16bdfd..84f0764 100644
--- a/logd/tests/AndroidTest.xml
+++ b/logd/tests/AndroidTest.xml
@@ -14,6 +14,8 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS Logging Daemon test cases">
+    <option name="test-suite-tag" value="cts" />
+    <option name="config-descriptor:metadata" key="component" value="systems" />
     <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
         <option name="cleanup" value="true" />
         <option name="push" value="CtsLogdTestCases->/data/local/tmp/CtsLogdTestCases" />
diff --git a/logd/tests/logd_test.cpp b/logd/tests/logd_test.cpp
index 4397b14..7d7a22f 100644
--- a/logd/tests/logd_test.cpp
+++ b/logd/tests/logd_test.cpp
@@ -646,16 +646,20 @@
                 recv(fd, msg_timeout.buf, sizeof(msg_timeout), 0) > 0;
         }
 
-        alarm_timeout =
-            alarm((old_alarm <= 0) ? old_alarm
-                                   : (old_alarm > (1 + 3 - alarm_wrap))
-                                         ? old_alarm - 3 + alarm_wrap
-                                         : 2);
+        if (old_alarm > 0) {
+            unsigned int time_spent = 3 - alarm_wrap;
+            if (old_alarm > time_spent + 1) {
+                old_alarm -= time_spent;
+            } else {
+                old_alarm = 2;
+            }
+        }
+        alarm_timeout = alarm(old_alarm);
         sigaction(SIGALRM, &old_sigaction, nullptr);
 
         close(fd);
 
-        if (!content_wrap && !alarm_wrap && content_timeout && alarm_timeout) {
+        if (content_wrap && alarm_wrap && content_timeout && alarm_timeout) {
             break;
         }
     }
@@ -710,8 +714,8 @@
     // A few tries to get it right just in case wrap kicks in due to
     // content providers being active during the test.
     int i = 5;
-    log_time now(android_log_clockid());
-    now.tv_sec -= 30;  // reach back a moderate period of time
+    log_time start(android_log_clockid());
+    start.tv_sec -= 30;  // reach back a moderate period of time
 
     while (--i) {
         int fd = socket_local_client("logdr", ANDROID_SOCKET_NAMESPACE_RESERVED,
@@ -726,7 +730,7 @@
         std::string ask = android::base::StringPrintf(
             "dumpAndClose lids=0,1,2,3,4,5 timeout=6 start=%" PRIu32
             ".%09" PRIu32,
-            now.tv_sec, now.tv_nsec);
+            start.tv_sec, start.tv_nsec);
 
         struct sigaction ignore, old_sigaction;
         memset(&ignore, 0, sizeof(ignore));
@@ -756,11 +760,15 @@
                 recv(fd, msg_timeout.buf, sizeof(msg_timeout), 0) > 0;
         }
 
-        alarm_timeout =
-            alarm((old_alarm <= 0) ? old_alarm
-                                   : (old_alarm > (1 + 3 - alarm_wrap))
-                                         ? old_alarm - 3 + alarm_wrap
-                                         : 2);
+        if (old_alarm > 0) {
+            unsigned int time_spent = 3 - alarm_wrap;
+            if (old_alarm > time_spent + 1) {
+                old_alarm -= time_spent;
+            } else {
+                old_alarm = 2;
+            }
+        }
+        alarm_timeout = alarm(old_alarm);
         sigaction(SIGALRM, &old_sigaction, nullptr);
 
         close(fd);
@@ -773,23 +781,23 @@
         // active _or_ inactive during the test.
         if (content_timeout) {
             log_time msg(msg_timeout.entry.sec, msg_timeout.entry.nsec);
-            if (msg < now) {
+            if (msg < start) {
                 fprintf(stderr, "%u.%09u < %u.%09u\n", msg_timeout.entry.sec,
-                        msg_timeout.entry.nsec, (unsigned)now.tv_sec,
-                        (unsigned)now.tv_nsec);
+                        msg_timeout.entry.nsec, (unsigned)start.tv_sec,
+                        (unsigned)start.tv_nsec);
                 _exit(-1);
             }
-            if (msg > now) {
-                now = msg;
-                now.tv_sec += 30;
-                msg = log_time(android_log_clockid());
-                if (now > msg) {
-                    now = msg;
-                    --now.tv_sec;
+            if (msg > start) {
+                start = msg;
+                start.tv_sec += 30;
+                log_time now = log_time(android_log_clockid());
+                if (start > now) {
+                    start = now;
+                    --start.tv_sec;
                 }
             }
         } else {
-            now.tv_sec -= 120;  // inactive, reach further back!
+            start.tv_sec -= 120;  // inactive, reach further back!
         }
     }
 
@@ -802,8 +810,8 @@
     }
 
     if (content_wrap || !content_timeout) {
-        fprintf(stderr, "now=%" PRIu32 ".%09" PRIu32 "\n", now.tv_sec,
-                now.tv_nsec);
+        fprintf(stderr, "start=%" PRIu32 ".%09" PRIu32 "\n", start.tv_sec,
+                start.tv_nsec);
     }
 
     EXPECT_TRUE(written);
@@ -1091,7 +1099,7 @@
     // and dac_read_search on every try to get past the message
     // de-duper.  We will also rotate the file name in the directory
     // as another measure.
-    static const char file[] = "/data/backup/cannot_access_directory_%u";
+    static const char file[] = "/data/drm/cannot_access_directory_%u";
     static const unsigned avc_requests_per_access = 2;
 
     rate /= avc_requests_per_access;
@@ -1187,51 +1195,14 @@
                           << "fail as this device is in a bad state, "
                           << "but is not strictly a unit test failure.";
     }
-    // sepolicy_rate_limiter_maximum
-    {  // maximum precharch test block.
-        static constexpr int rate = AUDIT_RATE_LIMIT_MAX;
-        static constexpr int duration = 2;
-        // Two seconds of a liveable sustained rate
-        EXPECT_EQ(rate * duration,
-                  count_avc(sepolicy_rate(rate, rate * duration)));
-    }
-    // sepolicy_rate_limiter_sub_burst
-    {  // maximum period below half way between sustainable and burst rate
-        static constexpr int threshold =
-            ((AUDIT_RATE_LIMIT_BURST_DURATION *
-              (AUDIT_RATE_LIMIT_DEFAULT + AUDIT_RATE_LIMIT_MAX)) +
-             1) /
-            2;
-        static constexpr int rate =
-            (threshold / AUDIT_RATE_LIMIT_BURST_DURATION) - 1;
-        static constexpr int duration = AUDIT_RATE_LIMIT_BURST_DURATION;
-        EXPECT_EQ(rate * duration,
-                  count_avc(sepolicy_rate(rate, rate * duration)));
-    }
-    // sepolicy_rate_limiter_spam
-    {  // hit avc: hard beyond reason block.
-        // maximum period of double the maximum burst rate
-        static constexpr int threshold =
-            ((AUDIT_RATE_LIMIT_BURST_DURATION *
-              (AUDIT_RATE_LIMIT_DEFAULT + AUDIT_RATE_LIMIT_MAX)) +
-             1) /
-            2;
-        static constexpr int rate = AUDIT_RATE_LIMIT_DEFAULT * 2;
-        static constexpr int duration = threshold / AUDIT_RATE_LIMIT_DEFAULT;
-        EXPECT_GE(
-            ((AUDIT_RATE_LIMIT_DEFAULT * duration) * 115) / 100,  // +15% margin
-            count_avc(sepolicy_rate(rate, rate * duration)));
-        // give logd another 3 seconds to react to the burst before checking
-        sepolicy_rate(rate, rate * 3);
-        // maximum period at double maximum burst rate (spam filter kicked in)
-        EXPECT_GE(threshold * 2,
-                  count_avc(sepolicy_rate(
-                      rate, rate * AUDIT_RATE_LIMIT_BURST_DURATION)));
-        // cool down, and check unspammy rate still works
-        sleep(2);
-        EXPECT_LE(AUDIT_RATE_LIMIT_BURST_DURATION - 1,  // allow _one_ lost
-                  count_avc(sepolicy_rate(1, AUDIT_RATE_LIMIT_BURST_DURATION)));
-    }
+
+    static const int rate = AUDIT_RATE_LIMIT;
+    static const int duration = 2;
+    // Two seconds of sustained denials. Depending on the overlap in the time
+    // window that the kernel is considering vs what this test is considering,
+    // allow some additional denials to prevent a flaky test.
+    EXPECT_LE(count_avc(sepolicy_rate(rate, rate * duration)),
+              rate * duration + rate);
 #else
     GTEST_LOG_(INFO) << "This test does nothing.\n";
 #endif
diff --git a/logwrapper/Android.bp b/logwrapper/Android.bp
index ccb1aa6..c378646 100644
--- a/logwrapper/Android.bp
+++ b/logwrapper/Android.bp
@@ -1,10 +1,18 @@
-
+cc_defaults {
+    name: "logwrapper_defaults",
+    cflags: [
+        "-Werror",
+    ],
+}
 
 // ========================================================
 // Static and shared library
 // ========================================================
+
 cc_library {
     name: "liblogwrap",
+    defaults: ["logwrapper_defaults"],
+    recovery_available: true,
     srcs: ["logwrap.c"],
     shared_libs: [
         "libcutils",
@@ -12,32 +20,42 @@
     ],
     export_include_dirs: ["include"],
     local_include_dirs: ["include"],
-    cflags: [
-        "-Werror",
-    ],
 }
 
 // ========================================================
 // Executable
 // ========================================================
+
+cc_defaults {
+    name: "logwrapper_common",
+    defaults: ["logwrapper_defaults"],
+    local_include_dirs: ["include"],
+    srcs: [
+        "logwrap.c",
+        "logwrapper.c",
+    ],
+    shared_libs: ["libcutils", "liblog"],
+}
+
 cc_binary {
     name: "logwrapper",
-    srcs: ["logwrapper.c"],
-    static_libs: [
-        "liblog",
-        "liblogwrap",
-        "libcutils",
-    ],
-    cflags: [
-        "-Werror",
-    ],
+    defaults: ["logwrapper_common"],
+}
+
+cc_binary {
+    name: "logwrapper_vendor",
+    defaults: ["logwrapper_common"],
+    stem: "logwrapper",
+    vendor: true,
 }
 
 // ========================================================
 // Benchmark
 // ========================================================
+
 cc_benchmark {
     name: "android_fork_execvp_ext_benchmark",
+    defaults: ["logwrapper_defaults"],
     srcs: [
         "android_fork_execvp_ext_benchmark.cpp",
     ],
@@ -47,7 +65,4 @@
         "liblog",
         "liblogwrap",
     ],
-    cflags: [
-        "-Werror",
-    ]
 }
diff --git a/logwrapper/OWNERS b/logwrapper/OWNERS
new file mode 100644
index 0000000..babbe4d
--- /dev/null
+++ b/logwrapper/OWNERS
@@ -0,0 +1 @@
+tomcherry@google.com
diff --git a/logwrapper/logwrap.c b/logwrapper/logwrap.c
index 7076078..8621993 100644
--- a/logwrapper/logwrap.c
+++ b/logwrapper/logwrap.c
@@ -31,7 +31,6 @@
 #include <cutils/klog.h>
 #include <log/log.h>
 #include <logwrap/logwrap.h>
-#include <private/android_filesystem_config.h>
 
 #define ARRAY_SIZE(x)   (sizeof(x) / sizeof(*(x)))
 #define MIN(a,b) (((a)<(b))?(a):(b))
diff --git a/mkbootimg/Android.bp b/mkbootimg/Android.bp
new file mode 100644
index 0000000..576a677
--- /dev/null
+++ b/mkbootimg/Android.bp
@@ -0,0 +1,33 @@
+// Copyright 2012 The Android Open Source Project
+
+cc_library_headers {
+    name: "libmkbootimg_abi_headers",
+    vendor_available: true,
+    export_include_dirs: ["include"],
+}
+
+cc_library_headers {
+    name: "bootimg_headers",
+    vendor_available: true,
+    recovery_available: true,
+    export_include_dirs: ["include/bootimg"],
+    host_supported: true,
+    target: {
+        windows: {
+            enabled: true,
+        },
+    },
+}
+
+cc_library {
+    name: "libmkbootimg_abi_check",
+    vendor_available: true,
+    vndk: {
+        enabled: true,
+    },
+    srcs: [
+        "mkbootimg_dummy.cpp",
+    ],
+    header_libs: ["libmkbootimg_abi_headers"],
+    export_header_lib_headers: ["libmkbootimg_abi_headers"],
+}
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..3a00860
--- /dev/null
+++ b/mkbootimg/OWNERS
@@ -0,0 +1,2 @@
+hridya@google.com
+tbao@google.com
diff --git a/mkbootimg/bootimg.h b/mkbootimg/bootimg.h
deleted file mode 100644
index 60834fe..0000000
--- a/mkbootimg/bootimg.h
+++ /dev/null
@@ -1,111 +0,0 @@
-/* tools/mkbootimg/bootimg.h
-**
-** Copyright 2007, 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 <stdint.h>
-
-#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
-{
-    uint8_t magic[BOOT_MAGIC_SIZE];
-
-    uint32_t kernel_size;  /* size in bytes */
-    uint32_t kernel_addr;  /* physical load addr */
-
-    uint32_t ramdisk_size; /* size in bytes */
-    uint32_t ramdisk_addr; /* physical load addr */
-
-    uint32_t second_size;  /* size in bytes */
-    uint32_t second_addr;  /* physical load addr */
-
-    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 */
-
-    /* operating system version and security patch level; for
-     * version "A.B.C" and patch level "Y-M-D":
-     * ver = A << 14 | B << 7 | C         (7 bits for each of A, B, C)
-     * lvl = ((Y - 2000) & 127) << 4 | M  (7 bits for Y, 4 bits for M)
-     * os_version = ver << 11 | lvl */
-    uint32_t os_version;
-
-    uint8_t name[BOOT_NAME_SIZE]; /* asciiz product name */
-
-    uint8_t cmdline[BOOT_ARGS_SIZE];
-
-    uint32_t id[8]; /* timestamp / checksum / sha1 / etc */
-
-    /* Supplemental command line data; kept here to maintain
-     * binary compatibility with older versions of mkbootimg */
-    uint8_t extra_cmdline[BOOT_EXTRA_ARGS_SIZE];
-} __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
-*/
-
-#if 0
-typedef struct ptentry ptentry;
-
-struct ptentry {
-    char name[16];      /* asciiz partition name    */
-    unsigned start;     /* starting block number    */
-    unsigned length;    /* length in blocks         */
-    unsigned flags;     /* set to zero              */
-};
-
-/* MSM Partition Table ATAG
-**
-** length: 2 + 7 * n
-** atag:   0x4d534d70
-**         <ptentry> x n
-*/
-#endif
-
-#endif
diff --git a/mkbootimg/include/abi_check/mkbootimg_abi_check.h b/mkbootimg/include/abi_check/mkbootimg_abi_check.h
new file mode 100644
index 0000000..d478aba
--- /dev/null
+++ b/mkbootimg/include/abi_check/mkbootimg_abi_check.h
@@ -0,0 +1,28 @@
+/*
+ * 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 <bootimg/bootimg.h>
+
+// This header has been created for the following reaons:
+//    1) In order for a change in a user defined type to be classified as API /
+//       ABI breaking, it needs to be referenced by an 'exported interface'
+//       (in this case the function mkbootimg_dummy).
+//    2) Since 'mkbootimg_dummy' needs to be exported, we need to have it
+//       exposed through a public header.
+//    3) It is desirable not to pollute bootimg.h with interfaces which are not
+//       'used' in reality by on device binaries. Furthermore, bootimg.h might
+//       be exported by a library in the future, so we must avoid polluting it.
+void mkbootimg_dummy(boot_img_hdr*);
diff --git a/mkbootimg/include/bootimg/bootimg.h b/mkbootimg/include/bootimg/bootimg.h
new file mode 100644
index 0000000..bce308b
--- /dev/null
+++ b/mkbootimg/include/bootimg/bootimg.h
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2007 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.
+ */
+
+#pragma once
+
+#include <stdint.h>
+
+#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
+
+// The bootloader expects the structure of boot_img_hdr with header
+// version 0 to be as follows:
+struct boot_img_hdr_v0 {
+    // Must be BOOT_MAGIC.
+    uint8_t magic[BOOT_MAGIC_SIZE];
+
+    uint32_t kernel_size; /* size in bytes */
+    uint32_t kernel_addr; /* physical load addr */
+
+    uint32_t ramdisk_size; /* size in bytes */
+    uint32_t ramdisk_addr; /* physical load addr */
+
+    uint32_t second_size; /* size in bytes */
+    uint32_t second_addr; /* physical load addr */
+
+    uint32_t tags_addr; /* physical addr for kernel tags */
+    uint32_t page_size; /* flash page size we assume */
+
+    // Version of 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":
+    //   (7 bits for each of A, B, C; 7 bits for (Y-2000), 4 bits for M)
+    //   os_version = A[31:25] B[24:18] C[17:11] (Y-2000)[10:4] M[3:0]
+    uint32_t os_version;
+
+#if __cplusplus
+    void SetOsVersion(unsigned major, unsigned minor, unsigned patch) {
+        os_version &= ((1 << 11) - 1);
+        os_version |= (((major & 0x7f) << 25) | ((minor & 0x7f) << 18) | ((patch & 0x7f) << 11));
+    }
+
+    void SetOsPatchLevel(unsigned year, unsigned month) {
+        os_version &= ~((1 << 11) - 1);
+        os_version |= (((year - 2000) & 0x7f) << 4) | ((month & 0xf) << 0);
+    }
+#endif
+
+    uint8_t name[BOOT_NAME_SIZE]; /* asciiz product name */
+
+    uint8_t cmdline[BOOT_ARGS_SIZE];
+
+    uint32_t id[8]; /* timestamp / checksum / sha1 / etc */
+
+    // Supplemental command line data; kept here to maintain
+    // binary compatibility with older versions of mkbootimg.
+    uint8_t extra_cmdline[BOOT_EXTRA_ARGS_SIZE];
+} __attribute__((packed));
+
+/*
+ * 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 0, 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
+ */
+
+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; /* offset to recovery dtbo in boot image */
+    uint32_t header_size;
+} __attribute__((packed));
+
+/* When the boot image header has a version of 1, 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) at
+ *    the specified physical address (kernel_addr, etc)
+ * 5. If booting to recovery mode in a non-A/B device, extract recovery dtbo and
+ *    apply the correct set of overlays on the base device tree depending on the
+ *    hardware/product revision.
+ * 6. prepare tags at tag_addr.  kernel_args[] is
+ *    appended to the kernel commandline in the tags.
+ * 7. r0 = 0, r1 = MACHINE_TYPE, r2 = tags_addr
+ * 8. if second_size != 0: jump to second_addr
+ *    else: jump to kernel_addr
+ */
diff --git a/mkbootimg/mkbootimg b/mkbootimg/mkbootimg
index 5a13da2..fda9af0 100755
--- a/mkbootimg/mkbootimg
+++ b/mkbootimg/mkbootimg
@@ -45,6 +45,22 @@
     f.write(pack(str(pad) + 'x'))
 
 
+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 get_recovery_dtbo_offset(args):
+    """calculates the offset of recovery_dtbo image in the boot image"""
+    num_header_pages = 1 # header occupies a page
+    num_kernel_pages = get_number_of_pages(filesize(args.kernel), args.pagesize)
+    num_ramdisk_pages = get_number_of_pages(filesize(args.ramdisk), args.pagesize)
+    num_second_pages = get_number_of_pages(filesize(args.second), args.pagesize)
+    dtbo_offset = args.pagesize * (num_header_pages + num_kernel_pages +
+                                   num_ramdisk_pages + num_second_pages)
+    return dtbo_offset
+
+
 def write_header(args):
     BOOT_MAGIC = 'ANDROID!'.encode()
     args.output.write(pack('8s', BOOT_MAGIC))
@@ -57,7 +73,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 +82,23 @@
     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
+        if args.recovery_dtbo:
+            args.output.write(pack('Q', get_recovery_dtbo_offset(args))) # recovery dtbo offset
+        else:
+            args.output.write(pack('Q', 0)) # Will be set to 0 for devices without a recovery dtbo
+        args.output.write(pack('I', args.output.tell() + 4))         # size of boot header
+
     pad_file(args.output, args.pagesize)
     return img_id
 
@@ -132,6 +161,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)
@@ -150,6 +180,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 +191,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/mkbootimg_dummy.cpp b/mkbootimg/mkbootimg_dummy.cpp
new file mode 100644
index 0000000..410d379
--- /dev/null
+++ b/mkbootimg/mkbootimg_dummy.cpp
@@ -0,0 +1,24 @@
+/*
+ * 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 <abi_check/mkbootimg_abi_check.h>
+
+void mkbootimg_dummy(boot_img_hdr* hdr) {
+    // TODO: Hack to trigger abi checks, remove this.
+    if (hdr) {
+        hdr--;
+    }
+}
diff --git a/mkbootimg/unpack_bootimg b/mkbootimg/unpack_bootimg
new file mode 100755
index 0000000..c37acd5
--- /dev/null
+++ b/mkbootimg/unpack_bootimg
@@ -0,0 +1,134 @@
+#!/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_offset = unpack('Q', args.boot_img.read(8))[0]
+        print('recovery dtbo offset: %s' % recovery_dtbo_offset)
+        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'))
+
+    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:
+        image_info_list.append((recovery_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/.clang-format b/property_service/.clang-format
new file mode 120000
index 0000000..fd0645f
--- /dev/null
+++ b/property_service/.clang-format
@@ -0,0 +1 @@
+../.clang-format-2
\ No newline at end of file
diff --git a/property_service/Android.bp b/property_service/Android.bp
new file mode 100644
index 0000000..b44c296
--- /dev/null
+++ b/property_service/Android.bp
@@ -0,0 +1 @@
+subdirs = ["*"]
diff --git a/property_service/OWNERS b/property_service/OWNERS
new file mode 100644
index 0000000..babbe4d
--- /dev/null
+++ b/property_service/OWNERS
@@ -0,0 +1 @@
+tomcherry@google.com
diff --git a/property_service/libpropertyinfoparser/Android.bp b/property_service/libpropertyinfoparser/Android.bp
new file mode 100644
index 0000000..70f6faa
--- /dev/null
+++ b/property_service/libpropertyinfoparser/Android.bp
@@ -0,0 +1,16 @@
+cc_library_static {
+    name: "libpropertyinfoparser",
+    host_supported: true,
+    vendor_available: true,
+    recovery_available: true,
+    srcs: ["property_info_parser.cpp"],
+
+    cpp_std: "experimental",
+    cppflags: [
+        "-Wall",
+        "-Wextra",
+        "-Werror",
+    ],
+    stl: "none",
+    export_include_dirs: ["include"],
+}
diff --git a/property_service/libpropertyinfoparser/include/property_info_parser/property_info_parser.h b/property_service/libpropertyinfoparser/include/property_info_parser/property_info_parser.h
new file mode 100644
index 0000000..0548021
--- /dev/null
+++ b/property_service/libpropertyinfoparser/include/property_info_parser/property_info_parser.h
@@ -0,0 +1,224 @@
+//
+// Copyright (C) 2017 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 PROPERTY_INFO_PARSER_H
+#define PROPERTY_INFO_PARSER_H
+
+#include <stdint.h>
+#include <stdlib.h>
+
+namespace android {
+namespace properties {
+
+// The below structs intentionally do not end with char name[0] or other tricks to allocate
+// with a dynamic size, such that they can be added onto in the future without breaking
+// backwards compatibility.
+struct PropertyEntry {
+  uint32_t name_offset;
+  uint32_t namelen;
+
+  // This is the context match for this node_; ~0u if it doesn't correspond to any.
+  uint32_t context_index;
+  // This is the type for this node_; ~0u if it doesn't correspond to any.
+  uint32_t type_index;
+};
+
+struct TrieNodeInternal {
+  // This points to a property entry struct, which includes the name for this node
+  uint32_t property_entry;
+
+  // Children are a sorted list of child nodes_; binary search them.
+  uint32_t num_child_nodes;
+  uint32_t child_nodes;
+
+  // Prefixes are terminating prefix matches at this node, sorted longest to smallest
+  // Take the first match sequentially found with StartsWith().
+  uint32_t num_prefixes;
+  uint32_t prefix_entries;
+
+  // Exact matches are a sorted list of exact matches at this node_; binary search them.
+  uint32_t num_exact_matches;
+  uint32_t exact_match_entries;
+};
+
+struct PropertyInfoAreaHeader {
+  // The current version of this data as created by property service.
+  uint32_t current_version;
+  // The lowest version of libc that can properly parse this data.
+  uint32_t minimum_supported_version;
+  uint32_t size;
+  uint32_t contexts_offset;
+  uint32_t types_offset;
+  uint32_t root_offset;
+};
+
+class SerializedData {
+ public:
+  uint32_t size() const {
+    return reinterpret_cast<const PropertyInfoAreaHeader*>(data_base_)->size;
+  }
+
+  const char* c_string(uint32_t offset) const {
+    if (offset != 0 && offset > size()) return nullptr;
+    return static_cast<const char*>(data_base_ + offset);
+  }
+
+  const uint32_t* uint32_array(uint32_t offset) const {
+    if (offset != 0 && offset > size()) return nullptr;
+    return reinterpret_cast<const uint32_t*>(data_base_ + offset);
+  }
+
+  uint32_t uint32(uint32_t offset) const {
+    if (offset != 0 && offset > size()) return ~0u;
+    return *reinterpret_cast<const uint32_t*>(data_base_ + offset);
+  }
+
+  const char* data_base() const { return data_base_; }
+
+ private:
+  const char data_base_[0];
+};
+
+class TrieNode {
+ public:
+  TrieNode() : serialized_data_(nullptr), trie_node_base_(nullptr) {}
+  TrieNode(const SerializedData* data_base, const TrieNodeInternal* trie_node_base)
+      : serialized_data_(data_base), trie_node_base_(trie_node_base) {}
+
+  const char* name() const {
+    return serialized_data_->c_string(node_property_entry()->name_offset);
+  }
+
+  uint32_t context_index() const { return node_property_entry()->context_index; }
+  uint32_t type_index() const { return node_property_entry()->type_index; }
+
+  uint32_t num_child_nodes() const { return trie_node_base_->num_child_nodes; }
+  TrieNode child_node(int n) const {
+    uint32_t child_node_offset = serialized_data_->uint32_array(trie_node_base_->child_nodes)[n];
+    const TrieNodeInternal* trie_node_base =
+        reinterpret_cast<const TrieNodeInternal*>(serialized_data_->data_base() + child_node_offset);
+    return TrieNode(serialized_data_, trie_node_base);
+  }
+
+  bool FindChildForString(const char* input, uint32_t namelen, TrieNode* child) const;
+
+  uint32_t num_prefixes() const { return trie_node_base_->num_prefixes; }
+  const PropertyEntry* prefix(int n) const {
+    uint32_t prefix_entry_offset =
+        serialized_data_->uint32_array(trie_node_base_->prefix_entries)[n];
+    return reinterpret_cast<const PropertyEntry*>(serialized_data_->data_base() +
+                                                  prefix_entry_offset);
+  }
+
+  uint32_t num_exact_matches() const { return trie_node_base_->num_exact_matches; }
+  const PropertyEntry* exact_match(int n) const {
+    uint32_t exact_match_entry_offset =
+        serialized_data_->uint32_array(trie_node_base_->exact_match_entries)[n];
+    return reinterpret_cast<const PropertyEntry*>(serialized_data_->data_base() +
+                                                  exact_match_entry_offset);
+  }
+
+ private:
+  const PropertyEntry* node_property_entry() const {
+    return reinterpret_cast<const PropertyEntry*>(serialized_data_->data_base() +
+                                                  trie_node_base_->property_entry);
+  }
+
+  const SerializedData* serialized_data_;
+  const TrieNodeInternal* trie_node_base_;
+};
+
+class PropertyInfoArea : private SerializedData {
+ public:
+  void GetPropertyInfoIndexes(const char* name, uint32_t* context_index, uint32_t* type_index) const;
+  void GetPropertyInfo(const char* property, const char** context, const char** type) const;
+
+  int FindContextIndex(const char* context) const;
+  int FindTypeIndex(const char* type) const;
+
+  const char* context(uint32_t index) const {
+    uint32_t context_array_size_offset = contexts_offset();
+    const uint32_t* context_array = uint32_array(context_array_size_offset + sizeof(uint32_t));
+    return data_base() + context_array[index];
+  }
+
+  const char* type(uint32_t index) const {
+    uint32_t type_array_size_offset = types_offset();
+    const uint32_t* type_array = uint32_array(type_array_size_offset + sizeof(uint32_t));
+    return data_base() + type_array[index];
+  }
+
+  uint32_t current_version() const { return header()->current_version; }
+  uint32_t minimum_supported_version() const { return header()->minimum_supported_version; }
+
+  uint32_t size() const { return SerializedData::size(); }
+
+  uint32_t num_contexts() const { return uint32_array(contexts_offset())[0]; }
+  uint32_t num_types() const { return uint32_array(types_offset())[0]; }
+
+  TrieNode root_node() const { return trie(header()->root_offset); }
+
+ private:
+  void CheckPrefixMatch(const char* remaining_name, const TrieNode& trie_node,
+                        uint32_t* context_index, uint32_t* type_index) const;
+
+  const PropertyInfoAreaHeader* header() const {
+    return reinterpret_cast<const PropertyInfoAreaHeader*>(data_base());
+  }
+  uint32_t contexts_offset() const { return header()->contexts_offset; }
+  uint32_t contexts_array_offset() const { return contexts_offset() + sizeof(uint32_t); }
+  uint32_t types_offset() const { return header()->types_offset; }
+  uint32_t types_array_offset() const { return types_offset() + sizeof(uint32_t); }
+
+  TrieNode trie(uint32_t offset) const {
+    if (offset != 0 && offset > size()) return TrieNode();
+    const TrieNodeInternal* trie_node_base =
+        reinterpret_cast<const TrieNodeInternal*>(data_base() + offset);
+    return TrieNode(this, trie_node_base);
+  }
+};
+
+// This is essentially a smart pointer for read only mmap region for property contexts.
+class PropertyInfoAreaFile {
+ public:
+  PropertyInfoAreaFile() : mmap_base_(nullptr), mmap_size_(0) {}
+  ~PropertyInfoAreaFile() { Reset(); }
+
+  PropertyInfoAreaFile(const PropertyInfoAreaFile&) = delete;
+  void operator=(const PropertyInfoAreaFile&) = delete;
+  PropertyInfoAreaFile(PropertyInfoAreaFile&&) = default;
+  PropertyInfoAreaFile& operator=(PropertyInfoAreaFile&&) = default;
+
+  bool LoadDefaultPath();
+  bool LoadPath(const char* filename);
+
+  const PropertyInfoArea* operator->() const {
+    return reinterpret_cast<const PropertyInfoArea*>(mmap_base_);
+  }
+
+  explicit operator bool() const { return mmap_base_ != nullptr; }
+
+  void Reset();
+
+ private:
+  void* mmap_base_;
+  size_t mmap_size_;
+};
+
+}  // namespace properties
+}  // namespace android
+
+#endif
diff --git a/property_service/libpropertyinfoparser/property_info_parser.cpp b/property_service/libpropertyinfoparser/property_info_parser.cpp
new file mode 100644
index 0000000..489d81a
--- /dev/null
+++ b/property_service/libpropertyinfoparser/property_info_parser.cpp
@@ -0,0 +1,246 @@
+//
+// Copyright (C) 2017 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 "property_info_parser/property_info_parser.h"
+
+#include <fcntl.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+namespace android {
+namespace properties {
+
+namespace {
+
+// Binary search to find index of element in an array compared via f(search).
+template <typename F>
+int Find(uint32_t array_length, F&& f) {
+  int bottom = 0;
+  int top = array_length - 1;
+  while (top >= bottom) {
+    int search = (top + bottom) / 2;
+
+    auto cmp = f(search);
+
+    if (cmp == 0) return search;
+    if (cmp < 0) bottom = search + 1;
+    if (cmp > 0) top = search - 1;
+  }
+  return -1;
+}
+
+}  // namespace
+
+// Binary search the list of contexts to find the index of a given context string.
+// Only should be used for TrieSerializer to construct the Trie.
+int PropertyInfoArea::FindContextIndex(const char* context) const {
+  return Find(num_contexts(), [this, context](auto array_offset) {
+    auto string_offset = uint32_array(contexts_array_offset())[array_offset];
+    return strcmp(c_string(string_offset), context);
+  });
+}
+
+// Binary search the list of types to find the index of a given type string.
+// Only should be used for TrieSerializer to construct the Trie.
+int PropertyInfoArea::FindTypeIndex(const char* type) const {
+  return Find(num_types(), [this, type](auto array_offset) {
+    auto string_offset = uint32_array(types_array_offset())[array_offset];
+    return strcmp(c_string(string_offset), type);
+  });
+}
+
+// Binary search the list of children nodes to find a TrieNode for a given property piece.
+// Used to traverse the Trie in GetPropertyInfoIndexes().
+bool TrieNode::FindChildForString(const char* name, uint32_t namelen, TrieNode* child) const {
+  auto node_index = Find(trie_node_base_->num_child_nodes, [this, name, namelen](auto array_offset) {
+    const char* child_name = child_node(array_offset).name();
+    int cmp = strncmp(child_name, name, namelen);
+    if (cmp == 0 && child_name[namelen] != '\0') {
+      // We use strncmp() since name isn't null terminated, but we don't want to match only a
+      // prefix of a child node's name, so we check here if we did only match a prefix and
+      // return 1, to indicate to the binary search to search earlier in the array for the real
+      // match.
+      return 1;
+    }
+    return cmp;
+  });
+
+  if (node_index == -1) {
+    return false;
+  }
+  *child = child_node(node_index);
+  return true;
+}
+
+void PropertyInfoArea::CheckPrefixMatch(const char* remaining_name, const TrieNode& trie_node,
+                                        uint32_t* context_index, uint32_t* type_index) const {
+  const uint32_t remaining_name_size = strlen(remaining_name);
+  for (uint32_t i = 0; i < trie_node.num_prefixes(); ++i) {
+    auto prefix_len = trie_node.prefix(i)->namelen;
+    if (prefix_len > remaining_name_size) continue;
+
+    if (!strncmp(c_string(trie_node.prefix(i)->name_offset), remaining_name, prefix_len)) {
+      if (trie_node.prefix(i)->context_index != ~0u) {
+        *context_index = trie_node.prefix(i)->context_index;
+      }
+      if (trie_node.prefix(i)->type_index != ~0u) {
+        *type_index = trie_node.prefix(i)->type_index;
+      }
+      return;
+    }
+  }
+}
+
+void PropertyInfoArea::GetPropertyInfoIndexes(const char* name, uint32_t* context_index,
+                                              uint32_t* type_index) const {
+  uint32_t return_context_index = ~0u;
+  uint32_t return_type_index = ~0u;
+  const char* remaining_name = name;
+  auto trie_node = root_node();
+  while (true) {
+    const char* sep = strchr(remaining_name, '.');
+
+    // Apply prefix match for prefix deliminated with '.'
+    if (trie_node.context_index() != ~0u) {
+      return_context_index = trie_node.context_index();
+    }
+    if (trie_node.type_index() != ~0u) {
+      return_type_index = trie_node.type_index();
+    }
+
+    // Check prefixes at this node.  This comes after the node check since these prefixes are by
+    // definition longer than the node itself.
+    CheckPrefixMatch(remaining_name, trie_node, &return_context_index, &return_type_index);
+
+    if (sep == nullptr) {
+      break;
+    }
+
+    const uint32_t substr_size = sep - remaining_name;
+    TrieNode child_node;
+    if (!trie_node.FindChildForString(remaining_name, substr_size, &child_node)) {
+      break;
+    }
+
+    trie_node = child_node;
+    remaining_name = sep + 1;
+  }
+
+  // We've made it to a leaf node, so check contents and return appropriately.
+  // Check exact matches
+  for (uint32_t i = 0; i < trie_node.num_exact_matches(); ++i) {
+    if (!strcmp(c_string(trie_node.exact_match(i)->name_offset), remaining_name)) {
+      if (context_index != nullptr) {
+        if (trie_node.exact_match(i)->context_index != ~0u) {
+          *context_index = trie_node.exact_match(i)->context_index;
+        } else {
+          *context_index = return_context_index;
+        }
+      }
+      if (type_index != nullptr) {
+        if (trie_node.exact_match(i)->type_index != ~0u) {
+          *type_index = trie_node.exact_match(i)->type_index;
+        } else {
+          *type_index = return_type_index;
+        }
+      }
+      return;
+    }
+  }
+  // Check prefix matches for prefixes not deliminated with '.'
+  CheckPrefixMatch(remaining_name, trie_node, &return_context_index, &return_type_index);
+  // Return previously found prefix match.
+  if (context_index != nullptr) *context_index = return_context_index;
+  if (type_index != nullptr) *type_index = return_type_index;
+  return;
+}
+
+void PropertyInfoArea::GetPropertyInfo(const char* property, const char** context,
+                                       const char** type) const {
+  uint32_t context_index;
+  uint32_t type_index;
+  GetPropertyInfoIndexes(property, &context_index, &type_index);
+  if (context != nullptr) {
+    if (context_index == ~0u) {
+      *context = nullptr;
+    } else {
+      *context = this->context(context_index);
+    }
+  }
+  if (type != nullptr) {
+    if (type_index == ~0u) {
+      *type = nullptr;
+    } else {
+      *type = this->type(type_index);
+    }
+  }
+}
+
+bool PropertyInfoAreaFile::LoadDefaultPath() {
+  return LoadPath("/dev/__properties__/property_info");
+}
+
+bool PropertyInfoAreaFile::LoadPath(const char* filename) {
+  int fd = open(filename, O_CLOEXEC | O_NOFOLLOW | O_RDONLY);
+
+  struct stat fd_stat;
+  if (fstat(fd, &fd_stat) < 0) {
+    close(fd);
+    return false;
+  }
+
+  if ((fd_stat.st_uid != 0) || (fd_stat.st_gid != 0) ||
+      ((fd_stat.st_mode & (S_IWGRP | S_IWOTH)) != 0) ||
+      (fd_stat.st_size < static_cast<off_t>(sizeof(PropertyInfoArea)))) {
+    close(fd);
+    return false;
+  }
+
+  auto mmap_size = fd_stat.st_size;
+
+  void* map_result = mmap(nullptr, mmap_size, PROT_READ, MAP_SHARED, fd, 0);
+  if (map_result == MAP_FAILED) {
+    close(fd);
+    return false;
+  }
+
+  auto property_info_area = reinterpret_cast<PropertyInfoArea*>(map_result);
+  if (property_info_area->minimum_supported_version() > 1 ||
+      property_info_area->size() != mmap_size) {
+    munmap(map_result, mmap_size);
+    close(fd);
+    return false;
+  }
+
+  close(fd);
+  mmap_base_ = map_result;
+  mmap_size_ = mmap_size;
+  return true;
+}
+
+void PropertyInfoAreaFile::Reset() {
+  if (mmap_size_ > 0) {
+    munmap(mmap_base_, mmap_size_);
+  }
+  mmap_base_ = nullptr;
+  mmap_size_ = 0;
+}
+
+}  // namespace properties
+}  // namespace android
diff --git a/property_service/libpropertyinfoserializer/Android.bp b/property_service/libpropertyinfoserializer/Android.bp
new file mode 100644
index 0000000..51c1226
--- /dev/null
+++ b/property_service/libpropertyinfoserializer/Android.bp
@@ -0,0 +1,40 @@
+cc_defaults {
+    name: "propertyinfoserializer_defaults",
+    host_supported: true,
+    vendor_available: true,
+    cpp_std: "experimental",
+    cppflags: [
+        "-Wall",
+        "-Wextra",
+        "-Werror",
+    ],
+    static_libs: [
+        "libpropertyinfoparser",
+        "libbase",
+    ],
+}
+
+cc_library_static {
+    name: "libpropertyinfoserializer",
+    defaults: ["propertyinfoserializer_defaults"],
+    recovery_available: true,
+    srcs: [
+        "property_info_file.cpp",
+        "property_info_serializer.cpp",
+        "trie_builder.cpp",
+        "trie_serializer.cpp",
+    ],
+
+    export_include_dirs: ["include"],
+}
+
+cc_test {
+    name: "propertyinfoserializer_tests",
+    defaults: ["propertyinfoserializer_defaults"],
+    srcs: [
+        "trie_builder_test.cpp",
+        "property_info_serializer_test.cpp",
+    ],
+    static_libs: ["libpropertyinfoserializer"],
+    test_suites: ["device-tests"],
+}
diff --git a/property_service/libpropertyinfoserializer/include/property_info_serializer/property_info_serializer.h b/property_service/libpropertyinfoserializer/include/property_info_serializer/property_info_serializer.h
new file mode 100644
index 0000000..439813d
--- /dev/null
+++ b/property_service/libpropertyinfoserializer/include/property_info_serializer/property_info_serializer.h
@@ -0,0 +1,51 @@
+//
+// Copyright (C) 2017 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 PROPERTY_INFO_SERIALIZER_H
+#define PROPERTY_INFO_SERIALIZER_H
+
+#include <string>
+#include <vector>
+
+namespace android {
+namespace properties {
+
+struct PropertyInfoEntry {
+  PropertyInfoEntry() {}
+  template <typename T, typename U, typename V>
+  PropertyInfoEntry(T&& name, U&& context, V&& type, bool exact_match)
+      : name(std::forward<T>(name)),
+        context(std::forward<U>(context)),
+        type(std::forward<V>(type)),
+        exact_match(exact_match) {}
+  std::string name;
+  std::string context;
+  std::string type;
+  bool exact_match;
+};
+
+bool BuildTrie(const std::vector<PropertyInfoEntry>& property_info,
+               const std::string& default_context, const std::string& default_type,
+               std::string* serialized_trie, std::string* error);
+
+void ParsePropertyInfoFile(const std::string& file_contents,
+                           std::vector<PropertyInfoEntry>* property_infos,
+                           std::vector<std::string>* errors);
+
+}  // namespace properties
+}  // namespace android
+
+#endif
diff --git a/property_service/libpropertyinfoserializer/property_info_file.cpp b/property_service/libpropertyinfoserializer/property_info_file.cpp
new file mode 100644
index 0000000..2cdc62d
--- /dev/null
+++ b/property_service/libpropertyinfoserializer/property_info_file.cpp
@@ -0,0 +1,121 @@
+//
+// Copyright (C) 2017 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 <property_info_serializer/property_info_serializer.h>
+
+#include <android-base/strings.h>
+
+#include "space_tokenizer.h"
+
+using android::base::Join;
+using android::base::Split;
+using android::base::StartsWith;
+using android::base::Trim;
+
+namespace android {
+namespace properties {
+
+namespace {
+
+bool IsTypeValid(const std::vector<std::string>& type_strings) {
+  if (type_strings.empty()) {
+    return false;
+  }
+
+  // There must be at least one string following 'enum'
+  if (type_strings[0] == "enum") {
+    return type_strings.size() > 1;
+  }
+
+  // There should not be any string following any other types.
+  if (type_strings.size() != 1) {
+    return false;
+  }
+
+  // Check that the type matches one of remaining valid types.
+  static const char* const no_parameter_types[] = {"string", "bool",   "int",
+                                                   "uint",   "double", "size"};
+  for (const auto& type : no_parameter_types) {
+    if (type_strings[0] == type) {
+      return true;
+    }
+  }
+  return false;
+}
+
+bool ParsePropertyInfoLine(const std::string& line, PropertyInfoEntry* out, std::string* error) {
+  auto tokenizer = SpaceTokenizer(line);
+
+  auto property = tokenizer.GetNext();
+  if (property.empty()) {
+    *error = "Did not find a property entry in '" + line + "'";
+    return false;
+  }
+
+  auto context = tokenizer.GetNext();
+  if (context.empty()) {
+    *error = "Did not find a context entry in '" + line + "'";
+    return false;
+  }
+
+  // It is not an error to not find exact_match or a type, as older files will not contain them.
+  auto exact_match = tokenizer.GetNext();
+  // We reformat type to be space deliminated regardless of the input whitespace for easier storage
+  // and subsequent parsing.
+  auto type_strings = std::vector<std::string>{};
+  auto type = tokenizer.GetNext();
+  while (!type.empty()) {
+    type_strings.emplace_back(type);
+    type = tokenizer.GetNext();
+  }
+
+  if (!type_strings.empty() && !IsTypeValid(type_strings)) {
+    *error = "Type '" + Join(type_strings, " ") + "' is not valid";
+    return false;
+  }
+
+  *out = {property, context, Join(type_strings, " "), exact_match == "exact"};
+  return true;
+}
+
+}  // namespace
+
+void ParsePropertyInfoFile(const std::string& file_contents,
+                           std::vector<PropertyInfoEntry>* property_infos,
+                           std::vector<std::string>* errors) {
+  // Do not clear property_infos to allow this function to be called on multiple files, with
+  // their results concatenated.
+  errors->clear();
+
+  for (const auto& line : Split(file_contents, "\n")) {
+    auto trimmed_line = Trim(line);
+    if (trimmed_line.empty() || StartsWith(trimmed_line, "#")) {
+      continue;
+    }
+
+    auto property_info_entry = PropertyInfoEntry{};
+    auto parse_error = std::string{};
+    if (!ParsePropertyInfoLine(trimmed_line, &property_info_entry, &parse_error)) {
+      errors->emplace_back(parse_error);
+      continue;
+    }
+
+    property_infos->emplace_back(property_info_entry);
+  }
+}
+
+}  // namespace properties
+}  // namespace android
diff --git a/property_service/libpropertyinfoserializer/property_info_serializer.cpp b/property_service/libpropertyinfoserializer/property_info_serializer.cpp
new file mode 100644
index 0000000..803657a
--- /dev/null
+++ b/property_service/libpropertyinfoserializer/property_info_serializer.cpp
@@ -0,0 +1,47 @@
+//
+// Copyright (C) 2017 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 "property_info_serializer/property_info_serializer.h"
+
+#include "property_info_parser/property_info_parser.h"
+
+#include <set>
+
+#include "trie_builder.h"
+#include "trie_serializer.h"
+
+namespace android {
+namespace properties {
+
+bool BuildTrie(const std::vector<PropertyInfoEntry>& property_info,
+               const std::string& default_context, const std::string& default_type,
+               std::string* serialized_trie, std::string* error) {
+  // Check that names are legal first
+  auto trie_builder = TrieBuilder(default_context, default_type);
+
+  for (const auto& [name, context, type, is_exact] : property_info) {
+    if (!trie_builder.AddToTrie(name, context, type, is_exact, error)) {
+      return false;
+    }
+  }
+
+  auto trie_serializer = TrieSerializer();
+  *serialized_trie = trie_serializer.SerializeTrie(trie_builder);
+  return true;
+}
+
+}  // namespace properties
+}  // namespace android
diff --git a/property_service/libpropertyinfoserializer/property_info_serializer_test.cpp b/property_service/libpropertyinfoserializer/property_info_serializer_test.cpp
new file mode 100644
index 0000000..f484550
--- /dev/null
+++ b/property_service/libpropertyinfoserializer/property_info_serializer_test.cpp
@@ -0,0 +1,889 @@
+//
+// Copyright (C) 2017 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 "property_info_serializer/property_info_serializer.h"
+
+#include "property_info_parser/property_info_parser.h"
+
+#include <gtest/gtest.h>
+
+namespace android {
+namespace properties {
+
+TEST(propertyinfoserializer, TrieNodeCheck) {
+  auto property_info = std::vector<PropertyInfoEntry>{
+      {"test.", "1st", "1st", false},     {"test.test", "2nd", "2nd", false},
+
+      {"test.test1", "3rd", "3rd", true}, {"test.test2", "3rd", "3rd", true},
+      {"test.test3", "3rd", "3rd", true}, {"this.is.a.long.string", "4th", "4th", true},
+  };
+
+  auto serialized_trie = std::string();
+  auto build_trie_error = std::string();
+  ASSERT_TRUE(BuildTrie(property_info, "default", "default", &serialized_trie, &build_trie_error))
+      << build_trie_error;
+
+  auto property_info_area = reinterpret_cast<const PropertyInfoArea*>(serialized_trie.data());
+
+  // Initial checks for property area.
+  EXPECT_EQ(1U, property_info_area->current_version());
+  EXPECT_EQ(1U, property_info_area->minimum_supported_version());
+
+  // Check the root node
+  auto root_node = property_info_area->root_node();
+  EXPECT_STREQ("root", root_node.name());
+  EXPECT_STREQ("default", property_info_area->context(root_node.context_index()));
+  EXPECT_STREQ("default", property_info_area->type(root_node.type_index()));
+
+  EXPECT_EQ(0U, root_node.num_prefixes());
+  EXPECT_EQ(0U, root_node.num_exact_matches());
+
+  ASSERT_EQ(2U, root_node.num_child_nodes());
+
+  // Check the 'test'. node
+  TrieNode test_node;
+  ASSERT_TRUE(root_node.FindChildForString("test", 4, &test_node));
+
+  EXPECT_STREQ("test", test_node.name());
+  EXPECT_STREQ("1st", property_info_area->context(test_node.context_index()));
+  EXPECT_STREQ("1st", property_info_area->type(test_node.type_index()));
+
+  EXPECT_EQ(0U, test_node.num_child_nodes());
+
+  EXPECT_EQ(1U, test_node.num_prefixes());
+  {
+    auto prefix = test_node.prefix(0);
+    EXPECT_STREQ("test", serialized_trie.data() + prefix->name_offset);
+    EXPECT_EQ(4U, prefix->namelen);
+    EXPECT_STREQ("2nd", property_info_area->context(prefix->context_index));
+    EXPECT_STREQ("2nd", property_info_area->type(prefix->type_index));
+  }
+
+  EXPECT_EQ(3U, test_node.num_exact_matches());
+  {
+    auto match1 = test_node.exact_match(0);
+    auto match2 = test_node.exact_match(1);
+    auto match3 = test_node.exact_match(2);
+    EXPECT_STREQ("test1", serialized_trie.data() + match1->name_offset);
+    EXPECT_STREQ("test2", serialized_trie.data() + match2->name_offset);
+    EXPECT_STREQ("test3", serialized_trie.data() + match3->name_offset);
+
+    EXPECT_STREQ("3rd", property_info_area->context(match1->context_index));
+    EXPECT_STREQ("3rd", property_info_area->context(match2->context_index));
+    EXPECT_STREQ("3rd", property_info_area->context(match3->context_index));
+
+    EXPECT_STREQ("3rd", property_info_area->type(match1->type_index));
+    EXPECT_STREQ("3rd", property_info_area->type(match2->type_index));
+    EXPECT_STREQ("3rd", property_info_area->type(match3->type_index));
+  }
+
+  // Check the long string node
+  auto expect_empty_one_child = [](auto& node) {
+    EXPECT_EQ(-1U, node.context_index());
+    EXPECT_EQ(0U, node.num_prefixes());
+    EXPECT_EQ(0U, node.num_exact_matches());
+    EXPECT_EQ(1U, node.num_child_nodes());
+  };
+
+  // Start with 'this'
+  TrieNode long_string_node;
+  ASSERT_TRUE(root_node.FindChildForString("this", 4, &long_string_node));
+  expect_empty_one_child(long_string_node);
+
+  // Move to 'is'
+  ASSERT_TRUE(long_string_node.FindChildForString("is", 2, &long_string_node));
+  expect_empty_one_child(long_string_node);
+
+  // Move to 'a'
+  ASSERT_TRUE(long_string_node.FindChildForString("a", 1, &long_string_node));
+  expect_empty_one_child(long_string_node);
+
+  // Move to 'long'
+  ASSERT_TRUE(long_string_node.FindChildForString("long", 4, &long_string_node));
+  EXPECT_EQ(0U, long_string_node.num_prefixes());
+  EXPECT_EQ(1U, long_string_node.num_exact_matches());
+  EXPECT_EQ(0U, long_string_node.num_child_nodes());
+
+  auto final_match = long_string_node.exact_match(0);
+  EXPECT_STREQ("string", serialized_trie.data() + final_match->name_offset);
+  EXPECT_STREQ("4th", property_info_area->context(final_match->context_index));
+  EXPECT_STREQ("4th", property_info_area->type(final_match->type_index));
+}
+
+TEST(propertyinfoserializer, GetPropertyInfo) {
+  auto property_info = std::vector<PropertyInfoEntry>{
+      {"test.", "1st", "1st", false},       {"test.test", "2nd", "2nd", false},
+      {"test.test2.", "6th", "6th", false}, {"test.test", "5th", "5th", true},
+      {"test.test1", "3rd", "3rd", true},   {"test.test2", "7th", "7th", true},
+      {"test.test3", "3rd", "3rd", true},   {"this.is.a.long.string", "4th", "4th", true},
+      {"testoneword", "8th", "8th", true},  {"testwordprefix", "9th", "9th", false},
+  };
+
+  auto serialized_trie = std::string();
+  auto build_trie_error = std::string();
+  ASSERT_TRUE(BuildTrie(property_info, "default", "default", &serialized_trie, &build_trie_error))
+      << build_trie_error;
+
+  auto property_info_area = reinterpret_cast<const PropertyInfoArea*>(serialized_trie.data());
+
+  // Sanity check
+  auto root_node = property_info_area->root_node();
+  EXPECT_STREQ("root", root_node.name());
+  EXPECT_STREQ("default", property_info_area->context(root_node.context_index()));
+  EXPECT_STREQ("default", property_info_area->type(root_node.type_index()));
+
+  const char* context;
+  const char* type;
+  property_info_area->GetPropertyInfo("abc", &context, &type);
+  EXPECT_STREQ("default", context);
+  EXPECT_STREQ("default", type);
+  property_info_area->GetPropertyInfo("abc.abc", &context, &type);
+  EXPECT_STREQ("default", context);
+  EXPECT_STREQ("default", type);
+  property_info_area->GetPropertyInfo("123.abc", &context, &type);
+  EXPECT_STREQ("default", context);
+  EXPECT_STREQ("default", type);
+
+  property_info_area->GetPropertyInfo("test.a", &context, &type);
+  EXPECT_STREQ("1st", context);
+  EXPECT_STREQ("1st", type);
+  property_info_area->GetPropertyInfo("test.b", &context, &type);
+  EXPECT_STREQ("1st", context);
+  EXPECT_STREQ("1st", type);
+  property_info_area->GetPropertyInfo("test.c", &context, &type);
+  EXPECT_STREQ("1st", context);
+  EXPECT_STREQ("1st", type);
+
+  property_info_area->GetPropertyInfo("test.test", &context, &type);
+  EXPECT_STREQ("5th", context);
+  EXPECT_STREQ("5th", type);
+  property_info_area->GetPropertyInfo("test.testa", &context, &type);
+  EXPECT_STREQ("2nd", context);
+  EXPECT_STREQ("2nd", type);
+  property_info_area->GetPropertyInfo("test.testb", &context, &type);
+  EXPECT_STREQ("2nd", context);
+  EXPECT_STREQ("2nd", type);
+  property_info_area->GetPropertyInfo("test.testc", &context, &type);
+  EXPECT_STREQ("2nd", context);
+  EXPECT_STREQ("2nd", type);
+
+  property_info_area->GetPropertyInfo("test.test.a", &context, &type);
+  EXPECT_STREQ("2nd", context);
+  EXPECT_STREQ("2nd", type);
+  property_info_area->GetPropertyInfo("test.test.b", &context, &type);
+  EXPECT_STREQ("2nd", context);
+  EXPECT_STREQ("2nd", type);
+  property_info_area->GetPropertyInfo("test.test.c", &context, &type);
+  EXPECT_STREQ("2nd", context);
+  EXPECT_STREQ("2nd", type);
+
+  property_info_area->GetPropertyInfo("test.test1", &context, &type);
+  EXPECT_STREQ("3rd", context);
+  EXPECT_STREQ("3rd", type);
+  property_info_area->GetPropertyInfo("test.test2", &context, &type);
+  EXPECT_STREQ("7th", context);
+  EXPECT_STREQ("7th", type);
+  property_info_area->GetPropertyInfo("test.test3", &context, &type);
+  EXPECT_STREQ("3rd", context);
+  EXPECT_STREQ("3rd", type);
+
+  property_info_area->GetPropertyInfo("test.test11", &context, &type);
+  EXPECT_STREQ("2nd", context);
+  EXPECT_STREQ("2nd", type);
+  property_info_area->GetPropertyInfo("test.test22", &context, &type);
+  EXPECT_STREQ("2nd", context);
+  EXPECT_STREQ("2nd", type);
+  property_info_area->GetPropertyInfo("test.test33", &context, &type);
+  EXPECT_STREQ("2nd", context);
+  EXPECT_STREQ("2nd", type);
+
+  property_info_area->GetPropertyInfo("this.is.a.long.string", &context, &type);
+  EXPECT_STREQ("4th", context);
+  EXPECT_STREQ("4th", type);
+
+  property_info_area->GetPropertyInfo("this.is.a.long", &context, &type);
+  EXPECT_STREQ("default", context);
+  EXPECT_STREQ("default", type);
+  property_info_area->GetPropertyInfo("this.is.a", &context, &type);
+  EXPECT_STREQ("default", context);
+  EXPECT_STREQ("default", type);
+  property_info_area->GetPropertyInfo("this.is", &context, &type);
+  EXPECT_STREQ("default", context);
+  EXPECT_STREQ("default", type);
+  property_info_area->GetPropertyInfo("this", &context, &type);
+  EXPECT_STREQ("default", context);
+  EXPECT_STREQ("default", type);
+
+  property_info_area->GetPropertyInfo("test.test2.a", &context, &type);
+  EXPECT_STREQ("6th", context);
+  EXPECT_STREQ("6th", type);
+
+  property_info_area->GetPropertyInfo("testoneword", &context, &type);
+  EXPECT_STREQ("8th", context);
+  EXPECT_STREQ("8th", type);
+
+  property_info_area->GetPropertyInfo("testwordprefix", &context, &type);
+  EXPECT_STREQ("9th", context);
+  EXPECT_STREQ("9th", type);
+
+  property_info_area->GetPropertyInfo("testwordprefixblah", &context, &type);
+  EXPECT_STREQ("9th", context);
+  EXPECT_STREQ("9th", type);
+
+  property_info_area->GetPropertyInfo("testwordprefix.blah", &context, &type);
+  EXPECT_STREQ("9th", context);
+  EXPECT_STREQ("9th", type);
+}
+
+TEST(propertyinfoserializer, RealProperties) {
+  auto property_info = std::vector<PropertyInfoEntry>{
+      // Contexts from system/sepolicy/private/property_contexts
+      {"net.rmnet", "u:object_r:net_radio_prop:s0", "string", false},
+      {"net.gprs", "u:object_r:net_radio_prop:s0", "string", false},
+      {"net.ppp", "u:object_r:net_radio_prop:s0", "string", false},
+      {"net.qmi", "u:object_r:net_radio_prop:s0", "string", false},
+      {"net.lte", "u:object_r:net_radio_prop:s0", "string", false},
+      {"net.cdma", "u:object_r:net_radio_prop:s0", "string", false},
+      {"net.dns", "u:object_r:net_dns_prop:s0", "string", false},
+      {"sys.usb.config", "u:object_r:system_radio_prop:s0", "string", false},
+      {"ril.", "u:object_r:radio_prop:s0", "string", false},
+      {"ro.ril.", "u:object_r:radio_prop:s0", "string", false},
+      {"gsm.", "u:object_r:radio_prop:s0", "string", false},
+      {"persist.radio", "u:object_r:radio_prop:s0", "string", false},
+
+      {"net.", "u:object_r:system_prop:s0", "string", false},
+      {"dev.", "u:object_r:system_prop:s0", "string", false},
+      {"ro.runtime.", "u:object_r:system_prop:s0", "string", false},
+      {"ro.runtime.firstboot", "u:object_r:firstboot_prop:s0", "string", false},
+      {"hw.", "u:object_r:system_prop:s0", "string", false},
+      {"ro.hw.", "u:object_r:system_prop:s0", "string", false},
+      {"sys.", "u:object_r:system_prop:s0", "string", false},
+      {"sys.cppreopt", "u:object_r:cppreopt_prop:s0", "string", false},
+      {"sys.powerctl", "u:object_r:powerctl_prop:s0", "string", false},
+      {"sys.usb.ffs.", "u:object_r:ffs_prop:s0", "string", false},
+      {"service.", "u:object_r:system_prop:s0", "string", false},
+      {"dhcp.", "u:object_r:dhcp_prop:s0", "string", false},
+      {"dhcp.bt-pan.result", "u:object_r:pan_result_prop:s0", "string", false},
+      {"bluetooth.", "u:object_r:bluetooth_prop:s0", "string", false},
+
+      {"debug.", "u:object_r:debug_prop:s0", "string", false},
+      {"debug.db.", "u:object_r:debuggerd_prop:s0", "string", false},
+      {"dumpstate.", "u:object_r:dumpstate_prop:s0", "string", false},
+      {"dumpstate.options", "u:object_r:dumpstate_options_prop:s0", "string", false},
+      {"log.", "u:object_r:log_prop:s0", "string", false},
+      {"log.tag", "u:object_r:log_tag_prop:s0", "string", false},
+      {"log.tag.WifiHAL", "u:object_r:wifi_log_prop:s0", "string", false},
+      {"security.perf_harden", "u:object_r:shell_prop:s0", "string", false},
+      {"service.adb.root", "u:object_r:shell_prop:s0", "string", false},
+      {"service.adb.tcp.port", "u:object_r:shell_prop:s0", "string", false},
+
+      {"persist.audio.", "u:object_r:audio_prop:s0", "string", false},
+      {"persist.bluetooth.", "u:object_r:bluetooth_prop:s0", "string", false},
+      {"persist.debug.", "u:object_r:persist_debug_prop:s0", "string", false},
+      {"persist.logd.", "u:object_r:logd_prop:s0", "string", false},
+      {"persist.logd.security", "u:object_r:device_logging_prop:s0", "string", false},
+      {"persist.logd.logpersistd", "u:object_r:logpersistd_logging_prop:s0", "string", false},
+      {"logd.logpersistd", "u:object_r:logpersistd_logging_prop:s0", "string", false},
+      {"persist.log.tag", "u:object_r:log_tag_prop:s0", "string", false},
+      {"persist.mmc.", "u:object_r:mmc_prop:s0", "string", false},
+      {"persist.netd.stable_secret", "u:object_r:netd_stable_secret_prop:s0", "string", false},
+      {"persist.sys.", "u:object_r:system_prop:s0", "string", false},
+      {"persist.sys.safemode", "u:object_r:safemode_prop:s0", "string", false},
+      {"ro.sys.safemode", "u:object_r:safemode_prop:s0", "string", false},
+      {"persist.sys.audit_safemode", "u:object_r:safemode_prop:s0", "string", false},
+      {"persist.service.", "u:object_r:system_prop:s0", "string", false},
+      {"persist.service.bdroid.", "u:object_r:bluetooth_prop:s0", "string", false},
+      {"persist.security.", "u:object_r:system_prop:s0", "string", false},
+      {"persist.vendor.overlay.", "u:object_r:overlay_prop:s0", "string", false},
+      {"ro.boot.vendor.overlay.", "u:object_r:overlay_prop:s0", "string", false},
+      {"ro.boottime.", "u:object_r:boottime_prop:s0", "string", false},
+      {"ro.serialno", "u:object_r:serialno_prop:s0", "string", false},
+      {"ro.boot.btmacaddr", "u:object_r:bluetooth_prop:s0", "string", false},
+      {"ro.boot.serialno", "u:object_r:serialno_prop:s0", "string", false},
+      {"ro.bt.", "u:object_r:bluetooth_prop:s0", "string", false},
+      {"ro.boot.bootreason", "u:object_r:bootloader_boot_reason_prop:s0", "string", false},
+      {"persist.sys.boot.reason", "u:object_r:last_boot_reason_prop:s0", "string", false},
+      {"sys.boot.reason", "u:object_r:system_boot_reason_prop:s0", "string", false},
+      {"ro.device_owner", "u:object_r:device_logging_prop:s0", "string", false},
+
+      {"selinux.restorecon_recursive", "u:object_r:restorecon_prop:s0", "string", false},
+
+      {"vold.", "u:object_r:vold_prop:s0", "string", false},
+      {"ro.crypto.", "u:object_r:vold_prop:s0", "string", false},
+
+      {"ro.build.fingerprint", "u:object_r:fingerprint_prop:s0", "string", false},
+
+      {"ro.persistent_properties.ready", "u:object_r:persistent_properties_ready_prop:s0", "string",
+       false},
+
+      {"ctl.bootanim", "u:object_r:ctl_bootanim_prop:s0", "string", false},
+      {"ctl.dumpstate", "u:object_r:ctl_dumpstate_prop:s0", "string", false},
+      {"ctl.fuse_", "u:object_r:ctl_fuse_prop:s0", "string", false},
+      {"ctl.mdnsd", "u:object_r:ctl_mdnsd_prop:s0", "string", false},
+      {"ctl.ril-daemon", "u:object_r:ctl_rildaemon_prop:s0", "string", false},
+      {"ctl.bugreport", "u:object_r:ctl_bugreport_prop:s0", "string", false},
+      {"ctl.console", "u:object_r:ctl_console_prop:s0", "string", false},
+      {"ctl.", "u:object_r:ctl_default_prop:s0", "string", false},
+
+      {"nfc.", "u:object_r:nfc_prop:s0", "string", false},
+
+      {"config.", "u:object_r:config_prop:s0", "string", false},
+      {"ro.config.", "u:object_r:config_prop:s0", "string", false},
+      {"dalvik.", "u:object_r:dalvik_prop:s0", "string", false},
+      {"ro.dalvik.", "u:object_r:dalvik_prop:s0", "string", false},
+
+      {"wlan.", "u:object_r:wifi_prop:s0", "string", false},
+
+      {"lowpan.", "u:object_r:lowpan_prop:s0", "string", false},
+      {"ro.lowpan.", "u:object_r:lowpan_prop:s0", "string", false},
+
+      {"hwservicemanager.", "u:object_r:hwservicemanager_prop:s0", "string", false},
+      // Contexts from device/lge/bullhead/sepolicy/property_contexts
+      {"wc_transport.", "u:object_r:wc_transport_prop:s0", "string", false},
+      {"sys.listeners.", "u:object_r:qseecomtee_prop:s0", "string", false},
+      {"sys.keymaster.", "u:object_r:qseecomtee_prop:s0", "string", false},
+      {"radio.atfwd.", "u:object_r:radio_atfwd_prop:s0", "string", false},
+      {"sys.ims.", "u:object_r:qcom_ims_prop:s0", "string", false},
+      {"sensors.contexthub.", "u:object_r:contexthub_prop:s0", "string", false},
+      {"net.r_rmnet", "u:object_r:net_radio_prop:s0", "string", false},
+  };
+
+  auto serialized_trie = std::string();
+  auto build_trie_error = std::string();
+  ASSERT_TRUE(BuildTrie(property_info, "u:object_r:default_prop:s0", "string", &serialized_trie,
+                        &build_trie_error))
+      << build_trie_error;
+
+  auto property_info_area = reinterpret_cast<const PropertyInfoArea*>(serialized_trie.data());
+
+  auto properties_and_contexts = std::vector<std::pair<std::string, std::string>>{
+      // Actual properties on bullhead via `getprop -Z`
+      {"af.fast_track_multiplier", "u:object_r:default_prop:s0"},
+      {"audio_hal.period_size", "u:object_r:default_prop:s0"},
+      {"bluetooth.enable_timeout_ms", "u:object_r:bluetooth_prop:s0"},
+      {"dalvik.vm.appimageformat", "u:object_r:dalvik_prop:s0"},
+      {"dalvik.vm.boot-dex2oat-threads", "u:object_r:dalvik_prop:s0"},
+      {"dalvik.vm.dex2oat-Xms", "u:object_r:dalvik_prop:s0"},
+      {"dalvik.vm.dex2oat-Xmx", "u:object_r:dalvik_prop:s0"},
+      {"dalvik.vm.dex2oat-threads", "u:object_r:dalvik_prop:s0"},
+      {"dalvik.vm.dexopt.secondary", "u:object_r:dalvik_prop:s0"},
+      {"dalvik.vm.heapgrowthlimit", "u:object_r:dalvik_prop:s0"},
+      {"dalvik.vm.heapmaxfree", "u:object_r:dalvik_prop:s0"},
+      {"dalvik.vm.heapminfree", "u:object_r:dalvik_prop:s0"},
+      {"dalvik.vm.heapsize", "u:object_r:dalvik_prop:s0"},
+      {"dalvik.vm.heapstartsize", "u:object_r:dalvik_prop:s0"},
+      {"dalvik.vm.heaptargetutilization", "u:object_r:dalvik_prop:s0"},
+      {"dalvik.vm.image-dex2oat-Xms", "u:object_r:dalvik_prop:s0"},
+      {"dalvik.vm.image-dex2oat-Xmx", "u:object_r:dalvik_prop:s0"},
+      {"dalvik.vm.image-dex2oat-threads", "u:object_r:dalvik_prop:s0"},
+      {"dalvik.vm.isa.arm.features", "u:object_r:dalvik_prop:s0"},
+      {"dalvik.vm.isa.arm.variant", "u:object_r:dalvik_prop:s0"},
+      {"dalvik.vm.isa.arm64.features", "u:object_r:dalvik_prop:s0"},
+      {"dalvik.vm.isa.arm64.variant", "u:object_r:dalvik_prop:s0"},
+      {"dalvik.vm.lockprof.threshold", "u:object_r:dalvik_prop:s0"},
+      {"dalvik.vm.stack-trace-file", "u:object_r:dalvik_prop:s0"},
+      {"dalvik.vm.usejit", "u:object_r:dalvik_prop:s0"},
+      {"dalvik.vm.usejitprofiles", "u:object_r:dalvik_prop:s0"},
+      {"debug.atrace.tags.enableflags", "u:object_r:debug_prop:s0"},
+      {"debug.force_rtl", "u:object_r:debug_prop:s0"},
+      {"dev.bootcomplete", "u:object_r:system_prop:s0"},
+      {"drm.service.enabled", "u:object_r:default_prop:s0"},
+      {"gsm.current.phone-type", "u:object_r:radio_prop:s0"},
+      {"gsm.network.type", "u:object_r:radio_prop:s0"},
+      {"gsm.operator.alpha", "u:object_r:radio_prop:s0"},
+      {"gsm.operator.iso-country", "u:object_r:radio_prop:s0"},
+      {"gsm.operator.isroaming", "u:object_r:radio_prop:s0"},
+      {"gsm.operator.numeric", "u:object_r:radio_prop:s0"},
+      {"gsm.sim.operator.alpha", "u:object_r:radio_prop:s0"},
+      {"gsm.sim.operator.iso-country", "u:object_r:radio_prop:s0"},
+      {"gsm.sim.operator.numeric", "u:object_r:radio_prop:s0"},
+      {"gsm.sim.state", "u:object_r:radio_prop:s0"},
+      {"gsm.version.baseband", "u:object_r:radio_prop:s0"},
+      {"gsm.version.ril-impl", "u:object_r:radio_prop:s0"},
+      {"hwservicemanager.ready", "u:object_r:hwservicemanager_prop:s0"},
+      {"init.svc.adbd", "u:object_r:default_prop:s0"},
+      {"init.svc.atfwd", "u:object_r:default_prop:s0"},
+      {"init.svc.audioserver", "u:object_r:default_prop:s0"},
+      {"init.svc.bootanim", "u:object_r:default_prop:s0"},
+      {"init.svc.bullhead-sh", "u:object_r:default_prop:s0"},
+      {"init.svc.cameraserver", "u:object_r:default_prop:s0"},
+      {"init.svc.cnd", "u:object_r:default_prop:s0"},
+      {"init.svc.cnss-daemon", "u:object_r:default_prop:s0"},
+      {"init.svc.cnss_diag", "u:object_r:default_prop:s0"},
+      {"init.svc.configstore-hal-1-0", "u:object_r:default_prop:s0"},
+      {"init.svc.console", "u:object_r:default_prop:s0"},
+      {"init.svc.devstart_sh", "u:object_r:default_prop:s0"},
+      {"init.svc.drm", "u:object_r:default_prop:s0"},
+      {"init.svc.dumpstate-1-0", "u:object_r:default_prop:s0"},
+      {"init.svc.flash-nanohub-fw", "u:object_r:default_prop:s0"},
+      {"init.svc.fps_hal", "u:object_r:default_prop:s0"},
+      {"init.svc.gatekeeperd", "u:object_r:default_prop:s0"},
+      {"init.svc.gralloc-2-0", "u:object_r:default_prop:s0"},
+      {"init.svc.healthd", "u:object_r:default_prop:s0"},
+      {"init.svc.hidl_memory", "u:object_r:default_prop:s0"},
+      {"init.svc.hostapd", "u:object_r:default_prop:s0"},
+      {"init.svc.hwservicemanager", "u:object_r:default_prop:s0"},
+      {"init.svc.imsdatadaemon", "u:object_r:default_prop:s0"},
+      {"init.svc.imsqmidaemon", "u:object_r:default_prop:s0"},
+      {"init.svc.installd", "u:object_r:default_prop:s0"},
+      {"init.svc.irsc_util", "u:object_r:default_prop:s0"},
+      {"init.svc.keystore", "u:object_r:default_prop:s0"},
+      {"init.svc.lmkd", "u:object_r:default_prop:s0"},
+      {"init.svc.loc_launcher", "u:object_r:default_prop:s0"},
+      {"init.svc.logd", "u:object_r:default_prop:s0"},
+      {"init.svc.logd-reinit", "u:object_r:default_prop:s0"},
+      {"init.svc.media", "u:object_r:default_prop:s0"},
+      {"init.svc.mediadrm", "u:object_r:default_prop:s0"},
+      {"init.svc.mediaextractor", "u:object_r:default_prop:s0"},
+      {"init.svc.mediametrics", "u:object_r:default_prop:s0"},
+      {"init.svc.msm_irqbalance", "u:object_r:default_prop:s0"},
+      {"init.svc.netd", "u:object_r:default_prop:s0"},
+      {"init.svc.netmgrd", "u:object_r:default_prop:s0"},
+      {"init.svc.per_mgr", "u:object_r:default_prop:s0"},
+      {"init.svc.per_proxy", "u:object_r:default_prop:s0"},
+      {"init.svc.perfd", "u:object_r:default_prop:s0"},
+      {"init.svc.qcamerasvr", "u:object_r:default_prop:s0"},
+      {"init.svc.qmuxd", "u:object_r:default_prop:s0"},
+      {"init.svc.qseecomd", "u:object_r:default_prop:s0"},
+      {"init.svc.qti", "u:object_r:default_prop:s0"},
+      {"init.svc.ril-daemon", "u:object_r:default_prop:s0"},
+      {"init.svc.rmt_storage", "u:object_r:default_prop:s0"},
+      {"init.svc.servicemanager", "u:object_r:default_prop:s0"},
+      {"init.svc.ss_ramdump", "u:object_r:default_prop:s0"},
+      {"init.svc.start_hci_filter", "u:object_r:default_prop:s0"},
+      {"init.svc.storaged", "u:object_r:default_prop:s0"},
+      {"init.svc.surfaceflinger", "u:object_r:default_prop:s0"},
+      {"init.svc.thermal-engine", "u:object_r:default_prop:s0"},
+      {"init.svc.time_daemon", "u:object_r:default_prop:s0"},
+      {"init.svc.tombstoned", "u:object_r:default_prop:s0"},
+      {"init.svc.ueventd", "u:object_r:default_prop:s0"},
+      {"init.svc.update_engine", "u:object_r:default_prop:s0"},
+      {"init.svc.usb-hal-1-0", "u:object_r:default_prop:s0"},
+      {"init.svc.vndservicemanager", "u:object_r:default_prop:s0"},
+      {"init.svc.vold", "u:object_r:default_prop:s0"},
+      {"init.svc.webview_zygote32", "u:object_r:default_prop:s0"},
+      {"init.svc.wifi_hal_legacy", "u:object_r:default_prop:s0"},
+      {"init.svc.wificond", "u:object_r:default_prop:s0"},
+      {"init.svc.wpa_supplicant", "u:object_r:default_prop:s0"},
+      {"init.svc.zygote", "u:object_r:default_prop:s0"},
+      {"init.svc.zygote_secondary", "u:object_r:default_prop:s0"},
+      {"keyguard.no_require_sim", "u:object_r:default_prop:s0"},
+      {"log.tag.WifiHAL", "u:object_r:wifi_log_prop:s0"},
+      {"logd.logpersistd.enable", "u:object_r:logpersistd_logging_prop:s0"},
+      {"media.aac_51_output_enabled", "u:object_r:default_prop:s0"},
+      {"media.recorder.show_manufacturer_and_model", "u:object_r:default_prop:s0"},
+      {"net.bt.name", "u:object_r:system_prop:s0"},
+      {"net.lte.ims.data.enabled", "u:object_r:net_radio_prop:s0"},
+      {"net.qtaguid_enabled", "u:object_r:system_prop:s0"},
+      {"net.tcp.default_init_rwnd", "u:object_r:system_prop:s0"},
+      {"nfc.initialized", "u:object_r:nfc_prop:s0"},
+      {"persist.audio.fluence.speaker", "u:object_r:audio_prop:s0"},
+      {"persist.audio.fluence.voicecall", "u:object_r:audio_prop:s0"},
+      {"persist.audio.fluence.voicecomm", "u:object_r:audio_prop:s0"},
+      {"persist.audio.fluence.voicerec", "u:object_r:audio_prop:s0"},
+      {"persist.camera.tnr.preview", "u:object_r:default_prop:s0"},
+      {"persist.camera.tnr.video", "u:object_r:default_prop:s0"},
+      {"persist.data.iwlan.enable", "u:object_r:default_prop:s0"},
+      {"persist.hwc.mdpcomp.enable", "u:object_r:default_prop:s0"},
+      {"persist.logd.logpersistd", "u:object_r:logpersistd_logging_prop:s0"},
+      {"persist.media.treble_omx", "u:object_r:default_prop:s0"},
+      {"persist.qcril.disable_retry", "u:object_r:default_prop:s0"},
+      {"persist.radio.adb_log_on", "u:object_r:radio_prop:s0"},
+      {"persist.radio.always_send_plmn", "u:object_r:radio_prop:s0"},
+      {"persist.radio.apm_sim_not_pwdn", "u:object_r:radio_prop:s0"},
+      {"persist.radio.custom_ecc", "u:object_r:radio_prop:s0"},
+      {"persist.radio.data_con_rprt", "u:object_r:radio_prop:s0"},
+      {"persist.radio.data_no_toggle", "u:object_r:radio_prop:s0"},
+      {"persist.radio.eons.enabled", "u:object_r:radio_prop:s0"},
+      {"persist.radio.eri64_as_home", "u:object_r:radio_prop:s0"},
+      {"persist.radio.mode_pref_nv10", "u:object_r:radio_prop:s0"},
+      {"persist.radio.process_sups_ind", "u:object_r:radio_prop:s0"},
+      {"persist.radio.redir_party_num", "u:object_r:radio_prop:s0"},
+      {"persist.radio.ril_payload_on", "u:object_r:radio_prop:s0"},
+      {"persist.radio.snapshot_enabled", "u:object_r:radio_prop:s0"},
+      {"persist.radio.snapshot_timer", "u:object_r:radio_prop:s0"},
+      {"persist.radio.use_cc_names", "u:object_r:radio_prop:s0"},
+      {"persist.speaker.prot.enable", "u:object_r:default_prop:s0"},
+      {"persist.sys.boot.reason", "u:object_r:last_boot_reason_prop:s0"},
+      {"persist.sys.dalvik.vm.lib.2", "u:object_r:system_prop:s0"},
+      {"persist.sys.debug.color_temp", "u:object_r:system_prop:s0"},
+      {"persist.sys.preloads.file_cache_expired", "u:object_r:system_prop:s0"},
+      {"persist.sys.timezone", "u:object_r:system_prop:s0"},
+      {"persist.sys.usb.config", "u:object_r:system_prop:s0"},
+      {"persist.sys.webview.vmsize", "u:object_r:system_prop:s0"},
+      {"persist.tom", "u:object_r:default_prop:s0"},
+      {"persist.tom2", "u:object_r:default_prop:s0"},
+      {"pm.dexopt.ab-ota", "u:object_r:default_prop:s0"},
+      {"pm.dexopt.bg-dexopt", "u:object_r:default_prop:s0"},
+      {"pm.dexopt.boot", "u:object_r:default_prop:s0"},
+      {"pm.dexopt.first-boot", "u:object_r:default_prop:s0"},
+      {"pm.dexopt.install", "u:object_r:default_prop:s0"},
+      {"qcom.bluetooth.soc", "u:object_r:default_prop:s0"},
+      {"radio.atfwd.start", "u:object_r:radio_atfwd_prop:s0"},
+      {"ril.ecclist", "u:object_r:radio_prop:s0"},
+      {"ril.nosim.ecc_list_1", "u:object_r:radio_prop:s0"},
+      {"ril.nosim.ecc_list_count", "u:object_r:radio_prop:s0"},
+      {"ril.qcril_pre_init_lock_held", "u:object_r:radio_prop:s0"},
+      {"rild.libpath", "u:object_r:default_prop:s0"},
+      {"ro.allow.mock.location", "u:object_r:default_prop:s0"},
+      {"ro.audio.flinger_standbytime_ms", "u:object_r:default_prop:s0"},
+      {"ro.baseband", "u:object_r:default_prop:s0"},
+      {"ro.bionic.ld.warning", "u:object_r:default_prop:s0"},
+      {"ro.board.platform", "u:object_r:default_prop:s0"},
+      {"ro.boot.baseband", "u:object_r:default_prop:s0"},
+      {"ro.boot.bootloader", "u:object_r:default_prop:s0"},
+      {"ro.boot.bootreason", "u:object_r:bootloader_boot_reason_prop:s0"},
+      {"ro.boot.dlcomplete", "u:object_r:default_prop:s0"},
+      {"ro.boot.emmc", "u:object_r:default_prop:s0"},
+      {"ro.boot.flash.locked", "u:object_r:default_prop:s0"},
+      {"ro.boot.hardware", "u:object_r:default_prop:s0"},
+      {"ro.boot.hardware.sku", "u:object_r:default_prop:s0"},
+      {"ro.boot.revision", "u:object_r:default_prop:s0"},
+      {"ro.boot.serialno", "u:object_r:serialno_prop:s0"},
+      {"ro.boot.verifiedbootstate", "u:object_r:default_prop:s0"},
+      {"ro.boot.veritymode", "u:object_r:default_prop:s0"},
+      {"ro.boot.wificountrycode", "u:object_r:default_prop:s0"},
+      {"ro.bootimage.build.date", "u:object_r:default_prop:s0"},
+      {"ro.bootimage.build.date.utc", "u:object_r:default_prop:s0"},
+      {"ro.bootimage.build.fingerprint", "u:object_r:default_prop:s0"},
+      {"ro.bootloader", "u:object_r:default_prop:s0"},
+      {"ro.bootmode", "u:object_r:default_prop:s0"},
+      {"ro.boottime.adbd", "u:object_r:boottime_prop:s0"},
+      {"ro.boottime.atfwd", "u:object_r:boottime_prop:s0"},
+      {"ro.boottime.audioserver", "u:object_r:boottime_prop:s0"},
+      {"ro.boottime.bootanim", "u:object_r:boottime_prop:s0"},
+      {"ro.boottime.bullhead-sh", "u:object_r:boottime_prop:s0"},
+      {"ro.boottime.cameraserver", "u:object_r:boottime_prop:s0"},
+      {"ro.boottime.cnd", "u:object_r:boottime_prop:s0"},
+      {"ro.boottime.cnss-daemon", "u:object_r:boottime_prop:s0"},
+      {"ro.boottime.cnss_diag", "u:object_r:boottime_prop:s0"},
+      {"ro.boottime.configstore-hal-1-0", "u:object_r:boottime_prop:s0"},
+      {"ro.boottime.console", "u:object_r:boottime_prop:s0"},
+      {"ro.boottime.devstart_sh", "u:object_r:boottime_prop:s0"},
+      {"ro.boottime.drm", "u:object_r:boottime_prop:s0"},
+      {"ro.boottime.dumpstate-1-0", "u:object_r:boottime_prop:s0"},
+      {"ro.boottime.flash-nanohub-fw", "u:object_r:boottime_prop:s0"},
+      {"ro.boottime.fps_hal", "u:object_r:boottime_prop:s0"},
+      {"ro.boottime.gatekeeperd", "u:object_r:boottime_prop:s0"},
+      {"ro.boottime.gralloc-2-0", "u:object_r:boottime_prop:s0"},
+      {"ro.boottime.healthd", "u:object_r:boottime_prop:s0"},
+      {"ro.boottime.hidl_memory", "u:object_r:boottime_prop:s0"},
+      {"ro.boottime.hwservicemanager", "u:object_r:boottime_prop:s0"},
+      {"ro.boottime.imsdatadaemon", "u:object_r:boottime_prop:s0"},
+      {"ro.boottime.imsqmidaemon", "u:object_r:boottime_prop:s0"},
+      {"ro.boottime.init", "u:object_r:boottime_prop:s0"},
+      {"ro.boottime.init.cold_boot_wait", "u:object_r:boottime_prop:s0"},
+      {"ro.boottime.init.mount_all.default", "u:object_r:boottime_prop:s0"},
+      {"ro.boottime.init.selinux", "u:object_r:boottime_prop:s0"},
+      {"ro.boottime.installd", "u:object_r:boottime_prop:s0"},
+      {"ro.boottime.irsc_util", "u:object_r:boottime_prop:s0"},
+      {"ro.boottime.keystore", "u:object_r:boottime_prop:s0"},
+      {"ro.boottime.lmkd", "u:object_r:boottime_prop:s0"},
+      {"ro.boottime.loc_launcher", "u:object_r:boottime_prop:s0"},
+      {"ro.boottime.logd", "u:object_r:boottime_prop:s0"},
+      {"ro.boottime.logd-reinit", "u:object_r:boottime_prop:s0"},
+      {"ro.boottime.media", "u:object_r:boottime_prop:s0"},
+      {"ro.boottime.mediadrm", "u:object_r:boottime_prop:s0"},
+      {"ro.boottime.mediaextractor", "u:object_r:boottime_prop:s0"},
+      {"ro.boottime.mediametrics", "u:object_r:boottime_prop:s0"},
+      {"ro.boottime.msm_irqbalance", "u:object_r:boottime_prop:s0"},
+      {"ro.boottime.netd", "u:object_r:boottime_prop:s0"},
+      {"ro.boottime.netmgrd", "u:object_r:boottime_prop:s0"},
+      {"ro.boottime.per_mgr", "u:object_r:boottime_prop:s0"},
+      {"ro.boottime.per_proxy", "u:object_r:boottime_prop:s0"},
+      {"ro.boottime.perfd", "u:object_r:boottime_prop:s0"},
+      {"ro.boottime.qcamerasvr", "u:object_r:boottime_prop:s0"},
+      {"ro.boottime.qmuxd", "u:object_r:boottime_prop:s0"},
+      {"ro.boottime.qseecomd", "u:object_r:boottime_prop:s0"},
+      {"ro.boottime.qti", "u:object_r:boottime_prop:s0"},
+      {"ro.boottime.ril-daemon", "u:object_r:boottime_prop:s0"},
+      {"ro.boottime.rmt_storage", "u:object_r:boottime_prop:s0"},
+      {"ro.boottime.servicemanager", "u:object_r:boottime_prop:s0"},
+      {"ro.boottime.ss_ramdump", "u:object_r:boottime_prop:s0"},
+      {"ro.boottime.start_hci_filter", "u:object_r:boottime_prop:s0"},
+      {"ro.boottime.storaged", "u:object_r:boottime_prop:s0"},
+      {"ro.boottime.surfaceflinger", "u:object_r:boottime_prop:s0"},
+      {"ro.boottime.thermal-engine", "u:object_r:boottime_prop:s0"},
+      {"ro.boottime.time_daemon", "u:object_r:boottime_prop:s0"},
+      {"ro.boottime.tombstoned", "u:object_r:boottime_prop:s0"},
+      {"ro.boottime.ueventd", "u:object_r:boottime_prop:s0"},
+      {"ro.boottime.update_engine", "u:object_r:boottime_prop:s0"},
+      {"ro.boottime.usb-hal-1-0", "u:object_r:boottime_prop:s0"},
+      {"ro.boottime.vndservicemanager", "u:object_r:boottime_prop:s0"},
+      {"ro.boottime.vold", "u:object_r:boottime_prop:s0"},
+      {"ro.boottime.webview_zygote32", "u:object_r:boottime_prop:s0"},
+      {"ro.boottime.wifi_hal_legacy", "u:object_r:boottime_prop:s0"},
+      {"ro.boottime.wificond", "u:object_r:boottime_prop:s0"},
+      {"ro.boottime.zygote", "u:object_r:boottime_prop:s0"},
+      {"ro.boottime.zygote_secondary", "u:object_r:boottime_prop:s0"},
+      {"ro.bt.bdaddr_path", "u:object_r:bluetooth_prop:s0"},
+      {"ro.build.characteristics", "u:object_r:default_prop:s0"},
+      {"ro.build.date", "u:object_r:default_prop:s0"},
+      {"ro.build.date.utc", "u:object_r:default_prop:s0"},
+      {"ro.build.description", "u:object_r:default_prop:s0"},
+      {"ro.build.display.id", "u:object_r:default_prop:s0"},
+      {"ro.build.expect.baseband", "u:object_r:default_prop:s0"},
+      {"ro.build.expect.bootloader", "u:object_r:default_prop:s0"},
+      {"ro.build.fingerprint", "u:object_r:fingerprint_prop:s0"},
+      {"ro.build.flavor", "u:object_r:default_prop:s0"},
+      {"ro.build.host", "u:object_r:default_prop:s0"},
+      {"ro.build.id", "u:object_r:default_prop:s0"},
+      {"ro.build.product", "u:object_r:default_prop:s0"},
+      {"ro.build.tags", "u:object_r:default_prop:s0"},
+      {"ro.build.type", "u:object_r:default_prop:s0"},
+      {"ro.build.user", "u:object_r:default_prop:s0"},
+      {"ro.build.version.all_codenames", "u:object_r:default_prop:s0"},
+      {"ro.build.version.base_os", "u:object_r:default_prop:s0"},
+      {"ro.build.version.codename", "u:object_r:default_prop:s0"},
+      {"ro.build.version.incremental", "u:object_r:default_prop:s0"},
+      {"ro.build.version.preview_sdk", "u:object_r:default_prop:s0"},
+      {"ro.build.version.release", "u:object_r:default_prop:s0"},
+      {"ro.build.version.sdk", "u:object_r:default_prop:s0"},
+      {"ro.build.version.security_patch", "u:object_r:default_prop:s0"},
+      {"ro.camera.notify_nfc", "u:object_r:default_prop:s0"},
+      {"ro.carrier", "u:object_r:default_prop:s0"},
+      {"ro.com.android.dataroaming", "u:object_r:default_prop:s0"},
+      {"ro.config.alarm_alert", "u:object_r:config_prop:s0"},
+      {"ro.config.notification_sound", "u:object_r:config_prop:s0"},
+      {"ro.config.ringtone", "u:object_r:config_prop:s0"},
+      {"ro.config.vc_call_vol_steps", "u:object_r:config_prop:s0"},
+      {"ro.crypto.fs_crypto_blkdev", "u:object_r:vold_prop:s0"},
+      {"ro.crypto.state", "u:object_r:vold_prop:s0"},
+      {"ro.crypto.type", "u:object_r:vold_prop:s0"},
+      {"ro.dalvik.vm.native.bridge", "u:object_r:dalvik_prop:s0"},
+      {"ro.debuggable", "u:object_r:default_prop:s0"},
+      {"ro.device_owner", "u:object_r:device_logging_prop:s0"},
+      {"ro.expect.recovery_id", "u:object_r:default_prop:s0"},
+      {"ro.frp.pst", "u:object_r:default_prop:s0"},
+      {"ro.hardware", "u:object_r:default_prop:s0"},
+      {"ro.hwui.drop_shadow_cache_size", "u:object_r:default_prop:s0"},
+      {"ro.hwui.gradient_cache_size", "u:object_r:default_prop:s0"},
+      {"ro.hwui.layer_cache_size", "u:object_r:default_prop:s0"},
+      {"ro.hwui.path_cache_size", "u:object_r:default_prop:s0"},
+      {"ro.hwui.r_buffer_cache_size", "u:object_r:default_prop:s0"},
+      {"ro.hwui.text_large_cache_height", "u:object_r:default_prop:s0"},
+      {"ro.hwui.text_large_cache_width", "u:object_r:default_prop:s0"},
+      {"ro.hwui.text_small_cache_height", "u:object_r:default_prop:s0"},
+      {"ro.hwui.text_small_cache_width", "u:object_r:default_prop:s0"},
+      {"ro.hwui.texture_cache_flushrate", "u:object_r:default_prop:s0"},
+      {"ro.hwui.texture_cache_size", "u:object_r:default_prop:s0"},
+      {"ro.min_freq_0", "u:object_r:default_prop:s0"},
+      {"ro.min_freq_4", "u:object_r:default_prop:s0"},
+      {"ro.oem_unlock_supported", "u:object_r:default_prop:s0"},
+      {"ro.opengles.version", "u:object_r:default_prop:s0"},
+      {"ro.persistent_properties.ready", "u:object_r:persistent_properties_ready_prop:s0"},
+      {"ro.product.board", "u:object_r:default_prop:s0"},
+      {"ro.product.brand", "u:object_r:default_prop:s0"},
+      {"ro.product.cpu.abi", "u:object_r:default_prop:s0"},
+      {"ro.product.cpu.abilist", "u:object_r:default_prop:s0"},
+      {"ro.product.cpu.abilist32", "u:object_r:default_prop:s0"},
+      {"ro.product.cpu.abilist64", "u:object_r:default_prop:s0"},
+      {"ro.product.device", "u:object_r:default_prop:s0"},
+      {"ro.product.first_api_level", "u:object_r:default_prop:s0"},
+      {"ro.product.locale", "u:object_r:default_prop:s0"},
+      {"ro.product.manufacturer", "u:object_r:default_prop:s0"},
+      {"ro.product.model", "u:object_r:default_prop:s0"},
+      {"ro.product.name", "u:object_r:default_prop:s0"},
+      {"ro.property_service.version", "u:object_r:default_prop:s0"},
+      {"ro.qc.sdk.audio.fluencetype", "u:object_r:default_prop:s0"},
+      {"ro.recovery_id", "u:object_r:default_prop:s0"},
+      {"ro.revision", "u:object_r:default_prop:s0"},
+      {"ro.ril.svdo", "u:object_r:radio_prop:s0"},
+      {"ro.ril.svlte1x", "u:object_r:radio_prop:s0"},
+      {"ro.runtime.firstboot", "u:object_r:firstboot_prop:s0"},
+      {"ro.secure", "u:object_r:default_prop:s0"},
+      {"ro.serialno", "u:object_r:serialno_prop:s0"},
+      {"ro.sf.lcd_density", "u:object_r:default_prop:s0"},
+      {"ro.telephony.call_ring.multiple", "u:object_r:default_prop:s0"},
+      {"ro.telephony.default_cdma_sub", "u:object_r:default_prop:s0"},
+      {"ro.telephony.default_network", "u:object_r:default_prop:s0"},
+      {"ro.treble.enabled", "u:object_r:default_prop:s0"},
+      {"ro.vendor.build.date", "u:object_r:default_prop:s0"},
+      {"ro.vendor.build.date.utc", "u:object_r:default_prop:s0"},
+      {"ro.vendor.build.fingerprint", "u:object_r:default_prop:s0"},
+      {"ro.vendor.extension_library", "u:object_r:default_prop:s0"},
+      {"ro.wifi.channels", "u:object_r:default_prop:s0"},
+      {"ro.zygote", "u:object_r:default_prop:s0"},
+      {"security.perf_harden", "u:object_r:shell_prop:s0"},
+      {"sensors.contexthub.lid_state", "u:object_r:contexthub_prop:s0"},
+      {"service.adb.root", "u:object_r:shell_prop:s0"},
+      {"service.bootanim.exit", "u:object_r:system_prop:s0"},
+      {"service.sf.present_timestamp", "u:object_r:system_prop:s0"},
+      {"sys.boot.reason", "u:object_r:system_boot_reason_prop:s0"},
+      {"sys.boot_completed", "u:object_r:system_prop:s0"},
+      {"sys.ims.QMI_DAEMON_STATUS", "u:object_r:qcom_ims_prop:s0"},
+      {"sys.listeners.registered", "u:object_r:qseecomtee_prop:s0"},
+      {"sys.logbootcomplete", "u:object_r:system_prop:s0"},
+      {"sys.oem_unlock_allowed", "u:object_r:system_prop:s0"},
+      {"sys.qcom.devup", "u:object_r:system_prop:s0"},
+      {"sys.sysctl.extra_free_kbytes", "u:object_r:system_prop:s0"},
+      {"sys.usb.config", "u:object_r:system_radio_prop:s0"},
+      {"sys.usb.configfs", "u:object_r:system_radio_prop:s0"},
+      {"sys.usb.controller", "u:object_r:system_prop:s0"},
+      {"sys.usb.ffs.aio_compat", "u:object_r:ffs_prop:s0"},
+      {"sys.usb.ffs.max_read", "u:object_r:ffs_prop:s0"},
+      {"sys.usb.ffs.max_write", "u:object_r:ffs_prop:s0"},
+      {"sys.usb.ffs.ready", "u:object_r:ffs_prop:s0"},
+      {"sys.usb.mtp.device_type", "u:object_r:system_prop:s0"},
+      {"sys.usb.state", "u:object_r:system_prop:s0"},
+      {"telephony.lteOnCdmaDevice", "u:object_r:default_prop:s0"},
+      {"tombstoned.max_tombstone_count", "u:object_r:default_prop:s0"},
+      {"vidc.debug.perf.mode", "u:object_r:default_prop:s0"},
+      {"vidc.enc.dcvs.extra-buff-count", "u:object_r:default_prop:s0"},
+      {"vold.decrypt", "u:object_r:vold_prop:s0"},
+      {"vold.has_adoptable", "u:object_r:vold_prop:s0"},
+      {"vold.post_fs_data_done", "u:object_r:vold_prop:s0"},
+      {"wc_transport.clean_up", "u:object_r:wc_transport_prop:s0"},
+      {"wc_transport.hci_filter_status", "u:object_r:wc_transport_prop:s0"},
+      {"wc_transport.ref_count", "u:object_r:wc_transport_prop:s0"},
+      {"wc_transport.soc_initialized", "u:object_r:wc_transport_prop:s0"},
+      {"wc_transport.start_hci", "u:object_r:wc_transport_prop:s0"},
+      {"wc_transport.vnd_power", "u:object_r:wc_transport_prop:s0"},
+      {"wifi.interface", "u:object_r:default_prop:s0"},
+      {"wifi.supplicant_scan_interval", "u:object_r:default_prop:s0"},
+  };
+
+  for (const auto& [property, context] : properties_and_contexts) {
+    const char* returned_context;
+    property_info_area->GetPropertyInfo(property.c_str(), &returned_context, nullptr);
+    EXPECT_EQ(context, returned_context) << property;
+  }
+}
+
+TEST(propertyinfoserializer, GetPropertyInfo_prefix_without_dot) {
+  auto property_info = std::vector<PropertyInfoEntry>{
+      {"persist.radio", "1st", "1st", false},
+      {"persist.radio.something.else.here", "2nd", "2nd", false},
+  };
+
+  auto serialized_trie = std::string();
+  auto build_trie_error = std::string();
+  ASSERT_TRUE(BuildTrie(property_info, "default", "default", &serialized_trie, &build_trie_error))
+      << build_trie_error;
+
+  auto property_info_area = reinterpret_cast<const PropertyInfoArea*>(serialized_trie.data());
+
+  const char* context;
+  const char* type;
+  property_info_area->GetPropertyInfo("persist.radio", &context, &type);
+  EXPECT_STREQ("1st", context);
+  EXPECT_STREQ("1st", type);
+  property_info_area->GetPropertyInfo("persist.radio.subproperty", &context, &type);
+  EXPECT_STREQ("1st", context);
+  EXPECT_STREQ("1st", type);
+  property_info_area->GetPropertyInfo("persist.radiowords", &context, &type);
+  EXPECT_STREQ("1st", context);
+  EXPECT_STREQ("1st", type);
+  property_info_area->GetPropertyInfo("persist.radio.long.long.long.sub.property", &context, &type);
+  EXPECT_STREQ("1st", context);
+  EXPECT_STREQ("1st", type);
+  property_info_area->GetPropertyInfo("persist.radio.something.else.here", &context, &type);
+  EXPECT_STREQ("2nd", context);
+  EXPECT_STREQ("2nd", type);
+  property_info_area->GetPropertyInfo("persist.radio.something.else.here2", &context, &type);
+  EXPECT_STREQ("2nd", context);
+  EXPECT_STREQ("2nd", type);
+  property_info_area->GetPropertyInfo("persist.radio.something.else.here.after", &context, &type);
+  EXPECT_STREQ("2nd", context);
+  EXPECT_STREQ("2nd", type);
+  property_info_area->GetPropertyInfo("persist.radio.something.else.nothere", &context, &type);
+  EXPECT_STREQ("1st", context);
+  EXPECT_STREQ("1st", type);
+  property_info_area->GetPropertyInfo("persist.radio.something.else", &context, &type);
+  EXPECT_STREQ("1st", context);
+  EXPECT_STREQ("1st", type);
+}
+
+TEST(propertyinfoserializer, GetPropertyInfo_prefix_with_dot_vs_without) {
+  auto property_info = std::vector<PropertyInfoEntry>{
+      {"persist.", "1st", "1st", false},
+      {"persist.radio", "2nd", "2nd", false},
+      {"persist.radio.long.property.exact.match", "3rd", "3rd", true},
+  };
+
+  auto serialized_trie = std::string();
+  auto build_trie_error = std::string();
+  ASSERT_TRUE(BuildTrie(property_info, "default", "default", &serialized_trie, &build_trie_error))
+      << build_trie_error;
+
+  auto property_info_area = reinterpret_cast<const PropertyInfoArea*>(serialized_trie.data());
+
+  const char* context;
+  const char* type;
+  property_info_area->GetPropertyInfo("persist.notradio", &context, &type);
+  EXPECT_STREQ("1st", context);
+  EXPECT_STREQ("1st", type);
+  property_info_area->GetPropertyInfo("persist.radio", &context, &type);
+  EXPECT_STREQ("2nd", context);
+  EXPECT_STREQ("2nd", type);
+  property_info_area->GetPropertyInfo("persist.radio.subproperty", &context, &type);
+  EXPECT_STREQ("2nd", context);
+  EXPECT_STREQ("2nd", type);
+  property_info_area->GetPropertyInfo("persist.radiowords", &context, &type);
+  EXPECT_STREQ("2nd", context);
+  EXPECT_STREQ("2nd", type);
+  property_info_area->GetPropertyInfo("persist.radio.long.property.prefix.match", &context, &type);
+  EXPECT_STREQ("2nd", context);
+  EXPECT_STREQ("2nd", type);
+  property_info_area->GetPropertyInfo("persist.radio.long.property.exact.match", &context, &type);
+  EXPECT_STREQ("3rd", context);
+  EXPECT_STREQ("3rd", type);
+}
+
+TEST(propertyinfoserializer, GetPropertyInfo_empty_context_and_type) {
+  auto property_info = std::vector<PropertyInfoEntry>{
+      {"persist.", "1st", "", false},
+      {"persist.dot_prefix.", "2nd", "", false},
+      {"persist.non_dot_prefix", "3rd", "", false},
+      {"persist.exact_match", "", "", true},
+      {"persist.dot_prefix2.", "", "4th", false},
+      {"persist.non_dot_prefix2", "", "5th", false},
+  };
+
+  auto serialized_trie = std::string();
+  auto build_trie_error = std::string();
+  ASSERT_TRUE(BuildTrie(property_info, "default", "default", &serialized_trie, &build_trie_error))
+      << build_trie_error;
+
+  auto property_info_area = reinterpret_cast<const PropertyInfoArea*>(serialized_trie.data());
+
+  const char* context;
+  const char* type;
+  property_info_area->GetPropertyInfo("notpersist.radio.something", &context, &type);
+  EXPECT_STREQ("default", context);
+  EXPECT_STREQ("default", type);
+  property_info_area->GetPropertyInfo("persist.nomatch", &context, &type);
+  EXPECT_STREQ("1st", context);
+  EXPECT_STREQ("default", type);
+  property_info_area->GetPropertyInfo("persist.dot_prefix.something", &context, &type);
+  EXPECT_STREQ("2nd", context);
+  EXPECT_STREQ("default", type);
+  property_info_area->GetPropertyInfo("persist.non_dot_prefix.something", &context, &type);
+  EXPECT_STREQ("3rd", context);
+  EXPECT_STREQ("default", type);
+  property_info_area->GetPropertyInfo("persist.exact_match", &context, &type);
+  EXPECT_STREQ("1st", context);
+  EXPECT_STREQ("default", type);
+  property_info_area->GetPropertyInfo("persist.dot_prefix2.something", &context, &type);
+  EXPECT_STREQ("1st", context);
+  EXPECT_STREQ("4th", type);
+  property_info_area->GetPropertyInfo("persist.non_dot_prefix2.something", &context, &type);
+  EXPECT_STREQ("1st", context);
+  EXPECT_STREQ("5th", type);
+}
+
+}  // namespace properties
+}  // namespace android
diff --git a/property_service/libpropertyinfoserializer/space_tokenizer.h b/property_service/libpropertyinfoserializer/space_tokenizer.h
new file mode 100644
index 0000000..fba0c58
--- /dev/null
+++ b/property_service/libpropertyinfoserializer/space_tokenizer.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2017 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 PROPERTY_INFO_SERIALIZER_SPACE_TOKENIZER_H
+#define PROPERTY_INFO_SERIALIZER_SPACE_TOKENIZER_H
+
+namespace android {
+namespace properties {
+
+class SpaceTokenizer {
+ public:
+  SpaceTokenizer(const std::string& string)
+      : string_(string), it_(string_.begin()), end_(string_.end()) {}
+
+  std::string GetNext() {
+    auto next = std::string();
+    while (it_ != end_ && !isspace(*it_)) {
+      next.push_back(*it_++);
+    }
+    while (it_ != end_ && isspace(*it_)) {
+      it_++;
+    }
+    return next;
+  }
+
+  std::string GetRemaining() { return std::string(it_, end_); }
+
+ private:
+  std::string string_;
+  std::string::const_iterator it_;
+  std::string::const_iterator end_;
+};
+
+}  // namespace properties
+}  // namespace android
+
+#endif
diff --git a/property_service/libpropertyinfoserializer/trie_builder.cpp b/property_service/libpropertyinfoserializer/trie_builder.cpp
new file mode 100644
index 0000000..8c5ce84
--- /dev/null
+++ b/property_service/libpropertyinfoserializer/trie_builder.cpp
@@ -0,0 +1,105 @@
+//
+// Copyright (C) 2017 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 "trie_builder.h"
+
+#include <android-base/strings.h>
+
+using android::base::Split;
+
+namespace android {
+namespace properties {
+
+TrieBuilder::TrieBuilder(const std::string& default_context, const std::string& default_type)
+    : builder_root_("root") {
+  auto* context_pointer = StringPointerFromContainer(default_context, &contexts_);
+  builder_root_.set_context(context_pointer);
+  auto* type_pointer = StringPointerFromContainer(default_type, &types_);
+  builder_root_.set_type(type_pointer);
+}
+
+bool TrieBuilder::AddToTrie(const std::string& name, const std::string& context,
+                            const std::string& type, bool exact, std::string* error) {
+  auto* context_pointer = StringPointerFromContainer(context, &contexts_);
+  auto* type_pointer = StringPointerFromContainer(type, &types_);
+  return AddToTrie(name, context_pointer, type_pointer, exact, error);
+}
+
+bool TrieBuilder::AddToTrie(const std::string& name, const std::string* context,
+                            const std::string* type, bool exact, std::string* error) {
+  TrieBuilderNode* current_node = &builder_root_;
+
+  auto name_pieces = Split(name, ".");
+
+  bool ends_with_dot = false;
+  if (name_pieces.back().empty()) {
+    ends_with_dot = true;
+    name_pieces.pop_back();
+  }
+
+  // Move us to the final node that we care about, adding incremental nodes if necessary.
+  while (name_pieces.size() > 1) {
+    auto child = current_node->FindChild(name_pieces.front());
+    if (child == nullptr) {
+      child = current_node->AddChild(name_pieces.front());
+    }
+    if (child == nullptr) {
+      *error = "Unable to allocate Trie node";
+      return false;
+    }
+    current_node = child;
+    name_pieces.erase(name_pieces.begin());
+  }
+
+  // Store our context based on what type of match it is.
+  if (exact) {
+    if (!current_node->AddExactMatchContext(name_pieces.front(), context, type)) {
+      *error = "Duplicate exact match detected for '" + name + "'";
+      return false;
+    }
+  } else if (!ends_with_dot) {
+    if (!current_node->AddPrefixContext(name_pieces.front(), context, type)) {
+      *error = "Duplicate prefix match detected for '" + name + "'";
+      return false;
+    }
+  } else {
+    auto child = current_node->FindChild(name_pieces.front());
+    if (child == nullptr) {
+      child = current_node->AddChild(name_pieces.front());
+    }
+    if (child == nullptr) {
+      *error = "Unable to allocate Trie node";
+      return false;
+    }
+    if (child->context() != nullptr || child->type() != nullptr) {
+      *error = "Duplicate prefix match detected for '" + name + "'";
+      return false;
+    }
+    child->set_context(context);
+    child->set_type(type);
+  }
+  return true;
+}
+
+const std::string* TrieBuilder::StringPointerFromContainer(const std::string& string,
+                                                           std::set<std::string>* container) {
+  // Get a pointer to the string in a given set, such that we only ever serialize each string once.
+  auto [iterator, _] = container->emplace(string);
+  return &(*iterator);
+}
+
+}  // namespace properties
+}  // namespace android
diff --git a/property_service/libpropertyinfoserializer/trie_builder.h b/property_service/libpropertyinfoserializer/trie_builder.h
new file mode 100644
index 0000000..b971589
--- /dev/null
+++ b/property_service/libpropertyinfoserializer/trie_builder.h
@@ -0,0 +1,123 @@
+//
+// Copyright (C) 2017 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 PROPERTY_INFO_SERIALIZER_TRIE_BUILDER_H
+#define PROPERTY_INFO_SERIALIZER_TRIE_BUILDER_H
+
+#include <memory>
+#include <set>
+#include <string>
+#include <vector>
+
+namespace android {
+namespace properties {
+
+struct PropertyEntryBuilder {
+  PropertyEntryBuilder() : context(nullptr), type(nullptr) {}
+  PropertyEntryBuilder(const std::string& name, const std::string* context, const std::string* type)
+      : name(name), context(context), type(type) {}
+  std::string name;
+  const std::string* context;
+  const std::string* type;
+};
+
+class TrieBuilderNode {
+ public:
+  TrieBuilderNode(const std::string& name) : property_entry_(name, nullptr, nullptr) {}
+
+  TrieBuilderNode* FindChild(const std::string& name) {
+    for (auto& child : children_) {
+      if (child.name() == name) return &child;
+    }
+    return nullptr;
+  }
+
+  const TrieBuilderNode* FindChild(const std::string& name) const {
+    for (const auto& child : children_) {
+      if (child.name() == name) return &child;
+    }
+    return nullptr;
+  }
+
+  TrieBuilderNode* AddChild(const std::string& name) { return &children_.emplace_back(name); }
+
+  bool AddPrefixContext(const std::string& prefix, const std::string* context,
+                        const std::string* type) {
+    if (std::find_if(prefixes_.begin(), prefixes_.end(),
+                     [&prefix](const auto& t) { return t.name == prefix; }) != prefixes_.end()) {
+      return false;
+    }
+
+    prefixes_.emplace_back(prefix, context, type);
+    return true;
+  }
+
+  bool AddExactMatchContext(const std::string& exact_match, const std::string* context,
+                            const std::string* type) {
+    if (std::find_if(exact_matches_.begin(), exact_matches_.end(), [&exact_match](const auto& t) {
+          return t.name == exact_match;
+        }) != exact_matches_.end()) {
+      return false;
+    }
+
+    exact_matches_.emplace_back(exact_match, context, type);
+    return true;
+  }
+
+  const std::string& name() const { return property_entry_.name; }
+  const std::string* context() const { return property_entry_.context; }
+  void set_context(const std::string* context) { property_entry_.context = context; }
+  const std::string* type() const { return property_entry_.type; }
+  void set_type(const std::string* type) { property_entry_.type = type; }
+
+  const PropertyEntryBuilder property_entry() const { return property_entry_; }
+
+  const std::vector<TrieBuilderNode>& children() const { return children_; }
+  const std::vector<PropertyEntryBuilder>& prefixes() const { return prefixes_; }
+  const std::vector<PropertyEntryBuilder>& exact_matches() const { return exact_matches_; }
+
+ private:
+  PropertyEntryBuilder property_entry_;
+  std::vector<TrieBuilderNode> children_;
+  std::vector<PropertyEntryBuilder> prefixes_;
+  std::vector<PropertyEntryBuilder> exact_matches_;
+};
+
+class TrieBuilder {
+ public:
+  TrieBuilder(const std::string& default_context, const std::string& default_type);
+  bool AddToTrie(const std::string& name, const std::string& context, const std::string& type,
+                 bool exact, std::string* error);
+
+  const TrieBuilderNode builder_root() const { return builder_root_; }
+  const std::set<std::string>& contexts() const { return contexts_; }
+  const std::set<std::string>& types() const { return types_; }
+
+ private:
+  bool AddToTrie(const std::string& name, const std::string* context, const std::string* type,
+                 bool exact, std::string* error);
+  const std::string* StringPointerFromContainer(const std::string& string,
+                                                std::set<std::string>* container);
+
+  TrieBuilderNode builder_root_;
+  std::set<std::string> contexts_;
+  std::set<std::string> types_;
+};
+
+}  // namespace properties
+}  // namespace android
+
+#endif
diff --git a/property_service/libpropertyinfoserializer/trie_builder_test.cpp b/property_service/libpropertyinfoserializer/trie_builder_test.cpp
new file mode 100644
index 0000000..5078810
--- /dev/null
+++ b/property_service/libpropertyinfoserializer/trie_builder_test.cpp
@@ -0,0 +1,129 @@
+//
+// Copyright (C) 2017 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 "trie_builder.h"
+
+#include <gtest/gtest.h>
+
+namespace android {
+namespace properties {
+
+TEST(propertyinfoserializer, BuildTrie_Simple) {
+  auto trie_builder = TrieBuilder("default", "default_type");
+
+  // Add test data to tree
+  auto error = std::string();
+  EXPECT_TRUE(trie_builder.AddToTrie("test.", "1st", "1st_type", false, &error));
+  EXPECT_TRUE(trie_builder.AddToTrie("test.test", "2nd", "2nd_type", false, &error));
+  EXPECT_TRUE(trie_builder.AddToTrie("test.test1", "3rd", "3rd_type", true, &error));
+  EXPECT_TRUE(trie_builder.AddToTrie("test.test2", "3rd", "3rd_type", true, &error));
+  EXPECT_TRUE(trie_builder.AddToTrie("test.test3", "3rd", "3rd_type", true, &error));
+  EXPECT_TRUE(trie_builder.AddToTrie("this.is.a.long.string", "4th", "4th_type", true, &error));
+
+  ASSERT_EQ(5U, trie_builder.contexts().size());
+  ASSERT_EQ(5U, trie_builder.types().size());
+
+  auto& builder_root = trie_builder.builder_root();
+
+  // Check the root node
+  EXPECT_EQ("root", builder_root.name());
+  ASSERT_NE(nullptr, builder_root.context());
+  EXPECT_EQ("default", *builder_root.context());
+  ASSERT_NE(nullptr, builder_root.type());
+  EXPECT_EQ("default_type", *builder_root.type());
+
+  EXPECT_EQ(0U, builder_root.prefixes().size());
+  EXPECT_EQ(0U, builder_root.exact_matches().size());
+
+  ASSERT_EQ(2U, builder_root.children().size());
+
+  // Check the 'test.' node
+  auto* test_node = builder_root.FindChild("test");
+  EXPECT_EQ("test", test_node->name());
+  ASSERT_NE(nullptr, test_node->context());
+  EXPECT_EQ("1st", *test_node->context());
+  ASSERT_NE(nullptr, test_node->type());
+  EXPECT_EQ("1st_type", *test_node->type());
+
+  EXPECT_EQ(0U, test_node->children().size());
+  EXPECT_EQ(1U, test_node->prefixes().size());
+  {
+    auto& property_entry = test_node->prefixes()[0];
+    EXPECT_EQ("test", property_entry.name);
+    ASSERT_NE(nullptr, property_entry.context);
+    EXPECT_EQ("2nd", *property_entry.context);
+    ASSERT_NE(nullptr, property_entry.type);
+    EXPECT_EQ("2nd_type", *property_entry.type);
+  }
+  EXPECT_EQ(3U, test_node->exact_matches().size());
+  EXPECT_EQ("test1", test_node->exact_matches()[0].name);
+  EXPECT_EQ("test2", test_node->exact_matches()[1].name);
+  EXPECT_EQ("test3", test_node->exact_matches()[2].name);
+
+  ASSERT_NE(nullptr, test_node->exact_matches()[0].context);
+  ASSERT_NE(nullptr, test_node->exact_matches()[1].context);
+  ASSERT_NE(nullptr, test_node->exact_matches()[2].context);
+  EXPECT_EQ("3rd", *test_node->exact_matches()[0].context);
+  EXPECT_EQ("3rd", *test_node->exact_matches()[1].context);
+  EXPECT_EQ("3rd", *test_node->exact_matches()[2].context);
+
+  ASSERT_NE(nullptr, test_node->exact_matches()[0].type);
+  ASSERT_NE(nullptr, test_node->exact_matches()[1].type);
+  ASSERT_NE(nullptr, test_node->exact_matches()[2].type);
+  EXPECT_EQ("3rd_type", *test_node->exact_matches()[0].type);
+  EXPECT_EQ("3rd_type", *test_node->exact_matches()[1].type);
+  EXPECT_EQ("3rd_type", *test_node->exact_matches()[2].type);
+
+  // Check the long string node
+  auto expect_empty_one_child = [](auto* node) {
+    ASSERT_NE(nullptr, node);
+    EXPECT_EQ(nullptr, node->context());
+    EXPECT_EQ(nullptr, node->type());
+    EXPECT_EQ(0U, node->prefixes().size());
+    EXPECT_EQ(0U, node->exact_matches().size());
+    EXPECT_EQ(1U, node->children().size());
+  };
+
+  // Start with 'this'
+  auto* long_string_node = builder_root.FindChild("this");
+  expect_empty_one_child(long_string_node);
+
+  // Move to 'is'
+  long_string_node = long_string_node->FindChild("is");
+  expect_empty_one_child(long_string_node);
+
+  // Move to 'a'
+  long_string_node = long_string_node->FindChild("a");
+  expect_empty_one_child(long_string_node);
+
+  // Move to 'long'
+  long_string_node = long_string_node->FindChild("long");
+  EXPECT_EQ(0U, long_string_node->prefixes().size());
+  EXPECT_EQ(1U, long_string_node->exact_matches().size());
+  EXPECT_EQ(0U, long_string_node->children().size());
+
+  {
+    auto& property_entry = long_string_node->exact_matches()[0];
+    EXPECT_EQ("string", property_entry.name);
+    ASSERT_NE(nullptr, property_entry.context);
+    EXPECT_EQ("4th", *property_entry.context);
+    ASSERT_NE(nullptr, property_entry.type);
+    EXPECT_EQ("4th_type", *property_entry.type);
+  }
+}
+
+}  // namespace properties
+}  // namespace android
diff --git a/property_service/libpropertyinfoserializer/trie_node_arena.h b/property_service/libpropertyinfoserializer/trie_node_arena.h
new file mode 100644
index 0000000..5e0ef82
--- /dev/null
+++ b/property_service/libpropertyinfoserializer/trie_node_arena.h
@@ -0,0 +1,108 @@
+//
+// Copyright (C) 2017 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 PROPERTY_INFO_SERIALIZER_TRIE_NODE_ARENA_H
+#define PROPERTY_INFO_SERIALIZER_TRIE_NODE_ARENA_H
+
+#include <string>
+#include <vector>
+
+namespace android {
+namespace properties {
+
+template <typename T>
+class ArenaObjectPointer {
+ public:
+  ArenaObjectPointer(std::string& arena_data, uint32_t offset)
+      : arena_data_(arena_data), offset_(offset) {}
+
+  T* operator->() { return reinterpret_cast<T*>(arena_data_.data() + offset_); }
+
+ private:
+  std::string& arena_data_;
+  uint32_t offset_;
+};
+
+class TrieNodeArena {
+ public:
+  TrieNodeArena() : current_data_pointer_(0) {}
+
+  // We can't return pointers to objects since data_ may move when reallocated, thus invalidating
+  // any pointers.  Therefore we return an ArenaObjectPointer, which always accesses elements via
+  // data_ + offset.
+  template <typename T>
+  ArenaObjectPointer<T> AllocateObject(uint32_t* return_offset) {
+    uint32_t offset;
+    AllocateData(sizeof(T), &offset);
+    if (return_offset) *return_offset = offset;
+    return ArenaObjectPointer<T>(data_, offset);
+  }
+
+  uint32_t AllocateUint32Array(int length) {
+    uint32_t offset;
+    AllocateData(sizeof(uint32_t) * length, &offset);
+    return offset;
+  }
+
+  uint32_t* uint32_array(uint32_t offset) {
+    return reinterpret_cast<uint32_t*>(data_.data() + offset);
+  }
+
+  uint32_t AllocateAndWriteString(const std::string& string) {
+    uint32_t offset;
+    char* data = static_cast<char*>(AllocateData(string.size() + 1, &offset));
+    strcpy(data, string.c_str());
+    return offset;
+  }
+
+  void AllocateAndWriteUint32(uint32_t value) {
+    auto location = static_cast<uint32_t*>(AllocateData(sizeof(uint32_t), nullptr));
+    *location = value;
+  }
+
+  void* AllocateData(size_t size, uint32_t* offset) {
+    size_t aligned_size = size + (sizeof(uint32_t) - 1) & ~(sizeof(uint32_t) - 1);
+
+    if (current_data_pointer_ + aligned_size > data_.size()) {
+      auto new_size = (current_data_pointer_ + aligned_size + data_.size()) * 2;
+      data_.resize(new_size, '\0');
+    }
+    if (offset) *offset = current_data_pointer_;
+
+    uint32_t return_offset = current_data_pointer_;
+    current_data_pointer_ += aligned_size;
+    return &data_[0] + return_offset;
+  }
+
+  uint32_t size() const { return current_data_pointer_; }
+
+  const std::string& data() const { return data_; }
+
+  std::string truncated_data() const {
+    auto result = data_;
+    result.resize(current_data_pointer_);
+    return result;
+  }
+
+ private:
+  std::string data_;
+  uint32_t current_data_pointer_;
+};
+
+}  // namespace properties
+}  // namespace android
+
+#endif
diff --git a/property_service/libpropertyinfoserializer/trie_serializer.cpp b/property_service/libpropertyinfoserializer/trie_serializer.cpp
new file mode 100644
index 0000000..adeed1b
--- /dev/null
+++ b/property_service/libpropertyinfoserializer/trie_serializer.cpp
@@ -0,0 +1,142 @@
+//
+// Copyright (C) 2017 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 "trie_serializer.h"
+
+namespace android {
+namespace properties {
+
+// Serialized strings contains:
+// 1) A uint32_t count of elements in the below array
+// 2) A sorted array of uint32_t offsets pointing to null terminated strings
+// 3) Each of the null terminated strings themselves packed back to back
+// This returns the offset into arena where the serialized strings start.
+void TrieSerializer::SerializeStrings(const std::set<std::string>& strings) {
+  arena_->AllocateAndWriteUint32(strings.size());
+
+  // Allocate space for the array.
+  uint32_t offset_array_offset = arena_->AllocateUint32Array(strings.size());
+
+  // Write offset pointers and strings; these are already alphabetically sorted by virtue of being
+  // in an std::set.
+  auto it = strings.begin();
+  for (unsigned int i = 0; i < strings.size(); ++i, ++it) {
+    uint32_t string_offset = arena_->AllocateAndWriteString(*it);
+    arena_->uint32_array(offset_array_offset)[i] = string_offset;
+  }
+}
+
+uint32_t TrieSerializer::WritePropertyEntry(const PropertyEntryBuilder& property_entry) {
+  uint32_t context_index = property_entry.context != nullptr && !property_entry.context->empty()
+                               ? serialized_info()->FindContextIndex(property_entry.context->c_str())
+                               : ~0u;
+  uint32_t type_index = property_entry.type != nullptr && !property_entry.type->empty()
+                            ? serialized_info()->FindTypeIndex(property_entry.type->c_str())
+                            : ~0u;
+  uint32_t offset;
+  auto serialized_property_entry = arena_->AllocateObject<PropertyEntry>(&offset);
+  serialized_property_entry->name_offset = arena_->AllocateAndWriteString(property_entry.name);
+  serialized_property_entry->namelen = property_entry.name.size();
+  serialized_property_entry->context_index = context_index;
+  serialized_property_entry->type_index = type_index;
+  return offset;
+}
+
+uint32_t TrieSerializer::WriteTrieNode(const TrieBuilderNode& builder_node) {
+  uint32_t trie_offset;
+  auto trie = arena_->AllocateObject<TrieNodeInternal>(&trie_offset);
+
+  trie->property_entry = WritePropertyEntry(builder_node.property_entry());
+
+  // Write prefix matches
+  auto sorted_prefix_matches = builder_node.prefixes();
+  // Prefixes are sorted by descending length
+  std::sort(sorted_prefix_matches.begin(), sorted_prefix_matches.end(),
+            [](const auto& lhs, const auto& rhs) { return lhs.name.size() > rhs.name.size(); });
+
+  trie->num_prefixes = sorted_prefix_matches.size();
+
+  uint32_t prefix_entries_array_offset = arena_->AllocateUint32Array(sorted_prefix_matches.size());
+  trie->prefix_entries = prefix_entries_array_offset;
+
+  for (unsigned int i = 0; i < sorted_prefix_matches.size(); ++i) {
+    uint32_t property_entry_offset = WritePropertyEntry(sorted_prefix_matches[i]);
+    arena_->uint32_array(prefix_entries_array_offset)[i] = property_entry_offset;
+  }
+
+  // Write exact matches
+  auto sorted_exact_matches = builder_node.exact_matches();
+  // Exact matches are sorted alphabetically
+  std::sort(sorted_exact_matches.begin(), sorted_exact_matches.end(),
+            [](const auto& lhs, const auto& rhs) { return lhs.name < rhs.name; });
+
+  trie->num_exact_matches = sorted_exact_matches.size();
+
+  uint32_t exact_match_entries_array_offset =
+      arena_->AllocateUint32Array(sorted_exact_matches.size());
+  trie->exact_match_entries = exact_match_entries_array_offset;
+
+  for (unsigned int i = 0; i < sorted_exact_matches.size(); ++i) {
+    uint32_t property_entry_offset = WritePropertyEntry(sorted_exact_matches[i]);
+    arena_->uint32_array(exact_match_entries_array_offset)[i] = property_entry_offset;
+  }
+
+  // Write children
+  auto sorted_children = builder_node.children();
+  std::sort(sorted_children.begin(), sorted_children.end(),
+            [](const auto& lhs, const auto& rhs) { return lhs.name() < rhs.name(); });
+
+  trie->num_child_nodes = sorted_children.size();
+  uint32_t children_offset_array_offset = arena_->AllocateUint32Array(sorted_children.size());
+  trie->child_nodes = children_offset_array_offset;
+
+  for (unsigned int i = 0; i < sorted_children.size(); ++i) {
+    arena_->uint32_array(children_offset_array_offset)[i] = WriteTrieNode(sorted_children[i]);
+  }
+  return trie_offset;
+}
+
+TrieSerializer::TrieSerializer() {}
+
+std::string TrieSerializer::SerializeTrie(const TrieBuilder& trie_builder) {
+  arena_.reset(new TrieNodeArena());
+
+  auto header = arena_->AllocateObject<PropertyInfoAreaHeader>(nullptr);
+  header->current_version = 1;
+  header->minimum_supported_version = 1;
+
+  // Store where we're about to write the contexts.
+  header->contexts_offset = arena_->size();
+  SerializeStrings(trie_builder.contexts());
+
+  // Store where we're about to write the types.
+  header->types_offset = arena_->size();
+  SerializeStrings(trie_builder.types());
+
+  // We need to store size() up to this point now for Find*Offset() to work.
+  header->size = arena_->size();
+
+  uint32_t root_trie_offset = WriteTrieNode(trie_builder.builder_root());
+  header->root_offset = root_trie_offset;
+
+  // Record the real size now that we've written everything
+  header->size = arena_->size();
+
+  return arena_->truncated_data();
+}
+
+}  // namespace properties
+}  // namespace android
diff --git a/property_service/libpropertyinfoserializer/trie_serializer.h b/property_service/libpropertyinfoserializer/trie_serializer.h
new file mode 100644
index 0000000..e4d3343
--- /dev/null
+++ b/property_service/libpropertyinfoserializer/trie_serializer.h
@@ -0,0 +1,55 @@
+//
+// Copyright (C) 2017 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 PROPERTY_INFO_SERIALIZER_TRIE_SERIALIZER_H
+#define PROPERTY_INFO_SERIALIZER_TRIE_SERIALIZER_H
+
+#include <string>
+#include <vector>
+
+#include "property_info_parser/property_info_parser.h"
+
+#include "trie_builder.h"
+#include "trie_node_arena.h"
+
+namespace android {
+namespace properties {
+
+class TrieSerializer {
+ public:
+  TrieSerializer();
+
+  std::string SerializeTrie(const TrieBuilder& trie_builder);
+
+ private:
+  void SerializeStrings(const std::set<std::string>& strings);
+  uint32_t WritePropertyEntry(const PropertyEntryBuilder& property_entry);
+
+  // Writes a new TrieNode to arena, and recursively writes its children.
+  // Returns the offset within arena.
+  uint32_t WriteTrieNode(const TrieBuilderNode& builder_node);
+
+  const PropertyInfoArea* serialized_info() const {
+    return reinterpret_cast<const PropertyInfoArea*>(arena_->data().data());
+  }
+
+  std::unique_ptr<TrieNodeArena> arena_;
+};
+
+}  // namespace properties
+}  // namespace android
+
+#endif
diff --git a/property_service/property_info_checker/Android.bp b/property_service/property_info_checker/Android.bp
new file mode 100644
index 0000000..7d66199
--- /dev/null
+++ b/property_service/property_info_checker/Android.bp
@@ -0,0 +1,13 @@
+cc_binary {
+    name: "property_info_checker",
+    host_supported: true,
+    static_executable: true,
+    cpp_std: "experimental",
+    static_libs: [
+        "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
new file mode 100644
index 0000000..52c4383
--- /dev/null
+++ b/property_service/property_info_checker/property_info_checker.cpp
@@ -0,0 +1,187 @@
+#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 < 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 = 2; i < argc; ++i) {
+    auto filename = argv[i];
+    auto file_contents = std::string{};
+    if (!ReadFileToString(filename, &file_contents)) {
+      std::cerr << "Could not read properties from '" << filename << "'" << std::endl;
+      return -1;
+    }
+
+    auto errors = std::vector<std::string>{};
+    ParsePropertyInfoFile(file_contents, &property_info_entries, &errors);
+    if (!errors.empty()) {
+      for (const auto& error : errors) {
+        std::cerr << "Could not read line from '" << filename << "': " << error << std::endl;
+      }
+      return -1;
+    }
+  }
+
+  auto serialized_contexts = std::string{};
+  auto build_trie_error = std::string{};
+
+  if (!BuildTrie(property_info_entries, "u:object_r:default_prop:s0", "\\s*", &serialized_contexts,
+                 &build_trie_error)) {
+    std::cerr << "Unable to serialize property contexts: " << build_trie_error << std::endl;
+    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/qemu_pipe/Android.bp b/qemu_pipe/Android.bp
new file mode 100644
index 0000000..c6bda4a
--- /dev/null
+++ b/qemu_pipe/Android.bp
@@ -0,0 +1,15 @@
+// Copyright 2011 The Android Open Source Project
+
+cc_library_static {
+    name: "libqemu_pipe",
+    vendor_available: true,
+    recovery_available: true,
+    sanitize: {
+        misc_undefined: ["integer"],
+    },
+    srcs: ["qemu_pipe.cpp"],
+    local_include_dirs: ["include"],
+    static_libs: ["libbase"],
+    export_include_dirs: ["include"],
+    cflags: ["-Werror"],
+}
diff --git a/qemu_pipe/OWNERS b/qemu_pipe/OWNERS
new file mode 100644
index 0000000..dbc1bf6
--- /dev/null
+++ b/qemu_pipe/OWNERS
@@ -0,0 +1 @@
+bohu@google.com
diff --git a/qemu_pipe/include/qemu_pipe.h b/qemu_pipe/include/qemu_pipe.h
new file mode 100644
index 0000000..0987498
--- /dev/null
+++ b/qemu_pipe/include/qemu_pipe.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2011 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 ANDROID_CORE_INCLUDE_QEMU_PIPE_H
+#define ANDROID_CORE_INCLUDE_QEMU_PIPE_H
+
+#include <stddef.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+// Try to open a new Qemu fast-pipe. This function returns a file descriptor
+// that can be used to communicate with a named service managed by the
+// emulator.
+//
+// This file descriptor can be used as a standard pipe/socket descriptor.
+//
+// 'pipeName' is the name of the emulator service you want to connect to,
+// and should begin with 'pipe:' (e.g. 'pipe:camera' or 'pipe:opengles').
+// For backward compatibility, the 'pipe:' prefix can be omitted, and in
+// that case, qemu_pipe_open will add it for you.
+
+// On success, return a valid file descriptor, or -1/errno on failure. E.g.:
+//
+// EINVAL  -> unknown/unsupported pipeName
+// ENOSYS  -> fast pipes not available in this system.
+//
+// ENOSYS should never happen, except if you're trying to run within a
+// misconfigured emulator.
+//
+// You should be able to open several pipes to the same pipe service,
+// except for a few special cases (e.g. GSM modem), where EBUSY will be
+// returned if more than one client tries to connect to it.
+int qemu_pipe_open(const char* pipeName);
+
+// Send a framed message |buff| of |len| bytes through the |fd| descriptor.
+// This really adds a 4-hexchar prefix describing the payload size.
+// Returns 0 on success, and -1 on error.
+int qemu_pipe_frame_send(int fd, const void* buff, size_t len);
+
+// Read a frame message from |fd|, and store it into |buff| of |len| bytes.
+// If the framed message is larger than |len|, then this returns -1 and the
+// content is lost. Otherwise, this returns the size of the message. NOTE:
+// empty messages are possible in a framed wire protocol and do not mean
+// end-of-stream.
+int qemu_pipe_frame_recv(int fd, void* buff, size_t len);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* ANDROID_CORE_INCLUDE_QEMU_PIPE_H */
diff --git a/qemu_pipe/qemu_pipe.cpp b/qemu_pipe/qemu_pipe.cpp
new file mode 100644
index 0000000..beeccb0
--- /dev/null
+++ b/qemu_pipe/qemu_pipe.cpp
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2011 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 "qemu_pipe.h"
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+#include <errno.h>
+#include <stdio.h>
+
+#include <android-base/file.h>
+
+using android::base::ReadFully;
+using android::base::WriteFully;
+
+// Define QEMU_PIPE_DEBUG if you want to print error messages when an error
+// occurs during pipe operations. The macro should simply take a printf-style
+// formatting string followed by optional arguments.
+#ifndef QEMU_PIPE_DEBUG
+#  define  QEMU_PIPE_DEBUG(...)   (void)0
+#endif
+
+int qemu_pipe_open(const char* pipeName) {
+    // Sanity check.
+    if (!pipeName) {
+        errno = EINVAL;
+        return -1;
+    }
+
+    int fd = TEMP_FAILURE_RETRY(open("/dev/qemu_pipe", O_RDWR));
+    if (fd < 0) {
+        QEMU_PIPE_DEBUG("%s: Could not open /dev/qemu_pipe: %s", __FUNCTION__,
+                        strerror(errno));
+        return -1;
+    }
+
+    // Write the pipe name, *including* the trailing zero which is necessary.
+    size_t pipeNameLen = strlen(pipeName);
+    if (WriteFully(fd, pipeName, pipeNameLen + 1U)) {
+        return fd;
+    }
+
+    // now, add 'pipe:' prefix and try again
+    // Note: host side will wait for the trailing '\0' to start
+    // service lookup.
+    const char pipe_prefix[] = "pipe:";
+    if (WriteFully(fd, pipe_prefix, strlen(pipe_prefix)) &&
+            WriteFully(fd, pipeName, pipeNameLen + 1U)) {
+        return fd;
+    }
+    QEMU_PIPE_DEBUG("%s: Could not write to %s pipe service: %s",
+            __FUNCTION__, pipeName, strerror(errno));
+    close(fd);
+    return -1;
+}
+
+int qemu_pipe_frame_send(int fd, const void* buff, size_t len) {
+    char header[5];
+    snprintf(header, sizeof(header), "%04zx", len);
+    if (!WriteFully(fd, header, 4)) {
+        QEMU_PIPE_DEBUG("Can't write qemud frame header: %s", strerror(errno));
+        return -1;
+    }
+    if (!WriteFully(fd, buff, len)) {
+        QEMU_PIPE_DEBUG("Can't write qemud frame payload: %s", strerror(errno));
+        return -1;
+    }
+    return 0;
+}
+
+int qemu_pipe_frame_recv(int fd, void* buff, size_t len) {
+    char header[5];
+    if (!ReadFully(fd, header, 4)) {
+        QEMU_PIPE_DEBUG("Can't read qemud frame header: %s", strerror(errno));
+        return -1;
+    }
+    header[4] = '\0';
+    size_t size;
+    if (sscanf(header, "%04zx", &size) != 1) {
+        QEMU_PIPE_DEBUG("Malformed qemud frame header: [%.*s]", 4, header);
+        return -1;
+    }
+    if (size > len) {
+        QEMU_PIPE_DEBUG("Oversized qemud frame (% bytes, expected <= %)", size,
+                        len);
+        return -1;
+    }
+    if (!ReadFully(fd, buff, size)) {
+        QEMU_PIPE_DEBUG("Could not read qemud frame payload: %s",
+                        strerror(errno));
+        return -1;
+    }
+    return size;
+}
diff --git a/reboot/reboot.c b/reboot/reboot.c
index 007dfba..fe763a8 100644
--- a/reboot/reboot.c
+++ b/reboot/reboot.c
@@ -21,13 +21,13 @@
 #include <cutils/android_reboot.h>
 #include <unistd.h>
 
-int main(int argc, char *argv[])
-{
+int main(int argc, char* argv[]) {
     int ret;
     size_t prop_len;
     char property_val[PROPERTY_VALUE_MAX];
-    const char *cmd = "reboot";
-    char *optarg = "";
+    static const char reboot[] = "reboot";
+    const char* cmd = reboot;
+    char* optarg = "";
 
     opterr = 0;
     do {
@@ -56,22 +56,27 @@
 
     if (argc > optind)
         optarg = argv[optind];
+    if (!optarg || !optarg[0]) optarg = "shell";
 
     prop_len = snprintf(property_val, sizeof(property_val), "%s,%s", cmd, optarg);
     if (prop_len >= sizeof(property_val)) {
-        fprintf(stderr, "reboot command too long: %s\n", optarg);
+        fprintf(stderr, "%s command too long: %s\n", cmd, optarg);
         exit(EXIT_FAILURE);
     }
 
     ret = property_set(ANDROID_RB_PROPERTY, property_val);
-    if(ret < 0) {
-        perror("reboot");
+    if (ret < 0) {
+        perror(cmd);
         exit(EXIT_FAILURE);
     }
 
     // Don't return early. Give the reboot command time to take effect
     // to avoid messing up scripts which do "adb shell reboot && adb wait-for-device"
-    while(1) { pause(); }
+    if (cmd == reboot) {
+        while (1) {
+            pause();
+        }
+    }
 
     fprintf(stderr, "Done\n");
     return 0;
diff --git a/rootdir/Android.mk b/rootdir/Android.mk
index 86fec6a..763dc79 100644
--- a/rootdir/Android.mk
+++ b/rootdir/Android.mk
@@ -36,69 +36,19 @@
 
 include $(BUILD_PREBUILT)
 
-# Modules for asan.options.X files.
-
-ASAN_OPTIONS_FILES :=
-
-define create-asan-options-module
-include $$(CLEAR_VARS)
-LOCAL_MODULE := asan.options.$(1)
-ASAN_OPTIONS_FILES += asan.options.$(1)
-LOCAL_MODULE_CLASS := ETC
-# The asan.options.off.template tries to turn off as much of ASAN as is possible.
-LOCAL_SRC_FILES := asan.options.off.template
-LOCAL_MODULE_PATH := $(TARGET_OUT)
-include $$(BUILD_PREBUILT)
-endef
-
-# Pretty comprehensive set of native services. This list is helpful if all that's to be checked is an
-# app.
-ifeq ($(SANITIZE_LITE_SERVICES),true)
-SANITIZE_ASAN_OPTIONS_FOR := \
-  adbd \
-  ATFWD-daemon \
-  audioserver \
-  bridgemgrd \
-  cameraserver \
-  cnd \
-  debuggerd \
-  debuggerd64 \
-  dex2oat \
-  drmserver \
-  fingerprintd \
-  gatekeeperd \
-  installd \
-  keystore \
-  lmkd \
-  logcat \
-  logd \
-  lowi-server \
-  media.codec \
-  mediadrmserver \
-  media.extractor \
-  mediaserver \
-  mm-qcamera-daemon \
-  mpdecision \
-  netmgrd \
-  perfd \
-  perfprofd \
-  qmuxd \
-  qseecomd \
-  rild \
-  sdcard \
-  servicemanager \
-  slim_daemon \
-  surfaceflinger \
-  thermal-engine \
-  time_daemon \
-  update_engine \
-  vold \
-  wpa_supplicant \
-  zip
-endif
-
-ifneq ($(SANITIZE_ASAN_OPTIONS_FOR),)
-  $(foreach binary, $(SANITIZE_ASAN_OPTIONS_FOR), $(eval $(call create-asan-options-module,$(binary))))
+# ASAN extration.
+ASAN_EXTRACT_FILES :=
+ifeq ($(SANITIZE_TARGET_SYSTEM),true)
+include $(CLEAR_VARS)
+LOCAL_MODULE:= asan_extract
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE_CLASS := EXECUTABLES
+LOCAL_SRC_FILES := asan_extract.sh
+LOCAL_INIT_RC := asan_extract.rc
+# We need bzip2 on device for extraction.
+LOCAL_REQUIRED_MODULES := bzip2
+include $(BUILD_PREBUILT)
+ASAN_EXTRACT_FILES := asan_extract
 endif
 
 endif
@@ -114,7 +64,7 @@
 EXPORT_GLOBAL_ASAN_OPTIONS :=
 ifneq ($(filter address,$(SANITIZE_TARGET)),)
   EXPORT_GLOBAL_ASAN_OPTIONS := export ASAN_OPTIONS include=/system/asan.options
-  LOCAL_REQUIRED_MODULES := asan.options $(ASAN_OPTIONS_FILES)
+  LOCAL_REQUIRED_MODULES := asan.options $(ASAN_OPTIONS_FILES) $(ASAN_EXTRACT_FILES)
 endif
 
 EXPORT_GLOBAL_GCOV_OPTIONS :=
@@ -127,7 +77,8 @@
 #
 # create some directories (some are mount points) and symlinks
 LOCAL_POST_INSTALL_CMD := mkdir -p $(addprefix $(TARGET_ROOT_OUT)/, \
-    sbin dev proc sys system data oem acct config storage mnt root $(BOARD_ROOT_EXTRA_FOLDERS)); \
+    sbin dev proc sys system data odm oem acct config storage mnt $(BOARD_ROOT_EXTRA_FOLDERS)); \
+    ln -sf /system/bin $(TARGET_ROOT_OUT)/bin; \
     ln -sf /system/etc $(TARGET_ROOT_OUT)/etc; \
     ln -sf /data/user_de/0/com.android.shell/files/bugreports $(TARGET_ROOT_OUT)/bugreports; \
     ln -sf /sys/kernel/debug $(TARGET_ROOT_OUT)/d; \
@@ -137,6 +88,33 @@
 else
   LOCAL_POST_INSTALL_CMD += ; ln -sf /system/vendor $(TARGET_ROOT_OUT)/vendor
 endif
+ifdef BOARD_USES_PRODUCTIMAGE
+  LOCAL_POST_INSTALL_CMD += ; mkdir -p $(TARGET_ROOT_OUT)/product
+else
+  LOCAL_POST_INSTALL_CMD += ; ln -sf /system/product $(TARGET_ROOT_OUT)/product
+endif
+ifdef BOARD_USES_PRODUCT_SERVICESIMAGE
+  LOCAL_POST_INSTALL_CMD += ; mkdir -p $(TARGET_ROOT_OUT)/product-services
+else
+  LOCAL_POST_INSTALL_CMD += ; ln -sf /system/product-services $(TARGET_ROOT_OUT)/product-services
+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
@@ -173,4 +151,205 @@
 
 bcp_md5 :=
 bcp_dep :=
+
+# Append PLATFORM_VNDK_VERSION to base name.
+define append_vndk_version
+$(strip \
+  $(basename $(1)).$(PLATFORM_VNDK_VERSION)$(suffix $(1)) \
+)
+endef
+
+# Update namespace configuration file with library lists and VNDK version
+#
+# $(1): Input source file (ld.config.txt)
+# $(2): Output built module
+# $(3): VNDK version suffix
+# $(4): true if libz must be included in llndk not in vndk-sp
+define update_and_install_ld_config
+# If $(4) is true, move libz to llndk from vndk-sp.
+$(if $(filter true,$(4)),\
+  $(eval llndk_libraries_list := $(LLNDK_LIBRARIES) libz) \
+  $(eval vndksp_libraries_list := $(filter-out libz,$(VNDK_SAMEPROCESS_LIBRARIES))),\
+  $(eval llndk_libraries_list := $(LLNDK_LIBRARIES)) \
+  $(eval vndksp_libraries_list := $(VNDK_SAMEPROCESS_LIBRARIES)))
+
+llndk_libraries := $(call normalize-path-list,$(addsuffix .so,\
+  $(filter-out $(VNDK_PRIVATE_LIBRARIES),$(llndk_libraries_list))))
+private_llndk_libraries := $(call normalize-path-list,$(addsuffix .so,\
+  $(filter $(VNDK_PRIVATE_LIBRARIES),$(llndk_libraries_list))))
+vndk_sameprocess_libraries := $(call normalize-path-list,$(addsuffix .so,\
+  $(filter-out $(VNDK_PRIVATE_LIBRARIES),$(vndksp_libraries_list))))
+vndk_core_libraries := $(call normalize-path-list,$(addsuffix .so,\
+  $(filter-out $(VNDK_PRIVATE_LIBRARIES),$(VNDK_CORE_LIBRARIES))))
+sanitizer_runtime_libraries := $(call normalize-path-list,$(addsuffix .so,\
+  $(ADDRESS_SANITIZER_RUNTIME_LIBRARY) \
+  $(UBSAN_RUNTIME_LIBRARY) \
+  $(TSAN_RUNTIME_LIBRARY) \
+  $(2ND_ADDRESS_SANITIZER_RUNTIME_LIBRARY) \
+  $(2ND_UBSAN_RUNTIME_LIBRARY) \
+  $(2ND_TSAN_RUNTIME_LIBRARY)))
+# If BOARD_VNDK_VERSION is not defined, VNDK version suffix will not be used.
+vndk_version_suffix := $(if $(strip $(3)),-$(strip $(3)))
+
+$(2): PRIVATE_LLNDK_LIBRARIES := $$(llndk_libraries)
+$(2): PRIVATE_PRIVATE_LLNDK_LIBRARIES := $$(private_llndk_libraries)
+$(2): PRIVATE_VNDK_SAMEPROCESS_LIBRARIES := $$(vndk_sameprocess_libraries)
+$(2): PRIVATE_VNDK_CORE_LIBRARIES := $$(vndk_core_libraries)
+$(2): PRIVATE_SANITIZER_RUNTIME_LIBRARIES := $$(sanitizer_runtime_libraries)
+$(2): PRIVATE_VNDK_VERSION := $$(vndk_version_suffix)
+$(2): $(1)
+	@echo "Generate: $$< -> $$@"
+	@mkdir -p $$(dir $$@)
+	$$(hide) sed -e 's?%LLNDK_LIBRARIES%?$$(PRIVATE_LLNDK_LIBRARIES)?g' $$< >$$@
+	$$(hide) sed -i -e 's?%PRIVATE_LLNDK_LIBRARIES%?$$(PRIVATE_PRIVATE_LLNDK_LIBRARIES)?g' $$@
+	$$(hide) sed -i -e 's?%VNDK_SAMEPROCESS_LIBRARIES%?$$(PRIVATE_VNDK_SAMEPROCESS_LIBRARIES)?g' $$@
+	$$(hide) sed -i -e 's?%VNDK_CORE_LIBRARIES%?$$(PRIVATE_VNDK_CORE_LIBRARIES)?g' $$@
+	$$(hide) sed -i -e 's?%SANITIZER_RUNTIME_LIBRARIES%?$$(PRIVATE_SANITIZER_RUNTIME_LIBRARIES)?g' $$@
+	$$(hide) sed -i -e 's?%VNDK_VER%?$$(PRIVATE_VNDK_VERSION)?g' $$@
+	$$(hide) sed -i -e 's?%PRODUCT%?$$(TARGET_COPY_OUT_PRODUCT)?g' $$@
+	$$(hide) sed -i -e 's?%PRODUCTSERVICES%?$$(TARGET_COPY_OUT_PRODUCTSERVICES)?g' $$@
+
+llndk_libraries_list :=
+vndksp_libraries_list :=
+llndk_libraries :=
+private_llndk_libraries :=
+vndk_sameprocess_libraries :=
+vndk_core_libraries :=
+sanitizer_runtime_libraries :=
+vndk_version_suffix :=
+endef # update_and_install_ld_config
+
+
 #######################################
+# ld.config.txt selection variables
+#
+_enforce_vndk_at_runtime := false
+ifdef BOARD_VNDK_VERSION
+  ifneq ($(BOARD_VNDK_RUNTIME_DISABLE),true)
+    _enforce_vndk_at_runtime := true
+  endif
+endif
+
+_enforce_vndk_lite_at_runtime := false
+ifeq ($(_enforce_vndk_at_runtime),false)
+  ifeq ($(PRODUCT_TREBLE_LINKER_NAMESPACES)|$(SANITIZE_TARGET),true|)
+    _enforce_vndk_lite_at_runtime := true
+  endif
+endif
+
+#######################################
+# ld.config.txt
+#
+# For VNDK enforced devices that have defined BOARD_VNDK_VERSION, use
+# "ld.config.txt" as a source file. This configuration includes strict VNDK
+# run-time restrictions for vendor process.
+#
+# Other treblized devices, that have not defined BOARD_VNDK_VERSION or that
+# have set BOARD_VNDK_RUNTIME_DISABLE to true, use "ld.config.vndk_lite.txt"
+# as a source file. This configuration does not have strict VNDK run-time
+# restrictions.
+#
+# If the device is not treblized, use "ld.config.legacy.txt" for legacy
+# namespace configuration.
+#
+include $(CLEAR_VARS)
+LOCAL_MODULE := ld.config.txt
+LOCAL_MODULE_CLASS := ETC
+LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)
+
+ifeq ($(_enforce_vndk_at_runtime),true)
+
+# for VNDK enforced devices
+LOCAL_MODULE_STEM := $(call append_vndk_version,$(LOCAL_MODULE))
+include $(BUILD_SYSTEM)/base_rules.mk
+$(eval $(call update_and_install_ld_config,\
+  $(LOCAL_PATH)/etc/ld.config.txt,\
+  $(LOCAL_BUILT_MODULE),\
+  $(PLATFORM_VNDK_VERSION)))
+
+else ifeq ($(_enforce_vndk_lite_at_runtime),true)
+
+# for treblized but VNDK lightly enforced devices
+LOCAL_MODULE_STEM := ld.config.vndk_lite.txt
+include $(BUILD_SYSTEM)/base_rules.mk
+$(eval $(call update_and_install_ld_config,\
+  $(LOCAL_PATH)/etc/ld.config.vndk_lite.txt,\
+  $(LOCAL_BUILT_MODULE),\
+  $(PLATFORM_VNDK_VERSION),\
+  true))
+
+else
+
+# for legacy non-treblized devices
+LOCAL_MODULE_STEM := $(LOCAL_MODULE)
+LOCAL_SRC_FILES := etc/ld.config.legacy.txt
+include $(BUILD_PREBUILT)
+
+endif  # ifeq ($(_enforce_vndk_at_runtime),true)
+
+
+#######################################
+# ld.config.vndk_lite.txt
+#
+# This module is only for GSI.
+#
+ifeq ($(_enforce_vndk_lite_at_runtime),false)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := ld.config.vndk_lite.txt
+LOCAL_MODULE_CLASS := ETC
+LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)
+LOCAL_MODULE_STEM := $(LOCAL_MODULE)
+include $(BUILD_SYSTEM)/base_rules.mk
+$(eval $(call update_and_install_ld_config,\
+  $(LOCAL_PATH)/etc/ld.config.vndk_lite.txt,\
+  $(LOCAL_BUILT_MODULE),\
+  $(PLATFORM_VNDK_VERSION),\
+  true))
+
+endif  # ifeq ($(_enforce_vndk_lite_at_runtime),false)
+
+_enforce_vndk_at_runtime :=
+_enforce_vndk_lite_at_runtime :=
+
+#######################################
+# ld.config.txt for recovery
+include $(CLEAR_VARS)
+LOCAL_MODULE := ld.config.recovery.txt
+LOCAL_MODULE_CLASS := ETC
+LOCAL_SRC_FILES := etc/ld.config.recovery.txt
+LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/etc
+LOCAL_MODULE_STEM := ld.config.txt
+include $(BUILD_PREBUILT)
+
+#######################################
+# llndk.libraries.txt
+include $(CLEAR_VARS)
+LOCAL_MODULE := llndk.libraries.txt
+LOCAL_MODULE_CLASS := ETC
+LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)
+LOCAL_MODULE_STEM := $(call append_vndk_version,$(LOCAL_MODULE))
+include $(BUILD_SYSTEM)/base_rules.mk
+$(LOCAL_BUILT_MODULE): PRIVATE_LLNDK_LIBRARIES := $(LLNDK_LIBRARIES)
+$(LOCAL_BUILT_MODULE):
+	@echo "Generate: $@"
+	@mkdir -p $(dir $@)
+	$(hide) echo -n > $@
+	$(hide) $(foreach lib,$(PRIVATE_LLNDK_LIBRARIES), \
+		echo $(lib).so >> $@;)
+
+#######################################
+# vndksp.libraries.txt
+include $(CLEAR_VARS)
+LOCAL_MODULE := vndksp.libraries.txt
+LOCAL_MODULE_CLASS := ETC
+LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)
+LOCAL_MODULE_STEM := $(call append_vndk_version,$(LOCAL_MODULE))
+include $(BUILD_SYSTEM)/base_rules.mk
+$(LOCAL_BUILT_MODULE): PRIVATE_VNDK_SAMEPROCESS_LIBRARIES := $(VNDK_SAMEPROCESS_LIBRARIES)
+$(LOCAL_BUILT_MODULE):
+	@echo "Generate: $@"
+	@mkdir -p $(dir $@)
+	$(hide) echo -n > $@
+	$(hide) $(foreach lib,$(PRIVATE_VNDK_SAMEPROCESS_LIBRARIES), \
+		echo $(lib).so >> $@;)
diff --git a/rootdir/OWNERS b/rootdir/OWNERS
new file mode 100644
index 0000000..6029ae7
--- /dev/null
+++ b/rootdir/OWNERS
@@ -0,0 +1,4 @@
+jeffv@google.com
+jiyong@google.com
+smoreland@google.com
+tomcherry@google.com
diff --git a/rootdir/asan.options b/rootdir/asan.options
index d728f12..a264d2d 100644
--- a/rootdir/asan.options
+++ b/rootdir/asan.options
@@ -5,3 +5,4 @@
 detect_container_overflow=0
 abort_on_error=1
 include_if_exists=/system/asan.options.%b
+include_if_exists=/data/asan/system/asan.options.%b
diff --git a/rootdir/asan.options.off.template b/rootdir/asan.options.off.template
deleted file mode 100644
index 59a1249..0000000
--- a/rootdir/asan.options.off.template
+++ /dev/null
@@ -1,7 +0,0 @@
-quarantine_size_mb=0
-max_redzone=16
-poison_heap=false
-poison_partial=false
-poison_array_cookie=false
-alloc_dealloc_mismatch=false
-new_delete_type_mismatch=false
diff --git a/rootdir/asan_extract.rc b/rootdir/asan_extract.rc
new file mode 100644
index 0000000..4aea6a3
--- /dev/null
+++ b/rootdir/asan_extract.rc
@@ -0,0 +1,3 @@
+# When /data is available, look for /system/asan.tar.gz and potentially extract.
+on post-fs-data
+    exec - system system -- /system/bin/asan_extract
diff --git a/rootdir/asan_extract.sh b/rootdir/asan_extract.sh
new file mode 100644
index 0000000..2d72320
--- /dev/null
+++ b/rootdir/asan_extract.sh
@@ -0,0 +1,95 @@
+#!/system/bin/sh
+
+#
+# Copyright (C) 2017 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.
+#
+
+# This script will extract ASAN libraries from /system/asan.tar.gz to /data and then reboot.
+
+# TODO:
+#   * Timestamp or something to know when to run this again. Right now take the existence of
+#     /data/lib as we're already done.
+#   * Need to distinguish pre- from post-decryption for FDE.
+
+SRC=/system/asan.tar.bz2
+MD5_FILE=/data/asan.md5sum
+ASAN_DIR=/data/asan
+# Minimum /data size in blocks. Arbitrarily 512M.
+MIN_DATA_SIZE=131072
+
+# Checks for FDE pre-decrypt state.
+
+VOLD_STATUS=$(getprop vold.decrypt)
+if [ "$VOLD_STATUS" = "trigger_restart_min_framework" ] ; then
+  log -p i -t asan_install "Pre-decrypt FDE detected (by vold property)!"
+  exit 1
+fi
+
+STATFS_BLOCKS=$(stat -f -c '%b' /data)
+if [ "$STATFS_BLOCKS" -le "$MIN_DATA_SIZE" ] ; then
+  log -p i -t asan_install "Pre-decrypt FDE detected (by /data size)!"
+  exit 1
+fi
+
+# Check for ASAN source.
+
+if ! test -f $SRC ; then
+  log -p i -t asan_install "Did not find $SRC!"
+  exit 1
+fi
+
+log -p i -t asan_install "Found $SRC, checking whether we need to apply it."
+
+# Checksum check.
+
+ASAN_TAR_MD5=$(md5sum $SRC)
+if test -f $MD5_FILE ; then
+  INSTALLED_MD5=$(cat $MD5_FILE)
+  if [ "x$ASAN_TAR_MD5" = "x$INSTALLED_MD5" ] ; then
+    log -p i -t asan_install "Checksums match, nothing to be done here."
+    exit 0
+  fi
+fi
+
+# Actually apply the source.
+
+# Just clean up, helps with restorecon.
+rm -rf $ASAN_DIR
+
+log -p i -t asan_install "Untarring $SRC..."
+
+# Unzip from /system/asan.tar.gz into data. Need to pipe as gunzip is not on device.
+bzip2 -c -d $SRC | tar -x -f - --no-same-owner -C / || exit 1
+
+# Cannot log here, log would run with system_data_file.
+
+# Set correct permission bits.
+chmod -R 744 $ASAN_DIR
+cd $ASAN_DIR ; find . -type d -exec chmod 755 {} \;
+
+restorecon -R -F $ASAN_DIR/*/lib*
+
+log -p i -t asan_install "Fixed selinux labels..."
+
+
+# Now write down our checksum to mark the extraction complete.
+echo "$ASAN_TAR_MD5" > $MD5_FILE
+
+# We want to reboot now. It seems it is not possible to run "reboot" here, the device will
+# just be stuck.
+
+log -p i -t asan_install "Signaling init to reboot..."
+
+setprop sys.powerctl reboot
diff --git a/rootdir/etc/ld.config.legacy.txt b/rootdir/etc/ld.config.legacy.txt
new file mode 100644
index 0000000..ca6aafe
--- /dev/null
+++ b/rootdir/etc/ld.config.legacy.txt
@@ -0,0 +1,40 @@
+# Copyright (C) 2017 The Android Open Source Project
+#
+# Bionic loader config file.
+# This gives the exactly the same namespace setup in pre-O.
+#
+
+# All binaries gets the same configuration 'legacy'
+dir.legacy = /system
+dir.legacy = /vendor
+dir.legacy = /odm
+dir.legacy = /sbin
+
+# Except for /postinstall, where only /system is searched
+dir.postinstall = /postinstall
+
+[legacy]
+namespace.default.isolated = false
+
+namespace.default.search.paths  = /system/${LIB}
+namespace.default.search.paths += /vendor/${LIB}
+namespace.default.search.paths += /odm/${LIB}
+
+namespace.default.asan.search.paths  = /data/asan/system/${LIB}
+namespace.default.asan.search.paths +=           /system/${LIB}
+namespace.default.asan.search.paths += /data/asan/odm/${LIB}
+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.recovery.txt b/rootdir/etc/ld.config.recovery.txt
new file mode 100644
index 0000000..5d6c01a
--- /dev/null
+++ b/rootdir/etc/ld.config.recovery.txt
@@ -0,0 +1,9 @@
+# Copyright (C) 2018 The Android Open Source Project
+#
+# Bionic loader config file for recovery mode
+#
+
+dir.recovery = /system/bin
+
+[recovery]
+namespace.default.search.paths = /system/${LIB}
diff --git a/rootdir/etc/ld.config.txt b/rootdir/etc/ld.config.txt
index e3468ca..7d22a3a 100644
--- a/rootdir/etc/ld.config.txt
+++ b/rootdir/etc/ld.config.txt
@@ -3,45 +3,360 @@
 # Bionic loader config file.
 #
 
-#dir.vendor=/vendor/bin/
-#dir.system=/system/bin/
+# Don't change the order here. The first pattern that matches with the
+# absolute path of an executable is selected.
+dir.system = /system/bin/
+dir.system = /system/xbin/
+dir.system = /%PRODUCT%/bin/
+dir.system = /%PRODUCTSERVICES%/bin/
 
-[vendor]
+dir.vendor = /odm/bin/
+dir.vendor = /vendor/bin/
+dir.vendor = /data/nativetest/odm
+dir.vendor = /data/nativetest64/odm
+dir.vendor = /data/benchmarktest/odm
+dir.vendor = /data/benchmarktest64/odm
+dir.vendor = /data/nativetest/vendor
+dir.vendor = /data/nativetest64/vendor
+dir.vendor = /data/benchmarktest/vendor
+dir.vendor = /data/benchmarktest64/vendor
 
-# When this flag is set to true linker will
-# set target_sdk_version for this binary to
-# the version specified in <dirname>/.version
-# file, where <dirname> = dirname(executable_path)
-#
-# default value is false
-enable.target.sdk.version = true
+dir.system = /data/nativetest
+dir.system = /data/nativetest64
+dir.system = /data/benchmarktest
+dir.system = /data/benchmarktest64
 
-# There is always the default namespace no
-# need to mention it in this list
-additional.namespaces=system
-
-# Is the namespace isolated
-namespace.default.isolated = true
-namespace.default.search.paths = /vendor/${LIB}
-
-# TODO: property for asan search path?
-namespace.default.permitted.paths = /vendor/${LIB}
-namespace.default.asan.permitted.paths = /data/vendor/${LIB}
-namespace.default.links = system
-
-# Todo complete this list
-namespace.default.link.system.shared_libs = libc.so:libm.so:libdl.so:libstdc++.so
-
-namespace.system.isolated = true
-namespace.system.search.paths = /system/${LIB}:/system/${LIB}/framework
-namespace.system.permitted.paths = /system/${LIB}
+dir.postinstall = /postinstall
 
 [system]
+additional.namespaces = sphal,vndk,rs
+
+###############################################################################
+# "default" namespace
+#
+# Framework-side code runs in this namespace. Libs from /vendor partition
+# can't be loaded in this namespace.
+###############################################################################
 namespace.default.isolated = true
-namespace.default.search.paths = /system/${LIB}
-namespace.default.permitted.paths = /system/${LIB}
 
-# app_process will setup additional vendor namespace manually.
-# Note that zygote will need vendor namespace to setup list of public
-# libraries provided by vendors to apps.
+namespace.default.search.paths  = /system/${LIB}
+namespace.default.search.paths += /%PRODUCT%/${LIB}
+namespace.default.search.paths += /%PRODUCTSERVICES%/${LIB}
 
+# We can't have entire /system/${LIB} as permitted paths because doing so
+# makes it possible to load libs in /system/${LIB}/vndk* directories by
+# their absolute paths (e.g. dlopen("/system/lib/vndk/libbase.so");).
+# VNDK libs are built with previous versions of Android and thus must not be
+# loaded into this namespace where libs built with the current version of
+# Android are loaded. Mixing the two types of libs in the same namespace can
+# cause unexpected problem.
+namespace.default.permitted.paths  = /system/${LIB}/drm
+namespace.default.permitted.paths += /system/${LIB}/extractors
+namespace.default.permitted.paths += /system/${LIB}/hw
+namespace.default.permitted.paths += /%PRODUCT%/${LIB}
+namespace.default.permitted.paths += /%PRODUCTSERVICES%/${LIB}
+# These are where odex files are located. libart has to be able to dlopen the files
+namespace.default.permitted.paths += /system/framework
+namespace.default.permitted.paths += /system/app
+namespace.default.permitted.paths += /system/priv-app
+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
+namespace.default.permitted.paths += /%PRODUCT%/priv-app
+namespace.default.permitted.paths += /%PRODUCTSERVICES%/framework
+namespace.default.permitted.paths += /%PRODUCTSERVICES%/app
+namespace.default.permitted.paths += /%PRODUCTSERVICES%/priv-app
+namespace.default.permitted.paths += /data
+namespace.default.permitted.paths += /mnt/expand
+
+namespace.default.asan.search.paths  = /data/asan/system/${LIB}
+namespace.default.asan.search.paths +=           /system/${LIB}
+namespace.default.asan.search.paths += /data/asan/product/${LIB}
+namespace.default.asan.search.paths +=           /product/${LIB}
+namespace.default.asan.search.paths += /data/asan/product-services/${LIB}
+namespace.default.asan.search.paths +=           /product-services/${LIB}
+
+namespace.default.asan.permitted.paths  = /data
+namespace.default.asan.permitted.paths += /system/${LIB}/drm
+namespace.default.asan.permitted.paths += /system/${LIB}/extractors
+namespace.default.asan.permitted.paths += /system/${LIB}/hw
+namespace.default.asan.permitted.paths += /system/framework
+namespace.default.asan.permitted.paths += /system/app
+namespace.default.asan.permitted.paths += /system/priv-app
+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%/${LIB}
+namespace.default.asan.permitted.paths += /%PRODUCT%/framework
+namespace.default.asan.permitted.paths += /%PRODUCT%/app
+namespace.default.asan.permitted.paths += /%PRODUCT%/priv-app
+namespace.default.asan.permitted.paths += /%PRODUCTSERVICES%/${LIB}
+namespace.default.asan.permitted.paths += /%PRODUCTSERVICES%/framework
+namespace.default.asan.permitted.paths += /%PRODUCTSERVICES%/app
+namespace.default.asan.permitted.paths += /%PRODUCTSERVICES%/priv-app
+namespace.default.asan.permitted.paths += /mnt/expand
+
+###############################################################################
+# "sphal" namespace
+#
+# SP-HAL(Sameprocess-HAL)s are the only vendor libraries that are allowed to be
+# loaded inside system processes. libEGL_<chipset>.so, libGLESv2_<chipset>.so,
+# android.hardware.graphics.mapper@2.0-impl.so, etc are SP-HALs.
+#
+# This namespace is exclusivly for SP-HALs. When the framework tries to dynami-
+# cally load SP-HALs, android_dlopen_ext() is used to explicitly specifying
+# that they should be searched and loaded from this namespace.
+#
+# Note that there is no link from the default namespace to this namespace.
+###############################################################################
+namespace.sphal.isolated = true
+namespace.sphal.visible = true
+
+namespace.sphal.search.paths  = /odm/${LIB}
+namespace.sphal.search.paths += /vendor/${LIB}
+
+namespace.sphal.permitted.paths  = /odm/${LIB}
+namespace.sphal.permitted.paths += /vendor/${LIB}
+
+namespace.sphal.asan.search.paths  = /data/asan/odm/${LIB}
+namespace.sphal.asan.search.paths +=           /odm/${LIB}
+namespace.sphal.asan.search.paths += /data/asan/vendor/${LIB}
+namespace.sphal.asan.search.paths +=           /vendor/${LIB}
+
+namespace.sphal.asan.permitted.paths  = /data/asan/odm/${LIB}
+namespace.sphal.asan.permitted.paths +=           /odm/${LIB}
+namespace.sphal.asan.permitted.paths += /data/asan/vendor/${LIB}
+namespace.sphal.asan.permitted.paths +=           /vendor/${LIB}
+
+# Once in this namespace, access to libraries in /system/lib is restricted. Only
+# libs listed here can be used.
+namespace.sphal.links = default,vndk,rs
+
+namespace.sphal.link.default.shared_libs  = %LLNDK_LIBRARIES%
+namespace.sphal.link.default.shared_libs += %SANITIZER_RUNTIME_LIBRARIES%
+
+namespace.sphal.link.vndk.shared_libs = %VNDK_SAMEPROCESS_LIBRARIES%
+
+# Renderscript gets separate namespace
+namespace.sphal.link.rs.shared_libs = libRS_internal.so
+
+###############################################################################
+# "rs" namespace
+#
+# This namespace is exclusively for Renderscript internal libraries.
+# This namespace has slightly looser restriction than the vndk namespace because
+# of the genuine characteristics of Renderscript; /data is in the permitted path
+# to load the compiled *.so file and libmediandk.so can be used here.
+###############################################################################
+namespace.rs.isolated = true
+namespace.rs.visible = true
+
+namespace.rs.search.paths  = /odm/${LIB}/vndk-sp
+namespace.rs.search.paths += /vendor/${LIB}/vndk-sp
+namespace.rs.search.paths += /system/${LIB}/vndk-sp%VNDK_VER%
+namespace.rs.search.paths += /odm/${LIB}
+namespace.rs.search.paths += /vendor/${LIB}
+
+namespace.rs.permitted.paths  = /odm/${LIB}
+namespace.rs.permitted.paths += /vendor/${LIB}
+namespace.rs.permitted.paths += /data
+
+namespace.rs.asan.search.paths  = /data/asan/odm/${LIB}/vndk-sp
+namespace.rs.asan.search.paths +=           /odm/${LIB}/vndk-sp
+namespace.rs.asan.search.paths += /data/asan/vendor/${LIB}/vndk-sp
+namespace.rs.asan.search.paths +=           /vendor/${LIB}/vndk-sp
+namespace.rs.asan.search.paths += /data/asan/system/${LIB}/vndk-sp%VNDK_VER%
+namespace.rs.asan.search.paths +=           /system/${LIB}/vndk-sp%VNDK_VER%
+namespace.rs.asan.search.paths += /data/asan/odm/${LIB}
+namespace.rs.asan.search.paths +=           /odm/${LIB}
+namespace.rs.asan.search.paths += /data/asan/vendor/${LIB}
+namespace.rs.asan.search.paths +=           /vendor/${LIB}
+
+namespace.rs.asan.permitted.paths  = /data/asan/odm/${LIB}
+namespace.rs.asan.permitted.paths +=           /odm/${LIB}
+namespace.rs.asan.permitted.paths += /data/asan/vendor/${LIB}
+namespace.rs.asan.permitted.paths +=           /vendor/${LIB}
+namespace.rs.asan.permitted.paths += /data
+
+namespace.rs.links = default,vndk
+
+namespace.rs.link.default.shared_libs  =  %LLNDK_LIBRARIES%
+namespace.rs.link.default.shared_libs += %SANITIZER_RUNTIME_LIBRARIES%
+# Private LLNDK libs (e.g. libft2.so) are exceptionally allowed to this
+# namespace because RS framework libs are using them.
+namespace.rs.link.default.shared_libs += %PRIVATE_LLNDK_LIBRARIES%
+
+namespace.rs.link.vndk.shared_libs = %VNDK_SAMEPROCESS_LIBRARIES%
+
+###############################################################################
+# "vndk" namespace
+#
+# This namespace is exclusively for vndk-sp libs.
+###############################################################################
+namespace.vndk.isolated = true
+namespace.vndk.visible = true
+
+namespace.vndk.search.paths  = /odm/${LIB}/vndk-sp
+namespace.vndk.search.paths += /vendor/${LIB}/vndk-sp
+namespace.vndk.search.paths += /system/${LIB}/vndk-sp%VNDK_VER%
+
+namespace.vndk.permitted.paths  = /odm/${LIB}/hw
+namespace.vndk.permitted.paths += /odm/${LIB}/egl
+namespace.vndk.permitted.paths += /vendor/${LIB}/hw
+namespace.vndk.permitted.paths += /vendor/${LIB}/egl
+# This is exceptionally required since android.hidl.memory@1.0-impl.so is here
+namespace.vndk.permitted.paths += /system/${LIB}/vndk-sp%VNDK_VER%/hw
+
+namespace.vndk.asan.search.paths  = /data/asan/odm/${LIB}/vndk-sp
+namespace.vndk.asan.search.paths +=           /odm/${LIB}/vndk-sp
+namespace.vndk.asan.search.paths += /data/asan/vendor/${LIB}/vndk-sp
+namespace.vndk.asan.search.paths +=           /vendor/${LIB}/vndk-sp
+namespace.vndk.asan.search.paths += /data/asan/system/${LIB}/vndk-sp%VNDK_VER%
+namespace.vndk.asan.search.paths +=           /system/${LIB}/vndk-sp%VNDK_VER%
+
+namespace.vndk.asan.permitted.paths  = /data/asan/odm/${LIB}/hw
+namespace.vndk.asan.permitted.paths +=           /odm/${LIB}/hw
+namespace.vndk.asan.permitted.paths += /data/asan/odm/${LIB}/egl
+namespace.vndk.asan.permitted.paths +=           /odm/${LIB}/egl
+namespace.vndk.asan.permitted.paths += /data/asan/vendor/${LIB}/hw
+namespace.vndk.asan.permitted.paths +=           /vendor/${LIB}/hw
+namespace.vndk.asan.permitted.paths += /data/asan/vendor/${LIB}/egl
+namespace.vndk.asan.permitted.paths +=           /vendor/${LIB}/egl
+
+namespace.vndk.asan.permitted.paths += /data/asan/system/${LIB}/vndk-sp%VNDK_VER%/hw
+namespace.vndk.asan.permitted.paths +=           /system/${LIB}/vndk-sp%VNDK_VER%/hw
+
+# The "vndk" namespace links to "default" namespace for LLNDK libs and links to
+# "sphal" namespace for vendor libs.  The ordering matters.  The "default"
+# namespace has higher priority than the "sphal" namespace.
+namespace.vndk.links = default,sphal
+
+# When these NDK libs are required inside this namespace, then it is redirected
+# to the default namespace. This is possible since their ABI is stable across
+# Android releases.
+namespace.vndk.link.default.shared_libs  = %LLNDK_LIBRARIES%
+namespace.vndk.link.default.shared_libs += %SANITIZER_RUNTIME_LIBRARIES%
+
+# Allow VNDK-SP extensions to use vendor libraries
+namespace.vndk.link.sphal.allow_all_shared_libs = true
+
+###############################################################################
+# Namespace config for vendor processes. In O, no restriction is enforced for
+# them. However, in O-MR1, access to /system/${LIB} will not be allowed to
+# the default namespace. 'system' namespace will be added to give limited
+# (LL-NDK only) access.
+###############################################################################
+[vendor]
+additional.namespaces = system,vndk
+
+###############################################################################
+# "default" namespace
+#
+# This is the default linker namespace for a vendor process (a process started
+# from /vendor/bin/*). The main executable and the libs under /vendor/lib[64]
+# are loaded directly into this namespace. However, other libs under the system
+# partition (VNDK and LLNDK libraries) are not loaded here but from the
+# separate namespace 'system'. The delegation to the system namespace is done
+# via the 'namespace.default.link.system.shared_libs' property below.
+###############################################################################
+namespace.default.isolated = true
+namespace.default.visible = true
+
+namespace.default.search.paths  = /odm/${LIB}
+namespace.default.search.paths += /vendor/${LIB}
+
+namespace.default.permitted.paths  = /odm
+namespace.default.permitted.paths += /vendor
+
+namespace.default.asan.search.paths  = /data/asan/odm/${LIB}
+namespace.default.asan.search.paths +=           /odm/${LIB}
+namespace.default.asan.search.paths += /data/asan/vendor/${LIB}
+namespace.default.asan.search.paths +=           /vendor/${LIB}
+
+namespace.default.asan.permitted.paths  = /data/asan/odm
+namespace.default.asan.permitted.paths +=           /odm
+namespace.default.asan.permitted.paths += /data/asan/vendor
+namespace.default.asan.permitted.paths +=           /vendor
+
+namespace.default.links = system,vndk
+namespace.default.link.system.shared_libs = %LLNDK_LIBRARIES%
+namespace.default.link.vndk.shared_libs  = %VNDK_SAMEPROCESS_LIBRARIES%
+namespace.default.link.vndk.shared_libs += %VNDK_CORE_LIBRARIES%
+
+###############################################################################
+# "vndk" namespace
+#
+# This namespace is where VNDK and VNDK-SP libraries are loaded for
+# a vendor process.
+###############################################################################
+namespace.vndk.isolated = false
+
+namespace.vndk.search.paths  = /odm/${LIB}/vndk
+namespace.vndk.search.paths += /odm/${LIB}/vndk-sp
+namespace.vndk.search.paths += /vendor/${LIB}/vndk
+namespace.vndk.search.paths += /vendor/${LIB}/vndk-sp
+namespace.vndk.search.paths += /system/${LIB}/vndk-sp%VNDK_VER%
+namespace.vndk.search.paths += /system/${LIB}/vndk%VNDK_VER%
+
+namespace.vndk.asan.search.paths  = /data/asan/odm/${LIB}/vndk
+namespace.vndk.asan.search.paths +=           /odm/${LIB}/vndk
+namespace.vndk.asan.search.paths += /data/asan/odm/${LIB}/vndk-sp
+namespace.vndk.asan.search.paths +=           /odm/${LIB}/vndk-sp
+namespace.vndk.asan.search.paths += /data/asan/vendor/${LIB}/vndk
+namespace.vndk.asan.search.paths +=           /vendor/${LIB}/vndk
+namespace.vndk.asan.search.paths += /data/asan/vendor/${LIB}/vndk-sp
+namespace.vndk.asan.search.paths +=           /vendor/${LIB}/vndk-sp
+namespace.vndk.asan.search.paths += /data/asan/system/${LIB}/vndk-sp%VNDK_VER%
+namespace.vndk.asan.search.paths +=           /system/${LIB}/vndk-sp%VNDK_VER%
+namespace.vndk.asan.search.paths += /data/asan/system/${LIB}/vndk%VNDK_VER%
+namespace.vndk.asan.search.paths +=           /system/${LIB}/vndk%VNDK_VER%
+
+# When these NDK libs are required inside this namespace, then it is redirected
+# to the system namespace. This is possible since their ABI is stable across
+# Android releases.
+namespace.vndk.links = system,default
+namespace.vndk.link.system.shared_libs  = %LLNDK_LIBRARIES%
+namespace.vndk.link.system.shared_libs += %SANITIZER_RUNTIME_LIBRARIES%
+
+namespace.vndk.link.default.allow_all_shared_libs = true
+
+###############################################################################
+# "system" namespace
+#
+# This namespace is where system libs (VNDK and LLNDK libs) are loaded for
+# a vendor process.
+###############################################################################
+namespace.system.isolated = false
+
+namespace.system.search.paths  = /system/${LIB}
+namespace.system.search.paths += /%PRODUCT%/${LIB}
+namespace.system.search.paths += /%PRODUCTSERVICES%/${LIB}
+
+namespace.system.asan.search.paths  = /data/asan/system/${LIB}
+namespace.system.asan.search.paths +=           /system/${LIB}
+namespace.system.asan.search.paths += /data/asan/product/${LIB}
+namespace.system.asan.search.paths +=           /product/${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}
+namespace.default.search.paths += /%PRODUCT%/${LIB}
+namespace.default.search.paths += /%PRODUCTSERVICES%/${LIB}
diff --git a/rootdir/etc/ld.config.vndk_lite.txt b/rootdir/etc/ld.config.vndk_lite.txt
new file mode 100644
index 0000000..db65c14
--- /dev/null
+++ b/rootdir/etc/ld.config.vndk_lite.txt
@@ -0,0 +1,246 @@
+# Copyright (C) 2017 The Android Open Source Project
+#
+# Bionic loader config file.
+#
+
+# Don't change the order here. The first pattern that matches with the
+# absolute path of an executable is selected.
+dir.system = /system/bin/
+dir.system = /system/xbin/
+dir.system = /product/bin/
+
+dir.vendor = /odm/bin/
+dir.vendor = /vendor/bin/
+dir.vendor = /data/nativetest/odm
+dir.vendor = /data/nativetest64/odm
+dir.vendor = /data/benchmarktest/odm
+dir.vendor = /data/benchmarktest64/odm
+dir.vendor = /data/nativetest/vendor
+dir.vendor = /data/nativetest64/vendor
+dir.vendor = /data/benchmarktest/vendor
+dir.vendor = /data/benchmarktest64/vendor
+
+dir.system = /data/nativetest
+dir.system = /data/nativetest64
+dir.system = /data/benchmarktest
+dir.system = /data/benchmarktest64
+
+dir.postinstall = /postinstall
+
+[system]
+additional.namespaces = sphal,vndk,rs
+
+###############################################################################
+# "default" namespace
+#
+# Framework-side code runs in this namespace. However, libs from other
+# partitions are also allowed temporarily.
+###############################################################################
+namespace.default.isolated = false
+
+namespace.default.search.paths  = /system/${LIB}
+namespace.default.search.paths += /odm/${LIB}
+namespace.default.search.paths += /vendor/${LIB}
+namespace.default.search.paths += /product/${LIB}
+
+namespace.default.asan.search.paths  = /data/asan/system/${LIB}
+namespace.default.asan.search.paths +=           /system/${LIB}
+namespace.default.asan.search.paths += /data/asan/odm/${LIB}
+namespace.default.asan.search.paths +=           /odm/${LIB}
+namespace.default.asan.search.paths += /data/asan/vendor/${LIB}
+namespace.default.asan.search.paths +=           /vendor/${LIB}
+namespace.default.asan.search.paths += /data/asan/product/${LIB}
+namespace.default.asan.search.paths +=           /product/${LIB}
+
+###############################################################################
+# "sphal" namespace
+#
+# SP-HAL(Sameprocess-HAL)s are the only vendor libraries that are allowed to be
+# loaded inside system processes. libEGL_<chipset>.so, libGLESv2_<chipset>.so,
+# android.hardware.graphics.mapper@2.0-impl.so, etc are SP-HALs.
+#
+# This namespace is exclusivly for SP-HALs. When the framework tries to dynami-
+# cally load SP-HALs, android_dlopen_ext() is used to explicitly specifying
+# that they should be searched and loaded from this namespace.
+#
+# Note that there is no link from the default namespace to this namespace.
+###############################################################################
+namespace.sphal.isolated = true
+namespace.sphal.visible = true
+
+namespace.sphal.search.paths  = /odm/${LIB}
+namespace.sphal.search.paths += /vendor/${LIB}
+
+namespace.sphal.permitted.paths  = /odm/${LIB}
+namespace.sphal.permitted.paths += /vendor/${LIB}
+
+namespace.sphal.asan.search.paths  = /data/asan/odm/${LIB}
+namespace.sphal.asan.search.paths +=           /odm/${LIB}
+namespace.sphal.asan.search.paths += /data/asan/vendor/${LIB}
+namespace.sphal.asan.search.paths +=           /vendor/${LIB}
+
+namespace.sphal.asan.permitted.paths  = /data/asan/odm/${LIB}
+namespace.sphal.asan.permitted.paths +=           /odm/${LIB}
+namespace.sphal.asan.permitted.paths += /data/asan/vendor/${LIB}
+namespace.sphal.asan.permitted.paths +=           /vendor/${LIB}
+
+# Once in this namespace, access to libraries in /system/lib is restricted. Only
+# libs listed here can be used.
+namespace.sphal.links = default,vndk,rs
+
+namespace.sphal.link.default.shared_libs  = %LLNDK_LIBRARIES%
+namespace.sphal.link.default.shared_libs += %SANITIZER_RUNTIME_LIBRARIES%
+
+namespace.sphal.link.vndk.shared_libs = %VNDK_SAMEPROCESS_LIBRARIES%
+
+# Renderscript gets separate namespace
+namespace.sphal.link.rs.shared_libs = libRS_internal.so
+
+###############################################################################
+# "rs" namespace
+#
+# This namespace is exclusively for Renderscript internal libraries.
+# This namespace has slightly looser restriction than the vndk namespace because
+# of the genuine characteristics of Renderscript; /data is in the permitted path
+# to load the compiled *.so file and libmediandk.so can be used here.
+###############################################################################
+namespace.rs.isolated = true
+namespace.rs.visible = true
+
+namespace.rs.search.paths  = /odm/${LIB}/vndk-sp
+namespace.rs.search.paths += /vendor/${LIB}/vndk-sp
+namespace.rs.search.paths += /system/${LIB}/vndk-sp%VNDK_VER%
+namespace.rs.search.paths += /odm/${LIB}
+namespace.rs.search.paths += /vendor/${LIB}
+
+namespace.rs.permitted.paths  = /odm/${LIB}
+namespace.rs.permitted.paths += /vendor/${LIB}
+namespace.rs.permitted.paths += /data
+
+namespace.rs.asan.search.paths  = /data/asan/odm/${LIB}/vndk-sp
+namespace.rs.asan.search.paths +=           /odm/${LIB}/vndk-sp
+namespace.rs.asan.search.paths += /data/asan/vendor/${LIB}/vndk-sp
+namespace.rs.asan.search.paths +=           /vendor/${LIB}/vndk-sp
+namespace.rs.asan.search.paths += /data/asan/system/${LIB}/vndk-sp%VNDK_VER%
+namespace.rs.asan.search.paths +=           /system/${LIB}/vndk-sp%VNDK_VER%
+namespace.rs.asan.search.paths += /data/asan/odm/${LIB}
+namespace.rs.asan.search.paths +=           /odm/${LIB}
+namespace.rs.asan.search.paths += /data/asan/vendor/${LIB}
+namespace.rs.asan.search.paths +=           /vendor/${LIB}
+
+namespace.rs.asan.permitted.paths  = /data/asan/odm/${LIB}
+namespace.rs.asan.permitted.paths +=           /odm/${LIB}
+namespace.rs.asan.permitted.paths += /data/asan/vendor/${LIB}
+namespace.rs.asan.permitted.paths +=           /vendor/${LIB}
+namespace.rs.asan.permitted.paths += /data
+
+namespace.rs.links = default,vndk
+
+namespace.rs.link.default.shared_libs  =  %LLNDK_LIBRARIES%
+namespace.rs.link.default.shared_libs += %SANITIZER_RUNTIME_LIBRARIES%
+# Private LLNDK libs (e.g. libft2.so) are exceptionally allowed to this
+# namespace because RS framework libs are using them.
+namespace.rs.link.default.shared_libs += %PRIVATE_LLNDK_LIBRARIES%
+
+namespace.rs.link.vndk.shared_libs = %VNDK_SAMEPROCESS_LIBRARIES%
+
+###############################################################################
+# "vndk" namespace
+#
+# This namespace is exclusively for vndk-sp libs.
+###############################################################################
+namespace.vndk.isolated = true
+namespace.vndk.visible = true
+
+namespace.vndk.search.paths  = /odm/${LIB}/vndk-sp
+namespace.vndk.search.paths += /vendor/${LIB}/vndk-sp
+namespace.vndk.search.paths += /system/${LIB}/vndk-sp%VNDK_VER%
+
+namespace.vndk.permitted.paths  = /odm/${LIB}/hw
+namespace.vndk.permitted.paths += /odm/${LIB}/egl
+namespace.vndk.permitted.paths += /vendor/${LIB}/hw
+namespace.vndk.permitted.paths += /vendor/${LIB}/egl
+# This is exceptionally required since android.hidl.memory@1.0-impl.so is here
+namespace.vndk.permitted.paths += /system/${LIB}/vndk-sp%VNDK_VER%/hw
+
+namespace.vndk.asan.search.paths  = /data/asan/odm/${LIB}/vndk-sp
+namespace.vndk.asan.search.paths +=           /odm/${LIB}/vndk-sp
+namespace.vndk.asan.search.paths += /data/asan/vendor/${LIB}/vndk-sp
+namespace.vndk.asan.search.paths +=           /vendor/${LIB}/vndk-sp
+namespace.vndk.asan.search.paths += /data/asan/system/${LIB}/vndk-sp%VNDK_VER%
+namespace.vndk.asan.search.paths +=           /system/${LIB}/vndk-sp%VNDK_VER%
+
+namespace.vndk.asan.permitted.paths  = /data/asan/odm/${LIB}/hw
+namespace.vndk.asan.permitted.paths +=           /odm/${LIB}/hw
+namespace.vndk.asan.permitted.paths += /data/asan/odm/${LIB}/egl
+namespace.vndk.asan.permitted.paths +=           /odm/${LIB}/egl
+namespace.vndk.asan.permitted.paths += /data/asan/vendor/${LIB}/hw
+namespace.vndk.asan.permitted.paths +=           /vendor/${LIB}/hw
+namespace.vndk.asan.permitted.paths += /data/asan/vendor/${LIB}/egl
+namespace.vndk.asan.permitted.paths +=           /vendor/${LIB}/egl
+
+namespace.vndk.asan.permitted.paths += /data/asan/system/${LIB}/vndk-sp%VNDK_VER%/hw
+namespace.vndk.asan.permitted.paths +=           /system/${LIB}/vndk-sp%VNDK_VER%/hw
+
+# When these NDK libs are required inside this namespace, then it is redirected
+# to the default namespace. This is possible since their ABI is stable across
+# Android releases.
+namespace.vndk.links = default
+namespace.vndk.link.default.shared_libs  = %LLNDK_LIBRARIES%
+namespace.vndk.link.default.shared_libs += %SANITIZER_RUNTIME_LIBRARIES%
+
+###############################################################################
+# Namespace config for vendor processes. In O, no restriction is enforced for
+# them. However, in O-MR1, access to /system/${LIB} will not be allowed to
+# the default namespace. 'system' namespace will be added to give limited
+# (LL-NDK only) access.
+###############################################################################
+[vendor]
+namespace.default.isolated = false
+
+namespace.default.search.paths  = /odm/${LIB}
+namespace.default.search.paths += /odm/${LIB}/vndk
+namespace.default.search.paths += /odm/${LIB}/vndk-sp
+namespace.default.search.paths += /vendor/${LIB}
+namespace.default.search.paths += /vendor/${LIB}/vndk
+namespace.default.search.paths += /vendor/${LIB}/vndk-sp
+
+# Access to system libraries are allowed
+namespace.default.search.paths += /system/${LIB}/vndk%VNDK_VER%
+namespace.default.search.paths += /system/${LIB}/vndk-sp%VNDK_VER%
+namespace.default.search.paths += /system/${LIB}
+namespace.default.search.paths += /product/${LIB}
+
+namespace.default.asan.search.paths  = /data/asan/odm/${LIB}
+namespace.default.asan.search.paths +=           /odm/${LIB}
+namespace.default.asan.search.paths += /data/asan/odm/${LIB}/vndk
+namespace.default.asan.search.paths +=           /odm/${LIB}/vndk
+namespace.default.asan.search.paths += /data/asan/odm/${LIB}/vndk-sp
+namespace.default.asan.search.paths +=           /odm/${LIB}/vndk-sp
+namespace.default.asan.search.paths += /data/asan/vendor/${LIB}
+namespace.default.asan.search.paths +=           /vendor/${LIB}
+namespace.default.asan.search.paths += /data/asan/vendor/${LIB}/vndk
+namespace.default.asan.search.paths +=           /vendor/${LIB}/vndk
+namespace.default.asan.search.paths += /data/asan/vendor/${LIB}/vndk-sp
+namespace.default.asan.search.paths +=           /vendor/${LIB}/vndk-sp
+namespace.default.asan.search.paths += /data/asan/system/${LIB}/vndk%VNDK_VER%
+namespace.default.asan.search.paths +=           /system/${LIB}/vndk%VNDK_VER%
+namespace.default.asan.search.paths += /data/asan/system/${LIB}/vndk-sp%VNDK_VER%
+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.default.asan.search.paths += /data/asan/product/${LIB}
+namespace.default.asan.search.paths +=           /product/${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}
+namespace.default.search.paths += /product/${LIB}
diff --git a/rootdir/etc/public.libraries.android.txt b/rootdir/etc/public.libraries.android.txt
index e6c94ff..e20b95d 100644
--- a/rootdir/etc/public.libraries.android.txt
+++ b/rootdir/etc/public.libraries.android.txt
@@ -1,4 +1,6 @@
+# See https://android.googlesource.com/platform/ndk/+/master/docs/PlatformApis.md
 libandroid.so
+libaaudio.so
 libc.so
 libcamera2ndk.so
 libdl.so
@@ -12,10 +14,13 @@
 liblog.so
 libmediandk.so
 libm.so
+libnativewindow.so
+libneuralnetworks.so
 libOpenMAXAL.so
 libOpenSLES.so
 libRS.so
 libstdc++.so
+libsync.so
 libvulkan.so
 libwebviewchromium_plat_support.so
 libz.so
diff --git a/rootdir/etc/public.libraries.iot.txt b/rootdir/etc/public.libraries.iot.txt
new file mode 100644
index 0000000..ff0813d
--- /dev/null
+++ b/rootdir/etc/public.libraries.iot.txt
@@ -0,0 +1,27 @@
+# See https://android.googlesource.com/platform/ndk/+/master/docs/PlatformApis.md
+libandroid.so
+libandroidthings.so
+libaaudio.so
+libc.so
+libcamera2ndk.so
+libdl.so
+libEGL.so
+libGLESv1_CM.so
+libGLESv2.so
+libGLESv3.so
+libicui18n.so
+libicuuc.so
+libjnigraphics.so
+liblog.so
+libmediandk.so
+libm.so
+libnativewindow.so
+libneuralnetworks.so
+libOpenMAXAL.so
+libOpenSLES.so
+libRS.so
+libstdc++.so
+libsync.so
+libvulkan.so
+libwebviewchromium_plat_support.so
+libz.so
diff --git a/rootdir/etc/public.libraries.wear.txt b/rootdir/etc/public.libraries.wear.txt
index 292730a..3c46094 100644
--- a/rootdir/etc/public.libraries.wear.txt
+++ b/rootdir/etc/public.libraries.wear.txt
@@ -1,4 +1,6 @@
+# See https://android.googlesource.com/platform/ndk/+/master/docs/PlatformApis.md
 libandroid.so
+libaaudio.so
 libc.so
 libcamera2ndk.so
 libdl.so
@@ -12,9 +14,12 @@
 liblog.so
 libmediandk.so
 libm.so
+libnativewindow.so
+libneuralnetworks.so
 libOpenMAXAL.so
 libOpenSLES.so
 libRS.so
 libstdc++.so
+libsync.so
 libvulkan.so
 libz.so
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 84d0f87..486d096 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -21,21 +21,24 @@
     # Set the security context of /adb_keys if present.
     restorecon /adb_keys
 
-    # Shouldn't be necessary, but sdcard won't start without it. http://b/22568628.
-    mkdir /mnt 0775 root system
-
     # Set the security context of /postinstall if present.
     restorecon /postinstall
 
     # Mount cgroup mount point for cpu accounting
-    mount cgroup none /acct cpuacct
+    mount cgroup none /acct nodev noexec nosuid cpuacct
+    chmod 0555 /acct
     mkdir /acct/uid
 
     # root memory control cgroup, used by lmkd
     mkdir /dev/memcg 0700 root system
-    mount cgroup none /dev/memcg memory
+    mount cgroup none /dev/memcg nodev noexec nosuid memory
+    # memory.pressure_level used by lmkd
+    chown root system /dev/memcg/memory.pressure_level
+    chmod 0040 /dev/memcg/memory.pressure_level
     # app mem cgroups, used by activity manager, lmkd and zygote
     mkdir /dev/memcg/apps/ 0755 system system
+    # cgroup for system_server and surfaceflinger
+    mkdir /dev/memcg/system 0550 system system
 
     start ueventd
 
@@ -46,8 +49,10 @@
     copy /proc/cmdline /dev/urandom
     copy /default.prop /dev/urandom
 
-    # Backward compatibility.
+    symlink /system/bin /bin
     symlink /system/etc /etc
+
+    # Backward compatibility.
     symlink /sys/kernel/debug /d
 
     # Link /vendor to /system/vendor for devices without a vendor partition.
@@ -55,29 +60,30 @@
 
     # Create energy-aware scheduler tuning nodes
     mkdir /dev/stune
-    mount cgroup none /dev/stune schedtune
+    mount cgroup none /dev/stune nodev noexec nosuid schedtune
     mkdir /dev/stune/foreground
     mkdir /dev/stune/background
     mkdir /dev/stune/top-app
+    mkdir /dev/stune/rt
     chown system system /dev/stune
     chown system system /dev/stune/foreground
     chown system system /dev/stune/background
     chown system system /dev/stune/top-app
+    chown system system /dev/stune/rt
     chown system system /dev/stune/tasks
     chown system system /dev/stune/foreground/tasks
     chown system system /dev/stune/background/tasks
     chown system system /dev/stune/top-app/tasks
+    chown system system /dev/stune/rt/tasks
     chmod 0664 /dev/stune/tasks
     chmod 0664 /dev/stune/foreground/tasks
     chmod 0664 /dev/stune/background/tasks
     chmod 0664 /dev/stune/top-app/tasks
+    chmod 0664 /dev/stune/rt/tasks
 
-    # Mount staging areas for devices managed by vold
-    # See storage config details at http://source.android.com/tech/storage/
-    mount tmpfs tmpfs /mnt mode=0755,uid=0,gid=1000
     restorecon_recursive /mnt
 
-    mount configfs none /config
+    mount configfs none /config nodev noexec nosuid
     chmod 0775 /config/sdcardfs
     chown system package_info /config/sdcardfs
 
@@ -142,9 +148,12 @@
     write /proc/sys/net/ipv4/conf/all/accept_redirects 0
     write /proc/sys/net/ipv6/conf/all/accept_redirects 0
 
+    # /proc/net/fib_trie leaks interface IP addresses
+    chmod 0400 /proc/net/fib_trie
+
     # Create cgroup mount points for process groups
     mkdir /dev/cpuctl
-    mount cgroup none /dev/cpuctl cpu
+    mount cgroup none /dev/cpuctl nodev noexec nosuid cpu
     chown system system /dev/cpuctl
     chown system system /dev/cpuctl/tasks
     chmod 0666 /dev/cpuctl/tasks
@@ -153,16 +162,13 @@
 
     # sets up initial cpusets for ActivityManager
     mkdir /dev/cpuset
-    mount cpuset none /dev/cpuset
+    mount cpuset none /dev/cpuset nodev noexec nosuid
 
     # this ensures that the cpusets are present and usable, but the device's
     # init.rc must actually set the correct cpus
     mkdir /dev/cpuset/foreground
     copy /dev/cpuset/cpus /dev/cpuset/foreground/cpus
     copy /dev/cpuset/mems /dev/cpuset/foreground/mems
-    mkdir /dev/cpuset/foreground/boost
-    copy /dev/cpuset/cpus /dev/cpuset/foreground/boost/cpus
-    copy /dev/cpuset/mems /dev/cpuset/foreground/boost/mems
     mkdir /dev/cpuset/background
     copy /dev/cpuset/cpus /dev/cpuset/background/cpus
     copy /dev/cpuset/mems /dev/cpuset/background/mems
@@ -181,13 +187,11 @@
     # change permissions for all cpusets we'll touch at runtime
     chown system system /dev/cpuset
     chown system system /dev/cpuset/foreground
-    chown system system /dev/cpuset/foreground/boost
     chown system system /dev/cpuset/background
     chown system system /dev/cpuset/system-background
     chown system system /dev/cpuset/top-app
     chown system system /dev/cpuset/tasks
     chown system system /dev/cpuset/foreground/tasks
-    chown system system /dev/cpuset/foreground/boost/tasks
     chown system system /dev/cpuset/background/tasks
     chown system system /dev/cpuset/system-background/tasks
     chown system system /dev/cpuset/top-app/tasks
@@ -196,7 +200,6 @@
     chmod 0775 /dev/cpuset/system-background
 
     chmod 0664 /dev/cpuset/foreground/tasks
-    chmod 0664 /dev/cpuset/foreground/boost/tasks
     chmod 0664 /dev/cpuset/background/tasks
     chmod 0664 /dev/cpuset/system-background/tasks
     chmod 0664 /dev/cpuset/top-app/tasks
@@ -213,12 +216,20 @@
     # This is needed by any process that uses socket tagging.
     chmod 0644 /dev/xt_qtaguid
 
+    mkdir /dev/cg2_bpf
+    mount cgroup2 cg2_bpf /dev/cg2_bpf nodev noexec nosuid
+    chown root root /dev/cg2_bpf
+    chmod 0600 /dev/cg2_bpf
+    mount bpf bpf /sys/fs/bpf nodev noexec nosuid
+
     # Create location for fs_mgr to store abbreviated output from filesystem
     # checker programs.
     mkdir /dev/fscklogs 0770 root system
 
     # pstore/ramoops previous console log
-    mount pstore pstore /sys/fs/pstore
+    mount pstore pstore /sys/fs/pstore nodev noexec nosuid
+    chown system log /sys/fs/pstore
+    chmod 0550 /sys/fs/pstore
     chown system log /sys/fs/pstore/console-ramoops
     chmod 0440 /sys/fs/pstore/console-ramoops
     chown system log /sys/fs/pstore/console-ramoops-0
@@ -236,7 +247,10 @@
     export DOWNLOAD_CACHE /data/cache
 
     # set RLIMIT_NICE to allow priorities from 19 to -20
-    setrlimit 13 40 40
+    setrlimit nice 40 40
+
+    # Allow up to 32K FDs per process
+    setrlimit nofile 32768 32768
 
     # This allows the ledtrig-transient properties to be created here so
     # that they can be chown'd to system:system later on boot
@@ -300,19 +314,22 @@
     load_system_props
     # start essential services
     start logd
+    start servicemanager
     start hwservicemanager
+    start vndservicemanager
 
-    # once everything is setup, no need to modify /
-    mount rootfs rootfs / ro remount
+    # Once everything is setup, no need to modify /.
+    # The bind+remount combination allows this to work in containers.
+    mount rootfs rootfs / remount bind ro nodev
     # Mount shared so changes propagate into child namespaces
     mount rootfs rootfs / shared rec
     # Mount default storage into root namespace
-    mount none /mnt/runtime/default /storage slave bind rec
+    mount none /mnt/runtime/default /storage bind rec
+    mount none none /storage slave rec
 
     # Make sure /sys/kernel/debug (if present) is labeled properly
     # Note that tracefs may be mounted under debug, so we need to cross filesystems
     restorecon --recursive --cross-filesystems /sys/kernel/debug
-    chmod 0755 /sys/kernel/debug/tracing
 
     # We chown/chmod /cache again so because mount is run as root + defaults
     chown system cache /cache
@@ -350,6 +367,10 @@
     mkdir /cache/lost+found 0770 root root
 
 on late-fs
+    # Ensure that tracefs has the correct permissions.
+    # This does not work correctly if it is called in post-fs.
+    chmod 0755 /sys/kernel/debug/tracing
+
     # HALs required before storage encryption can get unlocked (FBE/FDE)
     class_start early_hal
 
@@ -374,6 +395,20 @@
 
     # create basic filesystem structure
     mkdir /data/misc 01771 system misc
+    mkdir /data/misc/recovery 0770 system log
+    copy /data/misc/recovery/ro.build.fingerprint /data/misc/recovery/ro.build.fingerprint.1
+    chmod 0440 /data/misc/recovery/ro.build.fingerprint.1
+    chown system log /data/misc/recovery/ro.build.fingerprint.1
+    write /data/misc/recovery/ro.build.fingerprint ${ro.build.fingerprint}
+    chmod 0440 /data/misc/recovery/ro.build.fingerprint
+    chown system log /data/misc/recovery/ro.build.fingerprint
+    mkdir /data/misc/recovery/proc 0770 system log
+    copy /data/misc/recovery/proc/version /data/misc/recovery/proc/version.1
+    chmod 0440 /data/misc/recovery/proc/version.1
+    chown system log /data/misc/recovery/proc/version.1
+    copy /proc/version /data/misc/recovery/proc/version
+    chmod 0440 /data/misc/recovery/proc/version
+    chown system log /data/misc/recovery/proc/version
     mkdir /data/misc/bluedroid 02770 bluetooth bluetooth
     # Fix the access permissions and group ownership for 'bt_config.conf'
     chmod 0660 /data/misc/bluedroid/bt_config.conf
@@ -386,6 +421,8 @@
     mkdir /data/misc/net 0750 root shell
     mkdir /data/misc/radio 0770 system radio
     mkdir /data/misc/sms 0770 system radio
+    mkdir /data/misc/carrierid 0770 system radio
+    mkdir /data/misc/apns 0770 system radio
     mkdir /data/misc/zoneinfo 0775 system system
     mkdir /data/misc/textclassifier 0771 system system
     mkdir /data/misc/vpn 0770 system vpn
@@ -407,8 +444,10 @@
     mkdir /data/misc/vold 0700 root root
     mkdir /data/misc/boottrace 0771 system shell
     mkdir /data/misc/update_engine 0700 root root
+    mkdir /data/misc/update_engine_log 02750 root log
     mkdir /data/misc/trace 0700 root root
-    mkdir /data/misc/reboot 0700 system system
+    # create location to store surface and window trace files
+    mkdir /data/misc/wmtrace 0700 system system
     # profile file layout
     mkdir /data/misc/profiles 0771 system system
     mkdir /data/misc/profiles/cur 0771 system system
@@ -422,6 +461,7 @@
     # For security reasons, /data/local/tmp should always be empty.
     # Do not place files or directories in /data/local/tmp
     mkdir /data/local/tmp 0771 shell shell
+    mkdir /data/local/traces 0777 shell shell
     mkdir /data/data 0771 system system
     mkdir /data/app-private 0771 system system
     mkdir /data/app-ephemeral 0771 system system
@@ -430,6 +470,8 @@
     mkdir /data/app 0771 system system
     mkdir /data/property 0700 root root
     mkdir /data/tombstones 0771 system system
+    mkdir /data/vendor/tombstones 0771 root root
+    mkdir /data/vendor/tombstones/wifi 0771 wifi wifi
 
     # create dalvik-cache, so as to enforce our permissions
     mkdir /data/dalvik-cache 0771 root root
@@ -458,12 +500,17 @@
 
     mkdir /data/anr 0775 system system
 
+    # NFC: create data/nfc for nv storage
+    mkdir /data/nfc 0770 nfc nfc
+    mkdir /data/nfc/param 0770 nfc nfc
+
     # Create all remaining /data root dirs so that they are made through init
     # and get proper encryption policy installed
     mkdir /data/backup 0700 system system
     mkdir /data/ss 0700 system system
 
     mkdir /data/system 0775 system system
+    mkdir /data/system/dropbox 0700 system system
     mkdir /data/system/heapdump 0700 system system
     mkdir /data/system/users 0775 system system
 
@@ -528,6 +575,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.
@@ -657,12 +707,6 @@
 on property:sys.boot_completed=1
     bootchart stop
 
-on property:sys.boot_completed=1 && property:ro.build.type=user
-    write /proc/sys/kernel/modules_disabled 1
-
-on property:sys.boot_completed=1 && property:ro.build.type=userdebug
-    write /proc/sys/kernel/modules_disabled 1
-
 # system server cannot write to /proc/sys files,
 # and chown/chmod does not work for /proc/sys/ entries.
 # So proxy writes through init.
@@ -675,6 +719,9 @@
 
 on property:security.perf_harden=0
     write /proc/sys/kernel/perf_event_paranoid 1
+    write /proc/sys/kernel/perf_event_max_sample_rate ${debug.perf_event_max_sample_rate:-100000}
+    write /proc/sys/kernel/perf_cpu_time_max_percent ${debug.perf_cpu_time_max_percent:-25}
+    write /proc/sys/kernel/perf_event_mlock_kb ${debug.perf_event_mlock_kb:-516}
 
 on property:security.perf_harden=1
     write /proc/sys/kernel/perf_event_paranoid 3
@@ -691,10 +738,9 @@
     seclabel u:r:ueventd:s0
     shutdown critical
 
-service healthd /sbin/healthd
+service healthd /system/bin/healthd
     class core
     critical
-    seclabel u:r:healthd:s0
     group root system wakelock
 
 service console /system/bin/sh
@@ -704,11 +750,14 @@
     user shell
     group shell log readproc
     seclabel u:r:shell:s0
+    setenv HOSTNAME console
 
 on property:ro.debuggable=1
     # Give writes to anyone for the trace folder on debug builds.
     # The folder is used to store method traces.
     chmod 0773 /data/misc/trace
+    # Give reads to anyone for the window trace folder on debug builds.
+    chmod 0775 /data/misc/wmtrace
     start console
 
 service flash_recovery /system/bin/install-recovery.sh
diff --git a/rootdir/init.usb.rc b/rootdir/init.usb.rc
index 915d159..f0681d2 100644
--- a/rootdir/init.usb.rc
+++ b/rootdir/init.usb.rc
@@ -12,16 +12,12 @@
     mkdir /data/adb 0700 root root
 
 # adbd is controlled via property triggers in init.<platform>.usb.rc
-service adbd /sbin/adbd --root_seclabel=u:r:su:s0
+service adbd /system/bin/adbd --root_seclabel=u:r:su:s0
     class core
     socket adbd stream 660 system system
     disabled
     seclabel u:r:adbd:s0
 
-# adbd on at boot in emulator
-on property:ro.kernel.qemu=1
-    start adbd
-
 on boot
     setprop sys.usb.configfs 0
 
diff --git a/rootdir/init.zygote32.rc b/rootdir/init.zygote32.rc
index d836c4e..ac87979 100644
--- a/rootdir/init.zygote32.rc
+++ b/rootdir/init.zygote32.rc
@@ -2,7 +2,7 @@
     class main
     priority -20
     user root
-    group root readproc
+    group root readproc reserved_disk
     socket zygote stream 660 root system
     onrestart write /sys/android_power/request_state wake
     onrestart write /sys/power/state on
diff --git a/rootdir/init.zygote32_64.rc b/rootdir/init.zygote32_64.rc
index 80bb673..a535846 100644
--- a/rootdir/init.zygote32_64.rc
+++ b/rootdir/init.zygote32_64.rc
@@ -2,7 +2,7 @@
     class main
     priority -20
     user root
-    group root readproc
+    group root readproc reserved_disk
     socket zygote stream 660 root system
     onrestart write /sys/android_power/request_state wake
     onrestart write /sys/power/state on
@@ -17,7 +17,7 @@
     class main
     priority -20
     user root
-    group root readproc
+    group root readproc reserved_disk
     socket zygote_secondary stream 660 root system
     onrestart restart zygote
     writepid /dev/cpuset/foreground/tasks
diff --git a/rootdir/init.zygote64.rc b/rootdir/init.zygote64.rc
index 05ec16f..6fc810b 100644
--- a/rootdir/init.zygote64.rc
+++ b/rootdir/init.zygote64.rc
@@ -2,7 +2,7 @@
     class main
     priority -20
     user root
-    group root readproc
+    group root readproc reserved_disk
     socket zygote stream 660 root system
     onrestart write /sys/android_power/request_state wake
     onrestart write /sys/power/state on
diff --git a/rootdir/init.zygote64_32.rc b/rootdir/init.zygote64_32.rc
index 36bb443..7ddd52e 100644
--- a/rootdir/init.zygote64_32.rc
+++ b/rootdir/init.zygote64_32.rc
@@ -2,7 +2,7 @@
     class main
     priority -20
     user root
-    group root readproc
+    group root readproc reserved_disk
     socket zygote stream 660 root system
     onrestart write /sys/android_power/request_state wake
     onrestart write /sys/power/state on
@@ -13,11 +13,11 @@
     onrestart restart wificond
     writepid /dev/cpuset/foreground/tasks
 
-service zygote_secondary /system/bin/app_process32 -Xzygote /system/bin --zygote --socket-name=zygote_secondary
+service zygote_secondary /system/bin/app_process32 -Xzygote /system/bin --zygote --socket-name=zygote_secondary --enable-lazy-preload
     class main
     priority -20
     user root
-    group root readproc
+    group root readproc reserved_disk
     socket zygote_secondary stream 660 root system
     onrestart restart zygote
     writepid /dev/cpuset/foreground/tasks
diff --git a/rootdir/ueventd.rc b/rootdir/ueventd.rc
index efd0da5..2f85dec 100644
--- a/rootdir/ueventd.rc
+++ b/rootdir/ueventd.rc
@@ -1,3 +1,5 @@
+firmware_directories /etc/firmware/ /odm/firmware/ /vendor/firmware/ /firmware/image/
+
 subsystem adf
     devname uevent_devname
 
@@ -52,6 +54,7 @@
 /dev/ashmem               0666   root       root
 /dev/binder               0666   root       root
 /dev/hwbinder             0666   root       root
+/dev/vndbinder            0666   root       root
 
 /dev/pmsg0                0222   root       log
 
@@ -76,6 +79,7 @@
 /dev/graphics/*           0660   root       graphics
 /dev/msm_hw3dm            0660   system     graphics
 /dev/input/*              0660   root       input
+/dev/v4l-touch*           0660   root       input
 /dev/eac                  0660   root       audio
 /dev/cam                  0660   root       camera
 /dev/pmem                 0660   system     graphics
diff --git a/run-as/Android.bp b/run-as/Android.bp
new file mode 100644
index 0000000..840a43c
--- /dev/null
+++ b/run-as/Android.bp
@@ -0,0 +1,28 @@
+//
+// 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_binary {
+    name: "run-as",
+    srcs: [
+        "run-as.cpp",
+    ],
+    shared_libs: [
+        "libbase",
+        "libselinux",
+        "libpackagelistparser",
+        "libminijail",
+    ],
+}
diff --git a/run-as/Android.mk b/run-as/Android.mk
deleted file mode 100644
index 7111fbe..0000000
--- a/run-as/Android.mk
+++ /dev/null
@@ -1,8 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-LOCAL_CFLAGS := -Wall -Werror
-LOCAL_MODULE := run-as
-LOCAL_SHARED_LIBRARIES := libselinux libpackagelistparser libminijail
-LOCAL_SRC_FILES := run-as.cpp
-include $(BUILD_EXECUTABLE)
diff --git a/run-as/run-as.cpp b/run-as/run-as.cpp
index e7b8cc2..d005ecf 100644
--- a/run-as/run-as.cpp
+++ b/run-as/run-as.cpp
@@ -28,6 +28,7 @@
 #include <libminijail.h>
 #include <scoped_minijail.h>
 
+#include <android-base/properties.h>
 #include <packagelistparser/packagelistparser.h>
 #include <private/android_filesystem_config.h>
 #include <selinux/android.h>
@@ -40,6 +41,7 @@
 //  The 'run-as' binary is installed with CAP_SETUID and CAP_SETGID file
 //  capabilities, but will check the following:
 //
+//  - that the ro.boot.disable_runas property is not set
 //  - that it is invoked from the 'shell' or 'root' user (abort otherwise)
 //  - that '<package-name>' is the name of an installed and debuggable package
 //  - that the package's data directory is well-formed
@@ -139,6 +141,12 @@
     error(1, 0, "only 'shell' or 'root' users can run this program");
   }
 
+  // Some devices can disable running run-as, such as Chrome OS when running in
+  // non-developer mode.
+  if (android::base::GetBoolProperty("ro.boot.disable_runas", false)) {
+      error(1, 0, "run-as is disabled from the kernel commandline");
+  }
+
   char* pkgname = argv[1];
   int cmd_argv_offset = 2;
 
@@ -194,6 +202,7 @@
   ScopedMinijail j(minijail_new());
   minijail_change_uid(j.get(), uid);
   minijail_change_gid(j.get(), gid);
+  minijail_keep_supplementary_gids(j.get());
   minijail_enter(j.get());
 
   if (selinux_android_setcontext(uid, 0, info.seinfo, pkgname) < 0) {
diff --git a/sdcard/Android.bp b/sdcard/Android.bp
new file mode 100644
index 0000000..c096587
--- /dev/null
+++ b/sdcard/Android.bp
@@ -0,0 +1,17 @@
+cc_binary {
+    srcs: ["sdcard.cpp"],
+    name: "sdcard",
+    cflags: [
+        "-Wall",
+        "-Wno-unused-parameter",
+        "-Werror",
+    ],
+    shared_libs: [
+        "libbase",
+        "libcutils",
+        "libminijail",
+    ],
+    sanitize: {
+        misc_undefined: ["integer"],
+    },
+}
diff --git a/sdcard/Android.mk b/sdcard/Android.mk
deleted file mode 100644
index 0c58574..0000000
--- a/sdcard/Android.mk
+++ /dev/null
@@ -1,13 +0,0 @@
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := sdcard.cpp fuse.cpp
-LOCAL_MODULE := sdcard
-LOCAL_CFLAGS := -Wall -Wno-unused-parameter -Werror
-LOCAL_SHARED_LIBRARIES := libbase libcutils libminijail libpackagelistparser
-
-LOCAL_SANITIZE := integer
-LOCAL_CLANG := true
-
-include $(BUILD_EXECUTABLE)
diff --git a/sdcard/OWNERS b/sdcard/OWNERS
new file mode 100644
index 0000000..199a0f8
--- /dev/null
+++ b/sdcard/OWNERS
@@ -0,0 +1 @@
+drosen@google.com
diff --git a/sdcard/fuse.cpp b/sdcard/fuse.cpp
deleted file mode 100644
index 3f0f95f..0000000
--- a/sdcard/fuse.cpp
+++ /dev/null
@@ -1,1495 +0,0 @@
-/*
- * Copyright (C) 2010 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 <errno.h>
-#include <string.h>
-#include <unistd.h>
-
-#define LOG_TAG "sdcard"
-
-#include "fuse.h"
-
-#include <android-base/logging.h>
-
-/* FUSE_CANONICAL_PATH is not currently upstreamed */
-#define FUSE_CANONICAL_PATH 2016
-
-#define FUSE_UNKNOWN_INO 0xffffffff
-
-/* Pseudo-error constant used to indicate that no fuse status is needed
- * or that a reply has already been written. */
-#define NO_STATUS 1
-
-static inline void *id_to_ptr(__u64 nid)
-{
-    return (void *) (uintptr_t) nid;
-}
-
-static inline __u64 ptr_to_id(void *ptr)
-{
-    return (__u64) (uintptr_t) ptr;
-}
-
-static void acquire_node_locked(struct node* node)
-{
-    node->refcount++;
-    DLOG(INFO) << "ACQUIRE " << std::hex << node << std::dec
-               << " (" << node->name << ") rc=" << node->refcount;
-}
-
-static void remove_node_from_parent_locked(struct node* node);
-
-static void release_node_locked(struct node* node)
-{
-    DLOG(INFO) << "RELEASE " << std::hex << node << std::dec
-               << " (" << node->name << ") rc=" << node->refcount;
-    if (node->refcount > 0) {
-        node->refcount--;
-        if (!node->refcount) {
-            DLOG(INFO) << "DESTROY " << std::hex << node << std::dec << " (" << node->name << ")";
-            remove_node_from_parent_locked(node);
-
-            /* TODO: remove debugging - poison memory */
-            memset(node->name, 0xef, node->namelen);
-            free(node->name);
-            free(node->actual_name);
-            memset(node, 0xfc, sizeof(*node));
-            free(node);
-        }
-    } else {
-        LOG(ERROR) << std::hex << node << std::dec << " refcount=0";
-    }
-}
-
-static void add_node_to_parent_locked(struct node *node, struct node *parent) {
-    node->parent = parent;
-    node->next = parent->child;
-    parent->child = node;
-    acquire_node_locked(parent);
-}
-
-static void remove_node_from_parent_locked(struct node* node)
-{
-    if (node->parent) {
-        if (node->parent->child == node) {
-            node->parent->child = node->parent->child->next;
-        } else {
-            struct node *node2;
-            node2 = node->parent->child;
-            while (node2->next != node)
-                node2 = node2->next;
-            node2->next = node->next;
-        }
-        release_node_locked(node->parent);
-        node->parent = NULL;
-        node->next = NULL;
-    }
-}
-
-/* Gets the absolute path to a node into the provided buffer.
- *
- * Populates 'buf' with the path and returns the length of the path on success,
- * or returns -1 if the path is too long for the provided buffer.
- */
-static ssize_t get_node_path_locked(struct node* node, char* buf, size_t bufsize) {
-    const char* name;
-    size_t namelen;
-    if (node->graft_path) {
-        name = node->graft_path;
-        namelen = node->graft_pathlen;
-    } else if (node->actual_name) {
-        name = node->actual_name;
-        namelen = node->namelen;
-    } else {
-        name = node->name;
-        namelen = node->namelen;
-    }
-
-    if (bufsize < namelen + 1) {
-        return -1;
-    }
-
-    ssize_t pathlen = 0;
-    if (node->parent && node->graft_path == NULL) {
-        pathlen = get_node_path_locked(node->parent, buf, bufsize - namelen - 1);
-        if (pathlen < 0) {
-            return -1;
-        }
-        buf[pathlen++] = '/';
-    }
-
-    memcpy(buf + pathlen, name, namelen + 1); /* include trailing \0 */
-    return pathlen + namelen;
-}
-
-/* Finds the absolute path of a file within a given directory.
- * Performs a case-insensitive search for the file and sets the buffer to the path
- * of the first matching file.  If 'search' is zero or if no match is found, sets
- * the buffer to the path that the file would have, assuming the name were case-sensitive.
- *
- * Populates 'buf' with the path and returns the actual name (within 'buf') on success,
- * or returns NULL if the path is too long for the provided buffer.
- */
-static char* find_file_within(const char* path, const char* name,
-        char* buf, size_t bufsize, int search)
-{
-    size_t pathlen = strlen(path);
-    size_t namelen = strlen(name);
-    size_t childlen = pathlen + namelen + 1;
-    char* actual;
-
-    if (bufsize <= childlen) {
-        return NULL;
-    }
-
-    memcpy(buf, path, pathlen);
-    buf[pathlen] = '/';
-    actual = buf + pathlen + 1;
-    memcpy(actual, name, namelen + 1);
-
-    if (search && access(buf, F_OK)) {
-        struct dirent* entry;
-        DIR* dir = opendir(path);
-        if (!dir) {
-            PLOG(ERROR) << "opendir(" << path << ") failed";
-            return actual;
-        }
-        while ((entry = readdir(dir))) {
-            if (!strcasecmp(entry->d_name, name)) {
-                /* we have a match - replace the name, don't need to copy the null again */
-                memcpy(actual, entry->d_name, namelen);
-                break;
-            }
-        }
-        closedir(dir);
-    }
-    return actual;
-}
-
-static void attr_from_stat(struct fuse* fuse, struct fuse_attr *attr,
-        const struct stat *s, const struct node* node) {
-    attr->ino = node->ino;
-    attr->size = s->st_size;
-    attr->blocks = s->st_blocks;
-    attr->atime = s->st_atim.tv_sec;
-    attr->mtime = s->st_mtim.tv_sec;
-    attr->ctime = s->st_ctim.tv_sec;
-    attr->atimensec = s->st_atim.tv_nsec;
-    attr->mtimensec = s->st_mtim.tv_nsec;
-    attr->ctimensec = s->st_ctim.tv_nsec;
-    attr->mode = s->st_mode;
-    attr->nlink = s->st_nlink;
-
-    attr->uid = node->uid;
-
-    if (fuse->gid == AID_SDCARD_RW) {
-        /* As an optimization, certain trusted system components only run
-         * as owner but operate across all users. Since we're now handing
-         * out the sdcard_rw GID only to trusted apps, we're okay relaxing
-         * the user boundary enforcement for the default view. The UIDs
-         * assigned to app directories are still multiuser aware. */
-        attr->gid = AID_SDCARD_RW;
-    } else {
-        attr->gid = multiuser_get_uid(node->userid, fuse->gid);
-    }
-
-    int visible_mode = 0775 & ~fuse->mask;
-    if (node->perm == PERM_PRE_ROOT) {
-        /* Top of multi-user view should always be visible to ensure
-         * secondary users can traverse inside. */
-        visible_mode = 0711;
-    } else if (node->under_android) {
-        /* Block "other" access to Android directories, since only apps
-         * belonging to a specific user should be in there; we still
-         * leave +x open for the default view. */
-        if (fuse->gid == AID_SDCARD_RW) {
-            visible_mode = visible_mode & ~0006;
-        } else {
-            visible_mode = visible_mode & ~0007;
-        }
-    }
-    int owner_mode = s->st_mode & 0700;
-    int filtered_mode = visible_mode & (owner_mode | (owner_mode >> 3) | (owner_mode >> 6));
-    attr->mode = (attr->mode & S_IFMT) | filtered_mode;
-}
-
-static int touch(char* path, mode_t mode) {
-    int fd = TEMP_FAILURE_RETRY(open(path, O_RDWR | O_CREAT | O_EXCL | O_NOFOLLOW | O_CLOEXEC,
-                                     mode));
-    if (fd == -1) {
-        if (errno == EEXIST) {
-            return 0;
-        } else {
-            PLOG(ERROR) << "open(" << path << ") failed";
-            return -1;
-        }
-    }
-    close(fd);
-    return 0;
-}
-
-static void derive_permissions_locked(struct fuse* fuse, struct node *parent,
-        struct node *node) {
-    appid_t appid;
-
-    /* By default, each node inherits from its parent */
-    node->perm = PERM_INHERIT;
-    node->userid = parent->userid;
-    node->uid = parent->uid;
-    node->under_android = parent->under_android;
-
-    /* Derive custom permissions based on parent and current node */
-    switch (parent->perm) {
-    case PERM_INHERIT:
-        /* Already inherited above */
-        break;
-    case PERM_PRE_ROOT:
-        /* Legacy internal layout places users at top level */
-        node->perm = PERM_ROOT;
-        node->userid = strtoul(node->name, NULL, 10);
-        break;
-    case PERM_ROOT:
-        /* Assume masked off by default. */
-        if (!strcasecmp(node->name, "Android")) {
-            /* App-specific directories inside; let anyone traverse */
-            node->perm = PERM_ANDROID;
-            node->under_android = true;
-        }
-        break;
-    case PERM_ANDROID:
-        if (!strcasecmp(node->name, "data")) {
-            /* App-specific directories inside; let anyone traverse */
-            node->perm = PERM_ANDROID_DATA;
-        } else if (!strcasecmp(node->name, "obb")) {
-            /* App-specific directories inside; let anyone traverse */
-            node->perm = PERM_ANDROID_OBB;
-            /* Single OBB directory is always shared */
-            node->graft_path = fuse->global->obb_path;
-            node->graft_pathlen = strlen(fuse->global->obb_path);
-        } else if (!strcasecmp(node->name, "media")) {
-            /* App-specific directories inside; let anyone traverse */
-            node->perm = PERM_ANDROID_MEDIA;
-        }
-        break;
-    case PERM_ANDROID_DATA:
-    case PERM_ANDROID_OBB:
-    case PERM_ANDROID_MEDIA:
-        const auto& iter = fuse->global->package_to_appid->find(node->name);
-        if (iter != fuse->global->package_to_appid->end()) {
-            appid = iter->second;
-            node->uid = multiuser_get_uid(parent->userid, appid);
-        }
-        break;
-    }
-}
-
-void derive_permissions_recursive_locked(struct fuse* fuse, struct node *parent) {
-    struct node *node;
-    for (node = parent->child; node; node = node->next) {
-        derive_permissions_locked(fuse, parent, node);
-        if (node->child) {
-            derive_permissions_recursive_locked(fuse, node);
-        }
-    }
-}
-
-/* Kernel has already enforced everything we returned through
- * derive_permissions_locked(), so this is used to lock down access
- * even further, such as enforcing that apps hold sdcard_rw. */
-static bool check_caller_access_to_name(struct fuse* fuse,
-        const struct fuse_in_header *hdr, const struct node* parent_node,
-        const char* name, int mode) {
-    /* Always block security-sensitive files at root */
-    if (parent_node && parent_node->perm == PERM_ROOT) {
-        if (!strcasecmp(name, "autorun.inf")
-                || !strcasecmp(name, ".android_secure")
-                || !strcasecmp(name, "android_secure")) {
-            return false;
-        }
-    }
-
-    /* Root always has access; access for any other UIDs should always
-     * be controlled through packages.list. */
-    if (hdr->uid == 0) {
-        return true;
-    }
-
-    /* No extra permissions to enforce */
-    return true;
-}
-
-static bool check_caller_access_to_node(struct fuse* fuse,
-        const struct fuse_in_header *hdr, const struct node* node, int mode) {
-    return check_caller_access_to_name(fuse, hdr, node->parent, node->name, mode);
-}
-
-struct node *create_node_locked(struct fuse* fuse,
-        struct node *parent, const char *name, const char* actual_name)
-{
-    struct node *node;
-    size_t namelen = strlen(name);
-
-    // Detect overflows in the inode counter. "4 billion nodes should be enough
-    // for everybody".
-    if (fuse->global->inode_ctr == 0) {
-        LOG(ERROR) << "No more inode numbers available";
-        return NULL;
-    }
-
-    node = static_cast<struct node*>(calloc(1, sizeof(struct node)));
-    if (!node) {
-        return NULL;
-    }
-    node->name = static_cast<char*>(malloc(namelen + 1));
-    if (!node->name) {
-        free(node);
-        return NULL;
-    }
-    memcpy(node->name, name, namelen + 1);
-    if (strcmp(name, actual_name)) {
-        node->actual_name = static_cast<char*>(malloc(namelen + 1));
-        if (!node->actual_name) {
-            free(node->name);
-            free(node);
-            return NULL;
-        }
-        memcpy(node->actual_name, actual_name, namelen + 1);
-    }
-    node->namelen = namelen;
-    node->nid = ptr_to_id(node);
-    node->ino = fuse->global->inode_ctr++;
-    node->gen = fuse->global->next_generation++;
-
-    node->deleted = false;
-
-    derive_permissions_locked(fuse, parent, node);
-    acquire_node_locked(node);
-    add_node_to_parent_locked(node, parent);
-    return node;
-}
-
-static int rename_node_locked(struct node *node, const char *name,
-        const char* actual_name)
-{
-    size_t namelen = strlen(name);
-    int need_actual_name = strcmp(name, actual_name);
-
-    /* make the storage bigger without actually changing the name
-     * in case an error occurs part way */
-    if (namelen > node->namelen) {
-        char* new_name = static_cast<char*>(realloc(node->name, namelen + 1));
-        if (!new_name) {
-            return -ENOMEM;
-        }
-        node->name = new_name;
-        if (need_actual_name && node->actual_name) {
-            char* new_actual_name = static_cast<char*>(realloc(node->actual_name, namelen + 1));
-            if (!new_actual_name) {
-                return -ENOMEM;
-            }
-            node->actual_name = new_actual_name;
-        }
-    }
-
-    /* update the name, taking care to allocate storage before overwriting the old name */
-    if (need_actual_name) {
-        if (!node->actual_name) {
-            node->actual_name = static_cast<char*>(malloc(namelen + 1));
-            if (!node->actual_name) {
-                return -ENOMEM;
-            }
-        }
-        memcpy(node->actual_name, actual_name, namelen + 1);
-    } else {
-        free(node->actual_name);
-        node->actual_name = NULL;
-    }
-    memcpy(node->name, name, namelen + 1);
-    node->namelen = namelen;
-    return 0;
-}
-
-static struct node *lookup_node_by_id_locked(struct fuse *fuse, __u64 nid)
-{
-    if (nid == FUSE_ROOT_ID) {
-        return &fuse->global->root;
-    } else {
-        return static_cast<struct node*>(id_to_ptr(nid));
-    }
-}
-
-static struct node* lookup_node_and_path_by_id_locked(struct fuse* fuse, __u64 nid,
-        char* buf, size_t bufsize)
-{
-    struct node* node = lookup_node_by_id_locked(fuse, nid);
-    if (node && get_node_path_locked(node, buf, bufsize) < 0) {
-        node = NULL;
-    }
-    return node;
-}
-
-static struct node *lookup_child_by_name_locked(struct node *node, const char *name)
-{
-    for (node = node->child; node; node = node->next) {
-        /* use exact string comparison, nodes that differ by case
-         * must be considered distinct even if they refer to the same
-         * underlying file as otherwise operations such as "mv x x"
-         * will not work because the source and target nodes are the same. */
-        if (!strcmp(name, node->name) && !node->deleted) {
-            return node;
-        }
-    }
-    return 0;
-}
-
-static struct node* acquire_or_create_child_locked(
-        struct fuse* fuse, struct node* parent,
-        const char* name, const char* actual_name)
-{
-    struct node* child = lookup_child_by_name_locked(parent, name);
-    if (child) {
-        acquire_node_locked(child);
-    } else {
-        child = create_node_locked(fuse, parent, name, actual_name);
-    }
-    return child;
-}
-
-static void fuse_status(struct fuse *fuse, __u64 unique, int err)
-{
-    struct fuse_out_header hdr;
-    hdr.len = sizeof(hdr);
-    hdr.error = err;
-    hdr.unique = unique;
-    ssize_t ret = TEMP_FAILURE_RETRY(write(fuse->fd, &hdr, sizeof(hdr)));
-    if (ret == -1) {
-        PLOG(ERROR) << "*** STATUS FAILED ***";
-    } else if (static_cast<size_t>(ret) != sizeof(hdr)) {
-        LOG(ERROR) << "*** STATUS FAILED: written " << ret << " expected "
-                   << sizeof(hdr) << " ***";
-    }
-}
-
-static void fuse_reply(struct fuse *fuse, __u64 unique, void *data, int len)
-{
-    struct fuse_out_header hdr;
-    hdr.len = len + sizeof(hdr);
-    hdr.error = 0;
-    hdr.unique = unique;
-
-    struct iovec vec[2];
-    vec[0].iov_base = &hdr;
-    vec[0].iov_len = sizeof(hdr);
-    vec[1].iov_base = data;
-    vec[1].iov_len = len;
-
-    ssize_t ret = TEMP_FAILURE_RETRY(writev(fuse->fd, vec, 2));
-    if (ret == -1) {
-        PLOG(ERROR) << "*** REPLY FAILED ***";
-    } else if (static_cast<size_t>(ret) != sizeof(hdr) + len) {
-        LOG(ERROR) << "*** REPLY FAILED: written " << ret << " expected "
-                   << sizeof(hdr) + len << " ***";
-    }
-}
-
-static int fuse_reply_entry(struct fuse* fuse, __u64 unique,
-        struct node* parent, const char* name, const char* actual_name,
-        const char* path)
-{
-    struct node* node;
-    struct fuse_entry_out out;
-    struct stat s;
-
-    if (lstat(path, &s) == -1) {
-        return -errno;
-    }
-
-    pthread_mutex_lock(&fuse->global->lock);
-    node = acquire_or_create_child_locked(fuse, parent, name, actual_name);
-    if (!node) {
-        pthread_mutex_unlock(&fuse->global->lock);
-        return -ENOMEM;
-    }
-    memset(&out, 0, sizeof(out));
-    attr_from_stat(fuse, &out.attr, &s, node);
-    out.attr_valid = 10;
-    out.entry_valid = 10;
-    out.nodeid = node->nid;
-    out.generation = node->gen;
-    pthread_mutex_unlock(&fuse->global->lock);
-    fuse_reply(fuse, unique, &out, sizeof(out));
-    return NO_STATUS;
-}
-
-static int fuse_reply_attr(struct fuse* fuse, __u64 unique, const struct node* node,
-        const char* path)
-{
-    struct fuse_attr_out out;
-    struct stat s;
-
-    if (lstat(path, &s) == -1) {
-        return -errno;
-    }
-    memset(&out, 0, sizeof(out));
-    attr_from_stat(fuse, &out.attr, &s, node);
-    out.attr_valid = 10;
-    fuse_reply(fuse, unique, &out, sizeof(out));
-    return NO_STATUS;
-}
-
-static void fuse_notify_delete(struct fuse* fuse, const __u64 parent,
-        const __u64 child, const char* name) {
-    struct fuse_out_header hdr;
-    struct fuse_notify_delete_out data;
-    size_t namelen = strlen(name);
-    hdr.len = sizeof(hdr) + sizeof(data) + namelen + 1;
-    hdr.error = FUSE_NOTIFY_DELETE;
-    hdr.unique = 0;
-
-    data.parent = parent;
-    data.child = child;
-    data.namelen = namelen;
-    data.padding = 0;
-
-    struct iovec vec[3];
-    vec[0].iov_base = &hdr;
-    vec[0].iov_len = sizeof(hdr);
-    vec[1].iov_base = &data;
-    vec[1].iov_len = sizeof(data);
-    vec[2].iov_base = (void*) name;
-    vec[2].iov_len = namelen + 1;
-
-    ssize_t ret = TEMP_FAILURE_RETRY(writev(fuse->fd, vec, 3));
-    /* Ignore ENOENT, since other views may not have seen the entry */
-    if (ret == -1) {
-        if (errno != ENOENT) {
-            PLOG(ERROR) << "*** NOTIFY FAILED ***";
-        }
-    } else if (static_cast<size_t>(ret) != sizeof(hdr) + sizeof(data) + namelen + 1) {
-        LOG(ERROR) << "*** NOTIFY FAILED: written " << ret << " expected "
-                   << sizeof(hdr) + sizeof(data) + namelen + 1 << " ***";
-    }
-}
-
-static int handle_lookup(struct fuse* fuse, struct fuse_handler* handler,
-        const struct fuse_in_header *hdr, const char* name)
-{
-    struct node* parent_node;
-    char parent_path[PATH_MAX];
-    char child_path[PATH_MAX];
-    const char* actual_name;
-
-    pthread_mutex_lock(&fuse->global->lock);
-    parent_node = lookup_node_and_path_by_id_locked(fuse, hdr->nodeid,
-            parent_path, sizeof(parent_path));
-    DLOG(INFO) << "[" << handler->token << "] LOOKUP " << name << " @ " << hdr->nodeid
-               << " (" << (parent_node ? parent_node->name : "?") << ")";
-    pthread_mutex_unlock(&fuse->global->lock);
-
-    if (!parent_node || !(actual_name = find_file_within(parent_path, name,
-            child_path, sizeof(child_path), 1))) {
-        return -ENOENT;
-    }
-    if (!check_caller_access_to_name(fuse, hdr, parent_node, name, R_OK)) {
-        return -EACCES;
-    }
-
-    return fuse_reply_entry(fuse, hdr->unique, parent_node, name, actual_name, child_path);
-}
-
-static int handle_forget(struct fuse* fuse, struct fuse_handler* handler,
-        const struct fuse_in_header *hdr, const struct fuse_forget_in *req)
-{
-    struct node* node;
-
-    pthread_mutex_lock(&fuse->global->lock);
-    node = lookup_node_by_id_locked(fuse, hdr->nodeid);
-    DLOG(INFO) << "[" << handler->token << "] FORGET #" << req->nlookup
-               << " @ " << std::hex << hdr->nodeid
-               << " (" << (node ? node->name : "?") << ")";
-    if (node) {
-        __u64 n = req->nlookup;
-        while (n) {
-            n--;
-            release_node_locked(node);
-        }
-    }
-    pthread_mutex_unlock(&fuse->global->lock);
-    return NO_STATUS; /* no reply */
-}
-
-static int handle_getattr(struct fuse* fuse, struct fuse_handler* handler,
-        const struct fuse_in_header *hdr, const struct fuse_getattr_in *req)
-{
-    struct node* node;
-    char path[PATH_MAX];
-
-    pthread_mutex_lock(&fuse->global->lock);
-    node = lookup_node_and_path_by_id_locked(fuse, hdr->nodeid, path, sizeof(path));
-    DLOG(INFO) << "[" << handler->token << "] GETATTR flags=" << req->getattr_flags
-               << " fh=" << std::hex << req->fh << " @ " << hdr->nodeid << std::dec
-               << " (" << (node ? node->name : "?") << ")";
-    pthread_mutex_unlock(&fuse->global->lock);
-
-    if (!node) {
-        return -ENOENT;
-    }
-    if (!check_caller_access_to_node(fuse, hdr, node, R_OK)) {
-        return -EACCES;
-    }
-
-    return fuse_reply_attr(fuse, hdr->unique, node, path);
-}
-
-static int handle_setattr(struct fuse* fuse, struct fuse_handler* handler,
-        const struct fuse_in_header *hdr, const struct fuse_setattr_in *req)
-{
-    struct node* node;
-    char path[PATH_MAX];
-    struct timespec times[2];
-
-    pthread_mutex_lock(&fuse->global->lock);
-    node = lookup_node_and_path_by_id_locked(fuse, hdr->nodeid, path, sizeof(path));
-    DLOG(INFO) << "[" << handler->token << "] SETATTR fh=" << std::hex << req->fh
-               << " valid=" << std::hex << req->valid << " @ " << hdr->nodeid << std::dec
-               << " (" << (node ? node->name : "?") << ")";
-    pthread_mutex_unlock(&fuse->global->lock);
-
-    if (!node) {
-        return -ENOENT;
-    }
-
-    if (!(req->valid & FATTR_FH) &&
-            !check_caller_access_to_node(fuse, hdr, node, W_OK)) {
-        return -EACCES;
-    }
-
-    /* XXX: incomplete implementation on purpose.
-     * chmod/chown should NEVER be implemented.*/
-
-    if ((req->valid & FATTR_SIZE) && TEMP_FAILURE_RETRY(truncate64(path, req->size)) == -1) {
-        return -errno;
-    }
-
-    /* Handle changing atime and mtime.  If FATTR_ATIME_and FATTR_ATIME_NOW
-     * are both set, then set it to the current time.  Else, set it to the
-     * time specified in the request.  Same goes for mtime.  Use utimensat(2)
-     * as it allows ATIME and MTIME to be changed independently, and has
-     * nanosecond resolution which fuse also has.
-     */
-    if (req->valid & (FATTR_ATIME | FATTR_MTIME)) {
-        times[0].tv_nsec = UTIME_OMIT;
-        times[1].tv_nsec = UTIME_OMIT;
-        if (req->valid & FATTR_ATIME) {
-            if (req->valid & FATTR_ATIME_NOW) {
-              times[0].tv_nsec = UTIME_NOW;
-            } else {
-              times[0].tv_sec = req->atime;
-              times[0].tv_nsec = req->atimensec;
-            }
-        }
-        if (req->valid & FATTR_MTIME) {
-            if (req->valid & FATTR_MTIME_NOW) {
-              times[1].tv_nsec = UTIME_NOW;
-            } else {
-              times[1].tv_sec = req->mtime;
-              times[1].tv_nsec = req->mtimensec;
-            }
-        }
-        DLOG(INFO) << "[" << handler->token << "] Calling utimensat on " << path
-                   << " with atime " << times[0].tv_sec << ", mtime=" << times[1].tv_sec;
-        if (utimensat(-1, path, times, 0) < 0) {
-            return -errno;
-        }
-    }
-    return fuse_reply_attr(fuse, hdr->unique, node, path);
-}
-
-static int handle_mknod(struct fuse* fuse, struct fuse_handler* handler,
-        const struct fuse_in_header* hdr, const struct fuse_mknod_in* req, const char* name)
-{
-    struct node* parent_node;
-    char parent_path[PATH_MAX];
-    char child_path[PATH_MAX];
-    const char* actual_name;
-
-    pthread_mutex_lock(&fuse->global->lock);
-    parent_node = lookup_node_and_path_by_id_locked(fuse, hdr->nodeid,
-            parent_path, sizeof(parent_path));
-    DLOG(INFO) << "[" << handler->token << "] MKNOD " << name << " 0" << std::oct << req->mode
-               << " @ " << std::hex << hdr->nodeid
-               << " (" << (parent_node ? parent_node->name : "?") << ")";
-    pthread_mutex_unlock(&fuse->global->lock);
-
-    if (!parent_node || !(actual_name = find_file_within(parent_path, name,
-            child_path, sizeof(child_path), 1))) {
-        return -ENOENT;
-    }
-    if (!check_caller_access_to_name(fuse, hdr, parent_node, name, W_OK)) {
-        return -EACCES;
-    }
-    __u32 mode = (req->mode & (~0777)) | 0664;
-    if (mknod(child_path, mode, req->rdev) == -1) {
-        return -errno;
-    }
-    return fuse_reply_entry(fuse, hdr->unique, parent_node, name, actual_name, child_path);
-}
-
-static int handle_mkdir(struct fuse* fuse, struct fuse_handler* handler,
-        const struct fuse_in_header* hdr, const struct fuse_mkdir_in* req, const char* name)
-{
-    struct node* parent_node;
-    char parent_path[PATH_MAX];
-    char child_path[PATH_MAX];
-    const char* actual_name;
-
-    pthread_mutex_lock(&fuse->global->lock);
-    parent_node = lookup_node_and_path_by_id_locked(fuse, hdr->nodeid,
-            parent_path, sizeof(parent_path));
-    DLOG(INFO) << "[" << handler->token << "] MKDIR " << name << " 0" << std::oct << req->mode
-               << " @ " << std::hex << hdr->nodeid
-               << " (" << (parent_node ? parent_node->name : "?") << ")";
-    pthread_mutex_unlock(&fuse->global->lock);
-
-    if (!parent_node || !(actual_name = find_file_within(parent_path, name,
-            child_path, sizeof(child_path), 1))) {
-        return -ENOENT;
-    }
-    if (!check_caller_access_to_name(fuse, hdr, parent_node, name, W_OK)) {
-        return -EACCES;
-    }
-    __u32 mode = (req->mode & (~0777)) | 0775;
-    if (mkdir(child_path, mode) == -1) {
-        return -errno;
-    }
-
-    /* When creating /Android/data and /Android/obb, mark them as .nomedia */
-    if (parent_node->perm == PERM_ANDROID && !strcasecmp(name, "data")) {
-        char nomedia[PATH_MAX];
-        snprintf(nomedia, PATH_MAX, "%s/.nomedia", child_path);
-        if (touch(nomedia, 0664) != 0) {
-            PLOG(ERROR) << "touch(" << nomedia << ") failed";
-            return -ENOENT;
-        }
-    }
-    if (parent_node->perm == PERM_ANDROID && !strcasecmp(name, "obb")) {
-        char nomedia[PATH_MAX];
-        snprintf(nomedia, PATH_MAX, "%s/.nomedia", fuse->global->obb_path);
-        if (touch(nomedia, 0664) != 0) {
-            PLOG(ERROR) << "touch(" << nomedia << ") failed";
-            return -ENOENT;
-        }
-    }
-
-    return fuse_reply_entry(fuse, hdr->unique, parent_node, name, actual_name, child_path);
-}
-
-static int handle_unlink(struct fuse* fuse, struct fuse_handler* handler,
-        const struct fuse_in_header* hdr, const char* name)
-{
-    struct node* parent_node;
-    struct node* child_node;
-    char parent_path[PATH_MAX];
-    char child_path[PATH_MAX];
-
-    pthread_mutex_lock(&fuse->global->lock);
-    parent_node = lookup_node_and_path_by_id_locked(fuse, hdr->nodeid,
-            parent_path, sizeof(parent_path));
-    DLOG(INFO) << "[" << handler->token << "] UNLINK " << name << " @ " << std::hex << hdr->nodeid
-               << " (" << (parent_node ? parent_node->name : "?") << ")";
-    pthread_mutex_unlock(&fuse->global->lock);
-
-    if (!parent_node || !find_file_within(parent_path, name,
-            child_path, sizeof(child_path), 1)) {
-        return -ENOENT;
-    }
-    if (!check_caller_access_to_name(fuse, hdr, parent_node, name, W_OK)) {
-        return -EACCES;
-    }
-    if (unlink(child_path) == -1) {
-        return -errno;
-    }
-    pthread_mutex_lock(&fuse->global->lock);
-    child_node = lookup_child_by_name_locked(parent_node, name);
-    if (child_node) {
-        child_node->deleted = true;
-    }
-    pthread_mutex_unlock(&fuse->global->lock);
-    if (parent_node && child_node) {
-        /* Tell all other views that node is gone */
-        DLOG(INFO) << "[" << handler->token << "] fuse_notify_delete"
-                   << " parent=" << std::hex << parent_node->nid
-                   << ", child=" << std::hex << child_node->nid << std::dec
-                   << ", name=" << name;
-        if (fuse != fuse->global->fuse_default) {
-            fuse_notify_delete(fuse->global->fuse_default, parent_node->nid, child_node->nid, name);
-        }
-        if (fuse != fuse->global->fuse_read) {
-            fuse_notify_delete(fuse->global->fuse_read, parent_node->nid, child_node->nid, name);
-        }
-        if (fuse != fuse->global->fuse_write) {
-            fuse_notify_delete(fuse->global->fuse_write, parent_node->nid, child_node->nid, name);
-        }
-    }
-    return 0;
-}
-
-static int handle_rmdir(struct fuse* fuse, struct fuse_handler* handler,
-        const struct fuse_in_header* hdr, const char* name)
-{
-    struct node* child_node;
-    struct node* parent_node;
-    char parent_path[PATH_MAX];
-    char child_path[PATH_MAX];
-
-    pthread_mutex_lock(&fuse->global->lock);
-    parent_node = lookup_node_and_path_by_id_locked(fuse, hdr->nodeid,
-            parent_path, sizeof(parent_path));
-    DLOG(INFO) << "[" << handler->token << "] UNLINK " << name << " @ " << std::hex << hdr->nodeid
-               << " (" << (parent_node ? parent_node->name : "?") << ")";
-    pthread_mutex_unlock(&fuse->global->lock);
-
-    if (!parent_node || !find_file_within(parent_path, name,
-            child_path, sizeof(child_path), 1)) {
-        return -ENOENT;
-    }
-    if (!check_caller_access_to_name(fuse, hdr, parent_node, name, W_OK)) {
-        return -EACCES;
-    }
-    if (rmdir(child_path) == -1) {
-        return -errno;
-    }
-    pthread_mutex_lock(&fuse->global->lock);
-    child_node = lookup_child_by_name_locked(parent_node, name);
-    if (child_node) {
-        child_node->deleted = true;
-    }
-    pthread_mutex_unlock(&fuse->global->lock);
-    if (parent_node && child_node) {
-        /* Tell all other views that node is gone */
-        DLOG(INFO) << "[" << handler->token << "] fuse_notify_delete"
-                   << " parent=" << std::hex << parent_node->nid
-                   << ", child=" << std::hex << child_node->nid << std::dec
-                   << ", name=" << name;
-        if (fuse != fuse->global->fuse_default) {
-            fuse_notify_delete(fuse->global->fuse_default, parent_node->nid, child_node->nid, name);
-        }
-        if (fuse != fuse->global->fuse_read) {
-            fuse_notify_delete(fuse->global->fuse_read, parent_node->nid, child_node->nid, name);
-        }
-        if (fuse != fuse->global->fuse_write) {
-            fuse_notify_delete(fuse->global->fuse_write, parent_node->nid, child_node->nid, name);
-        }
-    }
-    return 0;
-}
-
-static int handle_rename(struct fuse* fuse, struct fuse_handler* handler,
-        const struct fuse_in_header* hdr, const struct fuse_rename_in* req,
-        const char* old_name, const char* new_name)
-{
-    struct node* old_parent_node;
-    struct node* new_parent_node;
-    struct node* child_node;
-    char old_parent_path[PATH_MAX];
-    char new_parent_path[PATH_MAX];
-    char old_child_path[PATH_MAX];
-    char new_child_path[PATH_MAX];
-    const char* new_actual_name;
-    int search;
-    int res;
-
-    pthread_mutex_lock(&fuse->global->lock);
-    old_parent_node = lookup_node_and_path_by_id_locked(fuse, hdr->nodeid,
-            old_parent_path, sizeof(old_parent_path));
-    new_parent_node = lookup_node_and_path_by_id_locked(fuse, req->newdir,
-            new_parent_path, sizeof(new_parent_path));
-    DLOG(INFO) << "[" << handler->token << "] RENAME " << old_name << "->" << new_name
-               << " @ " << std::hex << hdr->nodeid
-               << " (" << (old_parent_node ? old_parent_node->name : "?") << ") -> "
-               << std::hex << req->newdir
-               << " (" << (new_parent_node ? new_parent_node->name : "?") << ")";
-    if (!old_parent_node || !new_parent_node) {
-        res = -ENOENT;
-        goto lookup_error;
-    }
-    if (!check_caller_access_to_name(fuse, hdr, old_parent_node, old_name, W_OK)) {
-        res = -EACCES;
-        goto lookup_error;
-    }
-    if (!check_caller_access_to_name(fuse, hdr, new_parent_node, new_name, W_OK)) {
-        res = -EACCES;
-        goto lookup_error;
-    }
-    child_node = lookup_child_by_name_locked(old_parent_node, old_name);
-    if (!child_node || get_node_path_locked(child_node,
-            old_child_path, sizeof(old_child_path)) < 0) {
-        res = -ENOENT;
-        goto lookup_error;
-    }
-    acquire_node_locked(child_node);
-    pthread_mutex_unlock(&fuse->global->lock);
-
-    /* Special case for renaming a file where destination is same path
-     * differing only by case.  In this case we don't want to look for a case
-     * insensitive match.  This allows commands like "mv foo FOO" to work as expected.
-     */
-    search = old_parent_node != new_parent_node
-            || strcasecmp(old_name, new_name);
-    if (!(new_actual_name = find_file_within(new_parent_path, new_name,
-            new_child_path, sizeof(new_child_path), search))) {
-        res = -ENOENT;
-        goto io_error;
-    }
-
-    DLOG(INFO) << "[" << handler->token << "] RENAME " << old_child_path << "->" << new_child_path;
-    res = rename(old_child_path, new_child_path);
-    if (res == -1) {
-        res = -errno;
-        goto io_error;
-    }
-
-    pthread_mutex_lock(&fuse->global->lock);
-    res = rename_node_locked(child_node, new_name, new_actual_name);
-    if (!res) {
-        remove_node_from_parent_locked(child_node);
-        derive_permissions_locked(fuse, new_parent_node, child_node);
-        derive_permissions_recursive_locked(fuse, child_node);
-        add_node_to_parent_locked(child_node, new_parent_node);
-    }
-    goto done;
-
-io_error:
-    pthread_mutex_lock(&fuse->global->lock);
-done:
-    release_node_locked(child_node);
-lookup_error:
-    pthread_mutex_unlock(&fuse->global->lock);
-    return res;
-}
-
-static int open_flags_to_access_mode(int open_flags) {
-    if ((open_flags & O_ACCMODE) == O_RDONLY) {
-        return R_OK;
-    } else if ((open_flags & O_ACCMODE) == O_WRONLY) {
-        return W_OK;
-    } else {
-        /* Probably O_RDRW, but treat as default to be safe */
-        return R_OK | W_OK;
-    }
-}
-
-static int handle_open(struct fuse* fuse, struct fuse_handler* handler,
-        const struct fuse_in_header* hdr, const struct fuse_open_in* req)
-{
-    struct node* node;
-    char path[PATH_MAX];
-    struct fuse_open_out out;
-    struct handle *h;
-
-    pthread_mutex_lock(&fuse->global->lock);
-    node = lookup_node_and_path_by_id_locked(fuse, hdr->nodeid, path, sizeof(path));
-    DLOG(INFO) << "[" << handler->token << "] OPEN 0" << std::oct << req->flags
-               << " @ " << std::hex << hdr->nodeid << std::dec
-               << " (" << (node ? node->name : "?") << ")";
-    pthread_mutex_unlock(&fuse->global->lock);
-
-    if (!node) {
-        return -ENOENT;
-    }
-    if (!check_caller_access_to_node(fuse, hdr, node,
-            open_flags_to_access_mode(req->flags))) {
-        return -EACCES;
-    }
-    h = static_cast<struct handle*>(malloc(sizeof(*h)));
-    if (!h) {
-        return -ENOMEM;
-    }
-    DLOG(INFO) << "[" << handler->token << "] OPEN " << path;
-    h->fd = TEMP_FAILURE_RETRY(open(path, req->flags));
-    if (h->fd == -1) {
-        free(h);
-        return -errno;
-    }
-    out.fh = ptr_to_id(h);
-    out.open_flags = 0;
-
-#ifdef FUSE_SHORTCIRCUIT
-    out.lower_fd = h->fd;
-#else
-    out.padding = 0;
-#endif
-
-    fuse_reply(fuse, hdr->unique, &out, sizeof(out));
-    return NO_STATUS;
-}
-
-static int handle_read(struct fuse* fuse, struct fuse_handler* handler,
-        const struct fuse_in_header* hdr, const struct fuse_read_in* req)
-{
-    struct handle *h = static_cast<struct handle*>(id_to_ptr(req->fh));
-    __u64 unique = hdr->unique;
-    __u32 size = req->size;
-    __u64 offset = req->offset;
-    int res;
-    __u8 *read_buffer = (__u8 *) ((uintptr_t)(handler->read_buffer + PAGE_SIZE) & ~((uintptr_t)PAGE_SIZE-1));
-
-    /* Don't access any other fields of hdr or req beyond this point, the read buffer
-     * overlaps the request buffer and will clobber data in the request.  This
-     * saves us 128KB per request handler thread at the cost of this scary comment. */
-
-    DLOG(INFO) << "[" << handler->token << "] READ " << std::hex << h << std::dec
-               << "(" << h->fd << ") " << size << "@" << offset;
-    if (size > MAX_READ) {
-        return -EINVAL;
-    }
-    res = TEMP_FAILURE_RETRY(pread64(h->fd, read_buffer, size, offset));
-    if (res == -1) {
-        return -errno;
-    }
-    fuse_reply(fuse, unique, read_buffer, res);
-    return NO_STATUS;
-}
-
-static int handle_write(struct fuse* fuse, struct fuse_handler* handler,
-        const struct fuse_in_header* hdr, const struct fuse_write_in* req,
-        const void* buffer)
-{
-    struct fuse_write_out out;
-    struct handle *h = static_cast<struct handle*>(id_to_ptr(req->fh));
-    int res;
-    __u8 aligned_buffer[req->size] __attribute__((__aligned__(PAGE_SIZE)));
-
-    if (req->flags & O_DIRECT) {
-        memcpy(aligned_buffer, buffer, req->size);
-        buffer = (const __u8*) aligned_buffer;
-    }
-
-    DLOG(INFO) << "[" << handler->token << "] WRITE " << std::hex << h << std::dec
-               << "(" << h->fd << ") " << req->size << "@" << req->offset;
-    res = TEMP_FAILURE_RETRY(pwrite64(h->fd, buffer, req->size, req->offset));
-    if (res == -1) {
-        return -errno;
-    }
-    out.size = res;
-    out.padding = 0;
-    fuse_reply(fuse, hdr->unique, &out, sizeof(out));
-    return NO_STATUS;
-}
-
-static int handle_statfs(struct fuse* fuse, struct fuse_handler* handler,
-        const struct fuse_in_header* hdr)
-{
-    char path[PATH_MAX];
-    struct statfs stat;
-    struct fuse_statfs_out out;
-    int res;
-
-    pthread_mutex_lock(&fuse->global->lock);
-    DLOG(INFO) << "[" << handler->token << "] STATFS";
-    res = get_node_path_locked(&fuse->global->root, path, sizeof(path));
-    pthread_mutex_unlock(&fuse->global->lock);
-    if (res < 0) {
-        return -ENOENT;
-    }
-    if (TEMP_FAILURE_RETRY(statfs(fuse->global->root.name, &stat)) == -1) {
-        return -errno;
-    }
-    memset(&out, 0, sizeof(out));
-    out.st.blocks = stat.f_blocks;
-    out.st.bfree = stat.f_bfree;
-    out.st.bavail = stat.f_bavail;
-    out.st.files = stat.f_files;
-    out.st.ffree = stat.f_ffree;
-    out.st.bsize = stat.f_bsize;
-    out.st.namelen = stat.f_namelen;
-    out.st.frsize = stat.f_frsize;
-    fuse_reply(fuse, hdr->unique, &out, sizeof(out));
-    return NO_STATUS;
-}
-
-static int handle_release(struct fuse* fuse, struct fuse_handler* handler,
-        const struct fuse_in_header* hdr, const struct fuse_release_in* req)
-{
-    struct handle *h = static_cast<struct handle*>(id_to_ptr(req->fh));
-
-    DLOG(INFO) << "[" << handler->token << "] RELEASE " << std::hex << h << std::dec
-               << "(" << h->fd << ")";
-    close(h->fd);
-    free(h);
-    return 0;
-}
-
-static int handle_fsync(struct fuse* fuse, struct fuse_handler* handler,
-        const struct fuse_in_header* hdr, const struct fuse_fsync_in* req)
-{
-    bool is_dir = (hdr->opcode == FUSE_FSYNCDIR);
-    bool is_data_sync = req->fsync_flags & 1;
-
-    int fd = -1;
-    if (is_dir) {
-      struct dirhandle *dh = static_cast<struct dirhandle*>(id_to_ptr(req->fh));
-      fd = dirfd(dh->d);
-    } else {
-      struct handle *h = static_cast<struct handle*>(id_to_ptr(req->fh));
-      fd = h->fd;
-    }
-
-    DLOG(INFO) << "[" << handler->token << "] " << (is_dir ? "FSYNCDIR" : "FSYNC") << " "
-               << std::hex << req->fh << std::dec << "(" << fd << ") is_data_sync=" << is_data_sync;
-    int res = is_data_sync ? fdatasync(fd) : fsync(fd);
-    if (res == -1) {
-        return -errno;
-    }
-    return 0;
-}
-
-static int handle_flush(struct fuse* fuse, struct fuse_handler* handler,
-        const struct fuse_in_header* hdr)
-{
-    DLOG(INFO) << "[" << handler->token << "] FLUSH";
-    return 0;
-}
-
-static int handle_opendir(struct fuse* fuse, struct fuse_handler* handler,
-        const struct fuse_in_header* hdr, const struct fuse_open_in* req)
-{
-    struct node* node;
-    char path[PATH_MAX];
-    struct fuse_open_out out;
-    struct dirhandle *h;
-
-    pthread_mutex_lock(&fuse->global->lock);
-    node = lookup_node_and_path_by_id_locked(fuse, hdr->nodeid, path, sizeof(path));
-    DLOG(INFO) << "[" << handler->token << "] OPENDIR @ " << std::hex << hdr->nodeid
-               << " (" << (node ? node->name : "?") << ")";
-    pthread_mutex_unlock(&fuse->global->lock);
-
-    if (!node) {
-        return -ENOENT;
-    }
-    if (!check_caller_access_to_node(fuse, hdr, node, R_OK)) {
-        return -EACCES;
-    }
-    h = static_cast<struct dirhandle*>(malloc(sizeof(*h)));
-    if (!h) {
-        return -ENOMEM;
-    }
-    DLOG(INFO) << "[" << handler->token << "] OPENDIR " << path;
-    h->d = opendir(path);
-    if (!h->d) {
-        free(h);
-        return -errno;
-    }
-    out.fh = ptr_to_id(h);
-    out.open_flags = 0;
-
-#ifdef FUSE_SHORTCIRCUIT
-    out.lower_fd = -1;
-#else
-    out.padding = 0;
-#endif
-
-    fuse_reply(fuse, hdr->unique, &out, sizeof(out));
-    return NO_STATUS;
-}
-
-static int handle_readdir(struct fuse* fuse, struct fuse_handler* handler,
-        const struct fuse_in_header* hdr, const struct fuse_read_in* req)
-{
-    char buffer[8192];
-    struct fuse_dirent *fde = (struct fuse_dirent*) buffer;
-    struct dirent *de;
-    struct dirhandle *h = static_cast<struct dirhandle*>(id_to_ptr(req->fh));
-
-    DLOG(INFO) << "[" << handler->token << "] READDIR " << h;
-    if (req->offset == 0) {
-        /* rewinddir() might have been called above us, so rewind here too */
-        DLOG(INFO) << "[" << handler->token << "] calling rewinddir()";
-        rewinddir(h->d);
-    }
-    de = readdir(h->d);
-    if (!de) {
-        return 0;
-    }
-    fde->ino = FUSE_UNKNOWN_INO;
-    /* increment the offset so we can detect when rewinddir() seeks back to the beginning */
-    fde->off = req->offset + 1;
-    fde->type = de->d_type;
-    fde->namelen = strlen(de->d_name);
-    memcpy(fde->name, de->d_name, fde->namelen + 1);
-    fuse_reply(fuse, hdr->unique, fde,
-            FUSE_DIRENT_ALIGN(sizeof(struct fuse_dirent) + fde->namelen));
-    return NO_STATUS;
-}
-
-static int handle_releasedir(struct fuse* fuse, struct fuse_handler* handler,
-        const struct fuse_in_header* hdr, const struct fuse_release_in* req)
-{
-    struct dirhandle *h = static_cast<struct dirhandle*>(id_to_ptr(req->fh));
-
-    DLOG(INFO) << "[" << handler->token << "] RELEASEDIR " << h;
-    closedir(h->d);
-    free(h);
-    return 0;
-}
-
-static int handle_init(struct fuse* fuse, struct fuse_handler* handler,
-        const struct fuse_in_header* hdr, const struct fuse_init_in* req)
-{
-    struct fuse_init_out out;
-    size_t fuse_struct_size;
-
-    DLOG(INFO) << "[" << handler->token << "] INIT ver=" << req->major << "." << req->minor
-               << " maxread=" << req->max_readahead << " flags=" << std::hex << req->flags;
-
-    /* Kernel 2.6.16 is the first stable kernel with struct fuse_init_out
-     * defined (fuse version 7.6). The structure is the same from 7.6 through
-     * 7.22. Beginning with 7.23, the structure increased in size and added
-     * new parameters.
-     */
-    if (req->major != FUSE_KERNEL_VERSION || req->minor < 6) {
-        LOG(ERROR) << "Fuse kernel version mismatch: Kernel version "
-                   << req->major << "." << req->minor
-                   << ", Expected at least " << FUSE_KERNEL_VERSION << ".6";
-        return -1;
-    }
-
-    /* We limit ourselves to 15 because we don't handle BATCH_FORGET yet */
-    out.minor = MIN(req->minor, 15);
-    fuse_struct_size = sizeof(out);
-#if defined(FUSE_COMPAT_22_INIT_OUT_SIZE)
-    /* FUSE_KERNEL_VERSION >= 23. */
-
-    /* Since we return minor version 15, the kernel does not accept the latest
-     * fuse_init_out size. We need to use FUSE_COMPAT_22_INIT_OUT_SIZE always.*/
-    fuse_struct_size = FUSE_COMPAT_22_INIT_OUT_SIZE;
-#endif
-
-    out.major = FUSE_KERNEL_VERSION;
-    out.max_readahead = req->max_readahead;
-    out.flags = FUSE_ATOMIC_O_TRUNC | FUSE_BIG_WRITES;
-
-#ifdef FUSE_SHORTCIRCUIT
-    out.flags |= FUSE_SHORTCIRCUIT;
-#endif
-
-    out.max_background = 32;
-    out.congestion_threshold = 32;
-    out.max_write = MAX_WRITE;
-    fuse_reply(fuse, hdr->unique, &out, fuse_struct_size);
-    return NO_STATUS;
-}
-
-static int handle_canonical_path(struct fuse* fuse, struct fuse_handler* handler,
-        const struct fuse_in_header *hdr)
-{
-    struct node* node;
-    char path[PATH_MAX];
-    int len;
-
-    pthread_mutex_lock(&fuse->global->lock);
-    node = lookup_node_and_path_by_id_locked(fuse, hdr->nodeid,
-            path, sizeof(path));
-    DLOG(INFO) << "[" << handler->token << "] CANONICAL_PATH @ " << std::hex << hdr->nodeid
-               << std::dec << " (" << (node ? node->name : "?") << ")";
-    pthread_mutex_unlock(&fuse->global->lock);
-
-    if (!node) {
-        return -ENOENT;
-    }
-    if (!check_caller_access_to_node(fuse, hdr, node, R_OK)) {
-        return -EACCES;
-    }
-    len = strlen(path);
-    if (len + 1 > PATH_MAX)
-        len = PATH_MAX - 1;
-    path[PATH_MAX - 1] = 0;
-    fuse_reply(fuse, hdr->unique, path, len + 1);
-    return NO_STATUS;
-}
-
-static int handle_fuse_request(struct fuse *fuse, struct fuse_handler* handler,
-        const struct fuse_in_header *hdr, const void *data, size_t data_len)
-{
-    switch (hdr->opcode) {
-    case FUSE_LOOKUP: { /* bytez[] -> entry_out */
-        const char *name = static_cast<const char*>(data);
-        return handle_lookup(fuse, handler, hdr, name);
-    }
-
-    case FUSE_FORGET: {
-        const struct fuse_forget_in *req = static_cast<const struct fuse_forget_in*>(data);
-        return handle_forget(fuse, handler, hdr, req);
-    }
-
-    case FUSE_GETATTR: { /* getattr_in -> attr_out */
-        const struct fuse_getattr_in *req = static_cast<const struct fuse_getattr_in*>(data);
-        return handle_getattr(fuse, handler, hdr, req);
-    }
-
-    case FUSE_SETATTR: { /* setattr_in -> attr_out */
-        const struct fuse_setattr_in *req = static_cast<const struct fuse_setattr_in*>(data);
-        return handle_setattr(fuse, handler, hdr, req);
-    }
-
-//    case FUSE_READLINK:
-//    case FUSE_SYMLINK:
-    case FUSE_MKNOD: { /* mknod_in, bytez[] -> entry_out */
-        const struct fuse_mknod_in *req = static_cast<const struct fuse_mknod_in*>(data);
-        const char *name = ((const char*) data) + sizeof(*req);
-        return handle_mknod(fuse, handler, hdr, req, name);
-    }
-
-    case FUSE_MKDIR: { /* mkdir_in, bytez[] -> entry_out */
-        const struct fuse_mkdir_in *req = static_cast<const struct fuse_mkdir_in*>(data);
-        const char *name = ((const char*) data) + sizeof(*req);
-        return handle_mkdir(fuse, handler, hdr, req, name);
-    }
-
-    case FUSE_UNLINK: { /* bytez[] -> */
-        const char *name = static_cast<const char*>(data);
-        return handle_unlink(fuse, handler, hdr, name);
-    }
-
-    case FUSE_RMDIR: { /* bytez[] -> */
-        const char *name = static_cast<const char*>(data);
-        return handle_rmdir(fuse, handler, hdr, name);
-    }
-
-    case FUSE_RENAME: { /* rename_in, oldname, newname ->  */
-        const struct fuse_rename_in *req = static_cast<const struct fuse_rename_in*>(data);
-        const char *old_name = ((const char*) data) + sizeof(*req);
-        const char *new_name = old_name + strlen(old_name) + 1;
-        return handle_rename(fuse, handler, hdr, req, old_name, new_name);
-    }
-
-//    case FUSE_LINK:
-    case FUSE_OPEN: { /* open_in -> open_out */
-        const struct fuse_open_in *req = static_cast<const struct fuse_open_in*>(data);
-        return handle_open(fuse, handler, hdr, req);
-    }
-
-    case FUSE_READ: { /* read_in -> byte[] */
-        const struct fuse_read_in *req = static_cast<const struct fuse_read_in*>(data);
-        return handle_read(fuse, handler, hdr, req);
-    }
-
-    case FUSE_WRITE: { /* write_in, byte[write_in.size] -> write_out */
-        const struct fuse_write_in *req = static_cast<const struct fuse_write_in*>(data);
-        const void* buffer = (const __u8*)data + sizeof(*req);
-        return handle_write(fuse, handler, hdr, req, buffer);
-    }
-
-    case FUSE_STATFS: { /* getattr_in -> attr_out */
-        return handle_statfs(fuse, handler, hdr);
-    }
-
-    case FUSE_RELEASE: { /* release_in -> */
-        const struct fuse_release_in *req = static_cast<const struct fuse_release_in*>(data);
-        return handle_release(fuse, handler, hdr, req);
-    }
-
-    case FUSE_FSYNC:
-    case FUSE_FSYNCDIR: {
-        const struct fuse_fsync_in *req = static_cast<const struct fuse_fsync_in*>(data);
-        return handle_fsync(fuse, handler, hdr, req);
-    }
-
-//    case FUSE_SETXATTR:
-//    case FUSE_GETXATTR:
-//    case FUSE_LISTXATTR:
-//    case FUSE_REMOVEXATTR:
-    case FUSE_FLUSH: {
-        return handle_flush(fuse, handler, hdr);
-    }
-
-    case FUSE_OPENDIR: { /* open_in -> open_out */
-        const struct fuse_open_in *req = static_cast<const struct fuse_open_in*>(data);
-        return handle_opendir(fuse, handler, hdr, req);
-    }
-
-    case FUSE_READDIR: {
-        const struct fuse_read_in *req = static_cast<const struct fuse_read_in*>(data);
-        return handle_readdir(fuse, handler, hdr, req);
-    }
-
-    case FUSE_RELEASEDIR: { /* release_in -> */
-        const struct fuse_release_in *req = static_cast<const struct fuse_release_in*>(data);
-        return handle_releasedir(fuse, handler, hdr, req);
-    }
-
-    case FUSE_INIT: { /* init_in -> init_out */
-        const struct fuse_init_in *req = static_cast<const struct fuse_init_in*>(data);
-        return handle_init(fuse, handler, hdr, req);
-    }
-
-    case FUSE_CANONICAL_PATH: { /* nodeid -> bytez[] */
-        return handle_canonical_path(fuse, handler, hdr);
-    }
-
-    default: {
-        DLOG(INFO) << "[" << handler->token << "] NOTIMPL op=" << hdr->opcode
-                   << "uniq=" << std::hex << hdr->unique << "nid=" << hdr->nodeid << std::dec;
-        return -ENOSYS;
-    }
-    }
-}
-
-void handle_fuse_requests(struct fuse_handler* handler)
-{
-    struct fuse* fuse = handler->fuse;
-    for (;;) {
-        ssize_t len = TEMP_FAILURE_RETRY(read(fuse->fd,
-                handler->request_buffer, sizeof(handler->request_buffer)));
-        if (len == -1) {
-            if (errno == ENODEV) {
-                LOG(ERROR) << "[" << handler->token << "] someone stole our marbles!";
-                exit(2);
-            }
-            PLOG(ERROR) << "[" << handler->token << "] handle_fuse_requests";
-            continue;
-        }
-
-        if (static_cast<size_t>(len) < sizeof(struct fuse_in_header)) {
-            LOG(ERROR) << "[" << handler->token << "] request too short: len=" << len;
-            continue;
-        }
-
-        const struct fuse_in_header* hdr =
-            reinterpret_cast<const struct fuse_in_header*>(handler->request_buffer);
-        if (hdr->len != static_cast<size_t>(len)) {
-            LOG(ERROR) << "[" << handler->token << "] malformed header: len=" << len
-                       << ", hdr->len=" << hdr->len;
-            continue;
-        }
-
-        const void *data = handler->request_buffer + sizeof(struct fuse_in_header);
-        size_t data_len = len - sizeof(struct fuse_in_header);
-        __u64 unique = hdr->unique;
-        int res = handle_fuse_request(fuse, handler, hdr, data, data_len);
-
-        /* We do not access the request again after this point because the underlying
-         * buffer storage may have been reused while processing the request. */
-
-        if (res != NO_STATUS) {
-            if (res) {
-                DLOG(INFO) << "[" << handler->token << "] ERROR " << res;
-            }
-            fuse_status(fuse, unique, res);
-        }
-    }
-}
diff --git a/sdcard/fuse.h b/sdcard/fuse.h
deleted file mode 100644
index 9ccd21d..0000000
--- a/sdcard/fuse.h
+++ /dev/null
@@ -1,209 +0,0 @@
-/*
- * Copyright (C) 2016 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 FUSE_H_
-#define FUSE_H_
-
-#include <dirent.h>
-#include <fcntl.h>
-#include <linux/fuse.h>
-#include <pthread.h>
-#include <stdbool.h>
-#include <stdlib.h>
-#include <sys/param.h>
-#include <sys/stat.h>
-#include <sys/statfs.h>
-#include <sys/types.h>
-#include <sys/uio.h>
-#include <unistd.h>
-
-#include <map>
-#include <string>
-
-#include <android-base/logging.h>
-#include <cutils/fs.h>
-#include <cutils/multiuser.h>
-#include <packagelistparser/packagelistparser.h>
-
-#include <private/android_filesystem_config.h>
-
-#define FUSE_TRACE 0
-
-#if FUSE_TRACE
-static constexpr bool kEnableDLog = true;
-#else  // FUSE_TRACE == 0
-static constexpr bool kEnableDLog = false;
-#endif
-
-// Use same strategy as DCHECK().
-#define DLOG(x) \
-    if (kEnableDLog) LOG(x)
-
-/* Maximum number of bytes to write in one request. */
-#define MAX_WRITE (256 * 1024)
-
-/* Maximum number of bytes to read in one request. */
-#define MAX_READ (128 * 1024)
-
-/* Largest possible request.
- * The request size is bounded by the maximum size of a FUSE_WRITE request because it has
- * the largest possible data payload. */
-#define MAX_REQUEST_SIZE (sizeof(struct fuse_in_header) + sizeof(struct fuse_write_in) + MAX_WRITE)
-
-namespace {
-struct CaseInsensitiveCompare {
-    bool operator()(const std::string& lhs, const std::string& rhs) const {
-        return strcasecmp(lhs.c_str(), rhs.c_str()) < 0;
-    }
-};
-}
-
-using AppIdMap = std::map<std::string, appid_t, CaseInsensitiveCompare>;
-
-/* Permission mode for a specific node. Controls how file permissions
- * are derived for children nodes. */
-typedef enum {
-    /* Nothing special; this node should just inherit from its parent. */
-    PERM_INHERIT,
-    /* This node is one level above a normal root; used for legacy layouts
-     * which use the first level to represent user_id. */
-    PERM_PRE_ROOT,
-    /* This node is "/" */
-    PERM_ROOT,
-    /* This node is "/Android" */
-    PERM_ANDROID,
-    /* This node is "/Android/data" */
-    PERM_ANDROID_DATA,
-    /* This node is "/Android/obb" */
-    PERM_ANDROID_OBB,
-    /* This node is "/Android/media" */
-    PERM_ANDROID_MEDIA,
-} perm_t;
-
-struct handle {
-    int fd;
-};
-
-struct dirhandle {
-    DIR *d;
-};
-
-struct node {
-    __u32 refcount;
-    __u64 nid;
-    __u64 gen;
-    /*
-     * The inode number for this FUSE node. Note that this isn't stable across
-     * multiple invocations of the FUSE daemon.
-     */
-    __u32 ino;
-
-    /* State derived based on current position in hierarchy. */
-    perm_t perm;
-    userid_t userid;
-    uid_t uid;
-    bool under_android;
-
-    struct node *next;          /* per-dir sibling list */
-    struct node *child;         /* first contained file by this dir */
-    struct node *parent;        /* containing directory */
-
-    size_t namelen;
-    char *name;
-    /* If non-null, this is the real name of the file in the underlying storage.
-     * This may differ from the field "name" only by case.
-     * strlen(actual_name) will always equal strlen(name), so it is safe to use
-     * namelen for both fields.
-     */
-    char *actual_name;
-
-    /* If non-null, an exact underlying path that should be grafted into this
-     * position. Used to support things like OBB. */
-    char* graft_path;
-    size_t graft_pathlen;
-
-    bool deleted;
-};
-
-/* Global data for all FUSE mounts */
-struct fuse_global {
-    pthread_mutex_t lock;
-
-    uid_t uid;
-    gid_t gid;
-    bool multi_user;
-
-    char source_path[PATH_MAX];
-    char obb_path[PATH_MAX];
-
-    AppIdMap* package_to_appid;
-
-    __u64 next_generation;
-    struct node root;
-
-    /* Used to allocate unique inode numbers for fuse nodes. We use
-     * a simple counter based scheme where inode numbers from deleted
-     * nodes aren't reused. Note that inode allocations are not stable
-     * across multiple invocation of the sdcard daemon, but that shouldn't
-     * be a huge problem in practice.
-     *
-     * Note that we restrict inodes to 32 bit unsigned integers to prevent
-     * truncation on 32 bit processes when unsigned long long stat.st_ino is
-     * assigned to an unsigned long ino_t type in an LP32 process.
-     *
-     * Also note that fuse_attr and fuse_dirent inode values are 64 bits wide
-     * on both LP32 and LP64, but the fuse kernel code doesn't squash 64 bit
-     * inode numbers into 32 bit values on 64 bit kernels (see fuse_squash_ino
-     * in fs/fuse/inode.c).
-     *
-     * Accesses must be guarded by |lock|.
-     */
-    __u32 inode_ctr;
-
-    struct fuse* fuse_default;
-    struct fuse* fuse_read;
-    struct fuse* fuse_write;
-};
-
-/* Single FUSE mount */
-struct fuse {
-    struct fuse_global* global;
-
-    char dest_path[PATH_MAX];
-
-    int fd;
-
-    gid_t gid;
-    mode_t mask;
-};
-
-/* Private data used by a single FUSE handler */
-struct fuse_handler {
-    struct fuse* fuse;
-    int token;
-
-    /* To save memory, we never use the contents of the request buffer and the read
-     * buffer at the same time.  This allows us to share the underlying storage. */
-    union {
-        __u8 request_buffer[MAX_REQUEST_SIZE];
-        __u8 read_buffer[MAX_READ + PAGE_SIZE];
-    };
-};
-
-void handle_fuse_requests(struct fuse_handler* handler);
-void derive_permissions_recursive_locked(struct fuse* fuse, struct node *parent);
-
-#endif  /* FUSE_H_ */
diff --git a/sdcard/sdcard.cpp b/sdcard/sdcard.cpp
index df3ce85..574bbfe 100644
--- a/sdcard/sdcard.cpp
+++ b/sdcard/sdcard.cpp
@@ -38,164 +38,18 @@
 #include <cutils/multiuser.h>
 #include <cutils/properties.h>
 
-#include <packagelistparser/packagelistparser.h>
-
 #include <libminijail.h>
 #include <scoped_minijail.h>
 
 #include <private/android_filesystem_config.h>
 
-// README
-//
-// What is this?
-//
-// sdcard is a program that uses FUSE to emulate FAT-on-sdcard style
-// directory permissions (all files are given fixed owner, group, and
-// permissions at creation, owner, group, and permissions are not
-// changeable, symlinks and hardlinks are not createable, etc.
-//
-// See usage() for command line options.
-//
-// It must be run as root, but will drop to requested UID/GID as soon as it
-// mounts a filesystem.  It will refuse to run if requested UID/GID are zero.
-//
-// Things I believe to be true:
-//
-// - ops that return a fuse_entry (LOOKUP, MKNOD, MKDIR, LINK, SYMLINK,
-// CREAT) must bump that node's refcount
-// - don't forget that FORGET can forget multiple references (req->nlookup)
-// - if an op that returns a fuse_entry fails writing the reply to the
-// kernel, you must rollback the refcount to reflect the reference the
-// kernel did not actually acquire
-//
-// This daemon can also derive custom filesystem permissions based on directory
-// structure when requested. These custom permissions support several features:
-//
-// - Apps can access their own files in /Android/data/com.example/ without
-// requiring any additional GIDs.
-// - Separate permissions for protecting directories like Pictures and Music.
-// - Multi-user separation on the same physical device.
-
-#include "fuse.h"
-
-#define PROP_SDCARDFS_DEVICE "ro.sys.sdcardfs"
-#define PROP_SDCARDFS_USER "persist.sys.sdcardfs"
+// NOTE: This is a vestigial program that simply exists to mount the in-kernel
+// sdcardfs filesystem.  The older FUSE-based design that used to live here has
+// been completely removed to avoid confusion.
 
 /* Supplementary groups to execute with. */
 static const gid_t kGroups[1] = { AID_PACKAGE_INFO };
 
-static bool package_parse_callback(pkg_info *info, void *userdata) {
-    struct fuse_global *global = (struct fuse_global *)userdata;
-    bool res = global->package_to_appid->emplace(info->name, info->uid).second;
-    packagelist_free(info);
-    return res;
-}
-
-static bool read_package_list(struct fuse_global* global) {
-    pthread_mutex_lock(&global->lock);
-
-    global->package_to_appid->clear();
-    bool rc = packagelist_parse(package_parse_callback, global);
-    DLOG(INFO) << "read_package_list: found " << global->package_to_appid->size() << " packages";
-
-    // Regenerate ownership details using newly loaded mapping.
-    derive_permissions_recursive_locked(global->fuse_default, &global->root);
-
-    pthread_mutex_unlock(&global->lock);
-
-    return rc;
-}
-
-static void watch_package_list(struct fuse_global* global) {
-    struct inotify_event *event;
-    char event_buf[512];
-
-    int nfd = inotify_init();
-    if (nfd == -1) {
-        PLOG(ERROR) << "inotify_init failed";
-        return;
-    }
-
-    bool active = false;
-    while (1) {
-        if (!active) {
-            int res = inotify_add_watch(nfd, PACKAGES_LIST_FILE, IN_DELETE_SELF);
-            if (res == -1) {
-                if (errno == ENOENT || errno == EACCES) {
-                    /* Framework may not have created the file yet, sleep and retry. */
-                    LOG(ERROR) << "missing \"" << PACKAGES_LIST_FILE << "\"; retrying...";
-                    sleep(3);
-                    continue;
-                } else {
-                    PLOG(ERROR) << "inotify_add_watch failed";
-                    return;
-                }
-            }
-
-            /* Watch above will tell us about any future changes, so
-             * read the current state. */
-            if (read_package_list(global) == false) {
-                LOG(ERROR) << "read_package_list failed";
-                return;
-            }
-            active = true;
-        }
-
-        int event_pos = 0;
-        ssize_t res = TEMP_FAILURE_RETRY(read(nfd, event_buf, sizeof(event_buf)));
-        if (res == -1) {
-            PLOG(ERROR) << "failed to read inotify event";
-            return;
-        } else if (static_cast<size_t>(res) < sizeof(*event)) {
-            LOG(ERROR) << "failed to read inotify event: read " << res << " expected "
-                       << sizeof(event_buf);
-            return;
-        }
-
-        while (res >= static_cast<ssize_t>(sizeof(*event))) {
-            int event_size;
-            event = reinterpret_cast<struct inotify_event*>(event_buf + event_pos);
-
-            DLOG(INFO) << "inotify event: " << std::hex << event->mask << std::dec;
-            if ((event->mask & IN_IGNORED) == IN_IGNORED) {
-                /* Previously watched file was deleted, probably due to move
-                 * that swapped in new data; re-arm the watch and read. */
-                active = false;
-            }
-
-            event_size = sizeof(*event) + event->len;
-            res -= event_size;
-            event_pos += event_size;
-        }
-    }
-}
-
-static int fuse_setup(struct fuse* fuse, gid_t gid, mode_t mask) {
-    char opts[256];
-
-    fuse->fd = TEMP_FAILURE_RETRY(open("/dev/fuse", O_RDWR | O_CLOEXEC));
-    if (fuse->fd == -1) {
-        PLOG(ERROR) << "failed to open fuse device";
-        return -1;
-    }
-
-    umount2(fuse->dest_path, MNT_DETACH);
-
-    snprintf(opts, sizeof(opts),
-            "fd=%i,rootmode=40000,default_permissions,allow_other,user_id=%d,group_id=%d",
-            fuse->fd, fuse->global->uid, fuse->global->gid);
-    if (mount("/dev/fuse", fuse->dest_path, "fuse", MS_NOSUID | MS_NODEV | MS_NOEXEC | MS_NOATIME,
-              opts) == -1) {
-        PLOG(ERROR) << "failed to mount fuse filesystem";
-        return -1;
-    }
-
-    fuse->gid = gid;
-    fuse->mask = mask;
-
-    return 0;
-}
-
 static void drop_privs(uid_t uid, gid_t gid) {
     ScopedMinijail j(minijail_new());
     minijail_set_supplementary_gids(j.get(), arraysize(kGroups), kGroups);
@@ -205,130 +59,28 @@
     minijail_enter(j.get());
 }
 
-static void* start_handler(void* data) {
-    struct fuse_handler* handler = static_cast<fuse_handler*>(data);
-    handle_fuse_requests(handler);
-    return NULL;
-}
+static bool sdcardfs_setup(const std::string& source_path, const std::string& dest_path,
+                           uid_t fsuid, gid_t fsgid, bool multi_user, userid_t userid, gid_t gid,
+                           mode_t mask, bool derive_gid, bool default_normal) {
+    // Try several attempts, each time with one less option, to gracefully
+    // handle older kernels that aren't updated yet.
+    for (int i = 0; i < 4; i++) {
+        std::string new_opts;
+        if (multi_user && i < 3) new_opts += "multiuser,";
+        if (derive_gid && i < 2) new_opts += "derive_gid,";
+        if (default_normal && i < 1) new_opts += "default_normal,";
 
-static void run(const char* source_path, const char* label, uid_t uid,
-        gid_t gid, userid_t userid, bool multi_user, bool full_write) {
-    struct fuse_global global;
-    struct fuse fuse_default;
-    struct fuse fuse_read;
-    struct fuse fuse_write;
-    struct fuse_handler handler_default;
-    struct fuse_handler handler_read;
-    struct fuse_handler handler_write;
-    pthread_t thread_default;
-    pthread_t thread_read;
-    pthread_t thread_write;
-
-    memset(&global, 0, sizeof(global));
-    memset(&fuse_default, 0, sizeof(fuse_default));
-    memset(&fuse_read, 0, sizeof(fuse_read));
-    memset(&fuse_write, 0, sizeof(fuse_write));
-    memset(&handler_default, 0, sizeof(handler_default));
-    memset(&handler_read, 0, sizeof(handler_read));
-    memset(&handler_write, 0, sizeof(handler_write));
-
-    pthread_mutex_init(&global.lock, NULL);
-    global.package_to_appid = new AppIdMap;
-    global.uid = uid;
-    global.gid = gid;
-    global.multi_user = multi_user;
-    global.next_generation = 0;
-    global.inode_ctr = 1;
-
-    memset(&global.root, 0, sizeof(global.root));
-    global.root.nid = FUSE_ROOT_ID; /* 1 */
-    global.root.refcount = 2;
-    global.root.namelen = strlen(source_path);
-    global.root.name = strdup(source_path);
-    global.root.userid = userid;
-    global.root.uid = AID_ROOT;
-    global.root.under_android = false;
-
-    strcpy(global.source_path, source_path);
-
-    if (multi_user) {
-        global.root.perm = PERM_PRE_ROOT;
-        snprintf(global.obb_path, sizeof(global.obb_path), "%s/obb", source_path);
-    } else {
-        global.root.perm = PERM_ROOT;
-        snprintf(global.obb_path, sizeof(global.obb_path), "%s/Android/obb", source_path);
-    }
-
-    fuse_default.global = &global;
-    fuse_read.global = &global;
-    fuse_write.global = &global;
-
-    global.fuse_default = &fuse_default;
-    global.fuse_read = &fuse_read;
-    global.fuse_write = &fuse_write;
-
-    snprintf(fuse_default.dest_path, PATH_MAX, "/mnt/runtime/default/%s", label);
-    snprintf(fuse_read.dest_path, PATH_MAX, "/mnt/runtime/read/%s", label);
-    snprintf(fuse_write.dest_path, PATH_MAX, "/mnt/runtime/write/%s", label);
-
-    handler_default.fuse = &fuse_default;
-    handler_read.fuse = &fuse_read;
-    handler_write.fuse = &fuse_write;
-
-    handler_default.token = 0;
-    handler_read.token = 1;
-    handler_write.token = 2;
-
-    umask(0);
-
-    if (multi_user) {
-        /* Multi-user storage is fully isolated per user, so "other"
-         * permissions are completely masked off. */
-        if (fuse_setup(&fuse_default, AID_SDCARD_RW, 0006)
-                || fuse_setup(&fuse_read, AID_EVERYBODY, 0027)
-                || fuse_setup(&fuse_write, AID_EVERYBODY, full_write ? 0007 : 0027)) {
-            PLOG(FATAL) << "failed to fuse_setup";
-        }
-    } else {
-        /* Physical storage is readable by all users on device, but
-         * the Android directories are masked off to a single user
-         * deep inside attr_from_stat(). */
-        if (fuse_setup(&fuse_default, AID_SDCARD_RW, 0006)
-                || fuse_setup(&fuse_read, AID_EVERYBODY, full_write ? 0027 : 0022)
-                || fuse_setup(&fuse_write, AID_EVERYBODY, full_write ? 0007 : 0022)) {
-            PLOG(FATAL) << "failed to fuse_setup";
+        auto opts = android::base::StringPrintf("fsuid=%d,fsgid=%d,%smask=%d,userid=%d,gid=%d",
+                                                fsuid, fsgid, new_opts.c_str(), mask, userid, gid);
+        if (mount(source_path.c_str(), dest_path.c_str(), "sdcardfs",
+                  MS_NOSUID | MS_NODEV | MS_NOEXEC | MS_NOATIME, opts.c_str()) == -1) {
+            PLOG(WARNING) << "Failed to mount sdcardfs with options " << opts;
+        } else {
+            return true;
         }
     }
 
-    // Will abort if priv-dropping fails.
-    drop_privs(uid, gid);
-
-    if (multi_user) {
-        fs_prepare_dir(global.obb_path, 0775, uid, gid);
-    }
-
-    if (pthread_create(&thread_default, NULL, start_handler, &handler_default)
-            || pthread_create(&thread_read, NULL, start_handler, &handler_read)
-            || pthread_create(&thread_write, NULL, start_handler, &handler_write)) {
-        LOG(FATAL) << "failed to pthread_create";
-    }
-
-    watch_package_list(&global);
-    LOG(FATAL) << "terminated prematurely";
-}
-
-static bool sdcardfs_setup(const std::string& source_path, const std::string& dest_path, uid_t fsuid,
-                        gid_t fsgid, bool multi_user, userid_t userid, gid_t gid, mode_t mask) {
-    std::string opts = android::base::StringPrintf("fsuid=%d,fsgid=%d,%smask=%d,userid=%d,gid=%d",
-            fsuid, fsgid, multi_user?"multiuser,":"", mask, userid, gid);
-
-    if (mount(source_path.c_str(), dest_path.c_str(), "sdcardfs",
-              MS_NOSUID | MS_NODEV | MS_NOEXEC | MS_NOATIME, opts.c_str()) == -1) {
-        PLOG(ERROR) << "failed to mount sdcardfs filesystem";
-        return false;
-    }
-
-    return true;
+    return false;
 }
 
 static bool sdcardfs_setup_bind_remount(const std::string& source_path, const std::string& dest_path,
@@ -353,7 +105,8 @@
 }
 
 static void run_sdcardfs(const std::string& source_path, const std::string& label, uid_t uid,
-        gid_t gid, userid_t userid, bool multi_user, bool full_write) {
+                         gid_t gid, userid_t userid, bool multi_user, bool full_write,
+                         bool derive_gid, bool default_normal) {
     std::string dest_path_default = "/mnt/runtime/default/" + label;
     std::string dest_path_read = "/mnt/runtime/read/" + label;
     std::string dest_path_write = "/mnt/runtime/write/" + label;
@@ -363,10 +116,10 @@
         // Multi-user storage is fully isolated per user, so "other"
         // permissions are completely masked off.
         if (!sdcardfs_setup(source_path, dest_path_default, uid, gid, multi_user, userid,
-                                                      AID_SDCARD_RW, 0006)
-                || !sdcardfs_setup_bind_remount(dest_path_default, dest_path_read, AID_EVERYBODY, 0027)
-                || !sdcardfs_setup_bind_remount(dest_path_default, dest_path_write,
-                                                      AID_EVERYBODY, full_write ? 0007 : 0027)) {
+                            AID_SDCARD_RW, 0006, derive_gid, default_normal) ||
+            !sdcardfs_setup_bind_remount(dest_path_default, dest_path_read, AID_EVERYBODY, 0027) ||
+            !sdcardfs_setup_bind_remount(dest_path_default, dest_path_write, AID_EVERYBODY,
+                                         full_write ? 0007 : 0027)) {
             LOG(FATAL) << "failed to sdcardfs_setup";
         }
     } else {
@@ -374,11 +127,11 @@
         // the Android directories are masked off to a single user
         // deep inside attr_from_stat().
         if (!sdcardfs_setup(source_path, dest_path_default, uid, gid, multi_user, userid,
-                                                      AID_SDCARD_RW, 0006)
-                || !sdcardfs_setup_bind_remount(dest_path_default, dest_path_read,
-                                                      AID_EVERYBODY, full_write ? 0027 : 0022)
-                || !sdcardfs_setup_bind_remount(dest_path_default, dest_path_write,
-                                                      AID_EVERYBODY, full_write ? 0007 : 0022)) {
+                            AID_SDCARD_RW, 0006, derive_gid, default_normal) ||
+            !sdcardfs_setup_bind_remount(dest_path_default, dest_path_read, AID_EVERYBODY,
+                                         full_write ? 0027 : 0022) ||
+            !sdcardfs_setup_bind_remount(dest_path_default, dest_path_write, AID_EVERYBODY,
+                                         full_write ? 0007 : 0022)) {
             LOG(FATAL) << "failed to sdcardfs_setup";
         }
     }
@@ -394,48 +147,14 @@
     exit(0);
 }
 
-static bool supports_sdcardfs(void) {
-    std::string filesystems;
-    if (!android::base::ReadFileToString("/proc/filesystems", &filesystems)) {
-        PLOG(ERROR) << "Could not read /proc/filesystems";
-        return false;
-    }
-    for (const auto& fs : android::base::Split(filesystems, "\n")) {
-        if (fs.find("sdcardfs") != std::string::npos) return true;
-    }
-    return false;
-}
-
-static bool should_use_sdcardfs(void) {
-    char property[PROPERTY_VALUE_MAX];
-
-    // Allow user to have a strong opinion about state
-    property_get(PROP_SDCARDFS_USER, property, "");
-    if (!strcmp(property, "force_on")) {
-        LOG(WARNING) << "User explicitly enabled sdcardfs";
-        return supports_sdcardfs();
-    } else if (!strcmp(property, "force_off")) {
-        LOG(WARNING) << "User explicitly disabled sdcardfs";
-        return false;
-    }
-
-    // Fall back to device opinion about state
-    if (property_get_bool(PROP_SDCARDFS_DEVICE, false)) {
-        LOG(WARNING) << "Device explicitly enabled sdcardfs";
-        return supports_sdcardfs();
-    } else {
-        LOG(WARNING) << "Device explicitly disabled sdcardfs";
-        return false;
-    }
-}
-
 static int usage() {
     LOG(ERROR) << "usage: sdcard [OPTIONS] <source_path> <label>"
                << "    -u: specify UID to run as"
                << "    -g: specify GID to run as"
                << "    -U: specify user ID that owns device"
                << "    -m: source_path is multi-user"
-               << "    -w: runtime write mount has full write access";
+               << "    -w: runtime write mount has full write access"
+               << "    -P  preserve owners on the lower file system";
     return 1;
 }
 
@@ -447,12 +166,17 @@
     userid_t userid = 0;
     bool multi_user = false;
     bool full_write = false;
+    bool derive_gid = false;
+    bool default_normal = false;
     int i;
     struct rlimit rlim;
     int fs_version;
 
+    setenv("ANDROID_LOG_TAGS", "*:v", 1);
+    android::base::InitLogging(argv, android::base::LogdLogger(android::base::SYSTEM));
+
     int opt;
-    while ((opt = getopt(argc, argv, "u:g:U:mw")) != -1) {
+    while ((opt = getopt(argc, argv, "u:g:U:mwGi")) != -1) {
         switch (opt) {
             case 'u':
                 uid = strtoul(optarg, NULL, 10);
@@ -469,6 +193,12 @@
             case 'w':
                 full_write = true;
                 break;
+            case 'G':
+                derive_gid = true;
+                break;
+            case 'i':
+                default_normal = true;
+                break;
             case '?':
             default:
                 return usage();
@@ -511,10 +241,7 @@
         sleep(1);
     }
 
-    if (should_use_sdcardfs()) {
-        run_sdcardfs(source_path, label, uid, gid, userid, multi_user, full_write);
-    } else {
-        run(source_path, label, uid, gid, userid, multi_user, full_write);
-    }
+    run_sdcardfs(source_path, label, uid, gid, userid, multi_user, full_write, derive_gid,
+                 default_normal);
     return 1;
 }
diff --git a/shell_and_utilities/Android.bp b/shell_and_utilities/Android.bp
index 9620d63..3d7521c 100644
--- a/shell_and_utilities/Android.bp
+++ b/shell_and_utilities/Android.bp
@@ -1,16 +1,48 @@
 phony {
     name: "shell_and_utilities",
     required: [
+        "shell_and_utilities_system",
+        "shell_and_utilities_recovery",
+        "shell_and_utilities_vendor",
+    ],
+}
+
+phony {
+    name: "shell_and_utilities_system",
+    required: [
+        "awk",
         "bzip2",
         "grep",
-        "grep_vendor",
+        "logwrapper",
         "mkshrc",
-        "mkshrc_vendor",
+        "newfs_msdos",
         "reboot",
         "sh",
-        "sh_vendor",
+        "tcpdump",
         "toolbox",
         "toybox",
+    ],
+}
+
+phony {
+    name: "shell_and_utilities_recovery",
+    required: [
+        "grep.recovery",
+        "sh.recovery",
+        "toolbox.recovery",
+        "toybox.recovery",
+    ],
+}
+
+phony {
+    name: "shell_and_utilities_vendor",
+    required: [
+        "awk_vendor",
+        "grep_vendor",
+        "logwrapper_vendor",
+        "mkshrc_vendor",
+        "sh_vendor",
+        "toolbox_vendor",
         "toybox_vendor",
     ],
 }
diff --git a/shell_and_utilities/OWNERS b/shell_and_utilities/OWNERS
new file mode 100644
index 0000000..682a067
--- /dev/null
+++ b/shell_and_utilities/OWNERS
@@ -0,0 +1 @@
+enh@google.com
diff --git a/shell_and_utilities/README.md b/shell_and_utilities/README.md
index 5d10c18..d8cf867 100644
--- a/shell_and_utilities/README.md
+++ b/shell_and_utilities/README.md
@@ -4,20 +4,24 @@
 Since IceCreamSandwich Android has used
 [mksh](https://www.mirbsd.org/mksh.htm) as its shell. Before then it used
 [ash](https://en.wikipedia.org/wiki/Almquist_shell) (which actually
-remained in the tree up to and including KitKat).
+remained unused in the tree up to and including KitKat).
 
-Initially Android had a very limited command-line provided by its
-own "toolbox" binary. These days almost everything is supplied by
+Initially Android had a very limited command-line provided by its own
+"toolbox" binary. Since Marshmallow almost everything is supplied by
 [toybox](http://landley.net/toybox/) instead.
 
 We started moving a few of the more important tools to full
-BSD implementations in JellyBean before we started in earnest in
+BSD implementations in JellyBean, and continued this work in
 Lollipop. Lollipop was a major break with the past in many ways (LP64
 support and the switch to ART both having lots of knock-on effects around
 the system), so although this was the beginning of the end of toolbox it
 (a) didn't stand out given all the other systems-level changes and (b)
 in Marshmallow we changed direction and started the move to toybox.
 
+Not everything is provided by toybox, though. For the bzip2 command-line tools
+we use the ones that are part of the bzip2 distribution. The awk added in
+Android P is Brian Kernighan's "one true" awk.
+
 The lists below show what tools were provided and where they came from in
 each release starting with Gingerbread. This doesn't tell the full story,
 because the toolbox implementations did have bugs fixed and options added
@@ -25,6 +29,10 @@
 `-f`. But this gives you an idea of what was available in any given release,
 and how usable it was likely to be.
 
+Also note that in any given release `toybox` probably contains more
+commands than there are symlinks for in `/system/bin`. You can get the
+full list for a release by running `toybox` directly.
+
 
 Android 2.3 (Gingerbread)
 -------------------------
@@ -132,26 +140,78 @@
 uptime usleep vmstat wc which whoami xargs xxd yes
 
 
-Current AOSP
-------------
+Android 8.0 (Oreo)
+------------------
 
 BSD: dd grep
 
 bzip2: bzcat bzip2 bunzip2
 
-toolbox: getevent gzip newfs\_msdos gunzip zcat
+toolbox: getevent newfs\_msdos
 
 toybox: acpi base64 basename blockdev cal cat chcon chgrp chmod chown
 chroot chrt cksum clear cmp comm cp cpio cut date df diff dirname dmesg
 dos2unix du echo env expand expr fallocate false file find flock free
-getenforce getprop groups head hostname hwclock id ifconfig inotifyd
-insmod ionice iorenice kill killall ln load\_policy log logname losetup
-ls lsmod lsof lsusb md5sum microcom mkdir mknod mkswap mktemp modinfo
-modprobe more mount mountpoint mv netstat nice nl nohup od paste patch
-pgrep pidof pkill pmap printenv printf ps pwd readlink realpath renice
-restorecon rm rmdir rmmod runcon sed sendevent seq setenforce setprop
-setsid sha1sum sha224sum sha256sum sha384sum sha512sum sleep sort split
-start stat stop strings swapoff swapon sync sysctl tac tail tar taskset
-tee time timeout top touch tr true truncate tty ulimit umount uname uniq
-unix2dos uptime usleep uudecode uuencode vmstat wc which whoami xargs
-xxd yes
+getenforce getprop groups gunzip gzip head hostname hwclock id ifconfig
+inotifyd insmod ionice iorenice kill killall ln load\_policy log logname
+losetup ls lsmod lsof lspci lsusb md5sum microcom mkdir mkfifo mknod
+mkswap mktemp modinfo modprobe more mount mountpoint mv netstat nice
+nl nohup od paste patch pgrep pidof pkill pmap printenv printf ps pwd
+readlink realpath renice restorecon rm rmdir rmmod runcon sed sendevent
+seq setenforce setprop setsid sha1sum sha224sum sha256sum sha384sum
+sha512sum sleep sort split start stat stop strings swapoff swapon sync
+sysctl tac tail tar taskset tee time timeout top touch tr true truncate
+tty ulimit umount uname uniq unix2dos uptime usleep uudecode uuencode
+vmstat wc which whoami xargs xxd yes zcat
+
+Android P
+---------
+
+BSD: dd grep
+
+bzip2: bzcat bzip2 bunzip2
+
+one-true-awk: awk
+
+toolbox: getevent getprop newfs\_msdos
+
+toybox: acpi base64 basename blockdev cal cat chcon chgrp chmod chown
+chroot chrt cksum clear cmp comm cp cpio cut date df diff dirname dmesg
+dos2unix du echo env expand expr fallocate false file find flock fmt free
+getenforce groups gunzip gzip head hostname hwclock id ifconfig inotifyd
+insmod ionice iorenice kill killall ln load\_policy log logname losetup ls
+lsmod lsof lspci lsusb md5sum microcom mkdir mkfifo mknod mkswap mktemp
+modinfo modprobe more mount mountpoint mv netstat nice nl nohup od paste
+patch pgrep pidof pkill pmap printenv printf ps pwd readlink realpath
+renice restorecon rm rmdir rmmod runcon sed sendevent seq setenforce
+setprop setsid sha1sum sha224sum sha256sum sha384sum sha512sum sleep
+sort split start stat stop strings stty swapoff swapon sync sysctl tac
+tail tar taskset tee time timeout top touch tr true truncate tty ulimit
+umount uname uniq unix2dos uptime usleep uudecode uuencode vmstat wc
+which whoami xargs xxd yes zcat
+
+Android Q
+---------
+
+BSD: grep fsck\_msdos newfs\_msdos
+
+bzip2: bzcat bzip2 bunzip2
+
+one-true-awk: awk
+
+toolbox: getevent getprop
+
+toybox: acpi base64 basename blockdev cal cat chcon chgrp chmod chown
+chroot chrt cksum clear cmp comm cp cpio cut date dd df diff dirname
+dmesg dos2unix du echo env expand expr fallocate false file find flock
+fmt free getenforce groups gunzip gzip head hostname hwclock id ifconfig
+inotifyd insmod ionice iorenice kill killall ln load\_policy log logname
+losetup ls lsmod lsof lspci lsusb md5sum microcom mkdir mkfifo mknod
+mkswap mktemp modinfo modprobe more mount mountpoint mv nc netcat netstat
+nice nl nohup nsenter od paste patch pgrep pidof pkill pmap printenv
+printf ps pwd readlink realpath renice restorecon rm rmdir rmmod runcon
+sed sendevent seq setenforce setprop setsid sha1sum sha224sum sha256sum
+sha384sum sha512sum sleep sort split start stat stop strings stty swapoff
+swapon sync sysctl tac tail tar taskset tee time timeout top touch tr
+true truncate tty ulimit umount uname uniq unix2dos unshare uptime usleep
+uudecode uuencode vmstat wc which whoami xargs xxd yes zcat
diff --git a/storaged/Android.mk b/storaged/Android.mk
new file mode 100644
index 0000000..a1abe0f
--- /dev/null
+++ b/storaged/Android.mk
@@ -0,0 +1,44 @@
+# Copyright 2016 The Android Open Source Project
+
+LOCAL_PATH := $(call my-dir)
+
+LIBSTORAGED_SHARED_LIBRARIES := \
+    libbinder \
+    libbase \
+    libutils \
+    libcutils \
+    liblog \
+    libsysutils \
+    libbatteryservice \
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+    storaged.cpp \
+    storaged_info.cpp \
+    storaged_service.cpp \
+    storaged_utils.cpp \
+    storaged_uid_monitor.cpp \
+    EventLogTags.logtags
+
+LOCAL_MODULE := libstoraged
+LOCAL_CFLAGS := -Werror
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/include external/googletest/googletest/include
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
+LOCAL_SHARED_LIBRARIES := $(LIBSTORAGED_SHARED_LIBRARIES)
+include $(BUILD_STATIC_LIBRARY)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := storaged
+LOCAL_INIT_RC := storaged.rc
+LOCAL_SRC_FILES := main.cpp
+# libstoraged is an internal static library, only main.cpp and storaged_test.cpp should be using it
+LOCAL_STATIC_LIBRARIES := libstoraged
+LOCAL_SHARED_LIBRARIES := $(LIBSTORAGED_SHARED_LIBRARIES)
+LOCAL_CFLAGS := -Wall -Werror -Wno-unused-parameter
+LOCAL_C_INCLUDES := external/googletest/googletest/include
+
+include $(BUILD_EXECUTABLE)
+
+include $(call first-makefiles-under,$(LOCAL_PATH))
diff --git a/storaged/EventLogTags.logtags b/storaged/EventLogTags.logtags
new file mode 100644
index 0000000..2e25d4a
--- /dev/null
+++ b/storaged/EventLogTags.logtags
@@ -0,0 +1,39 @@
+# The entries in this file map a sparse set of log tag numbers to tag names.
+# This is installed on the device, in /system/etc, and parsed by logcat.
+#
+# Tag numbers are decimal integers, from 0 to 2^31.  (Let's leave the
+# negative values alone for now.)
+#
+# Tag names are one or more ASCII letters and numbers or underscores, i.e.
+# "[A-Z][a-z][0-9]_".  Do not include spaces or punctuation (the former
+# impacts log readability, the latter makes regex searches more annoying).
+#
+# Tag numbers and names are separated by whitespace.  Blank lines and lines
+# starting with '#' are ignored.
+#
+# Optionally, after the tag names can be put a description for the value(s)
+# of the tag. Description are in the format
+#    (<name>|data type[|data unit])
+# Multiple values are separated by commas.
+#
+# The data type is a number from the following values:
+# 1: int
+# 2: long
+# 3: string
+# 4: list
+# 5: float
+#
+# The data unit is a number taken from the following list:
+# 1: Number of objects
+# 2: Number of bytes
+# 3: Number of milliseconds
+# 4: Number of allocations
+# 5: Id
+# 6: Percent
+# Default value for data of type int/long is 2 (bytes).
+#
+# TODO: generate ".java" and ".h" files with integer constants from this file.
+
+2732 storaged_disk_stats (type|3),(start_time|2|3),(end_time|2|3),(read_ios|2|1),(read_merges|2|1),(read_sectors|2|1),(read_ticks|2|3),(write_ios|2|1),(write_merges|2|1),(write_sectors|2|1),(write_ticks|2|3),(o_in_flight|2|1),(io_ticks|2|3),(io_in_queue|2|1)
+
+2733 storaged_emmc_info (mmc_ver|3),(eol|1),(lifetime_a|1),(lifetime_b|1)
\ No newline at end of file
diff --git a/storaged/OWNERS b/storaged/OWNERS
new file mode 100644
index 0000000..d033f00
--- /dev/null
+++ b/storaged/OWNERS
@@ -0,0 +1,2 @@
+salyzyn@google.com
+dvander@google.com
diff --git a/storaged/README.properties b/storaged/README.properties
new file mode 100644
index 0000000..2d8397f
--- /dev/null
+++ b/storaged/README.properties
@@ -0,0 +1,5 @@
+ro.storaged.event.interval    # interval storaged scans for IO stats, in seconds
+ro.storaged.event.perf_check  # check for time spent in event loop, in microseconds
+ro.storaged.disk_stats_pub    # interval storaged publish disk stats, in seconds
+ro.storaged.uid_io.interval   # interval storaged checks Per UID IO usage, in seconds
+ro.storaged.uid_io.threshold  # Per UID IO usage limit, in bytes
diff --git a/storaged/include/storaged.h b/storaged/include/storaged.h
new file mode 100644
index 0000000..fa68406
--- /dev/null
+++ b/storaged/include/storaged.h
@@ -0,0 +1,299 @@
+/*
+ * Copyright (C) 2016 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 _STORAGED_H_
+#define _STORAGED_H_
+
+#include <semaphore.h>
+#include <stdint.h>
+#include <time.h>
+
+#include <queue>
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+#include <batteryservice/IBatteryPropertiesListener.h>
+#include <batteryservice/IBatteryPropertiesRegistrar.h>
+
+#include "storaged_info.h"
+#include "storaged_uid_monitor.h"
+
+using namespace android;
+
+#define FRIEND_TEST(test_case_name, test_name) \
+friend class test_case_name##_##test_name##_Test
+
+/* For debug */
+#ifdef DEBUG
+#define debuginfo(fmt, ...) \
+ do {printf("%s():\t" fmt "\t[%s:%d]\n", __FUNCTION__, ##__VA_ARGS__, __FILE__, __LINE__);} \
+ while(0)
+#else
+#define debuginfo(...)
+#endif
+
+#define ARRAY_SIZE(x)   (sizeof(x) / sizeof((x)[0]))
+
+#define SECTOR_SIZE ( 512 )
+#define SEC_TO_MSEC ( 1000 )
+#define MSEC_TO_USEC ( 1000 )
+#define USEC_TO_NSEC ( 1000 )
+#define SEC_TO_USEC ( 1000000 )
+#define HOUR_TO_SEC ( 3600 )
+#define DAY_TO_SEC ( 3600 * 24 )
+
+// number of attributes diskstats has
+#define DISK_STATS_SIZE ( 11 )
+// maximum size limit of a stats file
+#define DISK_STATS_FILE_MAX_SIZE ( 256 )
+#define DISK_STATS_IO_IN_FLIGHT_IDX ( 8 )
+struct disk_stats {
+    /* It will be extremely unlikely for any of the following entries to overflow.
+     * For read_bytes(which will be greater than any of the following entries), it
+     * will take 27 years to overflow uint64_t at the reading rate of 20GB/s, which
+     * is the peak memory transfer rate for current memory.
+     * The diskstats entries (first 11) need to be at top in this structure _after_
+     * compiler's optimization.
+     */
+    uint64_t read_ios;       // number of read I/Os processed
+    uint64_t read_merges;    // number of read I/Os merged with in-queue I/Os
+    uint64_t read_sectors;   // number of sectors read
+    uint64_t read_ticks;     // total wait time for read requests
+    uint64_t write_ios;      // number of write I/Os processed
+    uint64_t write_merges;   // number of write I/Os merged with in-queue I/Os
+    uint64_t write_sectors;  // number of sectors written
+    uint64_t write_ticks;    // total wait time for write requests
+    uint64_t io_in_flight;   // number of I/Os currently in flight
+    uint64_t io_ticks;       // total time this block device has been active
+    uint64_t io_in_queue;    // total wait time for all requests
+
+    uint64_t start_time;     // monotonic time accounting starts
+    uint64_t end_time;       // monotonic time accounting ends
+    uint32_t counter;        // private counter for accumulate calculations
+    double   io_avg;         // average io_in_flight for accumulate calculations
+};
+
+
+
+struct disk_perf {
+    uint32_t read_perf;         // read speed (kbytes/s)
+    uint32_t read_ios;          // read I/Os per second
+    uint32_t write_perf;        // write speed (kbytes/s)
+    uint32_t write_ios;         // write I/Os per second
+    uint32_t queue;             // I/Os in queue
+};
+
+#define CMD_MAX_LEN ( 64 )
+struct task_info {
+    uint32_t pid;                   // task id
+    uint64_t rchar;                 // characters read
+    uint64_t wchar;                 // characters written
+    uint64_t syscr;                 // read syscalls
+    uint64_t syscw;                 // write syscalls
+    uint64_t read_bytes;            // bytes read (from storage layer)
+    uint64_t write_bytes;           // bytes written (to storage layer)
+    uint64_t cancelled_write_bytes; // cancelled write byte by truncate
+
+    uint64_t starttime;             // start time of task
+
+    char cmd[CMD_MAX_LEN];          // filename of the executable
+};
+
+class lock_t {
+    sem_t* mSem;
+public:
+    lock_t(sem_t* sem) {
+        mSem = sem;
+        sem_wait(mSem);
+    }
+    ~lock_t() {
+        sem_post(mSem);
+    }
+};
+
+class stream_stats {
+private:
+    double mSum;
+    double mSquareSum;
+    uint32_t mCnt;
+public:
+    stream_stats() : mSum(0), mSquareSum(0), mCnt(0) {};
+    ~stream_stats() {};
+    double get_mean() {
+        return mSum / mCnt;
+    }
+    double get_std() {
+        return sqrt(mSquareSum / mCnt - mSum * mSum / (mCnt * mCnt));
+    }
+    void add(uint32_t num) {
+        mSum += (double)num;
+        mSquareSum += (double)num * (double)num;
+        mCnt++;
+    }
+    void evict(uint32_t num) {
+        if (mSum < num || mSquareSum < (double)num * (double)num) return;
+        mSum -= (double)num;
+        mSquareSum -= (double)num * (double)num;
+        mCnt--;
+    }
+};
+
+#define MMC_DISK_STATS_PATH "/sys/block/mmcblk0/stat"
+#define SDA_DISK_STATS_PATH "/sys/block/sda/stat"
+#define EMMC_ECSD_PATH "/d/mmc0/mmc0:0001/ext_csd"
+#define UID_IO_STATS_PATH "/proc/uid_io/stats"
+
+class disk_stats_monitor {
+private:
+    FRIEND_TEST(storaged_test, disk_stats_monitor);
+    const char* DISK_STATS_PATH;
+    struct disk_stats mPrevious;
+    struct disk_stats mAccumulate;
+    bool mStall;
+    std::queue<struct disk_perf> mBuffer;
+    struct {
+        stream_stats read_perf;           // read speed (bytes/s)
+        stream_stats read_ios;            // read I/Os per second
+        stream_stats write_perf;          // write speed (bytes/s)
+        stream_stats write_ios;           // write I/O per second
+        stream_stats queue;               // I/Os in queue
+    } mStats;
+    bool mValid;
+    const uint32_t mWindow;
+    const double mSigma;
+    struct disk_perf mMean;
+    struct disk_perf mStd;
+
+    void update_mean();
+    void update_std();
+    void add(struct disk_perf* perf);
+    void evict(struct disk_perf* perf);
+    bool detect(struct disk_perf* perf);
+
+    void update(struct disk_stats* stats);
+
+public:
+    disk_stats_monitor(uint32_t window_size = 5, double sigma = 1.0) :
+            mStall(false),
+            mValid(false),
+            mWindow(window_size),
+            mSigma(sigma) {
+        memset(&mPrevious, 0, sizeof(mPrevious));
+        memset(&mMean, 0, sizeof(mMean));
+        memset(&mStd, 0, sizeof(mStd));
+
+        if (access(MMC_DISK_STATS_PATH, R_OK) >= 0) {
+            DISK_STATS_PATH = MMC_DISK_STATS_PATH;
+        } else {
+            DISK_STATS_PATH = SDA_DISK_STATS_PATH;
+        }
+    }
+    void update(void);
+};
+
+class disk_stats_publisher {
+private:
+    FRIEND_TEST(storaged_test, disk_stats_publisher);
+    const char* DISK_STATS_PATH;
+    struct disk_stats mAccumulate;
+    struct disk_stats mPrevious;
+public:
+    disk_stats_publisher(void) {
+        memset(&mAccumulate, 0, sizeof(struct disk_stats));
+        memset(&mPrevious, 0, sizeof(struct disk_stats));
+
+        if (access(MMC_DISK_STATS_PATH, R_OK) >= 0) {
+            DISK_STATS_PATH = MMC_DISK_STATS_PATH;
+        } else {
+            DISK_STATS_PATH = SDA_DISK_STATS_PATH;
+        }
+    }
+
+    ~disk_stats_publisher(void) {}
+    void publish(void);
+    void update(void);
+};
+
+// Periodic chores intervals in seconds
+#define DEFAULT_PERIODIC_CHORES_INTERVAL_UNIT ( 60 )
+#define DEFAULT_PERIODIC_CHORES_INTERVAL_DISK_STATS_PUBLISH ( 3600 )
+#define DEFAULT_PERIODIC_CHORES_INTERVAL_UID_IO ( 3600 )
+#define DEFAULT_PERIODIC_CHORES_INTERVAL_UID_IO_LIMIT (300)
+
+// UID IO threshold in bytes
+#define DEFAULT_PERIODIC_CHORES_UID_IO_THRESHOLD ( 1024 * 1024 * 1024ULL )
+
+struct storaged_config {
+    int periodic_chores_interval_unit;
+    int periodic_chores_interval_disk_stats_publish;
+    int periodic_chores_interval_uid_io;
+    bool proc_uid_io_available;      // whether uid_io is accessible
+    bool diskstats_available;   // whether diskstats is accessible
+    int event_time_check_usec;  // check how much cputime spent in event loop
+};
+
+class storaged_t : public BnBatteryPropertiesListener,
+                   public IBinder::DeathRecipient {
+private:
+    time_t mTimer;
+    storaged_config mConfig;
+    disk_stats_publisher mDiskStats;
+    disk_stats_monitor mDsm;
+    uid_monitor mUidm;
+    time_t mStarttime;
+    sp<IBatteryPropertiesRegistrar> battery_properties;
+    std::unique_ptr<storage_info_t> storage_info;
+public:
+    storaged_t(void);
+    ~storaged_t() {}
+    void event(void);
+    void event_checked(void);
+    void pause(void) {
+        sleep(mConfig.periodic_chores_interval_unit);
+    }
+
+    time_t get_starttime(void) {
+        return mStarttime;
+    }
+
+    std::unordered_map<uint32_t, struct uid_info> get_uids(void) {
+        return mUidm.get_uid_io_stats();
+    }
+    std::map<uint64_t, struct uid_records> get_uid_records(
+            double hours, uint64_t threshold, bool force_report) {
+        return mUidm.dump(hours, threshold, force_report);
+    }
+    void update_uid_io_interval(int interval) {
+        if (interval >= DEFAULT_PERIODIC_CHORES_INTERVAL_UID_IO_LIMIT) {
+            mConfig.periodic_chores_interval_uid_io = interval;
+        }
+    }
+
+    void init_battery_service();
+    virtual void batteryPropertiesChanged(struct BatteryProperties props);
+    void binderDied(const wp<IBinder>& who);
+
+    void report_storage_info();
+};
+
+// Eventlog tag
+// The content must match the definition in EventLogTags.logtags
+#define EVENTLOGTAG_DISKSTATS ( 2732 )
+#define EVENTLOGTAG_EMMCINFO ( 2733 )
+#define EVENTLOGTAG_UID_IO_ALERT ( 2734 )
+
+#endif /* _STORAGED_H_ */
diff --git a/storaged/include/storaged_info.h b/storaged/include/storaged_info.h
new file mode 100644
index 0000000..7d04c7a
--- /dev/null
+++ b/storaged/include/storaged_info.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2017 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 _STORAGED_INFO_H_
+#define _STORAGED_INFO_H_
+
+#include <string.h>
+
+#define FRIEND_TEST(test_case_name, test_name) \
+friend class test_case_name##_##test_name##_Test
+
+using namespace std;
+
+class storage_info_t {
+protected:
+    FRIEND_TEST(storaged_test, storage_info_t);
+    // emmc lifetime
+    uint16_t eol;                   // pre-eol (end of life) information
+    uint16_t lifetime_a;            // device life time estimation (type A)
+    uint16_t lifetime_b;            // device life time estimation (type B)
+    string version;                 // version string
+    // free space
+    const string userdata_path = "/data";
+    uint64_t userdata_total_kb;
+    uint64_t userdata_free_kb;
+
+    storage_info_t() : eol(0), lifetime_a(0), lifetime_b(0),
+        userdata_total_kb(0), userdata_free_kb(0) {}
+    void publish();
+    storage_info_t* s_info;
+public:
+    static storage_info_t* get_storage_info();
+    virtual ~storage_info_t() {}
+    virtual void report() {};
+    void refresh();
+};
+
+class emmc_info_t : public storage_info_t {
+private:
+    bool report_sysfs();
+    bool report_debugfs();
+public:
+    static const string emmc_sysfs;
+    static const string emmc_debugfs;
+    static const char* emmc_ver_str[];
+
+    virtual ~emmc_info_t() {}
+    virtual void report();
+};
+
+class ufs_info_t : public storage_info_t {
+public:
+    static const string health_file;
+
+    virtual ~ufs_info_t() {}
+    virtual void report();
+};
+
+#endif /* _STORAGED_INFO_H_ */
diff --git a/storaged/include/storaged_service.h b/storaged/include/storaged_service.h
new file mode 100644
index 0000000..a8ddf4c
--- /dev/null
+++ b/storaged/include/storaged_service.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2016 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 _STORAGED_SERVICE_H_
+#define _STORAGED_SERVICE_H_
+
+#include <vector>
+
+#include <binder/IInterface.h>
+#include <binder/IBinder.h>
+
+#include "storaged.h"
+
+using namespace android;
+
+// Interface
+class IStoraged : public IInterface {
+public:
+    enum {
+        DUMPUIDS  = IBinder::FIRST_CALL_TRANSACTION,
+    };
+    // Request the service to run the test function
+    virtual std::vector<struct uid_info> dump_uids(const char* option) = 0;
+
+    DECLARE_META_INTERFACE(Storaged);
+};
+
+// Client
+class BpStoraged : public BpInterface<IStoraged> {
+public:
+    BpStoraged(const sp<IBinder>& impl) : BpInterface<IStoraged>(impl){};
+    virtual std::vector<struct uid_info> dump_uids(const char* option);
+};
+
+// Server
+class BnStoraged : public BnInterface<IStoraged> {
+    virtual status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags = 0);
+};
+
+class Storaged : public BnStoraged {
+    virtual std::vector<struct uid_info> dump_uids(const char* option);
+    virtual status_t dump(int fd, const Vector<String16>& args);
+};
+
+sp<IStoraged> get_storaged_service();
+
+#endif /* _STORAGED_SERVICE_H_ */
\ No newline at end of file
diff --git a/storaged/include/storaged_uid_monitor.h b/storaged/include/storaged_uid_monitor.h
new file mode 100644
index 0000000..901a872
--- /dev/null
+++ b/storaged/include/storaged_uid_monitor.h
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2016 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 _STORAGED_UID_MONITOR_H_
+#define _STORAGED_UID_MONITOR_H_
+
+#include <stdint.h>
+
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+enum uid_stat_t {
+    FOREGROUND = 0,
+    BACKGROUND = 1,
+    UID_STATS = 2
+};
+
+enum charger_stat_t {
+    CHARGER_OFF = 0,
+    CHARGER_ON = 1,
+    CHARGER_STATS = 2
+};
+
+enum io_type_t {
+    READ = 0,
+    WRITE = 1,
+    IO_TYPES = 2
+};
+
+struct uid_io_stats {
+    uint64_t rchar;                 // characters read
+    uint64_t wchar;                 // characters written
+    uint64_t read_bytes;            // bytes read (from storage layer)
+    uint64_t write_bytes;           // bytes written (to storage layer)
+    uint64_t fsync;                 // number of fsync syscalls
+};
+
+struct uid_info {
+    uint32_t uid;                   // user id
+    std::string name;               // package name
+    struct uid_io_stats io[UID_STATS];    // [0]:foreground [1]:background
+};
+
+struct uid_io_usage {
+    uint64_t bytes[IO_TYPES][UID_STATS][CHARGER_STATS];
+};
+
+struct uid_record {
+    std::string name;
+    struct uid_io_usage ios;
+};
+
+struct uid_records {
+    uint64_t start_ts;
+    std::vector<struct uid_record> entries;
+};
+
+class uid_monitor {
+private:
+    // last dump from /proc/uid_io/stats, uid -> uid_info
+    std::unordered_map<uint32_t, struct uid_info> last_uid_io_stats;
+    // current io usage for next report, app name -> uid_io_usage
+    std::unordered_map<std::string, struct uid_io_usage> curr_io_stats;
+    // io usage records, end timestamp -> {start timestamp, vector of records}
+    std::map<uint64_t, struct uid_records> records;
+    // charger ON/OFF
+    charger_stat_t charger_stat;
+    // protects curr_io_stats, last_uid_io_stats, records and charger_stat
+    sem_t um_lock;
+    // start time for IO records
+    uint64_t start_ts;
+
+    // reads from /proc/uid_io/stats
+    std::unordered_map<uint32_t, struct uid_info> get_uid_io_stats_locked();
+    // flushes curr_io_stats to records
+    void add_records_locked(uint64_t curr_ts);
+    // updates curr_io_stats and set last_uid_io_stats
+    void update_curr_io_stats_locked();
+
+public:
+    uid_monitor();
+    ~uid_monitor();
+    // called by storaged main thread
+    void init(charger_stat_t stat);
+    // called by storaged -u
+    std::unordered_map<uint32_t, struct uid_info> get_uid_io_stats();
+    // called by dumpsys
+    std::map<uint64_t, struct uid_records> dump(
+        double hours, uint64_t threshold, bool force_report);
+    // called by battery properties listener
+    void set_charger_state(charger_stat_t stat);
+    // called by storaged periodic_chore or dump with force_report
+    void report();
+};
+
+#endif /* _STORAGED_UID_MONITOR_H_ */
diff --git a/storaged/include/storaged_utils.h b/storaged/include/storaged_utils.h
new file mode 100644
index 0000000..2161c40
--- /dev/null
+++ b/storaged/include/storaged_utils.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2016 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 _STORAGED_UTILS_H_
+#define _STORAGED_UTILS_H_
+
+#include <stdint.h>
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+#include "storaged.h"
+
+// Diskstats
+bool parse_disk_stats(const char* disk_stats_path, struct disk_stats* stats);
+struct disk_perf get_disk_perf(struct disk_stats* stats);
+struct disk_stats get_inc_disk_stats(struct disk_stats* prev, struct disk_stats* curr);
+void add_disk_stats(struct disk_stats* src, struct disk_stats* dst);
+bool parse_emmc_ecsd(int ext_csd_fd, struct emmc_info* info);
+
+// UID I/O
+void sort_running_uids_info(std::vector<struct uid_info> &uids);
+
+// Logging
+void log_console_running_uids_info(std::vector<struct uid_info> uids);
+
+void log_debug_disk_perf(struct disk_perf* perf, const char* type);
+
+void log_event_disk_stats(struct disk_stats* stats, const char* type);
+void log_event_emmc_info(struct emmc_info* info_);
+#endif /* _STORAGED_UTILS_H_ */
diff --git a/storaged/main.cpp b/storaged/main.cpp
new file mode 100644
index 0000000..72ec58f
--- /dev/null
+++ b/storaged/main.cpp
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#define LOG_TAG "storaged"
+#define KLOG_LEVEL 6
+
+#include <fcntl.h>
+#include <getopt.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <vector>
+
+#include <android-base/macros.h>
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <binder/ProcessState.h>
+#include <binder/IServiceManager.h>
+#include <binder/IPCThreadState.h>
+#include <cutils/android_get_control_file.h>
+#include <cutils/sched_policy.h>
+#include <private/android_filesystem_config.h>
+
+#include <storaged.h>
+#include <storaged_service.h>
+#include <storaged_utils.h>
+
+sp<storaged_t> storaged;
+
+// Function of storaged's main thread
+void* storaged_main(void* /* unused */) {
+    storaged = new storaged_t();
+
+    storaged->init_battery_service();
+    storaged->report_storage_info();
+
+    LOG_TO(SYSTEM, INFO) << "storaged: Start";
+
+    for (;;) {
+        storaged->event_checked();
+        storaged->pause();
+    }
+    return NULL;
+}
+
+static void help_message(void) {
+    printf("usage: storaged [OPTION]\n");
+    printf("  -u    --uid                   Dump uid I/O usage to stdout\n");
+    printf("  -s    --start                 Start storaged (default)\n");
+    fflush(stdout);
+}
+
+int main(int argc, char** argv) {
+    int flag_main_service = 0;
+    int flag_dump_uid = 0;
+    int opt;
+
+    for (;;) {
+        int opt_idx = 0;
+        static struct option long_options[] = {
+            {"start",       no_argument,        0, 's'},
+            {"kill",        no_argument,        0, 'k'},
+            {"uid",         no_argument,        0, 'u'},
+            {"help",        no_argument,        0, 'h'}
+        };
+        opt = getopt_long(argc, argv, ":skdhu0", long_options, &opt_idx);
+        if (opt == -1) {
+            break;
+        }
+
+        switch (opt) {
+        case 's':
+            flag_main_service = 1;
+            break;
+        case 'u':
+            flag_dump_uid = 1;
+            break;
+        case 'h':
+            help_message();
+            return 0;
+        case '?':
+        default:
+            fprintf(stderr, "no supported option\n");
+            help_message();
+            return -1;
+        }
+    }
+
+    if (argc == 1) {
+        flag_main_service = 1;
+    }
+
+    if (flag_main_service && flag_dump_uid) {
+        fprintf(stderr, "Invalid arguments. Option \"start\" and \"dump\" cannot be used together.\n");
+        help_message();
+        return -1;
+    }
+
+    if (flag_main_service) { // start main thread
+        // Start the main thread of storaged
+        pthread_t storaged_main_thread;
+        errno = pthread_create(&storaged_main_thread, NULL, storaged_main, NULL);
+        if (errno != 0) {
+            PLOG_TO(SYSTEM, ERROR) << "Failed to create main thread";
+            return -1;
+        }
+
+        defaultServiceManager()->addService(String16("storaged"), new Storaged());
+        android::ProcessState::self()->startThreadPool();
+        IPCThreadState::self()->joinThreadPool();
+        pthread_join(storaged_main_thread, NULL);
+
+        return 0;
+    }
+
+    if (flag_dump_uid) {
+        sp<IStoraged> storaged_service = get_storaged_service();
+        if (storaged_service == NULL) {
+            fprintf(stderr, "Cannot find storaged service.\nMaybe run storaged --start first?\n");
+            return -1;
+        }
+        std::vector<struct uid_info> res = storaged_service->dump_uids(NULL);
+
+        if (res.size() == 0) {
+            fprintf(stderr, "UID I/O is not readable in this version of kernel.\n");
+            return 0;
+        }
+
+        sort_running_uids_info(res);
+        log_console_running_uids_info(res);
+
+        return 0;
+    }
+
+    return 0;
+}
diff --git a/storaged/storaged.cpp b/storaged/storaged.cpp
new file mode 100644
index 0000000..06afea6
--- /dev/null
+++ b/storaged/storaged.cpp
@@ -0,0 +1,283 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#define LOG_TAG "storaged"
+
+#include <stdlib.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <android-base/logging.h>
+#include <batteryservice/BatteryServiceConstants.h>
+#include <batteryservice/IBatteryPropertiesRegistrar.h>
+#include <binder/IPCThreadState.h>
+#include <binder/IServiceManager.h>
+#include <cutils/properties.h>
+#include <log/log.h>
+
+#include <storaged.h>
+#include <storaged_utils.h>
+
+/* disk_stats_publisher */
+void disk_stats_publisher::publish(void) {
+    // Logging
+    struct disk_perf perf = get_disk_perf(&mAccumulate);
+    log_debug_disk_perf(&perf, "regular");
+    log_event_disk_stats(&mAccumulate, "regular");
+    // Reset global structures
+    memset(&mAccumulate, 0, sizeof(struct disk_stats));
+}
+
+void disk_stats_publisher::update(void) {
+    struct disk_stats curr;
+    if (parse_disk_stats(DISK_STATS_PATH, &curr)) {
+        struct disk_stats inc = get_inc_disk_stats(&mPrevious, &curr);
+        add_disk_stats(&inc, &mAccumulate);
+#ifdef DEBUG
+//            log_kernel_disk_stats(&mPrevious, "prev stats");
+//            log_kernel_disk_stats(&curr, "curr stats");
+//            log_kernel_disk_stats(&inc, "inc stats");
+//            log_kernel_disk_stats(&mAccumulate, "accumulated stats");
+#endif
+        mPrevious = curr;
+    }
+}
+
+/* disk_stats_monitor */
+void disk_stats_monitor::update_mean() {
+    CHECK(mValid);
+    mMean.read_perf = (uint32_t)mStats.read_perf.get_mean();
+    mMean.read_ios = (uint32_t)mStats.read_ios.get_mean();
+    mMean.write_perf = (uint32_t)mStats.write_perf.get_mean();
+    mMean.write_ios = (uint32_t)mStats.write_ios.get_mean();
+    mMean.queue = (uint32_t)mStats.queue.get_mean();
+}
+
+void disk_stats_monitor::update_std() {
+    CHECK(mValid);
+    mStd.read_perf = (uint32_t)mStats.read_perf.get_std();
+    mStd.read_ios = (uint32_t)mStats.read_ios.get_std();
+    mStd.write_perf = (uint32_t)mStats.write_perf.get_std();
+    mStd.write_ios = (uint32_t)mStats.write_ios.get_std();
+    mStd.queue = (uint32_t)mStats.queue.get_std();
+}
+
+void disk_stats_monitor::add(struct disk_perf* perf) {
+    mStats.read_perf.add(perf->read_perf);
+    mStats.read_ios.add(perf->read_ios);
+    mStats.write_perf.add(perf->write_perf);
+    mStats.write_ios.add(perf->write_ios);
+    mStats.queue.add(perf->queue);
+}
+
+void disk_stats_monitor::evict(struct disk_perf* perf) {
+    mStats.read_perf.evict(perf->read_perf);
+    mStats.read_ios.evict(perf->read_ios);
+    mStats.write_perf.evict(perf->write_perf);
+    mStats.write_ios.evict(perf->write_ios);
+    mStats.queue.evict(perf->queue);
+}
+
+bool disk_stats_monitor::detect(struct disk_perf* perf) {
+    return ((double)perf->queue >= (double)mMean.queue + mSigma * (double)mStd.queue) &&
+            ((double)perf->read_perf < (double)mMean.read_perf - mSigma * (double)mStd.read_perf) &&
+            ((double)perf->write_perf < (double)mMean.write_perf - mSigma * (double)mStd.write_perf);
+}
+
+void disk_stats_monitor::update(struct disk_stats* stats) {
+    struct disk_stats inc = get_inc_disk_stats(&mPrevious, stats);
+    struct disk_perf perf = get_disk_perf(&inc);
+    // Update internal data structures
+    if (LIKELY(mValid)) {
+        CHECK_EQ(mBuffer.size(), mWindow);
+
+        if (UNLIKELY(detect(&perf))) {
+            mStall = true;
+            add_disk_stats(&inc, &mAccumulate);
+            log_debug_disk_perf(&mMean, "stalled_mean");
+            log_debug_disk_perf(&mStd, "stalled_std");
+        } else {
+            if (mStall) {
+                struct disk_perf acc_perf = get_disk_perf(&mAccumulate);
+                log_debug_disk_perf(&acc_perf, "stalled");
+                log_event_disk_stats(&mAccumulate, "stalled");
+                mStall = false;
+                memset(&mAccumulate, 0, sizeof(mAccumulate));
+            }
+        }
+
+        evict(&mBuffer.front());
+        mBuffer.pop();
+        add(&perf);
+        mBuffer.push(perf);
+
+        update_mean();
+        update_std();
+
+    } else { /* mValid == false */
+        CHECK_LT(mBuffer.size(), mWindow);
+        add(&perf);
+        mBuffer.push(perf);
+        if (mBuffer.size() == mWindow) {
+            mValid = true;
+            update_mean();
+            update_std();
+        }
+    }
+
+    mPrevious = *stats;
+}
+
+void disk_stats_monitor::update(void) {
+    struct disk_stats curr;
+    if (LIKELY(parse_disk_stats(DISK_STATS_PATH, &curr))) {
+        update(&curr);
+    }
+}
+
+static sp<IBatteryPropertiesRegistrar> get_battery_properties_service() {
+    sp<IServiceManager> sm = defaultServiceManager();
+    if (sm == NULL) return NULL;
+
+    sp<IBinder> binder = sm->getService(String16("batteryproperties"));
+    if (binder == NULL) return NULL;
+
+    sp<IBatteryPropertiesRegistrar> battery_properties =
+        interface_cast<IBatteryPropertiesRegistrar>(binder);
+
+    return battery_properties;
+}
+
+static inline charger_stat_t is_charger_on(int64_t prop) {
+    return (prop == BATTERY_STATUS_CHARGING || prop == BATTERY_STATUS_FULL) ?
+        CHARGER_ON : CHARGER_OFF;
+}
+
+void storaged_t::batteryPropertiesChanged(struct BatteryProperties props) {
+    mUidm.set_charger_state(is_charger_on(props.batteryStatus));
+}
+
+void storaged_t::init_battery_service() {
+    if (!mConfig.proc_uid_io_available)
+        return;
+
+    battery_properties = get_battery_properties_service();
+    if (battery_properties == NULL) {
+        LOG_TO(SYSTEM, WARNING) << "failed to find batteryproperties service";
+        return;
+    }
+
+    struct BatteryProperty val;
+    battery_properties->getProperty(BATTERY_PROP_BATTERY_STATUS, &val);
+    mUidm.init(is_charger_on(val.valueInt64));
+
+    // register listener after init uid_monitor
+    battery_properties->registerListener(this);
+    IInterface::asBinder(battery_properties)->linkToDeath(this);
+}
+
+void storaged_t::binderDied(const wp<IBinder>& who) {
+    if (battery_properties != NULL &&
+        IInterface::asBinder(battery_properties) == who) {
+        LOG_TO(SYSTEM, ERROR) << "batteryproperties service died, exiting";
+        IPCThreadState::self()->stopProcess();
+        exit(1);
+    } else {
+        LOG_TO(SYSTEM, ERROR) << "unknown service died";
+    }
+}
+
+void storaged_t::report_storage_info() {
+    storage_info->report();
+}
+
+/* storaged_t */
+storaged_t::storaged_t(void) {
+    if (access(MMC_DISK_STATS_PATH, R_OK) < 0 && access(SDA_DISK_STATS_PATH, R_OK) < 0) {
+        mConfig.diskstats_available = false;
+    } else {
+        mConfig.diskstats_available = true;
+    }
+
+    mConfig.proc_uid_io_available = (access(UID_IO_STATS_PATH, R_OK) == 0);
+
+    mConfig.periodic_chores_interval_unit =
+        property_get_int32("ro.storaged.event.interval", DEFAULT_PERIODIC_CHORES_INTERVAL_UNIT);
+
+    mConfig.event_time_check_usec =
+        property_get_int32("ro.storaged.event.perf_check", 0);
+
+    mConfig.periodic_chores_interval_disk_stats_publish =
+        property_get_int32("ro.storaged.disk_stats_pub", DEFAULT_PERIODIC_CHORES_INTERVAL_DISK_STATS_PUBLISH);
+
+    mConfig.periodic_chores_interval_uid_io =
+        property_get_int32("ro.storaged.uid_io.interval", DEFAULT_PERIODIC_CHORES_INTERVAL_UID_IO);
+
+    storage_info.reset(storage_info_t::get_storage_info());
+
+    mStarttime = time(NULL);
+}
+
+void storaged_t::event(void) {
+    if (mConfig.diskstats_available) {
+        mDiskStats.update();
+        mDsm.update();
+        storage_info->refresh();
+        if (mTimer && (mTimer % mConfig.periodic_chores_interval_disk_stats_publish) == 0) {
+            mDiskStats.publish();
+        }
+    }
+
+    if (mConfig.proc_uid_io_available && mTimer &&
+            (mTimer % mConfig.periodic_chores_interval_uid_io) == 0) {
+         mUidm.report();
+    }
+
+    mTimer += mConfig.periodic_chores_interval_unit;
+}
+
+void storaged_t::event_checked(void) {
+    struct timespec start_ts, end_ts;
+    bool check_time = true;
+
+    if (mConfig.event_time_check_usec &&
+        clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &start_ts) < 0) {
+        check_time = false;
+        static time_t state_a;
+        IF_ALOG_RATELIMIT_LOCAL(300, &state_a) {
+            PLOG_TO(SYSTEM, ERROR) << "clock_gettime() failed";
+        }
+    }
+
+    event();
+
+    if (mConfig.event_time_check_usec && check_time) {
+        if (clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &end_ts) < 0) {
+            static time_t state_b;
+            IF_ALOG_RATELIMIT_LOCAL(300, &state_b) {
+                PLOG_TO(SYSTEM, ERROR) << "clock_gettime() failed";
+            }
+            return;
+        }
+        int64_t cost = (end_ts.tv_sec - start_ts.tv_sec) * SEC_TO_USEC +
+                       (end_ts.tv_nsec - start_ts.tv_nsec) / USEC_TO_NSEC;
+        if (cost > mConfig.event_time_check_usec) {
+            LOG_TO(SYSTEM, ERROR)
+                << "event loop spent " << cost << " usec, threshold "
+                << mConfig.event_time_check_usec << " usec";
+        }
+    }
+}
diff --git a/storaged/storaged.rc b/storaged/storaged.rc
new file mode 100644
index 0000000..ed2cf14
--- /dev/null
+++ b/storaged/storaged.rc
@@ -0,0 +1,8 @@
+service storaged /system/bin/storaged
+    class main
+    capabilities DAC_READ_SEARCH
+    priority 10
+    file /d/mmc0/mmc0:0001/ext_csd r
+    writepid /dev/cpuset/system-background/tasks
+    user root
+    group package_info
\ No newline at end of file
diff --git a/storaged/storaged_info.cpp b/storaged/storaged_info.cpp
new file mode 100644
index 0000000..b5fb13e
--- /dev/null
+++ b/storaged/storaged_info.cpp
@@ -0,0 +1,212 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#define LOG_TAG "storaged"
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/statvfs.h>
+
+#include <android-base/file.h>
+#include <android-base/parseint.h>
+#include <android-base/logging.h>
+#include <android-base/strings.h>
+#include <log/log_event_list.h>
+
+#include "storaged.h"
+
+using namespace std;
+using namespace android::base;
+
+const string emmc_info_t::emmc_sysfs = "/sys/bus/mmc/devices/mmc0:0001/";
+const string emmc_info_t::emmc_debugfs = "/d/mmc0/mmc0:0001/ext_csd";
+const char* emmc_info_t::emmc_ver_str[9] = {
+    "4.0", "4.1", "4.2", "4.3", "Obsolete", "4.41", "4.5", "5.0", "5.1"
+};
+
+const string ufs_info_t::health_file = "/sys/devices/soc/624000.ufshc/health";
+
+static bool FileExists(const std::string& filename)
+{
+  struct stat buffer;
+  return stat(filename.c_str(), &buffer) == 0;
+}
+
+storage_info_t* storage_info_t::get_storage_info()
+{
+    if (FileExists(emmc_info_t::emmc_sysfs) ||
+        FileExists(emmc_info_t::emmc_debugfs)) {
+        return new emmc_info_t;
+    }
+    if (FileExists(ufs_info_t::health_file)) {
+        return new ufs_info_t;
+    }
+    return new storage_info_t;
+}
+
+void storage_info_t::refresh()
+{
+    struct statvfs buf;
+    if (statvfs(userdata_path.c_str(), &buf) != 0) {
+        PLOG_TO(SYSTEM, WARNING) << "Failed to get userdata info";
+        return;
+    }
+
+    userdata_total_kb = buf.f_bsize * buf.f_blocks >> 10;
+    userdata_free_kb = buf.f_bfree * buf.f_blocks >> 10;
+}
+
+void storage_info_t::publish()
+{
+    android_log_event_list(EVENTLOGTAG_EMMCINFO)
+        << version << eol << lifetime_a << lifetime_b
+        << LOG_ID_EVENTS;
+}
+
+void emmc_info_t::report()
+{
+    if (!report_sysfs() && !report_debugfs())
+        return;
+
+    publish();
+}
+
+bool emmc_info_t::report_sysfs()
+{
+    string buffer;
+    uint16_t rev = 0;
+
+    if (!ReadFileToString(emmc_sysfs + "rev", &buffer)) {
+        return false;
+    }
+
+    if (sscanf(buffer.c_str(), "0x%hx", &rev) < 1 ||
+        rev < 7 || rev > ARRAY_SIZE(emmc_ver_str)) {
+        return false;
+    }
+
+    version = "emmc ";
+    version += emmc_ver_str[rev];
+
+    if (!ReadFileToString(emmc_sysfs + "pre_eol_info", &buffer)) {
+        return false;
+    }
+
+    if (sscanf(buffer.c_str(), "%hx", &eol) < 1 || eol == 0) {
+        return false;
+    }
+
+    if (!ReadFileToString(emmc_sysfs + "life_time", &buffer)) {
+        return false;
+    }
+
+    if (sscanf(buffer.c_str(), "0x%hx 0x%hx", &lifetime_a, &lifetime_b) < 2 ||
+        (lifetime_a == 0 && lifetime_b == 0)) {
+        return false;
+    }
+
+    return true;
+}
+
+const size_t EXT_CSD_FILE_MIN_SIZE = 1024;
+/* 2 characters in string for each byte */
+const size_t EXT_CSD_REV_IDX = 192 * 2;
+const size_t EXT_PRE_EOL_INFO_IDX = 267 * 2;
+const size_t EXT_DEVICE_LIFE_TIME_EST_A_IDX = 268 * 2;
+const size_t EXT_DEVICE_LIFE_TIME_EST_B_IDX = 269 * 2;
+
+bool emmc_info_t::report_debugfs()
+{
+    string buffer;
+    uint16_t rev = 0;
+
+    if (!ReadFileToString(emmc_debugfs, &buffer) ||
+        buffer.length() < (size_t)EXT_CSD_FILE_MIN_SIZE) {
+        return false;
+    }
+
+    string str = buffer.substr(EXT_CSD_REV_IDX, 2);
+    if (!ParseUint(str, &rev) ||
+        rev < 7 || rev > ARRAY_SIZE(emmc_ver_str)) {
+        return false;
+    }
+
+    version = "emmc ";
+    version += emmc_ver_str[rev];
+
+    str = buffer.substr(EXT_PRE_EOL_INFO_IDX, 2);
+    if (!ParseUint(str, &eol)) {
+        return false;
+    }
+
+    str = buffer.substr(EXT_DEVICE_LIFE_TIME_EST_A_IDX, 2);
+    if (!ParseUint(str, &lifetime_a)) {
+        return false;
+    }
+
+    str = buffer.substr(EXT_DEVICE_LIFE_TIME_EST_B_IDX, 2);
+    if (!ParseUint(str, &lifetime_b)) {
+        return false;
+    }
+
+    return true;
+}
+
+void ufs_info_t::report()
+{
+    string buffer;
+    if (!ReadFileToString(health_file, &buffer)) {
+        return;
+    }
+
+    vector<string> lines = Split(buffer, "\n");
+    if (lines.empty()) {
+        return;
+    }
+
+    char rev[8];
+    if (sscanf(lines[0].c_str(), "ufs version: 0x%7s\n", rev) < 1) {
+        return;
+    }
+
+    version = "ufs " + string(rev);
+
+    for (size_t i = 1; i < lines.size(); i++) {
+        char token[32];
+        uint16_t val;
+        int ret;
+        if ((ret = sscanf(lines[i].c_str(),
+                   "Health Descriptor[Byte offset 0x%*d]: %31s = 0x%hx",
+                   token, &val)) < 2) {
+            continue;
+        }
+
+        if (string(token) == "bPreEOLInfo") {
+            eol = val;
+        } else if (string(token) == "bDeviceLifeTimeEstA") {
+            lifetime_a = val;
+        } else if (string(token) == "bDeviceLifeTimeEstB") {
+            lifetime_b = val;
+        }
+    }
+
+    if (eol == 0 || (lifetime_a == 0 && lifetime_b == 0)) {
+        return;
+    }
+
+    publish();
+}
+
diff --git a/storaged/storaged_service.cpp b/storaged/storaged_service.cpp
new file mode 100644
index 0000000..b1d3bfd
--- /dev/null
+++ b/storaged/storaged_service.cpp
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2016 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 <stdint.h>
+
+#include <vector>
+
+#include <android-base/parseint.h>
+#include <android-base/parsedouble.h>
+#include <binder/IBinder.h>
+#include <binder/IInterface.h>
+
+#include <binder/IPCThreadState.h>
+#include <binder/IServiceManager.h>
+#include <binder/PermissionCache.h>
+#include <private/android_filesystem_config.h>
+
+#include <storaged.h>
+#include <storaged_service.h>
+
+using namespace android::base;
+
+extern sp<storaged_t> storaged;
+
+std::vector<struct uid_info> BpStoraged::dump_uids(const char* /*option*/) {
+    Parcel data, reply;
+    data.writeInterfaceToken(IStoraged::getInterfaceDescriptor());
+
+    remote()->transact(DUMPUIDS, data, &reply);
+
+    uint32_t res_size = reply.readInt32();
+    std::vector<struct uid_info> res(res_size);
+    for (auto&& uid : res) {
+        uid.uid = reply.readInt32();
+        uid.name = reply.readCString();
+        reply.read(&uid.io, sizeof(uid.io));
+    }
+    return res;
+}
+IMPLEMENT_META_INTERFACE(Storaged, "Storaged");
+
+status_t BnStoraged::onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) {
+    switch(code) {
+        case DUMPUIDS: {
+                if (!data.checkInterface(this))
+                    return BAD_TYPE;
+                std::vector<struct uid_info> res = dump_uids(NULL);
+                reply->writeInt32(res.size());
+                for (auto uid : res) {
+                    reply->writeInt32(uid.uid);
+                    reply->writeCString(uid.name.c_str());
+                    reply->write(&uid.io, sizeof(uid.io));
+                }
+                return NO_ERROR;
+            }
+            break;
+        default:
+            return BBinder::onTransact(code, data, reply, flags);
+    }
+}
+
+std::vector<struct uid_info> Storaged::dump_uids(const char* /* option */) {
+    std::vector<struct uid_info> uids_v;
+    std::unordered_map<uint32_t, struct uid_info> uids_m = storaged->get_uids();
+
+    for (const auto& it : uids_m) {
+        uids_v.push_back(it.second);
+    }
+    return uids_v;
+}
+
+status_t Storaged::dump(int fd, const Vector<String16>& args) {
+    IPCThreadState* self = IPCThreadState::self();
+    const int pid = self->getCallingPid();
+    const int uid = self->getCallingUid();
+    if ((uid != AID_SHELL) &&
+        !PermissionCache::checkPermission(
+                String16("android.permission.DUMP"), pid, uid)) {
+        return PERMISSION_DENIED;
+    }
+
+    double hours = 0;
+    int time_window = 0;
+    uint64_t threshold = 0;
+    bool force_report = false;
+    for (size_t i = 0; i < args.size(); i++) {
+        const auto& arg = args[i];
+        if (arg == String16("--hours")) {
+            if (++i >= args.size())
+                break;
+            if(!ParseDouble(String8(args[i]).c_str(), &hours))
+                return BAD_VALUE;
+            continue;
+        }
+        if (arg == String16("--time_window")) {
+            if (++i >= args.size())
+                break;
+            if(!ParseInt(String8(args[i]).c_str(), &time_window))
+                return BAD_VALUE;
+            continue;
+        }
+        if (arg == String16("--threshold")) {
+            if (++i >= args.size())
+                break;
+            if(!ParseUint(String8(args[i]).c_str(), &threshold))
+                return BAD_VALUE;
+            continue;
+        }
+        if (arg == String16("--force")) {
+            force_report = true;
+            continue;
+        }
+    }
+
+    uint64_t last_ts = 0;
+    const std::map<uint64_t, struct uid_records>& records =
+                storaged->get_uid_records(hours, threshold, force_report);
+    for (const auto& it : records) {
+        if (last_ts != it.second.start_ts) {
+            dprintf(fd, "%llu", (unsigned long long)it.second.start_ts);
+        }
+        dprintf(fd, ",%llu\n", (unsigned long long)it.first);
+        last_ts = it.first;
+
+        for (const auto& record : it.second.entries) {
+            dprintf(fd, "%s %ju %ju %ju %ju %ju %ju %ju %ju\n",
+                record.name.c_str(),
+                record.ios.bytes[READ][FOREGROUND][CHARGER_OFF],
+                record.ios.bytes[WRITE][FOREGROUND][CHARGER_OFF],
+                record.ios.bytes[READ][BACKGROUND][CHARGER_OFF],
+                record.ios.bytes[WRITE][BACKGROUND][CHARGER_OFF],
+                record.ios.bytes[READ][FOREGROUND][CHARGER_ON],
+                record.ios.bytes[WRITE][FOREGROUND][CHARGER_ON],
+                record.ios.bytes[READ][BACKGROUND][CHARGER_ON],
+                record.ios.bytes[WRITE][BACKGROUND][CHARGER_ON]);
+        }
+    }
+
+    if (time_window) {
+        storaged->update_uid_io_interval(time_window);
+    }
+
+    return NO_ERROR;
+}
+
+sp<IStoraged> get_storaged_service() {
+    sp<IServiceManager> sm = defaultServiceManager();
+    if (sm == NULL) return NULL;
+
+    sp<IBinder> binder = sm->getService(String16("storaged"));
+    if (binder == NULL) return NULL;
+
+    sp<IStoraged> storaged = interface_cast<IStoraged>(binder);
+
+    return storaged;
+}
diff --git a/storaged/storaged_uid_monitor.cpp b/storaged/storaged_uid_monitor.cpp
new file mode 100644
index 0000000..dd8bdd6
--- /dev/null
+++ b/storaged/storaged_uid_monitor.cpp
@@ -0,0 +1,304 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#define LOG_TAG "storaged"
+
+#include <stdint.h>
+#include <time.h>
+
+#include <string>
+#include <unordered_map>
+
+#include <android/content/pm/IPackageManagerNative.h>
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/macros.h>
+#include <android-base/parseint.h>
+#include <android-base/strings.h>
+#include <android-base/stringprintf.h>
+#include <binder/IServiceManager.h>
+#include <log/log_event_list.h>
+
+#include "storaged.h"
+#include "storaged_uid_monitor.h"
+
+using namespace android;
+using namespace android::base;
+using namespace android::content::pm;
+
+static bool refresh_uid_names;
+
+std::unordered_map<uint32_t, struct uid_info> uid_monitor::get_uid_io_stats()
+{
+    std::unique_ptr<lock_t> lock(new lock_t(&um_lock));
+    return get_uid_io_stats_locked();
+};
+
+static void get_uid_names(const vector<int>& uids, const vector<std::string*>& uid_names)
+{
+    sp<IServiceManager> sm = defaultServiceManager();
+    if (sm == NULL) {
+        LOG_TO(SYSTEM, ERROR) << "defaultServiceManager failed";
+        return;
+    }
+
+    sp<IBinder> binder = sm->getService(String16("package_native"));
+    if (binder == NULL) {
+        LOG_TO(SYSTEM, ERROR) << "getService package_native failed";
+        return;
+    }
+
+    sp<IPackageManagerNative> package_mgr = interface_cast<IPackageManagerNative>(binder);
+    std::vector<std::string> names;
+    binder::Status status = package_mgr->getNamesForUids(uids, &names);
+    if (!status.isOk()) {
+        LOG_TO(SYSTEM, ERROR) << "package_native::getNamesForUids failed: "
+                              << status.exceptionMessage();
+        return;
+    }
+
+    for (uint32_t i = 0; i < uid_names.size(); i++) {
+        if (!names[i].empty()) {
+            *uid_names[i] = names[i];
+        }
+    }
+
+    refresh_uid_names = false;
+}
+
+std::unordered_map<uint32_t, struct uid_info> uid_monitor::get_uid_io_stats_locked()
+{
+    std::unordered_map<uint32_t, struct uid_info> uid_io_stats;
+    std::string buffer;
+    if (!ReadFileToString(UID_IO_STATS_PATH, &buffer)) {
+        PLOG_TO(SYSTEM, ERROR) << UID_IO_STATS_PATH << ": ReadFileToString failed";
+        return uid_io_stats;
+    }
+
+    std::vector<std::string> io_stats = Split(buffer, "\n");
+    struct uid_info u;
+    vector<int> uids;
+    vector<std::string*> uid_names;
+
+    for (uint32_t i = 0; i < io_stats.size(); i++) {
+        if (io_stats[i].empty()) {
+            continue;
+        }
+        std::vector<std::string> fields = Split(io_stats[i], " ");
+        if (fields.size() < 11 ||
+            !ParseUint(fields[0],  &u.uid) ||
+            !ParseUint(fields[1],  &u.io[FOREGROUND].rchar) ||
+            !ParseUint(fields[2],  &u.io[FOREGROUND].wchar) ||
+            !ParseUint(fields[3],  &u.io[FOREGROUND].read_bytes) ||
+            !ParseUint(fields[4],  &u.io[FOREGROUND].write_bytes) ||
+            !ParseUint(fields[5],  &u.io[BACKGROUND].rchar) ||
+            !ParseUint(fields[6],  &u.io[BACKGROUND].wchar) ||
+            !ParseUint(fields[7],  &u.io[BACKGROUND].read_bytes) ||
+            !ParseUint(fields[8],  &u.io[BACKGROUND].write_bytes) ||
+            !ParseUint(fields[9],  &u.io[FOREGROUND].fsync) ||
+            !ParseUint(fields[10], &u.io[BACKGROUND].fsync)) {
+            LOG_TO(SYSTEM, WARNING) << "Invalid I/O stats: \""
+                                    << io_stats[i] << "\"";
+            continue;
+        }
+
+        uid_io_stats[u.uid] = u;
+        uid_io_stats[u.uid].name = std::to_string(u.uid);
+        uids.push_back(u.uid);
+        uid_names.push_back(&uid_io_stats[u.uid].name);
+        if (last_uid_io_stats.find(u.uid) == last_uid_io_stats.end()) {
+            refresh_uid_names = true;
+        } else {
+            uid_io_stats[u.uid].name = last_uid_io_stats[u.uid].name;
+        }
+    }
+
+    if (!uids.empty() && refresh_uid_names) {
+        get_uid_names(uids, uid_names);
+    }
+
+    return uid_io_stats;
+}
+
+static const int MAX_UID_RECORDS_SIZE = 1000 * 48; // 1000 uids in 48 hours
+
+static inline int records_size(
+    const std::map<uint64_t, struct uid_records>& curr_records)
+{
+    int count = 0;
+    for (auto const& it : curr_records) {
+        count += it.second.entries.size();
+    }
+    return count;
+}
+
+static struct uid_io_usage zero_io_usage;
+
+void uid_monitor::add_records_locked(uint64_t curr_ts)
+{
+    // remove records more than 5 days old
+    if (curr_ts > 5 * DAY_TO_SEC) {
+        auto it = records.lower_bound(curr_ts - 5 * DAY_TO_SEC);
+        records.erase(records.begin(), it);
+    }
+
+    struct uid_records new_records;
+    for (const auto& p : curr_io_stats) {
+        struct uid_record record = {};
+        record.name = p.first;
+        record.ios = p.second;
+        if (memcmp(&record.ios, &zero_io_usage, sizeof(struct uid_io_usage))) {
+            new_records.entries.push_back(record);
+        }
+    }
+
+    curr_io_stats.clear();
+    new_records.start_ts = start_ts;
+    start_ts = curr_ts;
+
+    if (new_records.entries.empty())
+      return;
+
+    // make some room for new records
+    int overflow = records_size(records) +
+        new_records.entries.size() - MAX_UID_RECORDS_SIZE;
+    while (overflow > 0 && records.size() > 0) {
+        auto del_it = records.begin();
+        overflow -= del_it->second.entries.size();
+        records.erase(records.begin());
+    }
+
+    records[curr_ts] = new_records;
+}
+
+std::map<uint64_t, struct uid_records> uid_monitor::dump(
+    double hours, uint64_t threshold, bool force_report)
+{
+    if (force_report) {
+        report();
+    }
+
+    std::unique_ptr<lock_t> lock(new lock_t(&um_lock));
+
+    std::map<uint64_t, struct uid_records> dump_records;
+    uint64_t first_ts = 0;
+
+    if (hours != 0) {
+        first_ts = time(NULL) - hours * HOUR_TO_SEC;
+    }
+
+    for (auto it = records.lower_bound(first_ts); it != records.end(); ++it) {
+        const std::vector<struct uid_record>& recs = it->second.entries;
+        struct uid_records filtered;
+
+        for (const auto& rec : recs) {
+            if (rec.ios.bytes[READ][FOREGROUND][CHARGER_ON] +
+                rec.ios.bytes[READ][FOREGROUND][CHARGER_OFF] +
+                rec.ios.bytes[READ][BACKGROUND][CHARGER_ON] +
+                rec.ios.bytes[READ][BACKGROUND][CHARGER_OFF] +
+                rec.ios.bytes[WRITE][FOREGROUND][CHARGER_ON] +
+                rec.ios.bytes[WRITE][FOREGROUND][CHARGER_OFF] +
+                rec.ios.bytes[WRITE][BACKGROUND][CHARGER_ON] +
+                rec.ios.bytes[WRITE][BACKGROUND][CHARGER_OFF] > threshold) {
+                filtered.entries.push_back(rec);
+            }
+        }
+
+        if (filtered.entries.empty())
+            continue;
+
+        filtered.start_ts = it->second.start_ts;
+        dump_records.insert(
+            std::pair<uint64_t, struct uid_records>(it->first, filtered));
+    }
+
+    return dump_records;
+}
+
+void uid_monitor::update_curr_io_stats_locked()
+{
+    std::unordered_map<uint32_t, struct uid_info> uid_io_stats =
+        get_uid_io_stats_locked();
+    if (uid_io_stats.empty()) {
+        return;
+    }
+
+    for (const auto& it : uid_io_stats) {
+        const struct uid_info& uid = it.second;
+
+        if (curr_io_stats.find(uid.name) == curr_io_stats.end()) {
+          curr_io_stats[uid.name] = {};
+        }
+
+        struct uid_io_usage& usage = curr_io_stats[uid.name];
+        int64_t fg_rd_delta = uid.io[FOREGROUND].read_bytes -
+            last_uid_io_stats[uid.uid].io[FOREGROUND].read_bytes;
+        int64_t bg_rd_delta = uid.io[BACKGROUND].read_bytes -
+            last_uid_io_stats[uid.uid].io[BACKGROUND].read_bytes;
+        int64_t fg_wr_delta = uid.io[FOREGROUND].write_bytes -
+            last_uid_io_stats[uid.uid].io[FOREGROUND].write_bytes;
+        int64_t bg_wr_delta = uid.io[BACKGROUND].write_bytes -
+            last_uid_io_stats[uid.uid].io[BACKGROUND].write_bytes;
+
+        usage.bytes[READ][FOREGROUND][charger_stat] +=
+            (fg_rd_delta < 0) ? 0 : fg_rd_delta;
+        usage.bytes[READ][BACKGROUND][charger_stat] +=
+            (bg_rd_delta < 0) ? 0 : bg_rd_delta;
+        usage.bytes[WRITE][FOREGROUND][charger_stat] +=
+            (fg_wr_delta < 0) ? 0 : fg_wr_delta;
+        usage.bytes[WRITE][BACKGROUND][charger_stat] +=
+            (bg_wr_delta < 0) ? 0 : bg_wr_delta;
+    }
+
+    last_uid_io_stats = uid_io_stats;
+}
+
+void uid_monitor::report()
+{
+    std::unique_ptr<lock_t> lock(new lock_t(&um_lock));
+
+    update_curr_io_stats_locked();
+    add_records_locked(time(NULL));
+}
+
+void uid_monitor::set_charger_state(charger_stat_t stat)
+{
+    std::unique_ptr<lock_t> lock(new lock_t(&um_lock));
+
+    if (charger_stat == stat) {
+        return;
+    }
+
+    update_curr_io_stats_locked();
+    charger_stat = stat;
+}
+
+void uid_monitor::init(charger_stat_t stat)
+{
+    charger_stat = stat;
+    start_ts = time(NULL);
+    last_uid_io_stats = get_uid_io_stats();
+}
+
+uid_monitor::uid_monitor()
+{
+    sem_init(&um_lock, 0, 1);
+}
+
+uid_monitor::~uid_monitor()
+{
+    sem_destroy(&um_lock);
+}
diff --git a/storaged/storaged_utils.cpp b/storaged/storaged_utils.cpp
new file mode 100644
index 0000000..74b7436
--- /dev/null
+++ b/storaged/storaged_utils.cpp
@@ -0,0 +1,232 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#define LOG_TAG "storaged"
+
+#include <dirent.h>
+#include <fcntl.h>
+#include <linux/time.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <iomanip>
+#include <sstream>
+#include <string>
+#include <unordered_map>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+#include <log/log_event_list.h>
+
+#include <storaged.h>
+#include <storaged_utils.h>
+
+bool parse_disk_stats(const char* disk_stats_path, struct disk_stats* stats) {
+    // Get time
+    struct timespec ts;
+    // Use monotonic to exclude suspend time so that we measure IO bytes/sec
+    // when system is running.
+    int ret = clock_gettime(CLOCK_MONOTONIC, &ts);
+    if (ret < 0) {
+        PLOG_TO(SYSTEM, ERROR) << "clock_gettime() failed";
+        return false;
+    }
+
+    std::string buffer;
+    if (!android::base::ReadFileToString(disk_stats_path, &buffer)) {
+        PLOG_TO(SYSTEM, ERROR) << disk_stats_path << ": ReadFileToString failed.";
+        return false;
+    }
+
+    // Regular diskstats entries
+    std::stringstream ss(buffer);
+    for (uint i = 0; i < DISK_STATS_SIZE; ++i) {
+        ss >> *((uint64_t*)stats + i);
+    }
+    // Other entries
+    stats->start_time = 0;
+    stats->end_time = (uint64_t)ts.tv_sec * SEC_TO_MSEC +
+        ts.tv_nsec / (MSEC_TO_USEC * USEC_TO_NSEC);
+    stats->counter = 1;
+    stats->io_avg = (double)stats->io_in_flight;
+    return true;
+}
+
+struct disk_perf get_disk_perf(struct disk_stats* stats) {
+    struct disk_perf perf;
+    memset(&perf, 0, sizeof(struct disk_perf));  // initialize
+
+    if (stats->io_ticks) {
+        if (stats->read_ticks) {
+            unsigned long long divisor = stats->read_ticks * stats->io_ticks;
+            perf.read_perf = ((unsigned long long)SECTOR_SIZE *
+                                        stats->read_sectors *
+                                        stats->io_in_queue +
+                                        (divisor >> 1)) /
+                                            divisor;
+            perf.read_ios = ((unsigned long long)SEC_TO_MSEC *
+                                        stats->read_ios *
+                                        stats->io_in_queue +
+                                        (divisor >> 1)) /
+                                            divisor;
+        }
+        if (stats->write_ticks) {
+            unsigned long long divisor = stats->write_ticks * stats->io_ticks;
+                        perf.write_perf = ((unsigned long long)SECTOR_SIZE *
+                                                    stats->write_sectors *
+                                                    stats->io_in_queue +
+                                                    (divisor >> 1)) /
+                                                        divisor;
+                        perf.write_ios = ((unsigned long long)SEC_TO_MSEC *
+                                                    stats->write_ios *
+                                                    stats->io_in_queue +
+                                                    (divisor >> 1)) /
+                                                        divisor;
+        }
+        perf.queue = (stats->io_in_queue + (stats->io_ticks >> 1)) /
+                                stats->io_ticks;
+    }
+    return perf;
+}
+
+struct disk_stats get_inc_disk_stats(struct disk_stats* prev, struct disk_stats* curr) {
+    struct disk_stats inc;
+    for (uint i = 0; i < DISK_STATS_SIZE; ++i) {
+        if (i == DISK_STATS_IO_IN_FLIGHT_IDX) {
+            continue;
+        }
+
+        *((uint64_t*)&inc + i) =
+                *((uint64_t*)curr + i) - *((uint64_t*)prev + i);
+    }
+    // io_in_flight is exception
+    inc.io_in_flight = curr->io_in_flight;
+
+    inc.start_time = prev->end_time;
+    inc.end_time = curr->end_time;
+    inc.io_avg = curr->io_avg;
+    inc.counter = 1;
+
+    return inc;
+}
+
+// Add src to dst
+void add_disk_stats(struct disk_stats* src, struct disk_stats* dst) {
+    if (dst->end_time != 0 && dst->end_time != src->start_time) {
+        LOG_TO(SYSTEM, WARNING) << "Two dis-continuous periods of diskstats"
+            << " are added. dst end with " << dst->end_time
+            << ", src start with " << src->start_time;
+    }
+
+    for (uint i = 0; i < DISK_STATS_SIZE; ++i) {
+        if (i == DISK_STATS_IO_IN_FLIGHT_IDX) {
+            continue;
+        }
+
+        *((uint64_t*)dst + i) += *((uint64_t*)src + i);
+    }
+
+    dst->io_in_flight = src->io_in_flight;
+    if (dst->counter + src->counter) {
+        dst->io_avg = ((dst->io_avg * dst->counter) + (src->io_avg * src->counter)) /
+                        (dst->counter + src->counter);
+    }
+    dst->counter += src->counter;
+    dst->end_time = src->end_time;
+    if (dst->start_time == 0) {
+        dst->start_time = src->start_time;
+    }
+}
+
+static bool cmp_uid_info(struct uid_info l, struct uid_info r) {
+    // Compare background I/O first.
+    for (int i = UID_STATS - 1; i >= 0; i--) {
+        uint64_t l_bytes = l.io[i].read_bytes + l.io[i].write_bytes;
+        uint64_t r_bytes = r.io[i].read_bytes + r.io[i].write_bytes;
+        uint64_t l_chars = l.io[i].rchar + l.io[i].wchar;
+        uint64_t r_chars = r.io[i].rchar + r.io[i].wchar;
+
+        if (l_bytes != r_bytes) {
+            return l_bytes > r_bytes;
+        }
+        if (l_chars != r_chars) {
+            return l_chars > r_chars;
+        }
+    }
+
+    return l.name < r.name;
+}
+
+void sort_running_uids_info(std::vector<struct uid_info> &uids) {
+    std::sort(uids.begin(), uids.end(), cmp_uid_info);
+}
+
+// Logging functions
+void log_console_running_uids_info(std::vector<struct uid_info> uids) {
+    printf("name/uid fg_rchar fg_wchar fg_rbytes fg_wbytes "
+           "bg_rchar bg_wchar bg_rbytes bg_wbytes fg_fsync bg_fsync\n");
+
+    for (const auto& uid : uids) {
+        printf("%s %ju %ju %ju %ju %ju %ju %ju %ju %ju %ju\n", uid.name.c_str(),
+            uid.io[0].rchar, uid.io[0].wchar, uid.io[0].read_bytes, uid.io[0].write_bytes,
+            uid.io[1].rchar, uid.io[1].wchar, uid.io[1].read_bytes, uid.io[1].write_bytes,
+            uid.io[0].fsync, uid.io[1].fsync);
+    }
+    fflush(stdout);
+}
+
+#if DEBUG
+void log_debug_disk_perf(struct disk_perf* perf, const char* type) {
+    // skip if the input structure are all zeros
+    if (perf == NULL) return;
+    struct disk_perf zero_cmp;
+    memset(&zero_cmp, 0, sizeof(zero_cmp));
+    if (memcmp(&zero_cmp, perf, sizeof(struct disk_perf)) == 0) return;
+
+    LOG_TO(SYSTEM, INFO) << "perf(ios) " << type
+              << " rd:" << perf->read_perf << "KB/s(" << perf->read_ios << "/s)"
+              << " wr:" << perf->write_perf << "KB/s(" << perf->write_ios << "/s)"
+              << " q:" << perf->queue;
+}
+#else
+void log_debug_disk_perf(struct disk_perf* /* perf */, const char* /* type */) {}
+#endif
+
+void log_event_disk_stats(struct disk_stats* stats, const char* type) {
+    // skip if the input structure are all zeros
+    if (stats == NULL) return;
+    struct disk_stats zero_cmp;
+    memset(&zero_cmp, 0, sizeof(zero_cmp));
+    // skip event logging diskstats when it is zero increment (all first 11 entries are zero)
+    if (memcmp(&zero_cmp, stats, sizeof(uint64_t) * DISK_STATS_SIZE) == 0) return;
+
+    android_log_event_list(EVENTLOGTAG_DISKSTATS)
+        << type << stats->start_time << stats->end_time
+        << stats->read_ios << stats->read_merges
+        << stats->read_sectors << stats->read_ticks
+        << stats->write_ios << stats->write_merges
+        << stats->write_sectors << stats->write_ticks
+        << (uint64_t)stats->io_avg << stats->io_ticks << stats->io_in_queue
+        << LOG_ID_EVENTS;
+}
+
diff --git a/storaged/tests/Android.mk b/storaged/tests/Android.mk
new file mode 100644
index 0000000..26d04b1
--- /dev/null
+++ b/storaged/tests/Android.mk
@@ -0,0 +1,45 @@
+#
+# Copyright (C) 2014 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.
+#
+
+LOCAL_PATH := $(call my-dir)
+
+test_module_prefix := storaged-
+test_tags := tests
+
+# -----------------------------------------------------------------------------
+# Unit tests.
+# -----------------------------------------------------------------------------
+
+test_c_flags := \
+    -fstack-protector-all \
+    -g \
+    -Wall -Wextra \
+    -Werror \
+    -fno-builtin \
+
+test_src_files := \
+    storaged_test.cpp \
+
+# Build tests for the logger. Run with:
+#   adb shell /data/nativetest/storaged-unit-tests/storaged-unit-tests
+include $(CLEAR_VARS)
+LOCAL_MODULE := $(test_module_prefix)unit-tests
+LOCAL_MODULE_TAGS := $(test_tags)
+LOCAL_CFLAGS += $(test_c_flags)
+LOCAL_STATIC_LIBRARIES := libstoraged
+LOCAL_SHARED_LIBRARIES := libbase libcutils liblog libpackagelistparser
+LOCAL_SRC_FILES := $(test_src_files)
+include $(BUILD_NATIVE_TEST)
diff --git a/storaged/tests/storaged_test.cpp b/storaged/tests/storaged_test.cpp
new file mode 100644
index 0000000..b103ac1
--- /dev/null
+++ b/storaged/tests/storaged_test.cpp
@@ -0,0 +1,351 @@
+/*
+ * Copyright (C) 2016 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 <deque>
+#include <fcntl.h>
+#include <random>
+#include <string.h>
+#include <stdio.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <gtest/gtest.h>
+
+#include <storaged.h>               // data structures
+#include <storaged_utils.h>         // functions to test
+
+#define MMC_DISK_STATS_PATH "/sys/block/mmcblk0/stat"
+#define SDA_DISK_STATS_PATH "/sys/block/sda/stat"
+
+static void pause(uint32_t sec) {
+    const char* path = "/cache/test";
+    int fd = open(path, O_WRONLY | O_CREAT, 0600);
+    ASSERT_LT(-1, fd);
+    char buffer[2048];
+    memset(buffer, 1, sizeof(buffer));
+    int loop_size = 100;
+    for (int i = 0; i < loop_size; ++i) {
+        ASSERT_EQ(2048, write(fd, buffer, sizeof(buffer)));
+    }
+    fsync(fd);
+    close(fd);
+
+    fd = open(path, O_RDONLY);
+    ASSERT_LT(-1, fd);
+    for (int i = 0; i < loop_size; ++i) {
+        ASSERT_EQ(2048, read(fd, buffer, sizeof(buffer)));
+    }
+    close(fd);
+
+    sleep(sec);
+}
+
+// the return values of the tested functions should be the expected ones
+const char* DISK_STATS_PATH;
+TEST(storaged_test, retvals) {
+    struct disk_stats stats;
+    memset(&stats, 0, sizeof(struct disk_stats));
+
+    if (access(MMC_DISK_STATS_PATH, R_OK) >= 0) {
+        DISK_STATS_PATH = MMC_DISK_STATS_PATH;
+    } else if (access(SDA_DISK_STATS_PATH, R_OK) >= 0) {
+        DISK_STATS_PATH = SDA_DISK_STATS_PATH;
+    } else {
+        return;
+    }
+
+    EXPECT_TRUE(parse_disk_stats(DISK_STATS_PATH, &stats));
+
+    struct disk_stats old_stats;
+    memset(&old_stats, 0, sizeof(struct disk_stats));
+    old_stats = stats;
+
+    const char wrong_path[] = "/this/is/wrong";
+    EXPECT_FALSE(parse_disk_stats(wrong_path, &stats));
+
+    // reading a wrong path should not damage the output structure
+    EXPECT_EQ(0, memcmp(&stats, &old_stats, sizeof(disk_stats)));
+}
+
+TEST(storaged_test, disk_stats) {
+    struct disk_stats stats;
+    memset(&stats, 0, sizeof(struct disk_stats));
+
+    ASSERT_TRUE(parse_disk_stats(DISK_STATS_PATH, &stats));
+
+    // every entry of stats (except io_in_flight) should all be greater than 0
+    for (uint i = 0; i < DISK_STATS_SIZE; ++i) {
+        if (i == 8) continue; // skip io_in_flight which can be 0
+        EXPECT_LT((uint64_t)0, *((uint64_t*)&stats + i));
+    }
+
+    // accumulation of the increments should be the same with the overall increment
+    struct disk_stats base, tmp, curr, acc, inc[5];
+    memset(&base, 0, sizeof(struct disk_stats));
+    memset(&tmp, 0, sizeof(struct disk_stats));
+    memset(&acc, 0, sizeof(struct disk_stats));
+
+    for (uint i = 0; i < 5; ++i) {
+        ASSERT_TRUE(parse_disk_stats(DISK_STATS_PATH, &curr));
+        if (i == 0) {
+            base = curr;
+            tmp = curr;
+            sleep(5);
+            continue;
+        }
+        inc[i] = get_inc_disk_stats(&tmp, &curr);
+        add_disk_stats(&inc[i], &acc);
+        tmp = curr;
+        pause(5);
+    }
+    struct disk_stats overall_inc;
+    memset(&overall_inc, 0, sizeof(disk_stats));
+    overall_inc= get_inc_disk_stats(&base, &curr);
+
+    for (uint i = 0; i < DISK_STATS_SIZE; ++i) {
+        if (i == 8) continue; // skip io_in_flight which can be 0
+        EXPECT_EQ(*((uint64_t*)&overall_inc + i), *((uint64_t*)&acc + i));
+    }
+}
+
+static double mean(std::deque<uint32_t> nums) {
+    double sum = 0.0;
+    for (uint32_t i : nums) {
+    sum += i;
+    }
+    return sum / nums.size();
+}
+
+static double standard_deviation(std::deque<uint32_t> nums) {
+    double sum = 0.0;
+    double avg = mean(nums);
+    for (uint32_t i : nums) {
+    sum += ((double)i - avg) * ((double)i - avg);
+    }
+    return sqrt(sum / nums.size());
+}
+
+TEST(storaged_test, stream_stats) {
+    // 100 random numbers
+    std::vector<uint32_t> data = {8147,9058,1270,9134,6324,975,2785,5469,9575,9649,1576,9706,9572,4854,8003,1419,4218,9157,7922,9595,6557,357,8491,9340,6787,7577,7431,3922,6555,1712,7060,318,2769,462,971,8235,6948,3171,9502,344,4387,3816,7655,7952,1869,4898,4456,6463,7094,7547,2760,6797,6551,1626,1190,4984,9597,3404,5853,2238,7513,2551,5060,6991,8909,9593,5472,1386,1493,2575,8407,2543,8143,2435,9293,3500,1966,2511,6160,4733,3517,8308,5853,5497,9172,2858,7572,7537,3804,5678,759,540,5308,7792,9340,1299,5688,4694,119,3371};
+    std::deque<uint32_t> test_data;
+    stream_stats sstats;
+    for (uint32_t i : data) {
+        test_data.push_back(i);
+        sstats.add(i);
+
+        EXPECT_EQ((int)standard_deviation(test_data), (int)sstats.get_std());
+        EXPECT_EQ((int)mean(test_data), (int)sstats.get_mean());
+    }
+
+    for (uint32_t i : data) {
+        test_data.pop_front();
+        sstats.evict(i);
+
+        EXPECT_EQ((int)standard_deviation(test_data), (int)sstats.get_std());
+        EXPECT_EQ((int)mean(test_data), (int)sstats.get_mean());
+    }
+
+    // some real data
+    std::vector<uint32_t> another_data = {113875,81620,103145,28327,86855,207414,96526,52567,28553,250311};
+    test_data.clear();
+    uint32_t window_size = 2;
+    uint32_t idx;
+    stream_stats sstats1;
+    for (idx = 0; idx < window_size; ++idx) {
+        test_data.push_back(another_data[idx]);
+        sstats1.add(another_data[idx]);
+    }
+    EXPECT_EQ((int)standard_deviation(test_data), (int)sstats1.get_std());
+    EXPECT_EQ((int)mean(test_data), (int)sstats1.get_mean());
+    for (;idx < another_data.size(); ++idx) {
+        test_data.pop_front();
+        sstats1.evict(another_data[idx - window_size]);
+        test_data.push_back(another_data[idx]);
+        sstats1.add(another_data[idx]);
+        EXPECT_EQ((int)standard_deviation(test_data), (int)sstats1.get_std());
+        EXPECT_EQ((int)mean(test_data), (int)sstats1.get_mean());
+    }
+}
+
+static struct disk_perf disk_perf_multiply(struct disk_perf perf, double mul) {
+    struct disk_perf retval;
+    retval.read_perf = (double)perf.read_perf * mul;
+    retval.read_ios = (double)perf.read_ios * mul;
+    retval.write_perf = (double)perf.write_perf * mul;
+    retval.write_ios = (double)perf.write_ios * mul;
+    retval.queue = (double)perf.queue * mul;
+
+    return retval;
+}
+
+static struct disk_stats disk_stats_add(struct disk_stats stats1, struct disk_stats stats2) {
+    struct disk_stats retval;
+    retval.read_ios = stats1.read_ios + stats2.read_ios;
+    retval.read_merges = stats1.read_merges + stats2.read_merges;
+    retval.read_sectors = stats1.read_sectors + stats2.read_sectors;
+    retval.read_ticks = stats1.read_ticks + stats2.read_ticks;
+    retval.write_ios = stats1.write_ios + stats2.write_ios;
+    retval.write_merges = stats1.write_merges + stats2.write_merges;
+    retval.write_sectors = stats1.write_sectors + stats2.write_sectors;
+    retval.write_ticks = stats1.write_ticks + stats2.write_ticks;
+    retval.io_in_flight = stats1.io_in_flight + stats2.io_in_flight;
+    retval.io_ticks = stats1.io_ticks + stats2.io_ticks;
+    retval.io_in_queue = stats1.io_in_queue + stats2.io_in_queue;
+    retval.end_time = stats1.end_time + stats2.end_time;
+
+    return retval;
+}
+
+TEST(storaged_test, disk_stats_monitor) {
+    // asserting that there is one file for diskstats
+    ASSERT_TRUE(access(MMC_DISK_STATS_PATH, R_OK) >= 0 || access(SDA_DISK_STATS_PATH, R_OK) >= 0);
+    // testing if detect() will return the right value
+    disk_stats_monitor dsm_detect;
+    // feed monitor with constant perf data for io perf baseline
+    // using constant perf is reasonable since the functionality of stream_stats
+    // has already been tested
+    struct disk_perf norm_perf = {
+        .read_perf = 10 * 1024,
+        .read_ios = 50,
+        .write_perf = 5 * 1024,
+        .write_ios = 25,
+        .queue = 5
+    };
+
+    std::random_device rd;
+    std::mt19937 gen(rd());
+    std::uniform_real_distribution<> rand(0.8, 1.2);
+
+    for (uint i = 0; i < dsm_detect.mWindow; ++i) {
+        struct disk_perf perf = disk_perf_multiply(norm_perf, rand(gen));
+
+        dsm_detect.add(&perf);
+        dsm_detect.mBuffer.push(perf);
+        EXPECT_EQ(dsm_detect.mBuffer.size(), (uint64_t)i + 1);
+    }
+
+    dsm_detect.mValid = true;
+    dsm_detect.update_mean();
+    dsm_detect.update_std();
+
+    for (double i = 0; i < 2 * dsm_detect.mSigma; i += 0.5) {
+        struct disk_perf test_perf;
+        struct disk_perf test_mean = dsm_detect.mMean;
+        struct disk_perf test_std = dsm_detect.mStd;
+
+        test_perf.read_perf = (double)test_mean.read_perf - i * test_std.read_perf;
+        test_perf.read_ios = (double)test_mean.read_ios - i * test_std.read_ios;
+        test_perf.write_perf = (double)test_mean.write_perf - i * test_std.write_perf;
+        test_perf.write_ios = (double)test_mean.write_ios - i * test_std.write_ios;
+        test_perf.queue = (double)test_mean.queue + i * test_std.queue;
+
+        EXPECT_EQ((i > dsm_detect.mSigma), dsm_detect.detect(&test_perf));
+    }
+
+    // testing if stalled disk_stats can be correctly accumulated in the monitor
+    disk_stats_monitor dsm_acc;
+    struct disk_stats norm_inc = {
+        .read_ios = 200,
+        .read_merges = 0,
+        .read_sectors = 200,
+        .read_ticks = 200,
+        .write_ios = 100,
+        .write_merges = 0,
+        .write_sectors = 100,
+        .write_ticks = 100,
+        .io_in_flight = 0,
+        .io_ticks = 600,
+        .io_in_queue = 300,
+        .start_time = 0,
+        .end_time = 100,
+        .counter = 0,
+        .io_avg = 0
+    };
+
+    struct disk_stats stall_inc = {
+        .read_ios = 200,
+        .read_merges = 0,
+        .read_sectors = 20,
+        .read_ticks = 200,
+        .write_ios = 100,
+        .write_merges = 0,
+        .write_sectors = 10,
+        .write_ticks = 100,
+        .io_in_flight = 0,
+        .io_ticks = 600,
+        .io_in_queue = 1200,
+        .start_time = 0,
+        .end_time = 100,
+        .counter = 0,
+        .io_avg = 0
+    };
+
+    struct disk_stats stats_base;
+    memset(&stats_base, 0, sizeof(stats_base));
+
+    int loop_size = 100;
+    for (int i = 0; i < loop_size; ++i) {
+        stats_base = disk_stats_add(stats_base, norm_inc);
+        dsm_acc.update(&stats_base);
+        EXPECT_EQ(dsm_acc.mValid, (uint32_t)(i + 1) >= dsm_acc.mWindow);
+        EXPECT_FALSE(dsm_acc.mStall);
+    }
+
+    stats_base = disk_stats_add(stats_base, stall_inc);
+    dsm_acc.update(&stats_base);
+    EXPECT_TRUE(dsm_acc.mValid);
+    EXPECT_TRUE(dsm_acc.mStall);
+
+    for (int i = 0; i < 10; ++i) {
+        stats_base = disk_stats_add(stats_base, norm_inc);
+        dsm_acc.update(&stats_base);
+        EXPECT_TRUE(dsm_acc.mValid);
+        EXPECT_FALSE(dsm_acc.mStall);
+    }
+}
+
+static void expect_increasing(struct disk_stats stats1, struct disk_stats stats2) {
+    EXPECT_LE(stats1.read_ios, stats2.read_ios);
+    EXPECT_LE(stats1.read_merges, stats2.read_merges);
+    EXPECT_LE(stats1.read_sectors, stats2.read_sectors);
+    EXPECT_LE(stats1.read_ticks, stats2.read_ticks);
+
+    EXPECT_LE(stats1.write_ios, stats2.write_ios);
+    EXPECT_LE(stats1.write_merges, stats2.write_merges);
+    EXPECT_LE(stats1.write_sectors, stats2.write_sectors);
+    EXPECT_LE(stats1.write_ticks, stats2.write_ticks);
+
+    EXPECT_LE(stats1.io_ticks, stats2.io_ticks);
+    EXPECT_LE(stats1.io_in_queue, stats2.io_in_queue);
+}
+
+#define TEST_LOOPS 20
+TEST(storaged_test, disk_stats_publisher) {
+    // asserting that there is one file for diskstats
+    ASSERT_TRUE(access(MMC_DISK_STATS_PATH, R_OK) >= 0 || access(SDA_DISK_STATS_PATH, R_OK) >= 0);
+    disk_stats_publisher dsp;
+    struct disk_stats prev;
+    memset(&prev, 0, sizeof(prev));
+
+    for (int i = 0; i < TEST_LOOPS; ++i) {
+        dsp.update();
+        expect_increasing(prev, dsp.mPrevious);
+        prev = dsp.mPrevious;
+        pause(10);
+    }
+}
+
diff --git a/toolbox/Android.bp b/toolbox/Android.bp
index 8db8327..f08cf93 100644
--- a/toolbox/Android.bp
+++ b/toolbox/Android.bp
@@ -1,35 +1,70 @@
-common_cflags = [
-    "-Werror",
-    "-Wno-unused-parameter",
-    "-Wno-unused-const-variable",
-    "-include bsd-compatibility.h"
-]
+cc_defaults {
+    name: "toolbox_defaults",
 
-cc_library_static {
-    srcs: [
-        "upstream-netbsd/bin/dd/args.c",
-        "upstream-netbsd/bin/dd/conv.c",
-        "upstream-netbsd/bin/dd/dd.c",
-        "upstream-netbsd/bin/dd/dd_hostops.c",
-        "upstream-netbsd/bin/dd/misc.c",
-        "upstream-netbsd/bin/dd/position.c",
-        "upstream-netbsd/lib/libc/gen/getbsize.c",
-        "upstream-netbsd/lib/libc/gen/humanize_number.c",
-        "upstream-netbsd/lib/libc/stdlib/strsuftoll.c",
-        "upstream-netbsd/lib/libc/string/swab.c",
-        "upstream-netbsd/lib/libutil/raise_default_signal.c",
+    cflags: [
+        "-Werror",
+        "-Wno-unused-parameter",
+        "-Wno-unused-const-variable",
+        "-D_FILE_OFFSET_BITS=64",
+        "-DWITHOUT_NLS",
     ],
-    cflags: common_cflags + [
-        "-Dmain=dd_main",
-        "-DNO_CONV",
-    ],
-    local_include_dirs: ["upstream-netbsd/include/"],
-    name: "libtoolbox_dd",
 }
 
-// We build BSD grep separately, so it can provide egrep and fgrep too.
+genrule {
+    name: "toolbox_input_labels",
+    tool_files: ["generate-input.h-labels.py"],
+    cmd: "$(location) $(in) >$(out)",
+    srcs: [":kernel_input_headers"],
+    out: ["input.h-labels.h"],
+}
+
+cc_defaults {
+    name: "toolbox_binary_defaults",
+    defaults: ["toolbox_defaults"],
+    cpp_std: "experimental",
+    srcs: [
+        "toolbox.c",
+        "getevent.c",
+        "getprop.cpp",
+    ],
+    generated_headers: [
+        "toolbox_input_labels",
+    ],
+    shared_libs: [
+        "libbase",
+    ],
+    static_libs: ["libpropertyinfoparser"],
+
+    symlinks: [
+        "getevent",
+        "getprop",
+    ],
+}
+
+cc_binary {
+    name: "toolbox",
+    defaults: ["toolbox_binary_defaults"],
+    recovery_available: true,
+}
+
+cc_binary {
+    name: "toolbox_vendor",
+    stem: "toolbox",
+    vendor: true,
+    defaults: ["toolbox_binary_defaults"],
+}
+
+// We only want 'r' on userdebug and eng builds.
+cc_binary {
+    name: "r",
+    defaults: ["toolbox_defaults"],
+    srcs: ["r.c"],
+}
+
+// We build BSD grep separately (but see http://b/111849261).
 cc_defaults {
     name: "grep_common",
+    defaults: ["toolbox_defaults"],
     srcs: [
         "upstream-netbsd/usr.bin/grep/fastgrep.c",
         "upstream-netbsd/usr.bin/grep/file.c",
@@ -37,14 +72,19 @@
         "upstream-netbsd/usr.bin/grep/queue.c",
         "upstream-netbsd/usr.bin/grep/util.c",
     ],
-    cflags: common_cflags,
-    local_include_dirs: ["upstream-netbsd/include/"],
-    symlinks: ["egrep", "fgrep"],
+    symlinks: [
+        "egrep",
+        "fgrep",
+    ],
+    sanitize: {
+        integer_overflow: false,
+    },
 }
 
 cc_binary {
     name: "grep",
     defaults: ["grep_common"],
+    recovery_available: true,
 }
 
 // Build vendor grep.
diff --git a/toolbox/Android.mk b/toolbox/Android.mk
deleted file mode 100644
index 94029d8..0000000
--- a/toolbox/Android.mk
+++ /dev/null
@@ -1,61 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-
-common_cflags := \
-    -Werror -Wno-unused-parameter -Wno-unused-const-variable \
-    -include bsd-compatibility.h \
-
-include $(CLEAR_VARS)
-
-BSD_TOOLS := \
-    dd \
-
-OUR_TOOLS := \
-    getevent \
-    newfs_msdos \
-
-ALL_TOOLS = $(BSD_TOOLS) $(OUR_TOOLS)
-
-LOCAL_SRC_FILES := \
-    toolbox.c \
-    $(patsubst %,%.c,$(OUR_TOOLS)) \
-
-LOCAL_CFLAGS += $(common_cflags)
-LOCAL_C_INCLUDES += $(LOCAL_PATH)/upstream-netbsd/include/
-
-LOCAL_SHARED_LIBRARIES := \
-    libcutils \
-
-LOCAL_WHOLE_STATIC_LIBRARIES := $(patsubst %,libtoolbox_%,$(BSD_TOOLS))
-
-LOCAL_MODULE := toolbox
-
-# Install the symlinks.
-LOCAL_POST_INSTALL_CMD := $(hide) $(foreach t,$(ALL_TOOLS),ln -sf toolbox $(TARGET_OUT)/bin/$(t);)
-
-# Including this will define $(intermediates).
-#
-include $(BUILD_EXECUTABLE)
-
-$(LOCAL_PATH)/toolbox.c: $(intermediates)/tools.h
-
-TOOLS_H := $(intermediates)/tools.h
-$(TOOLS_H): PRIVATE_TOOLS := toolbox $(ALL_TOOLS)
-$(TOOLS_H): PRIVATE_CUSTOM_TOOL = echo "/* file generated automatically */" > $@ ; for t in $(PRIVATE_TOOLS) ; do echo "TOOL($$t)" >> $@ ; done
-$(TOOLS_H): $(LOCAL_PATH)/Android.mk
-$(TOOLS_H):
-	$(transform-generated-source)
-
-$(LOCAL_PATH)/getevent.c: $(intermediates)/input.h-labels.h
-
-UAPI_INPUT_EVENT_CODES_H := bionic/libc/kernel/uapi/linux/input.h bionic/libc/kernel/uapi/linux/input-event-codes.h
-INPUT_H_LABELS_H := $(intermediates)/input.h-labels.h
-$(INPUT_H_LABELS_H): PRIVATE_LOCAL_PATH := $(LOCAL_PATH)
-# The PRIVATE_CUSTOM_TOOL line uses = to evaluate the output path late.
-# We copy the input path so it can't be accidentally modified later.
-$(INPUT_H_LABELS_H): PRIVATE_UAPI_INPUT_EVENT_CODES_H := $(UAPI_INPUT_EVENT_CODES_H)
-$(INPUT_H_LABELS_H): PRIVATE_CUSTOM_TOOL = $(PRIVATE_LOCAL_PATH)/generate-input.h-labels.py $(PRIVATE_UAPI_INPUT_EVENT_CODES_H) > $@
-# The dependency line though gets evaluated now, so the PRIVATE_ copy doesn't exist yet,
-# and the original can't yet have been modified, so this is both sufficient and necessary.
-$(INPUT_H_LABELS_H): $(LOCAL_PATH)/Android.mk $(LOCAL_PATH)/generate-input.h-labels.py $(UAPI_INPUT_EVENT_CODES_H)
-$(INPUT_H_LABELS_H):
-	$(transform-generated-source)
diff --git a/toolbox/MODULE_LICENSE_BSD b/toolbox/MODULE_LICENSE_APACHE2
similarity index 100%
rename from toolbox/MODULE_LICENSE_BSD
rename to toolbox/MODULE_LICENSE_APACHE2
diff --git a/toolbox/NOTICE b/toolbox/NOTICE
index e9ab58d..8e8a91c 100644
--- a/toolbox/NOTICE
+++ b/toolbox/NOTICE
@@ -1,4 +1,4 @@
-Copyright (C) 2010 The Android Open Source Project
+Copyright (C) 2017 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,974 +14,3 @@
 
 -------------------------------------------------------------------
 
-Copyright (C) 2014, The Android Open Source Project
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
- * Redistributions of source code must retain the above copyright
-   notice, this list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above copyright
-   notice, this list of conditions and the following disclaimer in
-   the documentation and/or other materials provided with the
-   distribution.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
-FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
-COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
-INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
-BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
-OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
-AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
-OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGE.
-
--------------------------------------------------------------------
-
-Copyright (c) 1987, 1993
-   The Regents of the University of California.  All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-1. Redistributions of source code must retain the above copyright
-   notice, this list of conditions and the following disclaimer.
-2. Redistributions in binary form must reproduce the above copyright
-   notice, this list of conditions and the following disclaimer in the
-   documentation and/or other materials provided with the distribution.
-3. Neither the name of the University nor the names of its contributors
-   may be used to endorse or promote products derived from this software
-   without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
-FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGE.
-
--------------------------------------------------------------------
-
-Copyright (c) 1987, 1993, 1994
-   The Regents of the University of California.  All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-1. Redistributions of source code must retain the above copyright
-   notice, this list of conditions and the following disclaimer.
-2. Redistributions in binary form must reproduce the above copyright
-   notice, this list of conditions and the following disclaimer in the
-   documentation and/or other materials provided with the distribution.
-3. Neither the name of the University nor the names of its contributors
-   may be used to endorse or promote products derived from this software
-   without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
-FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGE.
-
--------------------------------------------------------------------
-
-Copyright (c) 1988, 1993
-   The Regents of the University of California.  All rights reserved.
-
-This code is derived from software contributed to Berkeley by
-Jeffrey Mogul.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-1. Redistributions of source code must retain the above copyright
-   notice, this list of conditions and the following disclaimer.
-2. Redistributions in binary form must reproduce the above copyright
-   notice, this list of conditions and the following disclaimer in the
-   documentation and/or other materials provided with the distribution.
-3. Neither the name of the University nor the names of its contributors
-   may be used to endorse or promote products derived from this software
-   without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
-FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGE.
-
--------------------------------------------------------------------
-
-Copyright (c) 1988, 1993, 1994
-   The Regents of the University of California.  All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-1. Redistributions of source code must retain the above copyright
-   notice, this list of conditions and the following disclaimer.
-2. Redistributions in binary form must reproduce the above copyright
-   notice, this list of conditions and the following disclaimer in the
-   documentation and/or other materials provided with the distribution.
-3. Neither the name of the University nor the names of its contributors
-   may be used to endorse or promote products derived from this software
-   without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
-FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGE.
-
--------------------------------------------------------------------
-
-Copyright (c) 1988, 1993, 1994
-   The Regents of the University of California.  All rights reserved.
-
-This code is derived from software contributed to Berkeley by
-David Hitz of Auspex Systems Inc.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-1. Redistributions of source code must retain the above copyright
-   notice, this list of conditions and the following disclaimer.
-2. Redistributions in binary form must reproduce the above copyright
-   notice, this list of conditions and the following disclaimer in the
-   documentation and/or other materials provided with the distribution.
-3. Neither the name of the University nor the names of its contributors
-   may be used to endorse or promote products derived from this software
-   without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
-FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGE.
-
--------------------------------------------------------------------
-
-Copyright (c) 1988, 1993, 1994, 2003
-   The Regents of the University of California.  All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-1. Redistributions of source code must retain the above copyright
-   notice, this list of conditions and the following disclaimer.
-2. Redistributions in binary form must reproduce the above copyright
-   notice, this list of conditions and the following disclaimer in the
-   documentation and/or other materials provided with the distribution.
-3. Neither the name of the University nor the names of its contributors
-   may be used to endorse or promote products derived from this software
-   without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
-FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGE.
-
--------------------------------------------------------------------
-
-Copyright (c) 1989, 1993
-   The Regents of the University of California.  All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-1. Redistributions of source code must retain the above copyright
-   notice, this list of conditions and the following disclaimer.
-2. Redistributions in binary form must reproduce the above copyright
-   notice, this list of conditions and the following disclaimer in the
-   documentation and/or other materials provided with the distribution.
-3. Neither the name of the University nor the names of its contributors
-   may be used to endorse or promote products derived from this software
-   without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
-FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGE.
-
--------------------------------------------------------------------
-
-Copyright (c) 1989, 1993
-   The Regents of the University of California.  All rights reserved.
-
-This code is derived from software contributed to Berkeley by
-Kevin Fall.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-1. Redistributions of source code must retain the above copyright
-   notice, this list of conditions and the following disclaimer.
-2. Redistributions in binary form must reproduce the above copyright
-   notice, this list of conditions and the following disclaimer in the
-   documentation and/or other materials provided with the distribution.
-3. Neither the name of the University nor the names of its contributors
-   may be used to endorse or promote products derived from this software
-   without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
-FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGE.
-
--------------------------------------------------------------------
-
-Copyright (c) 1989, 1993, 1994
-   The Regents of the University of California.  All rights reserved.
-
-This code is derived from software contributed to Berkeley by
-Chris Newcomb.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-1. Redistributions of source code must retain the above copyright
-   notice, this list of conditions and the following disclaimer.
-2. Redistributions in binary form must reproduce the above copyright
-   notice, this list of conditions and the following disclaimer in the
-   documentation and/or other materials provided with the distribution.
-3. Neither the name of the University nor the names of its contributors
-   may be used to endorse or promote products derived from this software
-   without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
-FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGE.
-
--------------------------------------------------------------------
-
-Copyright (c) 1989, 1993, 1994
-   The Regents of the University of California.  All rights reserved.
-
-This code is derived from software contributed to Berkeley by
-Ken Smith of The State University of New York at Buffalo.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-1. Redistributions of source code must retain the above copyright
-   notice, this list of conditions and the following disclaimer.
-2. Redistributions in binary form must reproduce the above copyright
-   notice, this list of conditions and the following disclaimer in the
-   documentation and/or other materials provided with the distribution.
-3. Neither the name of the University nor the names of its contributors
-   may be used to endorse or promote products derived from this software
-   without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
-FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGE.
-
--------------------------------------------------------------------
-
-Copyright (c) 1990, 1993, 1994, 2003
-   The Regents of the University of California.  All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-1. Redistributions of source code must retain the above copyright
-   notice, this list of conditions and the following disclaimer.
-2. Redistributions in binary form must reproduce the above copyright
-   notice, this list of conditions and the following disclaimer in the
-   documentation and/or other materials provided with the distribution.
-3. Neither the name of the University nor the names of its contributors
-   may be used to endorse or promote products derived from this software
-   without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
-FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGE.
-
--------------------------------------------------------------------
-
-Copyright (c) 1991, 1993
-   The Regents of the University of California.  All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-1. Redistributions of source code must retain the above copyright
-   notice, this list of conditions and the following disclaimer.
-2. Redistributions in binary form must reproduce the above copyright
-   notice, this list of conditions and the following disclaimer in the
-   documentation and/or other materials provided with the distribution.
-3. Neither the name of the University nor the names of its contributors
-   may be used to endorse or promote products derived from this software
-   without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
-FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGE.
-
--------------------------------------------------------------------
-
-Copyright (c) 1991, 1993, 1994
-   The Regents of the University of California.  All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-1. Redistributions of source code must retain the above copyright
-   notice, this list of conditions and the following disclaimer.
-2. Redistributions in binary form must reproduce the above copyright
-   notice, this list of conditions and the following disclaimer in the
-   documentation and/or other materials provided with the distribution.
-3. Neither the name of the University nor the names of its contributors
-   may be used to endorse or promote products derived from this software
-   without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
-FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGE.
-
--------------------------------------------------------------------
-
-Copyright (c) 1991, 1993, 1994
-   The Regents of the University of California.  All rights reserved.
-
-This code is derived from software contributed to Berkeley by
-Keith Muller of the University of California, San Diego and Lance
-Visser of Convex Computer Corporation.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-1. Redistributions of source code must retain the above copyright
-   notice, this list of conditions and the following disclaimer.
-2. Redistributions in binary form must reproduce the above copyright
-   notice, this list of conditions and the following disclaimer in the
-   documentation and/or other materials provided with the distribution.
-3. Neither the name of the University nor the names of its contributors
-   may be used to endorse or promote products derived from this software
-   without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
-FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGE.
-
--------------------------------------------------------------------
-
-Copyright (c) 1992, 1993, 1994
-   The Regents of the University of California.  All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-1. Redistributions of source code must retain the above copyright
-   notice, this list of conditions and the following disclaimer.
-2. Redistributions in binary form must reproduce the above copyright
-   notice, this list of conditions and the following disclaimer in the
-   documentation and/or other materials provided with the distribution.
-3. Neither the name of the University nor the names of its contributors
-   may be used to endorse or promote products derived from this software
-   without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
-FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGE.
-
--------------------------------------------------------------------
-
-Copyright (c) 1997, 1998, 1999, 2002 The NetBSD Foundation, Inc.
-All rights reserved.
-
-This code is derived from software contributed to The NetBSD Foundation
-by Jason R. Thorpe of the Numerical Aerospace Simulation Facility,
-NASA Ames Research Center, by Luke Mewburn and by Tomas Svensson.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-1. Redistributions of source code must retain the above copyright
-   notice, this list of conditions and the following disclaimer.
-2. Redistributions in binary form must reproduce the above copyright
-   notice, this list of conditions and the following disclaimer in the
-   documentation and/or other materials provided with the distribution.
-
-THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
-``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
-TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
-PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
-BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-POSSIBILITY OF SUCH DAMAGE.
-
--------------------------------------------------------------------
-
-Copyright (c) 1998 Robert Nordier
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-1. Redistributions of source code must retain the above copyright
-   notice, this list of conditions and the following disclaimer.
-2. Redistributions in binary form must reproduce the above copyright
-   notice, this list of conditions and the following disclaimer in
-   the documentation and/or other materials provided with the
-   distribution.
-
-THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS
-OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY
-DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
-GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
-IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
-OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
-IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
--------------------------------------------------------------------
-
-Copyright (c) 1999 James Howard and Dag-Erling Coïdan Smørgrav
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-1. Redistributions of source code must retain the above copyright
-   notice, this list of conditions and the following disclaimer.
-2. Redistributions in binary form must reproduce the above copyright
-   notice, this list of conditions and the following disclaimer in the
-   documentation and/or other materials provided with the distribution.
-
-THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
-FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGE.
-
--------------------------------------------------------------------
-
-Copyright (c) 1999 James Howard and Dag-Erling Coïdan Smørgrav
-Copyright (C) 2008 Gabor Kovesdan <gabor@FreeBSD.org>
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-1. Redistributions of source code must retain the above copyright
-   notice, this list of conditions and the following disclaimer.
-2. Redistributions in binary form must reproduce the above copyright
-   notice, this list of conditions and the following disclaimer in the
-   documentation and/or other materials provided with the distribution.
-
-THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
-FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGE.
-
--------------------------------------------------------------------
-
-Copyright (c) 1999 James Howard and Dag-Erling Coïdan Smørgrav
-Copyright (C) 2008-2009 Gabor Kovesdan <gabor@FreeBSD.org>
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-1. Redistributions of source code must retain the above copyright
-   notice, this list of conditions and the following disclaimer.
-2. Redistributions in binary form must reproduce the above copyright
-   notice, this list of conditions and the following disclaimer in the
-   documentation and/or other materials provided with the distribution.
-
-THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
-FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGE.
-
--------------------------------------------------------------------
-
-Copyright (c) 1999 James Howard and Dag-Erling Coïdan Smørgrav
-Copyright (C) 2008-2010 Gabor Kovesdan <gabor@FreeBSD.org>
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-1. Redistributions of source code must retain the above copyright
-   notice, this list of conditions and the following disclaimer.
-2. Redistributions in binary form must reproduce the above copyright
-   notice, this list of conditions and the following disclaimer in the
-   documentation and/or other materials provided with the distribution.
-
-THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
-FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGE.
-
--------------------------------------------------------------------
-
-Copyright (c) 1999 James Howard and Dag-Erling Coïdan Smørgrav
-Copyright (C) 2008-2010 Gabor Kovesdan <gabor@FreeBSD.org>
-Copyright (C) 2010 Dimitry Andric <dimitry@andric.com>
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-1. Redistributions of source code must retain the above copyright
-   notice, this list of conditions and the following disclaimer.
-2. Redistributions in binary form must reproduce the above copyright
-   notice, this list of conditions and the following disclaimer in the
-   documentation and/or other materials provided with the distribution.
-
-THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
-FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGE.
-
--------------------------------------------------------------------
-
-Copyright (c) 1999 James Howard and Dag-Erling Coïdan Smørgrav
-Copyright (c) 2008-2009 Gabor Kovesdan <gabor@FreeBSD.org>
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-1. Redistributions of source code must retain the above copyright
-   notice, this list of conditions and the following disclaimer.
-2. Redistributions in binary form must reproduce the above copyright
-   notice, this list of conditions and the following disclaimer in the
-   documentation and/or other materials provided with the distribution.
-
-THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
-FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGE.
-
--------------------------------------------------------------------
-
-Copyright (c) 2001-2002,2004 The NetBSD Foundation, Inc.
-All rights reserved.
-
-This code is derived from software contributed to The NetBSD Foundation
-by Luke Mewburn.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-1. Redistributions of source code must retain the above copyright
-   notice, this list of conditions and the following disclaimer.
-2. Redistributions in binary form must reproduce the above copyright
-   notice, this list of conditions and the following disclaimer in the
-   documentation and/or other materials provided with the distribution.
-
-THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
-``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
-TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
-PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
-BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-POSSIBILITY OF SUCH DAMAGE.
-
--------------------------------------------------------------------
-
-Copyright (c) 2007 The NetBSD Foundation, Inc.
-All rights reserved.
-
-This code is derived from software contributed to The NetBSD Foundation
-by Luke Mewburn.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-1. Redistributions of source code must retain the above copyright
-   notice, this list of conditions and the following disclaimer.
-2. Redistributions in binary form must reproduce the above copyright
-   notice, this list of conditions and the following disclaimer in the
-   documentation and/or other materials provided with the distribution.
-
-THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
-``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
-TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
-PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
-BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-POSSIBILITY OF SUCH DAMAGE.
-
--------------------------------------------------------------------
-
-Copyright (c) 2008, The Android Open Source Project
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
- * Redistributions of source code must retain the above copyright
-   notice, this list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above copyright
-   notice, this list of conditions and the following disclaimer in
-   the documentation and/or other materials provided with the
-   distribution.
- * Neither the name of Google, Inc. nor the names of its contributors
-   may be used to endorse or promote products derived from this
-   software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
-FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
-COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
-INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
-BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
-OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
-AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
-OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGE.
-
--------------------------------------------------------------------
-
-Copyright (c) 2009, The Android Open Source Project
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
- * Redistributions of source code must retain the above copyright
-   notice, this list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above copyright
-   notice, this list of conditions and the following disclaimer in
-   the documentation and/or other materials provided with the
-   distribution.
- * Neither the name of Google, Inc. nor the names of its contributors
-   may be used to endorse or promote products derived from this
-   software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
-FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
-COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
-INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
-BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
-OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
-AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
-OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGE.
-
--------------------------------------------------------------------
-
-Copyright (c) 2010 The NetBSD Foundation, Inc.
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-1. Redistributions of source code must retain the above copyright
-   notice, this list of conditions and the following disclaimer.
-2. Redistributions in binary form must reproduce the above copyright
-   notice, this list of conditions and the following disclaimer in the
-   documentation and/or other materials provided with the distribution.
-
-THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
-``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
-TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
-PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
-BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-POSSIBILITY OF SUCH DAMAGE.
-
--------------------------------------------------------------------
-
-Copyright (c) 2010, The Android Open Source Project
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
- * Redistributions of source code must retain the above copyright
-   notice, this list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above copyright
-   notice, this list of conditions and the following disclaimer in
-   the documentation and/or other materials provided with the
-   distribution.
- * Neither the name of Google, Inc. nor the names of its contributors
-   may be used to endorse or promote products derived from this
-   software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
-FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
-COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
-INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
-BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
-OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
-AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
-OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGE.
-
--------------------------------------------------------------------
-
-Copyright (c) 2012, The Android Open Source Project
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
- * Redistributions of source code must retain the above copyright
-   notice, this list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above copyright
-   notice, this list of conditions and the following disclaimer in
-   the documentation and/or other materials provided with the
-   distribution.
- * Neither the name of Google, Inc. nor the names of its contributors
-   may be used to endorse or promote products derived from this
-   software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
-FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
-COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
-INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
-BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
-OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
-AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
-OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGE.
-
--------------------------------------------------------------------
-
-Copyright (c) 2013, The Android Open Source Project
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
- * Redistributions of source code must retain the above copyright
-   notice, this list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above copyright
-   notice, this list of conditions and the following disclaimer in
-   the documentation and/or other materials provided with the
-   distribution.
- * Neither the name of Google, Inc. nor the names of its contributors
-   may be used to endorse or promote products derived from this
-   software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
-FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
-COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
-INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
-BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
-OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
-AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
-OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGE.
-
--------------------------------------------------------------------
-
-Copyright (c) 2014, The Android Open Source Project
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
- * Redistributions of source code must retain the above copyright
-   notice, this list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above copyright
-   notice, this list of conditions and the following disclaimer in
-   the documentation and/or other materials provided with the
-   distribution.
- * Neither the name of Google, Inc. nor the names of its contributors
-   may be used to endorse or promote products derived from this
-   software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
-FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
-COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
-INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
-BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
-OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
-AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
-OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGE.
-
--------------------------------------------------------------------
-
diff --git a/toolbox/OWNERS b/toolbox/OWNERS
new file mode 100644
index 0000000..682a067
--- /dev/null
+++ b/toolbox/OWNERS
@@ -0,0 +1 @@
+enh@google.com
diff --git a/toolbox/bsd-compatibility.h b/toolbox/bsd-compatibility.h
deleted file mode 100644
index 7c3ddd4..0000000
--- a/toolbox/bsd-compatibility.h
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- * Copyright (C) 2014, The Android Open Source Project
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *  * Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- *  * Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in
- *    the documentation and/or other materials provided with the
- *    distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
- * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
- * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
- * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
- * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
- * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#include <stdbool.h>
-#include <sys/types.h>
-
-/* We want chown to support user.group as well as user:group. */
-#define SUPPORT_DOT
-
-/* We don't localize /system/bin! */
-#define WITHOUT_NLS
-
-// NetBSD uses _DIAGASSERT to null-check arguments and the like.
-#include <assert.h>
-#define _DIAGASSERT(e) ((e) ? (void) 0 : __assert2(__FILE__, __LINE__, __func__, #e))
-
-// TODO: update our <sys/cdefs.h> to support this properly.
-#define __type_fit(t, a) (0 == 0)
-
-// TODO: should this be in our <sys/cdefs.h>?
-#define __arraycount(a) (sizeof(a) / sizeof((a)[0]))
-
-// This at least matches GNU dd(1) behavior.
-#define SIGINFO SIGUSR1
-
-#define S_ISWHT(x) false
-
-__BEGIN_DECLS
-
-/* From NetBSD <stdlib.h>. */
-#define HN_DECIMAL              0x01
-#define HN_NOSPACE              0x02
-#define HN_B                    0x04
-#define HN_DIVISOR_1000         0x08
-#define HN_GETSCALE             0x10
-#define HN_AUTOSCALE            0x20
-int	humanize_number(char *, size_t, int64_t, const char *, int, int);
-int	dehumanize_number(const char *, int64_t *);
-char	*getbsize(int *, long *);
-long long strsuftoll(const char *, const char *, long long, long long);
-long long strsuftollx(const char *, const char *, long long, long long,
-			char *, size_t);
-
-/* From NetBSD <string.h>. */
-void strmode(mode_t, char*);
-
-/* From NetBSD <sys/param.h>. */
-#define MAXBSIZE 65536
-
-/* From NetBSD <sys/stat.h>. */
-#define DEFFILEMODE (S_IRUSR | S_IWUSR)
-
-/* From NetBSD <unistd.h>. */
-void	swab(const void * __restrict, void * __restrict, ssize_t);
-
-/* From NetBSD <util.h>. */
-int		raise_default_signal(int);
-
-__END_DECLS
diff --git a/toolbox/getprop.cpp b/toolbox/getprop.cpp
new file mode 100644
index 0000000..ca345cb
--- /dev/null
+++ b/toolbox/getprop.cpp
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2017 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 <getopt.h>
+#include <sys/system_properties.h>
+
+#include <iostream>
+#include <string>
+#include <vector>
+
+#include <android-base/properties.h>
+#include <property_info_parser/property_info_parser.h>
+
+using android::base::GetProperty;
+using android::properties::PropertyInfoAreaFile;
+
+PropertyInfoAreaFile property_info_file;
+
+enum class ResultType {
+    Value,
+    Context,
+    Type,
+};
+
+void PrintAllProperties(ResultType result_type) {
+    std::vector<std::pair<std::string, std::string>> properties;
+    __system_property_foreach(
+        [](const prop_info* pi, void* cookie) {
+            __system_property_read_callback(
+                pi,
+                [](void* cookie, const char* name, const char* value, unsigned) {
+                    auto properties =
+                        reinterpret_cast<std::vector<std::pair<std::string, std::string>>*>(cookie);
+                    properties->emplace_back(name, value);
+                },
+                cookie);
+        },
+        &properties);
+
+    std::sort(properties.begin(), properties.end());
+
+    if (result_type != ResultType::Value) {
+        for (auto& [name, value] : properties) {
+            const char* context = nullptr;
+            const char* type = nullptr;
+            property_info_file->GetPropertyInfo(name.c_str(), &context, &type);
+            if (result_type == ResultType::Context) {
+                value = context;
+            } else {
+                value = type;
+            }
+        }
+    }
+
+    for (const auto& [name, value] : properties) {
+        std::cout << "[" << name << "]: [" << value << "]" << std::endl;
+    }
+}
+
+void PrintProperty(const char* name, const char* default_value, ResultType result_type) {
+    switch (result_type) {
+        case ResultType::Value:
+            std::cout << GetProperty(name, default_value) << std::endl;
+            break;
+        case ResultType::Context: {
+            const char* context = nullptr;
+            property_info_file->GetPropertyInfo(name, &context, nullptr);
+            std::cout << context << std::endl;
+            break;
+        }
+        case ResultType::Type: {
+            const char* type = nullptr;
+            property_info_file->GetPropertyInfo(name, nullptr, &type);
+            std::cout << type << std::endl;
+            break;
+        }
+    }
+}
+
+extern "C" int getprop_main(int argc, char** argv) {
+    auto result_type = ResultType::Value;
+
+    while (true) {
+        static const struct option long_options[] = {
+            {"help", no_argument, nullptr, 'h'},
+            {nullptr, 0, nullptr, 0},
+        };
+
+        int arg = getopt_long(argc, argv, "TZ", long_options, nullptr);
+
+        if (arg == -1) {
+            break;
+        }
+
+        switch (arg) {
+            case 'h':
+                std::cout << "usage: getprop [-TZ] [NAME [DEFAULT]]\n"
+                             "\n"
+                             "Gets an Android system property, or lists them all.\n"
+                             "\n"
+                             "-T\tShow property types instead of values\n"
+                             "-Z\tShow property contexts instead of values\n"
+                          << std::endl;
+                return 0;
+            case 'T':
+                if (result_type != ResultType::Value) {
+                    std::cerr << "Only one of -T or -Z may be specified" << std::endl;
+                    return -1;
+                }
+                result_type = ResultType::Type;
+                break;
+            case 'Z':
+                if (result_type != ResultType::Value) {
+                    std::cerr << "Only one of -T or -Z may be specified" << std::endl;
+                    return -1;
+                }
+                result_type = ResultType::Context;
+                break;
+            case '?':
+                return -1;
+            default:
+                std::cerr << "getprop: getopt returned invalid result: " << arg << std::endl;
+                return -1;
+        }
+    }
+
+    if (result_type != ResultType::Value) {
+        property_info_file.LoadDefaultPath();
+        if (!property_info_file) {
+            std::cerr << "Unable to load property info file" << std::endl;
+            return -1;
+        }
+    }
+
+    if (optind >= argc) {
+        PrintAllProperties(result_type);
+        return 0;
+    }
+
+    if (optind < argc - 2) {
+        std::cerr << "getprop: Max 2 arguments (see \"getprop --help\")" << std::endl;
+        return -1;
+    }
+
+    PrintProperty(argv[optind], (optind == argc - 1) ? "" : argv[optind + 1], result_type);
+
+    return 0;
+}
diff --git a/toolbox/newfs_msdos.c b/toolbox/newfs_msdos.c
deleted file mode 100644
index d7047e2..0000000
--- a/toolbox/newfs_msdos.c
+++ /dev/null
@@ -1,1099 +0,0 @@
-/*
- * Copyright (c) 1998 Robert Nordier
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in
- *    the documentation and/or other materials provided with the
- *    distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS
- * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY
- * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
- * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
- * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
- * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
- * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#ifndef lint
-static const char rcsid[] =
-        "$FreeBSD: src/sbin/newfs_msdos/newfs_msdos.c,v 1.33 2009/04/11 14:56:29 ed Exp $";
-#endif /* not lint */
-
-#include <sys/param.h>
-
-#ifndef ANDROID
-#include <sys/fdcio.h>
-#include <sys/disk.h>
-#include <sys/disklabel.h>
-#include <sys/mount.h>
-#else
-#include <stdarg.h>
-#include <linux/fs.h>
-#include <linux/hdreg.h>
-#endif
-
-#include <sys/stat.h>
-#include <sys/time.h>
-
-#include <ctype.h>
-#include <err.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <inttypes.h>
-#include <paths.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <time.h>
-#include <unistd.h>
-
-#define MAXU16   0xffff     /* maximum unsigned 16-bit quantity */
-#define BPN      4          /* bits per nibble */
-#define NPB      2          /* nibbles per byte */
-
-#define DOSMAGIC  0xaa55    /* DOS magic number */
-#define MINBPS    512       /* minimum bytes per sector */
-#define MAXSPC    128       /* maximum sectors per cluster */
-#define MAXNFT    16        /* maximum number of FATs */
-#define DEFBLK    4096      /* default block size */
-#define DEFBLK16  2048      /* default block size FAT16 */
-#define DEFRDE    512       /* default root directory entries */
-#define RESFTE    2         /* reserved FAT entries */
-#define MINCLS12  1         /* minimum FAT12 clusters */
-#define MINCLS16  0x1000    /* minimum FAT16 clusters */
-#define MINCLS32  2         /* minimum FAT32 clusters */
-#define MAXCLS12  0xfed     /* maximum FAT12 clusters */
-#define MAXCLS16  0xfff5    /* maximum FAT16 clusters */
-#define MAXCLS32  0xffffff5 /* maximum FAT32 clusters */
-
-#define mincls(fat)  ((fat) == 12 ? MINCLS12 :    \
-                      (fat) == 16 ? MINCLS16 :    \
-                                        MINCLS32)
-
-#define maxcls(fat)  ((fat) == 12 ? MAXCLS12 :    \
-                      (fat) == 16 ? MAXCLS16 :    \
-                                        MAXCLS32)
-
-#define mk1(p, x)                           \
-    (p) = (u_int8_t)(x)
-
-#define mk2(p, x)                           \
-    (p)[0] = (u_int8_t)(x),                 \
-    (p)[1] = (u_int8_t)((x) >> 010)
-
-#define mk4(p, x)                           \
-    (p)[0] = (u_int8_t)(x),                 \
-    (p)[1] = (u_int8_t)((x) >> 010),        \
-    (p)[2] = (u_int8_t)((x) >> 020),        \
-    (p)[3] = (u_int8_t)((x) >> 030)
-
-#define argto1(arg, lo, msg)  argtou(arg, lo, 0xff, msg)
-#define argto2(arg, lo, msg)  argtou(arg, lo, 0xffff, msg)
-#define argto4(arg, lo, msg)  argtou(arg, lo, 0xffffffff, msg)
-#define argtox(arg, lo, msg)  argtou(arg, lo, UINT_MAX, msg)
-
-struct bs {
-    u_int8_t jmp[3];        /* bootstrap entry point */
-    u_int8_t oem[8];        /* OEM name and version */
-};
-
-struct bsbpb {
-    u_int8_t bps[2];    /* bytes per sector */
-    u_int8_t spc;       /* sectors per cluster */
-    u_int8_t res[2];    /* reserved sectors */
-    u_int8_t nft;       /* number of FATs */
-    u_int8_t rde[2];    /* root directory entries */
-    u_int8_t sec[2];    /* total sectors */
-    u_int8_t mid;       /* media descriptor */
-    u_int8_t spf[2];    /* sectors per FAT */
-    u_int8_t spt[2];    /* sectors per track */
-    u_int8_t hds[2];    /* drive heads */
-    u_int8_t hid[4];    /* hidden sectors */
-    u_int8_t bsec[4];   /* big total sectors */
-};
-
-struct bsxbpb {
-    u_int8_t bspf[4];       /* big sectors per FAT */
-    u_int8_t xflg[2];       /* FAT control flags */
-    u_int8_t vers[2];       /* file system version */
-    u_int8_t rdcl[4];       /* root directory start cluster */
-    u_int8_t infs[2];       /* file system info sector */
-    u_int8_t bkbs[2];       /* backup boot sector */
-    u_int8_t rsvd[12];      /* reserved */
-};
-
-struct bsx {
-    u_int8_t drv;           /* drive number */
-    u_int8_t rsvd;          /* reserved */
-    u_int8_t sig;           /* extended boot signature */
-    u_int8_t volid[4];      /* volume ID number */
-    u_int8_t label[11];     /* volume label */
-    u_int8_t type[8];       /* file system type */
-};
-
-struct de {
-    u_int8_t namext[11];    /* name and extension */
-    u_int8_t attr;          /* attributes */
-    u_int8_t rsvd[10];      /* reserved */
-    u_int8_t time[2];       /* creation time */
-    u_int8_t date[2];       /* creation date */
-    u_int8_t clus[2];       /* starting cluster */
-    u_int8_t size[4];       /* size */
-};
-
-struct bpb {
-    u_int bps;          /* bytes per sector */
-    u_int spc;          /* sectors per cluster */
-    u_int res;          /* reserved sectors */
-    u_int nft;          /* number of FATs */
-    u_int rde;          /* root directory entries */
-    u_int sec;          /* total sectors */
-    u_int mid;          /* media descriptor */
-    u_int spf;          /* sectors per FAT */
-    u_int spt;          /* sectors per track */
-    u_int hds;          /* drive heads */
-    u_int hid;          /* hidden sectors */
-    u_int bsec;         /* big total sectors */
-    u_int bspf;         /* big sectors per FAT */
-    u_int rdcl;         /* root directory start cluster */
-    u_int infs;         /* file system info sector */
-    u_int bkbs;         /* backup boot sector */
-};
-
-#define BPBGAP 0, 0, 0, 0, 0, 0
-
-static struct {
-    const char *name;
-    struct bpb bpb;
-} const stdfmt[] = {
-    {"160",  {512, 1, 1, 2,  64,  320, 0xfe, 1,  8, 1, BPBGAP}},
-    {"180",  {512, 1, 1, 2,  64,  360, 0xfc, 2,  9, 1, BPBGAP}},
-    {"320",  {512, 2, 1, 2, 112,  640, 0xff, 1,  8, 2, BPBGAP}},
-    {"360",  {512, 2, 1, 2, 112,  720, 0xfd, 2,  9, 2, BPBGAP}},
-    {"640",  {512, 2, 1, 2, 112, 1280, 0xfb, 2,  8, 2, BPBGAP}},
-    {"720",  {512, 2, 1, 2, 112, 1440, 0xf9, 3,  9, 2, BPBGAP}},
-    {"1200", {512, 1, 1, 2, 224, 2400, 0xf9, 7, 15, 2, BPBGAP}},
-    {"1232", {1024,1, 1, 2, 192, 1232, 0xfe, 2,  8, 2, BPBGAP}},
-    {"1440", {512, 1, 1, 2, 224, 2880, 0xf0, 9, 18, 2, BPBGAP}},
-    {"2880", {512, 2, 1, 2, 240, 5760, 0xf0, 9, 36, 2, BPBGAP}}
-};
-
-static const u_int8_t bootcode[] = {
-    0xfa,               /* cli             */
-    0x31, 0xc0,         /* xor    ax,ax    */
-    0x8e, 0xd0,         /* mov    ss,ax    */
-    0xbc, 0x00, 0x7c,   /* mov    sp,7c00h */
-    0xfb,               /* sti             */
-    0x8e, 0xd8,         /* mov    ds,ax    */
-    0xe8, 0x00, 0x00,   /* call   $ + 3    */
-    0x5e,               /* pop    si       */
-    0x83, 0xc6, 0x19,   /* add    si,+19h  */
-    0xbb, 0x07, 0x00,   /* mov    bx,0007h */
-    0xfc,               /* cld             */
-    0xac,               /* lodsb           */
-    0x84, 0xc0,         /* test   al,al    */
-    0x74, 0x06,         /* jz     $ + 8    */
-    0xb4, 0x0e,         /* mov    ah,0eh   */
-    0xcd, 0x10,         /* int    10h      */
-    0xeb, 0xf5,         /* jmp    $ - 9    */
-    0x30, 0xe4,         /* xor    ah,ah    */
-    0xcd, 0x16,         /* int    16h      */
-    0xcd, 0x19,         /* int    19h      */
-    0x0d, 0x0a,
-    'N', 'o', 'n', '-', 's', 'y', 's', 't',
-    'e', 'm', ' ', 'd', 'i', 's', 'k',
-    0x0d, 0x0a,
-    'P', 'r', 'e', 's', 's', ' ', 'a', 'n',
-    'y', ' ', 'k', 'e', 'y', ' ', 't', 'o',
-    ' ', 'r', 'e', 'b', 'o', 'o', 't',
-    0x0d, 0x0a,
-    0
-};
-
-static void check_mounted(const char *, mode_t);
-static void getstdfmt(const char *, struct bpb *);
-static void getdiskinfo(int, const char *, const char *, int, struct bpb *);
-static void print_bpb(struct bpb *);
-static u_int ckgeom(const char *, u_int, const char *);
-static u_int argtou(const char *, u_int, u_int, const char *);
-static off_t argtooff(const char *, const char *);
-static int oklabel(const char *);
-static void mklabel(u_int8_t *, const char *);
-static void setstr(u_int8_t *, const char *, size_t);
-static void usage(void);
-
-/*
- * Construct a FAT12, FAT16, or FAT32 file system.
- */
-int newfs_msdos_main(int argc, char *argv[])
-{
-    static const char opts[] = "@:NAB:C:F:I:L:O:S:a:b:c:e:f:h:i:k:m:n:o:r:s:u:";
-    const char *opt_B = NULL, *opt_L = NULL, *opt_O = NULL, *opt_f = NULL;
-    u_int opt_F = 0, opt_I = 0, opt_S = 0, opt_a = 0, opt_b = 0, opt_c = 0;
-    u_int opt_e = 0, opt_h = 0, opt_i = 0, opt_k = 0, opt_m = 0, opt_n = 0;
-    u_int opt_o = 0, opt_r = 0, opt_s = 0, opt_u = 0;
-    u_int opt_A = 0;
-    int opt_N = 0;
-    int Iflag = 0, mflag = 0, oflag = 0;
-    char buf[MAXPATHLEN];
-    struct stat sb;
-    struct timeval tv;
-    struct bpb bpb;
-    struct tm *tm;
-    struct bs *bs;
-    struct bsbpb *bsbpb;
-    struct bsxbpb *bsxbpb;
-    struct bsx *bsx;
-    struct de *de;
-    u_int8_t *img;
-    const char *fname, *dtype, *bname;
-    ssize_t n;
-    time_t now;
-    u_int fat, bss, rds, cls, dir, lsn, x, x1, x2;
-    u_int extra_res, alignment=0, set_res, set_spf, set_spc, tempx, attempts=0;
-    int ch, fd, fd1;
-    off_t opt_create = 0, opt_ofs = 0;
-
-    while ((ch = getopt(argc, argv, opts)) != -1)
-        switch (ch) {
-        case '@':
-            opt_ofs = argtooff(optarg, "offset");
-            break;
-        case 'N':
-            opt_N = 1;
-            break;
-        case 'A':
-            opt_A = 1;
-            break;
-        case 'B':
-            opt_B = optarg;
-            break;
-        case 'C':
-            opt_create = argtooff(optarg, "create size");
-            break;
-        case 'F':
-            if (strcmp(optarg, "12") &&  strcmp(optarg, "16") && strcmp(optarg, "32"))
-                errx(1, "%s: bad FAT type", optarg);
-            opt_F = atoi(optarg);
-            break;
-        case 'I':
-            opt_I = argto4(optarg, 0, "volume ID");
-            Iflag = 1;
-            break;
-        case 'L':
-            if (!oklabel(optarg))
-                errx(1, "%s: bad volume label", optarg);
-            opt_L = optarg;
-            break;
-        case 'O':
-            if (strlen(optarg) > 8)
-                errx(1, "%s: bad OEM string", optarg);
-            opt_O = optarg;
-            break;
-        case 'S':
-            opt_S = argto2(optarg, 1, "bytes/sector");
-            break;
-        case 'a':
-            opt_a = argto4(optarg, 1, "sectors/FAT");
-            break;
-        case 'b':
-            opt_b = argtox(optarg, 1, "block size");
-            opt_c = 0;
-            break;
-        case 'c':
-            opt_c = argto1(optarg, 1, "sectors/cluster");
-            opt_b = 0;
-            break;
-        case 'e':
-            opt_e = argto2(optarg, 1, "directory entries");
-            break;
-        case 'f':
-            opt_f = optarg;
-            break;
-        case 'h':
-            opt_h = argto2(optarg, 1, "drive heads");
-            break;
-        case 'i':
-            opt_i = argto2(optarg, 1, "info sector");
-            break;
-        case 'k':
-            opt_k = argto2(optarg, 1, "backup sector");
-            break;
-        case 'm':
-            opt_m = argto1(optarg, 0, "media descriptor");
-            mflag = 1;
-            break;
-        case 'n':
-            opt_n = argto1(optarg, 1, "number of FATs");
-            break;
-        case 'o':
-            opt_o = argto4(optarg, 0, "hidden sectors");
-            oflag = 1;
-            break;
-        case 'r':
-            opt_r = argto2(optarg, 1, "reserved sectors");
-            break;
-        case 's':
-            opt_s = argto4(optarg, 1, "file system size");
-            break;
-        case 'u':
-            opt_u = argto2(optarg, 1, "sectors/track");
-            break;
-        default:
-            usage();
-        }
-    argc -= optind;
-    argv += optind;
-    if (argc < 1 || argc > 2)
-        usage();
-    fname = *argv++;
-    if (!opt_create && !strchr(fname, '/')) {
-        snprintf(buf, sizeof(buf), "%s%s", _PATH_DEV, fname);
-        if (!(fname = strdup(buf)))
-            err(1, "%s", buf);
-    }
-    dtype = *argv;
-    if (opt_A) {
-        if (opt_r)
-            errx(1, "align (-A) is incompatible with -r");
-        if (opt_N)
-            errx(1, "align (-A) is incompatible with -N");
-    }
-    if (opt_create) {
-        if (opt_N)
-            errx(1, "create (-C) is incompatible with -N");
-        fd = open(fname, O_RDWR | O_CREAT | O_TRUNC, 0644);
-        if (fd == -1)
-            errx(1, "failed to create %s", fname);
-        if (ftruncate(fd, opt_create))
-            errx(1, "failed to initialize %jd bytes", (intmax_t)opt_create);
-    } else if ((fd = open(fname, opt_N ? O_RDONLY : O_RDWR)) == -1)
-        err(1, "%s", fname);
-    if (fstat(fd, &sb))
-        err(1, "%s", fname);
-    if (opt_create) {
-        if (!S_ISREG(sb.st_mode))
-            warnx("warning, %s is not a regular file", fname);
-    } else {
-        if (!S_ISCHR(sb.st_mode))
-            warnx("warning, %s is not a character device", fname);
-    }
-    if (!opt_N)
-        check_mounted(fname, sb.st_mode);
-    if (opt_ofs && opt_ofs != lseek(fd, opt_ofs, SEEK_SET))
-        errx(1, "cannot seek to %jd", (intmax_t)opt_ofs);
-    memset(&bpb, 0, sizeof(bpb));
-    if (opt_f) {
-        getstdfmt(opt_f, &bpb);
-        bpb.bsec = bpb.sec;
-        bpb.sec = 0;
-        bpb.bspf = bpb.spf;
-        bpb.spf = 0;
-    }
-    if (opt_h)
-        bpb.hds = opt_h;
-    if (opt_u)
-        bpb.spt = opt_u;
-    if (opt_S)
-        bpb.bps = opt_S;
-    if (opt_s)
-        bpb.bsec = opt_s;
-    if (oflag)
-        bpb.hid = opt_o;
-    if (!(opt_f || (opt_h && opt_u && opt_S && opt_s && oflag))) {
-        off_t delta;
-        getdiskinfo(fd, fname, dtype, oflag, &bpb);
-        if (opt_s) {
-            bpb.bsec = opt_s;
-        }
-        bpb.bsec -= (opt_ofs / bpb.bps);
-        delta = bpb.bsec % bpb.spt;
-        if (delta != 0) {
-            warnx("trim %d sectors from %d to adjust to a multiple of %d",
-                  (int)delta, bpb.bsec, bpb.spt);
-            bpb.bsec -= delta;
-        }
-        if (bpb.spc == 0) {    /* set defaults */
-            if (bpb.bsec <= 6000)    /* about 3MB -> 512 bytes */
-                bpb.spc = 1;
-            else if (bpb.bsec <= (1<<17)) /* 64M -> 4k */
-                bpb.spc = 8;
-            else if (bpb.bsec <= (1<<19)) /* 256M -> 8k */
-                bpb.spc = 16;
-            else if (bpb.bsec <= (1<<22)) /* 2G -> 16k, some versions of windows
-                                         require a minimum of 65527 clusters */
-                bpb.spc = 32;
-            else
-                bpb.spc = 64;        /* otherwise 32k */
-        }
-    }
-    if (!powerof2(bpb.bps))
-        errx(1, "bytes/sector (%u) is not a power of 2", bpb.bps);
-    if (bpb.bps < MINBPS)
-        errx(1, "bytes/sector (%u) is too small; minimum is %u",
-             bpb.bps, MINBPS);
-    if (!(fat = opt_F)) {
-        if (opt_f)
-            fat = 12;
-        else if (!opt_e && (opt_i || opt_k))
-            fat = 32;
-    }
-    if ((fat == 32 && opt_e) || (fat != 32 && (opt_i || opt_k)))
-        errx(1, "-%c is not a legal FAT%s option",
-             fat == 32 ? 'e' : opt_i ? 'i' : 'k',
-                     fat == 32 ? "32" : "12/16");
-    if (opt_f && fat == 32)
-        bpb.rde = 0;
-    if (opt_b) {
-        if (!powerof2(opt_b))
-            errx(1, "block size (%u) is not a power of 2", opt_b);
-        if (opt_b < bpb.bps)
-            errx(1, "block size (%u) is too small; minimum is %u",
-                 opt_b, bpb.bps);
-        if (opt_b > bpb.bps * MAXSPC)
-            errx(1, "block size (%u) is too large; maximum is %u", opt_b, bpb.bps * MAXSPC);
-        bpb.spc = opt_b / bpb.bps;
-    }
-    if (opt_c) {
-        if (!powerof2(opt_c))
-            errx(1, "sectors/cluster (%u) is not a power of 2", opt_c);
-        bpb.spc = opt_c;
-    }
-    if (opt_r)
-        bpb.res = opt_r;
-    if (opt_n) {
-        if (opt_n > MAXNFT)
-            errx(1, "number of FATs (%u) is too large; maximum is %u", opt_n, MAXNFT);
-        bpb.nft = opt_n;
-    }
-    if (opt_e)
-        bpb.rde = opt_e;
-    if (mflag) {
-        if (opt_m < 0xf0)
-            errx(1, "illegal media descriptor (%#x)", opt_m);
-        bpb.mid = opt_m;
-    }
-    if (opt_a)
-        bpb.bspf = opt_a;
-    if (opt_i)
-        bpb.infs = opt_i;
-    if (opt_k)
-        bpb.bkbs = opt_k;
-    bss = 1;
-    bname = NULL;
-    fd1 = -1;
-    if (opt_B) {
-        bname = opt_B;
-        if (!strchr(bname, '/')) {
-            snprintf(buf, sizeof(buf), "/boot/%s", bname);
-            if (!(bname = strdup(buf)))
-                err(1, "%s", buf);
-        }
-        if ((fd1 = open(bname, O_RDONLY)) == -1 || fstat(fd1, &sb))
-            err(1, "%s", bname);
-        if (!S_ISREG(sb.st_mode) || sb.st_size % bpb.bps ||
-                sb.st_size < bpb.bps || sb.st_size > bpb.bps * MAXU16)
-            errx(1, "%s: inappropriate file type or format", bname);
-        bss = sb.st_size / bpb.bps;
-    }
-    if (!bpb.nft)
-        bpb.nft = 2;
-    if (!fat) {
-        if (bpb.bsec < (bpb.res ? bpb.res : bss) +
-                howmany((RESFTE + (bpb.spc ? MINCLS16 : MAXCLS12 + 1)) *
-                        ((bpb.spc ? 16 : 12) / BPN), bpb.bps * NPB) *
-                        bpb.nft +
-                        howmany(bpb.rde ? bpb.rde : DEFRDE,
-                                bpb.bps / sizeof(struct de)) +
-                                (bpb.spc ? MINCLS16 : MAXCLS12 + 1) *
-                                (bpb.spc ? bpb.spc : howmany(DEFBLK, bpb.bps)))
-            fat = 12;
-        else if (bpb.rde || bpb.bsec <
-                (bpb.res ? bpb.res : bss) +
-                howmany((RESFTE + MAXCLS16) * 2, bpb.bps) * bpb.nft +
-                howmany(DEFRDE, bpb.bps / sizeof(struct de)) +
-                (MAXCLS16 + 1) *
-                (bpb.spc ? bpb.spc : howmany(8192, bpb.bps)))
-            fat = 16;
-        else
-            fat = 32;
-    }
-    x = bss;
-    if (fat == 32) {
-        if (!bpb.infs) {
-            if (x == MAXU16 || x == bpb.bkbs)
-                errx(1, "no room for info sector");
-            bpb.infs = x;
-        }
-        if (bpb.infs != MAXU16 && x <= bpb.infs)
-            x = bpb.infs + 1;
-        if (!bpb.bkbs) {
-            if (x == MAXU16)
-                errx(1, "no room for backup sector");
-            bpb.bkbs = x;
-        } else if (bpb.bkbs != MAXU16 && bpb.bkbs == bpb.infs)
-            errx(1, "backup sector would overwrite info sector");
-        if (bpb.bkbs != MAXU16 && x <= bpb.bkbs)
-            x = bpb.bkbs + 1;
-    }
-
-    extra_res = 0;
-    set_res = !bpb.res;
-    set_spf = !bpb.bspf;
-    set_spc = !bpb.spc;
-    tempx = x;
-    /*
-     * Attempt to align if opt_A is set. This is done by increasing the number
-     * of reserved blocks. This can cause other factors to change, which can in
-     * turn change the alignment. This should take at most 2 iterations, as
-     * increasing the reserved amount may cause the FAT size to decrease by 1,
-     * requiring another nft reserved blocks. If spc changes, it will
-     * be half of its previous size, and thus will not throw off alignment.
-     */
-    do {
-        x = tempx;
-        if (set_res)
-            bpb.res = (fat == 32 ? MAX(x, MAX(16384 / bpb.bps, 4)) : x) + extra_res;
-        else if (bpb.res < x)
-            errx(1, "too few reserved sectors");
-        if (fat != 32 && !bpb.rde)
-            bpb.rde = DEFRDE;
-        rds = howmany(bpb.rde, bpb.bps / sizeof(struct de));
-        if (set_spc)
-            for (bpb.spc = howmany(fat == 16 ? DEFBLK16 : DEFBLK, bpb.bps);
-                    bpb.spc < MAXSPC &&
-                    bpb.res +
-                    howmany((RESFTE + maxcls(fat)) * (fat / BPN),
-                            bpb.bps * NPB) * bpb.nft +
-                            rds +
-                            (u_int64_t)(maxcls(fat) + 1) * bpb.spc <= bpb.bsec;
-                    bpb.spc <<= 1);
-        if (fat != 32 && bpb.bspf > MAXU16)
-            errx(1, "too many sectors/FAT for FAT12/16");
-        x1 = bpb.res + rds;
-        x = bpb.bspf ? bpb.bspf : 1;
-        if (x1 + (u_int64_t)x * bpb.nft > bpb.bsec)
-            errx(1, "meta data exceeds file system size");
-        x1 += x * bpb.nft;
-        x = (u_int64_t)(bpb.bsec - x1) * bpb.bps * NPB /
-                (bpb.spc * bpb.bps * NPB + fat / BPN * bpb.nft);
-        x2 = howmany((RESFTE + MIN(x, maxcls(fat))) * (fat / BPN), bpb.bps * NPB);
-        if (set_spf) {
-            if (!bpb.bspf) {
-                bpb.bspf = x2;
-            }
-            x1 += (bpb.bspf - 1) * bpb.nft;
-        }
-        if(set_res) {
-            /* attempt to align root directory */
-            alignment = (bpb.res + bpb.bspf * bpb.nft) % bpb.spc;
-            extra_res += bpb.spc - alignment;
-        }
-        attempts++;
-    } while(opt_A && alignment != 0 && attempts < 2);
-    if (alignment != 0)
-        warnx("warning: Alignment failed.");
-
-    cls = (bpb.bsec - x1) / bpb.spc;
-    x = (u_int64_t)bpb.bspf * bpb.bps * NPB / (fat / BPN) - RESFTE;
-    if (cls > x)
-        cls = x;
-    if (bpb.bspf < x2)
-        warnx("warning: sectors/FAT limits file system to %u clusters", cls);
-    if (cls < mincls(fat))
-        errx(1, "%u clusters too few clusters for FAT%u, need %u", cls, fat, mincls(fat));
-    if (cls > maxcls(fat)) {
-        cls = maxcls(fat);
-        bpb.bsec = x1 + (cls + 1) * bpb.spc - 1;
-        warnx("warning: FAT type limits file system to %u sectors", bpb.bsec);
-    }
-    printf("%s: %u sector%s in %u FAT%u cluster%s (%u bytes/cluster)\n",
-           fname, cls * bpb.spc, cls * bpb.spc == 1 ? "" : "s", cls, fat,
-           cls == 1 ? "" : "s", bpb.bps * bpb.spc);
-    if (!bpb.mid)
-        bpb.mid = !bpb.hid ? 0xf0 : 0xf8;
-    if (fat == 32)
-        bpb.rdcl = RESFTE;
-    if (bpb.hid + bpb.bsec <= MAXU16) {
-        bpb.sec = bpb.bsec;
-        bpb.bsec = 0;
-    }
-    if (fat != 32) {
-        bpb.spf = bpb.bspf;
-        bpb.bspf = 0;
-    }
-    print_bpb(&bpb);
-    if (!opt_N) {
-        gettimeofday(&tv, NULL);
-        now = tv.tv_sec;
-        tm = localtime(&now);
-        if (!(img = malloc(bpb.bps)))
-            err(1, "%u", bpb.bps);
-        dir = bpb.res + (bpb.spf ? bpb.spf : bpb.bspf) * bpb.nft;
-        for (lsn = 0; lsn < dir + (fat == 32 ? bpb.spc : rds); lsn++) {
-            x = lsn;
-            if (opt_B && fat == 32 && bpb.bkbs != MAXU16 && bss <= bpb.bkbs && x >= bpb.bkbs) {
-                x -= bpb.bkbs;
-                if (!x && lseek(fd1, opt_ofs, SEEK_SET))
-                    err(1, "%s", bname);
-            }
-            if (opt_B && x < bss) {
-                if ((n = read(fd1, img, bpb.bps)) == -1)
-                    err(1, "%s", bname);
-                if ((unsigned)n != bpb.bps)
-                    errx(1, "%s: can't read sector %u", bname, x);
-            } else
-                memset(img, 0, bpb.bps);
-            if (!lsn || (fat == 32 && bpb.bkbs != MAXU16 && lsn == bpb.bkbs)) {
-                x1 = sizeof(struct bs);
-                bsbpb = (struct bsbpb *)(img + x1);
-                mk2(bsbpb->bps, bpb.bps);
-                mk1(bsbpb->spc, bpb.spc);
-                mk2(bsbpb->res, bpb.res);
-                mk1(bsbpb->nft, bpb.nft);
-                mk2(bsbpb->rde, bpb.rde);
-                mk2(bsbpb->sec, bpb.sec);
-                mk1(bsbpb->mid, bpb.mid);
-                mk2(bsbpb->spf, bpb.spf);
-                mk2(bsbpb->spt, bpb.spt);
-                mk2(bsbpb->hds, bpb.hds);
-                mk4(bsbpb->hid, bpb.hid);
-                mk4(bsbpb->bsec, bpb.bsec);
-                x1 += sizeof(struct bsbpb);
-                if (fat == 32) {
-                    bsxbpb = (struct bsxbpb *)(img + x1);
-                    mk4(bsxbpb->bspf, bpb.bspf);
-                    mk2(bsxbpb->xflg, 0);
-                    mk2(bsxbpb->vers, 0);
-                    mk4(bsxbpb->rdcl, bpb.rdcl);
-                    mk2(bsxbpb->infs, bpb.infs);
-                    mk2(bsxbpb->bkbs, bpb.bkbs);
-                    x1 += sizeof(struct bsxbpb);
-                }
-                bsx = (struct bsx *)(img + x1);
-                mk1(bsx->sig, 0x29);
-                if (Iflag)
-                    x = opt_I;
-                else
-                    x = (((u_int)(1 + tm->tm_mon) << 8 |
-                            (u_int)tm->tm_mday) +
-                            ((u_int)tm->tm_sec << 8 |
-                                    (u_int)(tv.tv_usec / 10))) << 16 |
-                                    ((u_int)(1900 + tm->tm_year) +
-                                            ((u_int)tm->tm_hour << 8 |
-                                                    (u_int)tm->tm_min));
-                mk4(bsx->volid, x);
-                mklabel(bsx->label, opt_L ? opt_L : "NO NAME");
-                snprintf(buf, sizeof(buf), "FAT%u", fat);
-                setstr(bsx->type, buf, sizeof(bsx->type));
-                if (!opt_B) {
-                    x1 += sizeof(struct bsx);
-                    bs = (struct bs *)img;
-                    mk1(bs->jmp[0], 0xeb);
-                    mk1(bs->jmp[1], x1 - 2);
-                    mk1(bs->jmp[2], 0x90);
-                    setstr(bs->oem, opt_O ? opt_O : "BSD  4.4",
-                            sizeof(bs->oem));
-                    memcpy(img + x1, bootcode, sizeof(bootcode));
-                    mk2(img + MINBPS - 2, DOSMAGIC);
-                }
-            } else if (fat == 32 && bpb.infs != MAXU16 &&
-                    (lsn == bpb.infs || (bpb.bkbs != MAXU16 &&
-                                    lsn == bpb.bkbs + bpb.infs))) {
-                mk4(img, 0x41615252);
-                mk4(img + MINBPS - 28, 0x61417272);
-                mk4(img + MINBPS - 24, 0xffffffff);
-                mk4(img + MINBPS - 20, bpb.rdcl);
-                mk2(img + MINBPS - 2, DOSMAGIC);
-            } else if (lsn >= bpb.res && lsn < dir &&
-                    !((lsn - bpb.res) % (bpb.spf ? bpb.spf : bpb.bspf))) {
-                mk1(img[0], bpb.mid);
-                for (x = 1; x < fat * (fat == 32 ? 3 : 2) / 8; x++)
-                    mk1(img[x], fat == 32 && x % 4 == 3 ? 0x0f : 0xff);
-            } else if (lsn == dir && opt_L) {
-                de = (struct de *)img;
-                mklabel(de->namext, opt_L);
-                mk1(de->attr, 050);
-                x = (u_int)tm->tm_hour << 11 |
-                        (u_int)tm->tm_min << 5 |
-                        (u_int)tm->tm_sec >> 1;
-                mk2(de->time, x);
-                x = (u_int)(tm->tm_year - 80) << 9 |
-                        (u_int)(tm->tm_mon + 1) << 5 |
-                        (u_int)tm->tm_mday;
-                mk2(de->date, x);
-            }
-            if ((n = write(fd, img, bpb.bps)) == -1)
-                err(1, "%s", fname);
-            if ((unsigned)n != bpb.bps) {
-                errx(1, "%s: can't write sector %u", fname, lsn);
-                exit(1);
-            }
-        }
-        free(img);
-    }
-    return 0;
-}
-
-/*
- * Exit with error if file system is mounted.
- */
-static void check_mounted(const char *fname, mode_t mode)
-{
-#ifdef ANDROID
-    warnx("Skipping mount checks");
-#else
-    struct statfs *mp;
-    const char *s1, *s2;
-    size_t len;
-    int n, r;
-
-    if (!(n = getmntinfo(&mp, MNT_NOWAIT)))
-        err(1, "getmntinfo");
-    len = strlen(_PATH_DEV);
-    s1 = fname;
-    if (!strncmp(s1, _PATH_DEV, len))
-        s1 += len;
-    r = S_ISCHR(mode) && s1 != fname && *s1 == 'r';
-    for (; n--; mp++) {
-        s2 = mp->f_mntfromname;
-        if (!strncmp(s2, _PATH_DEV, len))
-            s2 += len;
-        if ((r && s2 != mp->f_mntfromname && !strcmp(s1 + 1, s2)) || !strcmp(s1, s2))
-            errx(1, "%s is mounted on %s", fname, mp->f_mntonname);
-    }
-#endif
-}
-
-/*
- * Get a standard format.
- */
-static void getstdfmt(const char *fmt, struct bpb *bpb)
-{
-    u_int x, i;
-
-    x = sizeof(stdfmt) / sizeof(stdfmt[0]);
-    for (i = 0; i < x && strcmp(fmt, stdfmt[i].name); i++);
-    if (i == x)
-        errx(1, "%s: unknown standard format", fmt);
-    *bpb = stdfmt[i].bpb;
-}
-
-/*
- * Get disk slice, partition, and geometry information.
- */
-
-#ifdef ANDROID
-static void getdiskinfo(int fd, const char *fname, const char *dtype,
-                        __unused int oflag,struct bpb *bpb)
-{
-    struct hd_geometry geom;
-    u_long block_size;
-
-    if (ioctl(fd, BLKSSZGET, &bpb->bps)) {
-        fprintf(stderr, "Error getting bytes / sector (%s)\n", strerror(errno));
-        exit(1);
-    }
-
-    ckgeom(fname, bpb->bps, "bytes/sector");
-
-    if (ioctl(fd, BLKGETSIZE, &block_size)) {
-        fprintf(stderr, "Error getting blocksize (%s)\n", strerror(errno));
-        exit(1);
-    }
-
-    if (block_size > UINT32_MAX) {
-        fprintf(stderr, "Error blocksize too large: %lu\n", block_size);
-        exit(1);
-    }
-
-    bpb->bsec = (u_int)block_size;
-
-    if (ioctl(fd, HDIO_GETGEO, &geom)) {
-        fprintf(stderr, "Error getting gemoetry (%s) - trying sane values\n", strerror(errno));
-        geom.heads = 64;
-        geom.sectors = 63;
-    }
-
-    if (!geom.heads) {
-        printf("Bogus heads from kernel - setting sane value\n");
-        geom.heads = 64;
-    }
-
-    if (!geom.sectors) {
-        printf("Bogus sectors from kernel - setting sane value\n");
-        geom.sectors = 63;
-    }
-
-    bpb->spt = geom.sectors;
-    ckgeom(fname, bpb->spt, "sectors/track");
-
-    bpb->hds = geom.heads;
-    ckgeom(fname, bpb->hds, "drive heads");
-}
-
-#else
-
-static void getdiskinfo(int fd, const char *fname, const char *dtype,
-                        __unused int oflag, struct bpb *bpb)
-{
-    struct disklabel *lp, dlp;
-    struct fd_type type;
-    off_t ms, hs = 0;
-
-    lp = NULL;
-
-    /* If the user specified a disk type, try to use that */
-    if (dtype != NULL) {
-        lp = getdiskbyname(dtype);
-    }
-
-    /* Maybe it's a floppy drive */
-    if (lp == NULL) {
-        if (ioctl(fd, DIOCGMEDIASIZE, &ms) == -1) {
-            struct stat st;
-
-            if (fstat(fd, &st))
-                err(1, "Cannot get disk size");
-            /* create a fake geometry for a file image */
-            ms = st.st_size;
-            dlp.d_secsize = 512;
-            dlp.d_nsectors = 63;
-            dlp.d_ntracks = 255;
-            dlp.d_secperunit = ms / dlp.d_secsize;
-            lp = &dlp;
-        } else if (ioctl(fd, FD_GTYPE, &type) != -1) {
-            dlp.d_secsize = 128 << type.secsize;
-            dlp.d_nsectors = type.sectrac;
-            dlp.d_ntracks = type.heads;
-            dlp.d_secperunit = ms / dlp.d_secsize;
-            lp = &dlp;
-        }
-    }
-
-    /* Maybe it's a fixed drive */
-    if (lp == NULL) {
-        if (ioctl(fd, DIOCGDINFO, &dlp) == -1) {
-            if (bpb->bps == 0 && ioctl(fd, DIOCGSECTORSIZE, &dlp.d_secsize) == -1)
-                errx(1, "Cannot get sector size, %s", strerror(errno));
-
-            /* XXX Should we use bpb->bps if it's set? */
-            dlp.d_secperunit = ms / dlp.d_secsize;
-
-            if (bpb->spt == 0 && ioctl(fd, DIOCGFWSECTORS, &dlp.d_nsectors) == -1) {
-                warnx("Cannot get number of sectors per track, %s", strerror(errno));
-                dlp.d_nsectors = 63;
-            }
-            if (bpb->hds == 0 && ioctl(fd, DIOCGFWHEADS, &dlp.d_ntracks) == -1) {
-                warnx("Cannot get number of heads, %s", strerror(errno));
-                if (dlp.d_secperunit <= 63*1*1024)
-                    dlp.d_ntracks = 1;
-                else if (dlp.d_secperunit <= 63*16*1024)
-                    dlp.d_ntracks = 16;
-                else
-                    dlp.d_ntracks = 255;
-            }
-        }
-
-        hs = (ms / dlp.d_secsize) - dlp.d_secperunit;
-        lp = &dlp;
-    }
-
-    if (bpb->bps == 0)
-        bpb->bps = ckgeom(fname, lp->d_secsize, "bytes/sector");
-    if (bpb->spt == 0)
-        bpb->spt = ckgeom(fname, lp->d_nsectors, "sectors/track");
-    if (bpb->hds == 0)
-        bpb->hds = ckgeom(fname, lp->d_ntracks, "drive heads");
-    if (bpb->bsec == 0)
-        bpb->bsec = lp->d_secperunit;
-    if (bpb->hid == 0)
-        bpb->hid = hs;
-}
-#endif
-
-/*
- * Print out BPB values.
- */
-static void print_bpb(struct bpb *bpb)
-{
-    printf("bps=%u spc=%u res=%u nft=%u", bpb->bps, bpb->spc, bpb->res,
-           bpb->nft);
-    if (bpb->rde)
-        printf(" rde=%u", bpb->rde);
-    if (bpb->sec)
-        printf(" sec=%u", bpb->sec);
-    printf(" mid=%#x", bpb->mid);
-    if (bpb->spf)
-        printf(" spf=%u", bpb->spf);
-    printf(" spt=%u hds=%u hid=%u", bpb->spt, bpb->hds, bpb->hid);
-    if (bpb->bsec)
-        printf(" bsec=%u", bpb->bsec);
-    if (!bpb->spf) {
-        printf(" bspf=%u rdcl=%u", bpb->bspf, bpb->rdcl);
-        printf(" infs=");
-        printf(bpb->infs == MAXU16 ? "%#x" : "%u", bpb->infs);
-        printf(" bkbs=");
-        printf(bpb->bkbs == MAXU16 ? "%#x" : "%u", bpb->bkbs);
-    }
-    printf("\n");
-}
-
-/*
- * Check a disk geometry value.
- */
-static u_int ckgeom(const char *fname, u_int val, const char *msg)
-{
-    if (!val)
-        errx(1, "%s: no default %s", fname, msg);
-    if (val > MAXU16)
-        errx(1, "%s: illegal %s %d", fname, msg, val);
-    return val;
-}
-
-/*
- * Convert and check a numeric option argument.
- */
-static u_int argtou(const char *arg, u_int lo, u_int hi, const char *msg)
-{
-    char *s;
-    u_long x;
-
-    errno = 0;
-    x = strtoul(arg, &s, 0);
-    if (errno || !*arg || *s || x < lo || x > hi)
-        errx(1, "%s: bad %s", arg, msg);
-    return x;
-}
-
-/*
- * Same for off_t, with optional skmgpP suffix
- */
-static off_t argtooff(const char *arg, const char *msg)
-{
-    char *s;
-    off_t x;
-
-    x = strtoll(arg, &s, 0);
-    /* allow at most one extra char */
-    if (errno || x < 0 || (s[0] && s[1]) )
-        errx(1, "%s: bad %s", arg, msg);
-    if (*s) {    /* the extra char is the multiplier */
-        switch (*s) {
-            default:
-                errx(1, "%s: bad %s", arg, msg);
-                /* notreached */
-
-            case 's':       /* sector */
-            case 'S':
-                x <<= 9;    /* times 512 */
-                break;
-
-            case 'k':       /* kilobyte */
-            case 'K':
-                x <<= 10;   /* times 1024 */
-                break;
-
-            case 'm':       /* megabyte */
-            case 'M':
-                x <<= 20;   /* times 1024*1024 */
-                break;
-
-            case 'g':       /* gigabyte */
-            case 'G':
-                x <<= 30;   /* times 1024*1024*1024 */
-                break;
-
-            case 'p':       /* partition start */
-            case 'P':       /* partition start */
-            case 'l':       /* partition length */
-            case 'L':       /* partition length */
-                errx(1, "%s: not supported yet %s", arg, msg);
-                /* notreached */
-        }
-    }
-    return x;
-}
-
-/*
- * Check a volume label.
- */
-static int oklabel(const char *src)
-{
-    int c, i;
-
-    for (i = 0; i <= 11; i++) {
-        c = (u_char)*src++;
-        if (c < ' ' + !i || strchr("\"*+,./:;<=>?[\\]|", c))
-            break;
-    }
-    return i && !c;
-}
-
-/*
- * Make a volume label.
- */
-static void mklabel(u_int8_t *dest, const char *src)
-{
-    int c, i;
-
-    for (i = 0; i < 11; i++) {
-        c = *src ? toupper(*src++) : ' ';
-        *dest++ = !i && c == '\xe5' ? 5 : c;
-    }
-}
-
-/*
- * Copy string, padding with spaces.
- */
-static void setstr(u_int8_t *dest, const char *src, size_t len)
-{
-    while (len--)
-        *dest++ = *src ? *src++ : ' ';
-}
-
-/*
- * Print usage message.
- */
-static void usage(void)
-{
-    fprintf(stderr,
-            "usage: newfs_msdos [ -options ] special [disktype]\n"
-            "where the options are:\n"
-            "\t-@ create file system at specified offset\n"
-            "\t-A Attempt to cluster align root directory\n"
-            "\t-B get bootstrap from file\n"
-            "\t-C create image file with specified size\n"
-            "\t-F FAT type (12, 16, or 32)\n"
-            "\t-I volume ID\n"
-            "\t-L volume label\n"
-            "\t-N don't create file system: just print out parameters\n"
-            "\t-O OEM string\n"
-            "\t-S bytes/sector\n"
-            "\t-a sectors/FAT\n"
-            "\t-b block size\n"
-            "\t-c sectors/cluster\n"
-            "\t-e root directory entries\n"
-            "\t-f standard format\n"
-            "\t-h drive heads\n"
-            "\t-i file system info sector\n"
-            "\t-k backup boot sector\n"
-            "\t-m media descriptor\n"
-            "\t-n number of FATs\n"
-            "\t-o hidden sectors\n"
-            "\t-r reserved sectors\n"
-            "\t-s file system size (sectors)\n"
-            "\t-u sectors/track\n");
-    exit(1);
-}
diff --git a/toolbox/r.c b/toolbox/r.c
new file mode 100644
index 0000000..b96cdb2
--- /dev/null
+++ b/toolbox/r.c
@@ -0,0 +1,102 @@
+#include <fcntl.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <unistd.h>
+
+#if __LP64__
+#define strtoptr strtoull
+#else
+#define strtoptr strtoul
+#endif
+
+static int usage()
+{
+    fprintf(stderr,"r [-b|-s] <address> [<value>]\n");
+    return -1;
+}
+
+int main(int argc, char *argv[])
+{
+    if(argc < 2) return usage();
+
+    int width = 4;
+    if(!strcmp(argv[1], "-b")) {
+        width = 1;
+        argc--;
+        argv++;
+    } else if(!strcmp(argv[1], "-s")) {
+        width = 2;
+        argc--;
+        argv++;
+    }
+
+    if(argc < 2) return usage();
+    uintptr_t addr = strtoptr(argv[1], 0, 16);
+
+    uintptr_t endaddr = 0;
+    char* end = strchr(argv[1], '-');
+    if (end)
+        endaddr = strtoptr(end + 1, 0, 16);
+
+    if (!endaddr)
+        endaddr = addr + width - 1;
+
+    if (endaddr <= addr) {
+        fprintf(stderr, "end address <= start address\n");
+        return -1;
+    }
+
+    bool set = false;
+    uint32_t value = 0;
+    if(argc > 2) {
+        set = true;
+        value = strtoul(argv[2], 0, 16);
+    }
+
+    int fd = open("/dev/mem", O_RDWR | O_SYNC);
+    if(fd < 0) {
+        fprintf(stderr,"cannot open /dev/mem\n");
+        return -1;
+    }
+
+    off64_t mmap_start = addr & ~(PAGE_SIZE - 1);
+    size_t mmap_size = endaddr - mmap_start + 1;
+    mmap_size = (mmap_size + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1);
+
+    void* page = mmap64(0, mmap_size, PROT_READ | PROT_WRITE,
+                        MAP_SHARED, fd, mmap_start);
+
+    if(page == MAP_FAILED){
+        fprintf(stderr,"cannot mmap region\n");
+        return -1;
+    }
+
+    while (addr <= endaddr) {
+        switch(width){
+        case 4: {
+            uint32_t* x = (uint32_t*) (((uintptr_t) page) + (addr & 4095));
+            if(set) *x = value;
+            fprintf(stderr,"%08"PRIxPTR": %08x\n", addr, *x);
+            break;
+        }
+        case 2: {
+            uint16_t* x = (uint16_t*) (((uintptr_t) page) + (addr & 4095));
+            if(set) *x = value;
+            fprintf(stderr,"%08"PRIxPTR": %04x\n", addr, *x);
+            break;
+        }
+        case 1: {
+            uint8_t* x = (uint8_t*) (((uintptr_t) page) + (addr & 4095));
+            if(set) *x = value;
+            fprintf(stderr,"%08"PRIxPTR": %02x\n", addr, *x);
+            break;
+        }
+        }
+        addr += width;
+    }
+    return 0;
+}
diff --git a/toolbox/tools.h b/toolbox/tools.h
new file mode 100644
index 0000000..abeb3ef
--- /dev/null
+++ b/toolbox/tools.h
@@ -0,0 +1,3 @@
+TOOL(getevent)
+TOOL(getprop)
+TOOL(toolbox)
diff --git a/toolbox/upstream-netbsd/bin/dd/args.c b/toolbox/upstream-netbsd/bin/dd/args.c
deleted file mode 100644
index 207e300..0000000
--- a/toolbox/upstream-netbsd/bin/dd/args.c
+++ /dev/null
@@ -1,391 +0,0 @@
-/*	$NetBSD: args.c,v 1.38 2013/07/17 12:55:48 christos Exp $	*/
-
-/*-
- * Copyright (c) 1991, 1993, 1994
- *	The Regents of the University of California.  All rights reserved.
- *
- * This code is derived from software contributed to Berkeley by
- * Keith Muller of the University of California, San Diego and Lance
- * Visser of Convex Computer Corporation.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- * 3. Neither the name of the University nor the names of its contributors
- *    may be used to endorse or promote products derived from this software
- *    without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#include <sys/cdefs.h>
-#ifndef lint
-#if 0
-static char sccsid[] = "@(#)args.c	8.3 (Berkeley) 4/2/94";
-#else
-__RCSID("$NetBSD: args.c,v 1.38 2013/07/17 12:55:48 christos Exp $");
-#endif
-#endif /* not lint */
-
-#include <sys/types.h>
-#include <sys/time.h>
-
-#include <err.h>
-#include <errno.h>
-#include <limits.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include "dd.h"
-#include "extern.h"
-
-static int	c_arg(const void *, const void *);
-
-#ifdef NO_MSGFMT
-static void	f_msgfmt(char *) __dead;
-#else
-static void	f_msgfmt(char *);
-#endif /* NO_MSGFMT */
-
-#ifdef NO_CONV
-static void	f_conv(char *) __dead;
-#else
-static void	f_conv(char *);
-static int	c_conv(const void *, const void *);
-#endif /* NO_CONV */
-
-static void	f_bs(char *);
-static void	f_cbs(char *);
-static void	f_count(char *);
-static void	f_files(char *);
-static void	f_ibs(char *);
-static void	f_if(char *);
-static void	f_obs(char *);
-static void	f_of(char *);
-static void	f_seek(char *);
-static void	f_skip(char *);
-static void	f_progress(char *);
-
-static const struct arg {
-	const char *name;
-	void (*f)(char *);
-	u_int set, noset;
-} args[] = {
-     /* the array needs to be sorted by the first column so
-	bsearch() can be used to find commands quickly */
-	{ "bs",		f_bs,		C_BS,	 C_BS|C_IBS|C_OBS|C_OSYNC },
-	{ "cbs",	f_cbs,		C_CBS,	 C_CBS },
-	{ "conv",	f_conv,		0,	 0 },
-	{ "count",	f_count,	C_COUNT, C_COUNT },
-	{ "files",	f_files,	C_FILES, C_FILES },
-	{ "ibs",	f_ibs,		C_IBS,	 C_BS|C_IBS },
-	{ "if",		f_if,		C_IF,	 C_IF },
-	{ "iseek",	f_skip,		C_SKIP,	 C_SKIP },
-	{ "msgfmt",	f_msgfmt,	0,	 0 },
-	{ "obs",	f_obs,		C_OBS,	 C_BS|C_OBS },
-	{ "of",		f_of,		C_OF,	 C_OF },
-	{ "oseek",	f_seek,		C_SEEK,	 C_SEEK },
-	{ "progress",	f_progress,	0,	 0 },
-	{ "seek",	f_seek,		C_SEEK,	 C_SEEK },
-	{ "skip",	f_skip,		C_SKIP,	 C_SKIP },
-};
-
-/*
- * args -- parse JCL syntax of dd.
- */
-void
-jcl(char **argv)
-{
-	struct arg *ap, tmp;
-	char *oper, *arg;
-
-	in.dbsz = out.dbsz = 512;
-
-	while ((oper = *++argv) != NULL) {
-		if ((oper = strdup(oper)) == NULL) {
-			errx(EXIT_FAILURE,
-			    "unable to allocate space for the argument %s",
-			    *argv);
-			/* NOTREACHED */
-		}
-		if ((arg = strchr(oper, '=')) == NULL) {
-			errx(EXIT_FAILURE, "unknown operand %s", oper);
-			/* NOTREACHED */
-		}
-		*arg++ = '\0';
-		if (!*arg) {
-			errx(EXIT_FAILURE, "no value specified for %s", oper);
-			/* NOTREACHED */
-		}
-		tmp.name = oper;
-		if (!(ap = bsearch(&tmp, args,
-		    __arraycount(args), sizeof(*args), c_arg))) {
-			errx(EXIT_FAILURE, "unknown operand %s", tmp.name);
-			/* NOTREACHED */
-		}
-		if (ddflags & ap->noset) {
-			errx(EXIT_FAILURE,
-			    "%s: illegal argument combination or already set",
-			    tmp.name);
-			/* NOTREACHED */
-		}
-		ddflags |= ap->set;
-		ap->f(arg);
-	}
-
-	/* Final sanity checks. */
-
-	if (ddflags & C_BS) {
-		/*
-		 * Bs is turned off by any conversion -- we assume the user
-		 * just wanted to set both the input and output block sizes
-		 * and didn't want the bs semantics, so we don't warn.
-		 */
-		if (ddflags & (C_BLOCK | C_LCASE | C_SWAB | C_UCASE |
-		    C_UNBLOCK | C_OSYNC | C_ASCII | C_EBCDIC | C_SPARSE)) {
-			ddflags &= ~C_BS;
-			ddflags |= C_IBS|C_OBS;
-		}
-
-		/* Bs supersedes ibs and obs. */
-		if (ddflags & C_BS && ddflags & (C_IBS|C_OBS))
-			warnx("bs supersedes ibs and obs");
-	}
-
-	/*
-	 * Ascii/ebcdic and cbs implies block/unblock.
-	 * Block/unblock requires cbs and vice-versa.
-	 */
-	if (ddflags & (C_BLOCK|C_UNBLOCK)) {
-		if (!(ddflags & C_CBS)) {
-			errx(EXIT_FAILURE, "record operations require cbs");
-			/* NOTREACHED */
-		}
-		cfunc = ddflags & C_BLOCK ? block : unblock;
-	} else if (ddflags & C_CBS) {
-		if (ddflags & (C_ASCII|C_EBCDIC)) {
-			if (ddflags & C_ASCII) {
-				ddflags |= C_UNBLOCK;
-				cfunc = unblock;
-			} else {
-				ddflags |= C_BLOCK;
-				cfunc = block;
-			}
-		} else {
-			errx(EXIT_FAILURE,
-			    "cbs meaningless if not doing record operations");
-			/* NOTREACHED */
-		}
-	} else
-		cfunc = def;
-
-	/* Read, write and seek calls take off_t as arguments.
-	 *
-	 * The following check is not done because an off_t is a quad
-	 *  for current NetBSD implementations.
-	 *
-	 * if (in.offset > INT_MAX/in.dbsz || out.offset > INT_MAX/out.dbsz)
-	 *	errx(1, "seek offsets cannot be larger than %d", INT_MAX);
-	 */
-}
-
-static int
-c_arg(const void *a, const void *b)
-{
-
-	return (strcmp(((const struct arg *)a)->name,
-	    ((const struct arg *)b)->name));
-}
-
-static void
-f_bs(char *arg)
-{
-
-	in.dbsz = out.dbsz = strsuftoll("block size", arg, 1, UINT_MAX);
-}
-
-static void
-f_cbs(char *arg)
-{
-
-	cbsz = strsuftoll("conversion record size", arg, 1, UINT_MAX);
-}
-
-static void
-f_count(char *arg)
-{
-
-	cpy_cnt = strsuftoll("block count", arg, 0, LLONG_MAX);
-	if (!cpy_cnt)
-		terminate(0);
-}
-
-static void
-f_files(char *arg)
-{
-
-	files_cnt = (u_int)strsuftoll("file count", arg, 0, UINT_MAX);
-	if (!files_cnt)
-		terminate(0);
-}
-
-static void
-f_ibs(char *arg)
-{
-
-	if (!(ddflags & C_BS))
-		in.dbsz = strsuftoll("input block size", arg, 1, UINT_MAX);
-}
-
-static void
-f_if(char *arg)
-{
-
-	in.name = arg;
-}
-
-#ifdef NO_MSGFMT
-/* Build a small version (i.e. for a ramdisk root) */
-static void
-f_msgfmt(char *arg)
-{
-
-	errx(EXIT_FAILURE, "msgfmt option disabled");
-	/* NOTREACHED */
-}
-#else	/* NO_MSGFMT */
-static void
-f_msgfmt(char *arg)
-{
-
-	/*
-	 * If the format string is not valid, dd_write_msg() will print
-	 * an error and exit.
-	 */
-	dd_write_msg(arg, 0);
-
-	msgfmt = arg;
-}
-#endif	/* NO_MSGFMT */
-
-static void
-f_obs(char *arg)
-{
-
-	if (!(ddflags & C_BS))
-		out.dbsz = strsuftoll("output block size", arg, 1, UINT_MAX);
-}
-
-static void
-f_of(char *arg)
-{
-
-	out.name = arg;
-}
-
-static void
-f_seek(char *arg)
-{
-
-	out.offset = strsuftoll("seek blocks", arg, 0, LLONG_MAX);
-}
-
-static void
-f_skip(char *arg)
-{
-
-	in.offset = strsuftoll("skip blocks", arg, 0, LLONG_MAX);
-}
-
-static void
-f_progress(char *arg)
-{
-
-	progress = strsuftoll("progress blocks", arg, 0, LLONG_MAX);
-}
-
-#ifdef	NO_CONV
-/* Build a small version (i.e. for a ramdisk root) */
-static void
-f_conv(char *arg)
-{
-
-	errx(EXIT_FAILURE, "conv option disabled");
-	/* NOTREACHED */
-}
-#else	/* NO_CONV */
-
-static const struct conv {
-	const char *name;
-	u_int set, noset;
-	const u_char *ctab;
-} clist[] = {
-	{ "ascii",	C_ASCII,	C_EBCDIC,	e2a_POSIX },
-	{ "block",	C_BLOCK,	C_UNBLOCK,	NULL },
-	{ "ebcdic",	C_EBCDIC,	C_ASCII,	a2e_POSIX },
-	{ "ibm",	C_EBCDIC,	C_ASCII,	a2ibm_POSIX },
-	{ "lcase",	C_LCASE,	C_UCASE,	NULL },
-	{ "noerror",	C_NOERROR,	0,		NULL },
-	{ "notrunc",	C_NOTRUNC,	0,		NULL },
-	{ "oldascii",	C_ASCII,	C_EBCDIC,	e2a_32V },
-	{ "oldebcdic",	C_EBCDIC,	C_ASCII,	a2e_32V },
-	{ "oldibm",	C_EBCDIC,	C_ASCII,	a2ibm_32V },
-	{ "osync",	C_OSYNC,	C_BS,		NULL },
-	{ "sparse",	C_SPARSE,	0,		NULL },
-	{ "swab",	C_SWAB,		0,		NULL },
-	{ "sync",	C_SYNC,		0,		NULL },
-	{ "ucase",	C_UCASE,	C_LCASE,	NULL },
-	{ "unblock",	C_UNBLOCK,	C_BLOCK,	NULL },
-	/* If you add items to this table, be sure to add the
-	 * conversions to the C_BS check in the jcl routine above.
-	 */
-};
-
-static void
-f_conv(char *arg)
-{
-	struct conv *cp, tmp;
-
-	while (arg != NULL) {
-		tmp.name = strsep(&arg, ",");
-		if (!(cp = bsearch(&tmp, clist,
-		    __arraycount(clist), sizeof(*clist), c_conv))) {
-			errx(EXIT_FAILURE, "unknown conversion %s", tmp.name);
-			/* NOTREACHED */
-		}
-		if (ddflags & cp->noset) {
-			errx(EXIT_FAILURE,
-			    "%s: illegal conversion combination", tmp.name);
-			/* NOTREACHED */
-		}
-		ddflags |= cp->set;
-		if (cp->ctab)
-			ctab = cp->ctab;
-	}
-}
-
-static int
-c_conv(const void *a, const void *b)
-{
-
-	return (strcmp(((const struct conv *)a)->name,
-	    ((const struct conv *)b)->name));
-}
-
-#endif	/* NO_CONV */
diff --git a/toolbox/upstream-netbsd/bin/dd/conv.c b/toolbox/upstream-netbsd/bin/dd/conv.c
deleted file mode 100644
index d4a8a09..0000000
--- a/toolbox/upstream-netbsd/bin/dd/conv.c
+++ /dev/null
@@ -1,283 +0,0 @@
-/*	$NetBSD: conv.c,v 1.17 2003/08/07 09:05:10 agc Exp $	*/
-
-/*-
- * Copyright (c) 1991, 1993, 1994
- *	The Regents of the University of California.  All rights reserved.
- *
- * This code is derived from software contributed to Berkeley by
- * Keith Muller of the University of California, San Diego and Lance
- * Visser of Convex Computer Corporation.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- * 3. Neither the name of the University nor the names of its contributors
- *    may be used to endorse or promote products derived from this software
- *    without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#include <sys/cdefs.h>
-#ifndef lint
-#if 0
-static char sccsid[] = "@(#)conv.c	8.3 (Berkeley) 4/2/94";
-#else
-__RCSID("$NetBSD: conv.c,v 1.17 2003/08/07 09:05:10 agc Exp $");
-#endif
-#endif /* not lint */
-
-#include <sys/param.h>
-#include <sys/time.h>
-
-#include <err.h>
-#include <string.h>
-#include <stdlib.h>
-
-#include "dd.h"
-#include "extern.h"
-
-/*
- * def --
- * Copy input to output.  Input is buffered until reaches obs, and then
- * output until less than obs remains.  Only a single buffer is used.
- * Worst case buffer calculation is (ibs + obs - 1).
- */
-void
-def(void)
-{
-	uint64_t cnt;
-	u_char *inp;
-	const u_char *t;
-
-	if ((t = ctab) != NULL)
-		for (inp = in.dbp - (cnt = in.dbrcnt); cnt--; ++inp)
-			*inp = t[*inp];
-
-	/* Make the output buffer look right. */
-	out.dbp = in.dbp;
-	out.dbcnt = in.dbcnt;
-
-	if (in.dbcnt >= out.dbsz) {
-		/* If the output buffer is full, write it. */
-		dd_out(0);
-
-		/*
-		 * Ddout copies the leftover output to the beginning of
-		 * the buffer and resets the output buffer.  Reset the
-		 * input buffer to match it.
-	 	 */
-		in.dbp = out.dbp;
-		in.dbcnt = out.dbcnt;
-	}
-}
-
-void
-def_close(void)
-{
-
-	/* Just update the count, everything is already in the buffer. */
-	if (in.dbcnt)
-		out.dbcnt = in.dbcnt;
-}
-
-#ifdef	NO_CONV
-/* Build a smaller version (i.e. for a miniroot) */
-/* These can not be called, but just in case...  */
-static const char no_block[] = "unblock and -DNO_CONV?";
-void block(void)		{ errx(EXIT_FAILURE, "%s", no_block + 2); }
-void block_close(void)		{ errx(EXIT_FAILURE, "%s", no_block + 2); }
-void unblock(void)		{ errx(EXIT_FAILURE, "%s", no_block); }
-void unblock_close(void)	{ errx(EXIT_FAILURE, "%s", no_block); }
-#else	/* NO_CONV */
-
-/*
- * Copy variable length newline terminated records with a max size cbsz
- * bytes to output.  Records less than cbs are padded with spaces.
- *
- * max in buffer:  MAX(ibs, cbsz)
- * max out buffer: obs + cbsz
- */
-void
-block(void)
-{
-	static int intrunc;
-	int ch = 0;	/* pacify gcc */
-	uint64_t cnt, maxlen;
-	u_char *inp, *outp;
-	const u_char *t;
-
-	/*
-	 * Record truncation can cross block boundaries.  If currently in a
-	 * truncation state, keep tossing characters until reach a newline.
-	 * Start at the beginning of the buffer, as the input buffer is always
-	 * left empty.
-	 */
-	if (intrunc) {
-		for (inp = in.db, cnt = in.dbrcnt;
-		    cnt && *inp++ != '\n'; --cnt);
-		if (!cnt) {
-			in.dbcnt = 0;
-			in.dbp = in.db;
-			return;
-		}
-		intrunc = 0;
-		/* Adjust the input buffer numbers. */
-		in.dbcnt = cnt - 1;
-		in.dbp = inp + cnt - 1;
-	}
-
-	/*
-	 * Copy records (max cbsz size chunks) into the output buffer.  The
-	 * translation is done as we copy into the output buffer.
-	 */
-	for (inp = in.dbp - in.dbcnt, outp = out.dbp; in.dbcnt;) {
-		maxlen = MIN(cbsz, in.dbcnt);
-		if ((t = ctab) != NULL)
-			for (cnt = 0;
-			    cnt < maxlen && (ch = *inp++) != '\n'; ++cnt)
-				*outp++ = t[ch];
-		else
-			for (cnt = 0;
-			    cnt < maxlen && (ch = *inp++) != '\n'; ++cnt)
-				*outp++ = ch;
-		/*
-		 * Check for short record without a newline.  Reassemble the
-		 * input block.
-		 */
-		if (ch != '\n' && in.dbcnt < cbsz) {
-			(void)memmove(in.db, in.dbp - in.dbcnt, in.dbcnt);
-			break;
-		}
-
-		/* Adjust the input buffer numbers. */
-		in.dbcnt -= cnt;
-		if (ch == '\n')
-			--in.dbcnt;
-
-		/* Pad short records with spaces. */
-		if (cnt < cbsz)
-			(void)memset(outp, ctab ? ctab[' '] : ' ', cbsz - cnt);
-		else {
-			/*
-			 * If the next character wouldn't have ended the
-			 * block, it's a truncation.
-			 */
-			if (!in.dbcnt || *inp != '\n')
-				++st.trunc;
-
-			/* Toss characters to a newline. */
-			for (; in.dbcnt && *inp++ != '\n'; --in.dbcnt);
-			if (!in.dbcnt)
-				intrunc = 1;
-			else
-				--in.dbcnt;
-		}
-
-		/* Adjust output buffer numbers. */
-		out.dbp += cbsz;
-		if ((out.dbcnt += cbsz) >= out.dbsz)
-			dd_out(0);
-		outp = out.dbp;
-	}
-	in.dbp = in.db + in.dbcnt;
-}
-
-void
-block_close(void)
-{
-
-	/*
-	 * Copy any remaining data into the output buffer and pad to a record.
-	 * Don't worry about truncation or translation, the input buffer is
-	 * always empty when truncating, and no characters have been added for
-	 * translation.  The bottom line is that anything left in the input
-	 * buffer is a truncated record.  Anything left in the output buffer
-	 * just wasn't big enough.
-	 */
-	if (in.dbcnt) {
-		++st.trunc;
-		(void)memmove(out.dbp, in.dbp - in.dbcnt, in.dbcnt);
-		(void)memset(out.dbp + in.dbcnt,
-		    ctab ? ctab[' '] : ' ', cbsz - in.dbcnt);
-		out.dbcnt += cbsz;
-	}
-}
-
-/*
- * Convert fixed length (cbsz) records to variable length.  Deletes any
- * trailing blanks and appends a newline.
- *
- * max in buffer:  MAX(ibs, cbsz) + cbsz
- * max out buffer: obs + cbsz
- */
-void
-unblock(void)
-{
-	uint64_t cnt;
-	u_char *inp;
-	const u_char *t;
-
-	/* Translation and case conversion. */
-	if ((t = ctab) != NULL)
-		for (cnt = in.dbrcnt, inp = in.dbp - 1; cnt--; inp--)
-			*inp = t[*inp];
-	/*
-	 * Copy records (max cbsz size chunks) into the output buffer.  The
-	 * translation has to already be done or we might not recognize the
-	 * spaces.
-	 */
-	for (inp = in.db; in.dbcnt >= cbsz; inp += cbsz, in.dbcnt -= cbsz) {
-		for (t = inp + cbsz - 1; t >= inp && *t == ' '; --t);
-		if (t >= inp) {
-			cnt = t - inp + 1;
-			(void)memmove(out.dbp, inp, cnt);
-			out.dbp += cnt;
-			out.dbcnt += cnt;
-		}
-		++out.dbcnt;
-		*out.dbp++ = '\n';
-		if (out.dbcnt >= out.dbsz)
-			dd_out(0);
-	}
-	if (in.dbcnt)
-		(void)memmove(in.db, in.dbp - in.dbcnt, in.dbcnt);
-	in.dbp = in.db + in.dbcnt;
-}
-
-void
-unblock_close(void)
-{
-	uint64_t cnt;
-	u_char *t;
-
-	if (in.dbcnt) {
-		warnx("%s: short input record", in.name);
-		for (t = in.db + in.dbcnt - 1; t >= in.db && *t == ' '; --t);
-		if (t >= in.db) {
-			cnt = t - in.db + 1;
-			(void)memmove(out.dbp, in.db, cnt);
-			out.dbp += cnt;
-			out.dbcnt += cnt;
-		}
-		++out.dbcnt;
-		*out.dbp++ = '\n';
-	}
-}
-
-#endif	/* NO_CONV */
diff --git a/toolbox/upstream-netbsd/bin/dd/dd.c b/toolbox/upstream-netbsd/bin/dd/dd.c
deleted file mode 100644
index 03d080c..0000000
--- a/toolbox/upstream-netbsd/bin/dd/dd.c
+++ /dev/null
@@ -1,598 +0,0 @@
-/*	$NetBSD: dd.c,v 1.49 2012/02/21 01:49:01 matt Exp $	*/
-
-/*-
- * Copyright (c) 1991, 1993, 1994
- *	The Regents of the University of California.  All rights reserved.
- *
- * This code is derived from software contributed to Berkeley by
- * Keith Muller of the University of California, San Diego and Lance
- * Visser of Convex Computer Corporation.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- * 3. Neither the name of the University nor the names of its contributors
- *    may be used to endorse or promote products derived from this software
- *    without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#include <sys/cdefs.h>
-#ifndef lint
-__COPYRIGHT("@(#) Copyright (c) 1991, 1993, 1994\
- The Regents of the University of California.  All rights reserved.");
-#endif /* not lint */
-
-#ifndef lint
-#if 0
-static char sccsid[] = "@(#)dd.c	8.5 (Berkeley) 4/2/94";
-#else
-__RCSID("$NetBSD: dd.c,v 1.49 2012/02/21 01:49:01 matt Exp $");
-#endif
-#endif /* not lint */
-
-#include <sys/param.h>
-#include <sys/stat.h>
-#include <sys/ioctl.h>
-#include <sys/mtio.h>
-#include <sys/time.h>
-
-#include <ctype.h>
-#include <err.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <locale.h>
-#include <signal.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-#include "dd.h"
-#include "extern.h"
-
-static void dd_close(void);
-static void dd_in(void);
-static void getfdtype(IO *);
-static void redup_clean_fd(IO *);
-static void setup(void);
-
-int main(int, char *[]);
-
-IO		in, out;		/* input/output state */
-STAT		st;			/* statistics */
-void		(*cfunc)(void);		/* conversion function */
-uint64_t	cpy_cnt;		/* # of blocks to copy */
-static off_t	pending = 0;		/* pending seek if sparse */
-u_int		ddflags;		/* conversion options */
-uint64_t	cbsz;			/* conversion block size */
-u_int		files_cnt = 1;		/* # of files to copy */
-uint64_t	progress = 0;		/* display sign of life */
-const u_char	*ctab;			/* conversion table */
-sigset_t	infoset;		/* a set blocking SIGINFO */
-const char	*msgfmt = "posix";	/* default summary() message format */
-
-/*
- * Ops for stdin/stdout and crunch'd dd.  These are always host ops.
- */
-static const struct ddfops ddfops_stdfd = {
-	.op_open = open,
-	.op_close = close,
-	.op_fcntl = fcntl,
-	.op_ioctl = ioctl,
-	.op_fstat = fstat,
-	.op_fsync = fsync,
-	.op_ftruncate = ftruncate,
-	.op_lseek = lseek,
-	.op_read = read,
-	.op_write = write,
-};
-extern const struct ddfops ddfops_prog;
-
-int
-main(int argc, char *argv[])
-{
-	int ch;
-
-	setprogname(argv[0]);
-	(void)setlocale(LC_ALL, "");
-
-	while ((ch = getopt(argc, argv, "")) != -1) {
-		switch (ch) {
-		default:
-			errx(EXIT_FAILURE, "usage: dd [operand ...]");
-			/* NOTREACHED */
-		}
-	}
-	argc -= (optind - 1);
-	argv += (optind - 1);
-
-	jcl(argv);
-#ifndef CRUNCHOPS
-	if (ddfops_prog.op_init && ddfops_prog.op_init() == -1)
-		err(1, "prog init");
-#endif
-	setup();
-
-	(void)signal(SIGINFO, summaryx);
-	(void)signal(SIGINT, terminate);
-	(void)sigemptyset(&infoset);
-	(void)sigaddset(&infoset, SIGINFO);
-
-	(void)atexit(summary);
-
-	while (files_cnt--)
-		dd_in();
-
-	dd_close();
-	exit(0);
-	/* NOTREACHED */
-}
-
-static void
-setup(void)
-{
-#ifdef CRUNCHOPS
-	const struct ddfops *prog_ops = &ddfops_stdfd;
-#else
-	const struct ddfops *prog_ops = &ddfops_prog;
-#endif
-
-	if (in.name == NULL) {
-		in.name = "stdin";
-		in.fd = STDIN_FILENO;
-		in.ops = &ddfops_stdfd;
-	} else {
-		in.ops = prog_ops;
-		in.fd = ddop_open(in, in.name, O_RDONLY, 0);
-		if (in.fd < 0)
-			err(EXIT_FAILURE, "%s", in.name);
-			/* NOTREACHED */
-
-		/* Ensure in.fd is outside the stdio descriptor range */
-		redup_clean_fd(&in);
-	}
-
-	getfdtype(&in);
-
-	if (files_cnt > 1 && !(in.flags & ISTAPE)) {
-		errx(EXIT_FAILURE, "files is not supported for non-tape devices");
-		/* NOTREACHED */
-	}
-
-	if (out.name == NULL) {
-		/* No way to check for read access here. */
-		out.fd = STDOUT_FILENO;
-		out.name = "stdout";
-		out.ops = &ddfops_stdfd;
-	} else {
-		out.ops = prog_ops;
-#define	OFLAGS \
-    (O_CREAT | (ddflags & (C_SEEK | C_NOTRUNC) ? 0 : O_TRUNC))
-		out.fd = ddop_open(out, out.name, O_RDWR | OFLAGS, DEFFILEMODE);
-		/*
-		 * May not have read access, so try again with write only.
-		 * Without read we may have a problem if output also does
-		 * not support seeks.
-		 */
-		if (out.fd < 0) {
-			out.fd = ddop_open(out, out.name, O_WRONLY | OFLAGS,
-			    DEFFILEMODE);
-			out.flags |= NOREAD;
-		}
-		if (out.fd < 0) {
-			err(EXIT_FAILURE, "%s", out.name);
-			/* NOTREACHED */
-		}
-
-		/* Ensure out.fd is outside the stdio descriptor range */
-		redup_clean_fd(&out);
-	}
-
-	getfdtype(&out);
-
-	/*
-	 * Allocate space for the input and output buffers.  If not doing
-	 * record oriented I/O, only need a single buffer.
-	 */
-	if (!(ddflags & (C_BLOCK|C_UNBLOCK))) {
-		size_t dbsz = out.dbsz;
-		if (!(ddflags & C_BS))
-			dbsz += in.dbsz - 1;
-		if ((in.db = malloc(dbsz)) == NULL) {
-			err(EXIT_FAILURE, NULL);
-			/* NOTREACHED */
-		}
-		out.db = in.db;
-	} else if ((in.db =
-	    malloc((u_int)(MAX(in.dbsz, cbsz) + cbsz))) == NULL ||
-	    (out.db = malloc((u_int)(out.dbsz + cbsz))) == NULL) {
-		err(EXIT_FAILURE, NULL);
-		/* NOTREACHED */
-	}
-	in.dbp = in.db;
-	out.dbp = out.db;
-
-	/* Position the input/output streams. */
-	if (in.offset)
-		pos_in();
-	if (out.offset)
-		pos_out();
-
-	/*
-	 * Truncate the output file; ignore errors because it fails on some
-	 * kinds of output files, tapes, for example.
-	 */
-	if ((ddflags & (C_OF | C_SEEK | C_NOTRUNC)) == (C_OF | C_SEEK))
-		(void)ddop_ftruncate(out, out.fd, (off_t)out.offset * out.dbsz);
-
-	/*
-	 * If converting case at the same time as another conversion, build a
-	 * table that does both at once.  If just converting case, use the
-	 * built-in tables.
-	 */
-	if (ddflags & (C_LCASE|C_UCASE)) {
-#ifdef	NO_CONV
-		/* Should not get here, but just in case... */
-		errx(EXIT_FAILURE, "case conv and -DNO_CONV");
-		/* NOTREACHED */
-#else	/* NO_CONV */
-		u_int cnt;
-
-		if (ddflags & C_ASCII || ddflags & C_EBCDIC) {
-			if (ddflags & C_LCASE) {
-				for (cnt = 0; cnt < 256; ++cnt)
-					casetab[cnt] = tolower(ctab[cnt]);
-			} else {
-				for (cnt = 0; cnt < 256; ++cnt)
-					casetab[cnt] = toupper(ctab[cnt]);
-			}
-		} else {
-			if (ddflags & C_LCASE) {
-				for (cnt = 0; cnt < 256; ++cnt)
-					casetab[cnt] = tolower(cnt);
-			} else {
-				for (cnt = 0; cnt < 256; ++cnt)
-					casetab[cnt] = toupper(cnt);
-			}
-		}
-
-		ctab = casetab;
-#endif	/* NO_CONV */
-	}
-
-	(void)gettimeofday(&st.start, NULL);	/* Statistics timestamp. */
-}
-
-static void
-getfdtype(IO *io)
-{
-	struct mtget mt;
-	struct stat sb;
-
-	if (io->ops->op_fstat(io->fd, &sb)) {
-		err(EXIT_FAILURE, "%s", io->name);
-		/* NOTREACHED */
-	}
-	if (S_ISCHR(sb.st_mode))
-		io->flags |= io->ops->op_ioctl(io->fd, MTIOCGET, &mt)
-		    ? ISCHR : ISTAPE;
-	else if (io->ops->op_lseek(io->fd, (off_t)0, SEEK_CUR) == -1
-	    && errno == ESPIPE)
-		io->flags |= ISPIPE;		/* XXX fixed in 4.4BSD */
-}
-
-/*
- * Move the parameter file descriptor to a descriptor that is outside the
- * stdio descriptor range, if necessary.  This is required to avoid
- * accidentally outputting completion or error messages into the
- * output file that were intended for the tty.
- */
-static void
-redup_clean_fd(IO *io)
-{
-	int fd = io->fd;
-	int newfd;
-
-	if (fd != STDIN_FILENO && fd != STDOUT_FILENO &&
-	    fd != STDERR_FILENO)
-		/* File descriptor is ok, return immediately. */
-		return;
-
-	/*
-	 * 3 is the first descriptor greater than STD*_FILENO.  Any
-	 * free descriptor valued 3 or above is acceptable...
-	 */
-	newfd = io->ops->op_fcntl(fd, F_DUPFD, 3);
-	if (newfd < 0) {
-		err(EXIT_FAILURE, "dupfd IO");
-		/* NOTREACHED */
-	}
-
-	io->ops->op_close(fd);
-	io->fd = newfd;
-}
-
-static void
-dd_in(void)
-{
-	int flags;
-	int64_t n;
-
-	for (flags = ddflags;;) {
-		if (cpy_cnt && (st.in_full + st.in_part) >= cpy_cnt)
-			return;
-
-		/*
-		 * Clear the buffer first if doing "sync" on input.
-		 * If doing block operations use spaces.  This will
-		 * affect not only the C_NOERROR case, but also the
-		 * last partial input block which should be padded
-		 * with zero and not garbage.
-		 */
-		if (flags & C_SYNC) {
-			if (flags & (C_BLOCK|C_UNBLOCK))
-				(void)memset(in.dbp, ' ', in.dbsz);
-			else
-				(void)memset(in.dbp, 0, in.dbsz);
-		}
-
-		n = ddop_read(in, in.fd, in.dbp, in.dbsz);
-		if (n == 0) {
-			in.dbrcnt = 0;
-			return;
-		}
-
-		/* Read error. */
-		if (n < 0) {
-
-			/*
-			 * If noerror not specified, die.  POSIX requires that
-			 * the warning message be followed by an I/O display.
-			 */
-			if (!(flags & C_NOERROR)) {
-				err(EXIT_FAILURE, "%s", in.name);
-				/* NOTREACHED */
-			}
-			warn("%s", in.name);
-			summary();
-
-			/*
-			 * If it's not a tape drive or a pipe, seek past the
-			 * error.  If your OS doesn't do the right thing for
-			 * raw disks this section should be modified to re-read
-			 * in sector size chunks.
-			 */
-			if (!(in.flags & (ISPIPE|ISTAPE)) &&
-			    ddop_lseek(in, in.fd, (off_t)in.dbsz, SEEK_CUR))
-				warn("%s", in.name);
-
-			/* If sync not specified, omit block and continue. */
-			if (!(ddflags & C_SYNC))
-				continue;
-
-			/* Read errors count as full blocks. */
-			in.dbcnt += in.dbrcnt = in.dbsz;
-			++st.in_full;
-
-		/* Handle full input blocks. */
-		} else if ((uint64_t)n == in.dbsz) {
-			in.dbcnt += in.dbrcnt = n;
-			++st.in_full;
-
-		/* Handle partial input blocks. */
-		} else {
-			/* If sync, use the entire block. */
-			if (ddflags & C_SYNC)
-				in.dbcnt += in.dbrcnt = in.dbsz;
-			else
-				in.dbcnt += in.dbrcnt = n;
-			++st.in_part;
-		}
-
-		/*
-		 * POSIX states that if bs is set and no other conversions
-		 * than noerror, notrunc or sync are specified, the block
-		 * is output without buffering as it is read.
-		 */
-		if (ddflags & C_BS) {
-			out.dbcnt = in.dbcnt;
-			dd_out(1);
-			in.dbcnt = 0;
-			continue;
-		}
-
-		if (ddflags & C_SWAB) {
-			if ((n = in.dbrcnt) & 1) {
-				++st.swab;
-				--n;
-			}
-			swab(in.dbp, in.dbp, n);
-		}
-
-		in.dbp += in.dbrcnt;
-		(*cfunc)();
-	}
-}
-
-/*
- * Cleanup any remaining I/O and flush output.  If necessary, output file
- * is truncated.
- */
-static void
-dd_close(void)
-{
-
-	if (cfunc == def)
-		def_close();
-	else if (cfunc == block)
-		block_close();
-	else if (cfunc == unblock)
-		unblock_close();
-	if (ddflags & C_OSYNC && out.dbcnt < out.dbsz) {
-		(void)memset(out.dbp, 0, out.dbsz - out.dbcnt);
-		out.dbcnt = out.dbsz;
-	}
-	/* If there are pending sparse blocks, make sure
-	 * to write out the final block un-sparse
-	 */
-	if ((out.dbcnt == 0) && pending) {
-		memset(out.db, 0, out.dbsz);
-		out.dbcnt = out.dbsz;
-		out.dbp = out.db + out.dbcnt;
-		pending -= out.dbsz;
-	}
-	if (out.dbcnt)
-		dd_out(1);
-
-	/*
-	 * Reporting nfs write error may be deferred until next
-	 * write(2) or close(2) system call.  So, we need to do an
-	 * extra check.  If an output is stdout, the file structure
-	 * may be shared with other processes and close(2) just
-	 * decreases the reference count.
-	 */
-	if (out.fd == STDOUT_FILENO && ddop_fsync(out, out.fd) == -1
-	    && errno != EINVAL) {
-		err(EXIT_FAILURE, "fsync stdout");
-		/* NOTREACHED */
-	}
-	if (ddop_close(out, out.fd) == -1) {
-		err(EXIT_FAILURE, "close");
-		/* NOTREACHED */
-	}
-}
-
-void
-dd_out(int force)
-{
-	static int warned;
-	int64_t cnt, n, nw;
-	u_char *outp;
-
-	/*
-	 * Write one or more blocks out.  The common case is writing a full
-	 * output block in a single write; increment the full block stats.
-	 * Otherwise, we're into partial block writes.  If a partial write,
-	 * and it's a character device, just warn.  If a tape device, quit.
-	 *
-	 * The partial writes represent two cases.  1: Where the input block
-	 * was less than expected so the output block was less than expected.
-	 * 2: Where the input block was the right size but we were forced to
-	 * write the block in multiple chunks.  The original versions of dd(1)
-	 * never wrote a block in more than a single write, so the latter case
-	 * never happened.
-	 *
-	 * One special case is if we're forced to do the write -- in that case
-	 * we play games with the buffer size, and it's usually a partial write.
-	 */
-	outp = out.db;
-	for (n = force ? out.dbcnt : out.dbsz;; n = out.dbsz) {
-		for (cnt = n;; cnt -= nw) {
-
-			if (!force && ddflags & C_SPARSE) {
-				int sparse, i;
-				sparse = 1;	/* Is buffer sparse? */
-				for (i = 0; i < cnt; i++)
-					if (outp[i] != 0) {
-						sparse = 0;
-						break;
-					}
-				if (sparse) {
-					pending += cnt;
-					outp += cnt;
-					nw = 0;
-					break;
-				}
-			}
-			if (pending != 0) {
-				if (ddop_lseek(out,
-				    out.fd, pending, SEEK_CUR) == -1)
-					err(EXIT_FAILURE, "%s: seek error creating sparse file",
-					    out.name);
-			}
-			nw = bwrite(&out, outp, cnt);
-			if (nw <= 0) {
-				if (nw == 0)
-					errx(EXIT_FAILURE,
-						"%s: end of device", out.name);
-					/* NOTREACHED */
-				if (errno != EINTR)
-					err(EXIT_FAILURE, "%s", out.name);
-					/* NOTREACHED */
-				nw = 0;
-			}
-			if (pending) {
-				st.bytes += pending;
-				st.sparse += pending/out.dbsz;
-				st.out_full += pending/out.dbsz;
-				pending = 0;
-			}
-			outp += nw;
-			st.bytes += nw;
-			if (nw == n) {
-				if ((uint64_t)n != out.dbsz)
-					++st.out_part;
-				else
-					++st.out_full;
-				break;
-			}
-			++st.out_part;
-			if (nw == cnt)
-				break;
-			if (out.flags & ISCHR && !warned) {
-				warned = 1;
-				warnx("%s: short write on character device", out.name);
-			}
-			if (out.flags & ISTAPE)
-				errx(EXIT_FAILURE,
-					"%s: short write on tape device", out.name);
-				/* NOTREACHED */
-
-		}
-		if ((out.dbcnt -= n) < out.dbsz)
-			break;
-	}
-
-	/* Reassemble the output block. */
-	if (out.dbcnt)
-		(void)memmove(out.db, out.dbp - out.dbcnt, out.dbcnt);
-	out.dbp = out.db + out.dbcnt;
-
-	if (progress && (st.out_full + st.out_part) % progress == 0)
-		(void)write(STDERR_FILENO, ".", 1);
-}
-
-/*
- * A protected against SIGINFO write
- */
-ssize_t
-bwrite(IO *io, const void *buf, size_t len)
-{
-	sigset_t oset;
-	ssize_t rv;
-	int oerrno;
-
-	(void)sigprocmask(SIG_BLOCK, &infoset, &oset);
-	rv = io->ops->op_write(io->fd, buf, len);
-	oerrno = errno;
-	(void)sigprocmask(SIG_SETMASK, &oset, NULL);
-	errno = oerrno;
-	return (rv);
-}
diff --git a/toolbox/upstream-netbsd/bin/dd/dd.h b/toolbox/upstream-netbsd/bin/dd/dd.h
deleted file mode 100644
index b01c7b3..0000000
--- a/toolbox/upstream-netbsd/bin/dd/dd.h
+++ /dev/null
@@ -1,127 +0,0 @@
-/*	$NetBSD: dd.h,v 1.15 2011/02/04 19:42:12 pooka Exp $	*/
-
-/*-
- * Copyright (c) 1991, 1993, 1994
- *	The Regents of the University of California.  All rights reserved.
- *
- * This code is derived from software contributed to Berkeley by
- * Keith Muller of the University of California, San Diego and Lance
- * Visser of Convex Computer Corporation.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- * 3. Neither the name of the University nor the names of its contributors
- *    may be used to endorse or promote products derived from this software
- *    without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- *
- *	@(#)dd.h	8.3 (Berkeley) 4/2/94
- */
-
-#include <sys/stat.h>
-
-struct ddfops {
-	int (*op_init)(void);
-
-	int (*op_open)(const char *, int, ...);
-	int (*op_close)(int);
-
-	int (*op_fcntl)(int, int, ...);
-#ifdef __ANDROID__
-	int (*op_ioctl)(int, int, ...);
-#else
-	int (*op_ioctl)(int, unsigned long, ...);
-#endif
-
-	int (*op_fstat)(int, struct stat *);
-	int (*op_fsync)(int);
-	int (*op_ftruncate)(int, off_t);
-
-	off_t (*op_lseek)(int, off_t, int);
-
-	ssize_t (*op_read)(int, void *, size_t);
-	ssize_t (*op_write)(int, const void *, size_t);
-};
-
-#define ddop_open(dir, a1, a2, ...)	dir.ops->op_open(a1, a2, __VA_ARGS__)
-#define ddop_close(dir, a1)		dir.ops->op_close(a1)
-#define ddop_fcntl(dir, a1, a2, ...)	dir.ops->op_fcntl(a1, a2, __VA_ARGS__)
-#define ddop_ioctl(dir, a1, a2, ...)	dir.ops->op_ioctl(a1, a2, __VA_ARGS__)
-#define ddop_fsync(dir, a1)		dir.ops->op_fsync(a1)
-#define ddop_ftruncate(dir, a1, a2)	dir.ops->op_ftruncate(a1, a2)
-#define ddop_lseek(dir, a1, a2, a3)	dir.ops->op_lseek(a1, a2, a3)
-#define ddop_read(dir, a1, a2, a3)	dir.ops->op_read(a1, a2, a3)
-#define ddop_write(dir, a1, a2, a3)	dir.ops->op_write(a1, a2, a3)
-
-/* Input/output stream state. */
-typedef struct {
-	u_char		*db;		/* buffer address */
-	u_char		*dbp;		/* current buffer I/O address */
-	uint64_t	dbcnt;		/* current buffer byte count */
-	int64_t		dbrcnt;		/* last read byte count */
-	uint64_t	dbsz;		/* buffer size */
-
-#define	ISCHR		0x01		/* character device (warn on short) */
-#define	ISPIPE		0x02		/* pipe (not truncatable) */
-#define	ISTAPE		0x04		/* tape (not seekable) */
-#define	NOREAD		0x08		/* not readable */
-	u_int		flags;
-
-	const char  	*name;		/* name */
-	int		fd;		/* file descriptor */
-	uint64_t	offset;		/* # of blocks to skip */
-	struct ddfops	const *ops;	/* ops to use with fd */
-} IO;
-
-typedef struct {
-	uint64_t	in_full;	/* # of full input blocks */
-	uint64_t	in_part;	/* # of partial input blocks */
-	uint64_t	out_full;	/* # of full output blocks */
-	uint64_t	out_part;	/* # of partial output blocks */
-	uint64_t	trunc;		/* # of truncated records */
-	uint64_t	swab;		/* # of odd-length swab blocks */
-	uint64_t	sparse;		/* # of sparse output blocks */
-	uint64_t	bytes;		/* # of bytes written */
-	struct timeval	start;		/* start time of dd */
-} STAT;
-
-/* Flags (in ddflags). */
-#define	C_ASCII		0x00001
-#define	C_BLOCK		0x00002
-#define	C_BS		0x00004
-#define	C_CBS		0x00008
-#define	C_COUNT		0x00010
-#define	C_EBCDIC	0x00020
-#define	C_FILES		0x00040
-#define	C_IBS		0x00080
-#define	C_IF		0x00100
-#define	C_LCASE		0x00200
-#define	C_NOERROR	0x00400
-#define	C_NOTRUNC	0x00800
-#define	C_OBS		0x01000
-#define	C_OF		0x02000
-#define	C_SEEK		0x04000
-#define	C_SKIP		0x08000
-#define	C_SWAB		0x10000
-#define	C_SYNC		0x20000
-#define	C_UCASE		0x40000
-#define	C_UNBLOCK	0x80000
-#define	C_OSYNC		0x100000
-#define	C_SPARSE	0x200000
diff --git a/toolbox/upstream-netbsd/bin/dd/dd_hostops.c b/toolbox/upstream-netbsd/bin/dd/dd_hostops.c
deleted file mode 100644
index d6e7a89..0000000
--- a/toolbox/upstream-netbsd/bin/dd/dd_hostops.c
+++ /dev/null
@@ -1,53 +0,0 @@
-/*      $NetBSD: dd_hostops.c,v 1.1 2011/02/04 19:42:12 pooka Exp $	*/
-
-/*-
- * Copyright (c) 2010 The NetBSD Foundation, Inc.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
- * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
- * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
- * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include <sys/cdefs.h>
-#ifndef lint
-__RCSID("$NetBSD: dd_hostops.c,v 1.1 2011/02/04 19:42:12 pooka Exp $");
-#endif /* !lint */
-
-#include <sys/types.h>
-#include <sys/ioctl.h>
-
-#include <fcntl.h>
-#include <unistd.h>
-
-#include "dd.h"
-
-const struct ddfops ddfops_prog = {
-	.op_open = open,
-	.op_close = close,
-	.op_fcntl = fcntl,
-	.op_ioctl = ioctl,
-	.op_fstat = fstat,
-	.op_fsync = fsync,
-	.op_ftruncate = ftruncate,
-	.op_lseek = lseek,
-	.op_read = read,
-	.op_write = write,
-};
diff --git a/toolbox/upstream-netbsd/bin/dd/extern.h b/toolbox/upstream-netbsd/bin/dd/extern.h
deleted file mode 100644
index 9c59021..0000000
--- a/toolbox/upstream-netbsd/bin/dd/extern.h
+++ /dev/null
@@ -1,82 +0,0 @@
-/*	$NetBSD: extern.h,v 1.22 2011/11/07 22:24:23 jym Exp $	*/
-
-/*-
- * Copyright (c) 1991, 1993, 1994
- *	The Regents of the University of California.  All rights reserved.
- *
- * This code is derived from software contributed to Berkeley by
- * Keith Muller of the University of California, San Diego and Lance
- * Visser of Convex Computer Corporation.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- * 3. Neither the name of the University nor the names of its contributors
- *    may be used to endorse or promote products derived from this software
- *    without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- *
- *	@(#)extern.h	8.3 (Berkeley) 4/2/94
- */
-
-#include <sys/cdefs.h>
-
-#ifdef NO_CONV
-__dead void block(void);
-__dead void block_close(void);
-__dead void unblock(void);
-__dead void unblock_close(void);
-#else
-void block(void);
-void block_close(void);
-void unblock(void);
-void unblock_close(void);
-#endif
-
-#ifndef NO_MSGFMT
-int dd_write_msg(const char *, int);
-#endif
-
-void dd_out(int);
-void def(void);
-void def_close(void);
-void jcl(char **);
-void pos_in(void);
-void pos_out(void);
-void summary(void);
-void summaryx(int);
-__dead void terminate(int);
-void unblock(void);
-void unblock_close(void);
-ssize_t bwrite(IO *, const void *, size_t);
-
-extern IO		in, out;
-extern STAT		st;
-extern void		(*cfunc)(void);
-extern uint64_t		cpy_cnt;
-extern uint64_t		cbsz;
-extern u_int		ddflags;
-extern u_int		files_cnt;
-extern uint64_t		progress;
-extern const u_char	*ctab;
-extern const u_char	a2e_32V[], a2e_POSIX[];
-extern const u_char	e2a_32V[], e2a_POSIX[];
-extern const u_char	a2ibm_32V[], a2ibm_POSIX[];
-extern u_char		casetab[];
-extern const char	*msgfmt;
diff --git a/toolbox/upstream-netbsd/bin/dd/misc.c b/toolbox/upstream-netbsd/bin/dd/misc.c
deleted file mode 100644
index 0fac98b..0000000
--- a/toolbox/upstream-netbsd/bin/dd/misc.c
+++ /dev/null
@@ -1,342 +0,0 @@
-/*	$NetBSD: misc.c,v 1.23 2011/11/07 22:24:23 jym Exp $	*/
-
-/*-
- * Copyright (c) 1991, 1993, 1994
- *	The Regents of the University of California.  All rights reserved.
- *
- * This code is derived from software contributed to Berkeley by
- * Keith Muller of the University of California, San Diego and Lance
- * Visser of Convex Computer Corporation.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- * 3. Neither the name of the University nor the names of its contributors
- *    may be used to endorse or promote products derived from this software
- *    without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#include <sys/cdefs.h>
-#ifndef lint
-#if 0
-static char sccsid[] = "@(#)misc.c	8.3 (Berkeley) 4/2/94";
-#else
-__RCSID("$NetBSD: misc.c,v 1.23 2011/11/07 22:24:23 jym Exp $");
-#endif
-#endif /* not lint */
-
-#include <sys/param.h>
-#include <sys/types.h>
-#include <sys/time.h>
-
-#include <err.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <util.h>
-#include <inttypes.h>
-
-#include "dd.h"
-#include "extern.h"
-
-#define	tv2mS(tv) ((tv).tv_sec * 1000LL + ((tv).tv_usec + 500) / 1000)
-
-static void posix_summary(void);
-#ifndef NO_MSGFMT
-static void custom_summary(void);
-static void human_summary(void);
-static void quiet_summary(void);
-
-static void buffer_write(const char *, size_t, int);
-#endif /* NO_MSGFMT */
-
-void
-summary(void)
-{
-
-	if (progress)
-		(void)write(STDERR_FILENO, "\n", 1);
-
-#ifdef NO_MSGFMT
-	return posix_summary();
-#else /* NO_MSGFMT */
-	if (strncmp(msgfmt, "human", sizeof("human")) == 0)
-		return human_summary();
-
-	if (strncmp(msgfmt, "posix", sizeof("posix")) == 0)
-		return posix_summary();
-
-	if (strncmp(msgfmt, "quiet", sizeof("quiet")) == 0)
-		return quiet_summary();
-
-	return custom_summary();
-#endif /* NO_MSGFMT */
-}
-
-static void
-posix_summary(void)
-{
-	char buf[100];
-	int64_t mS;
-	struct timeval tv;
-
-	if (progress)
-		(void)write(STDERR_FILENO, "\n", 1);
-
-	(void)gettimeofday(&tv, NULL);
-	mS = tv2mS(tv) - tv2mS(st.start);
-	if (mS == 0)
-		mS = 1;
-
-	/* Use snprintf(3) so that we don't reenter stdio(3). */
-	(void)snprintf(buf, sizeof(buf),
-	    "%llu+%llu records in\n%llu+%llu records out\n",
-	    (unsigned long long)st.in_full,  (unsigned long long)st.in_part,
-	    (unsigned long long)st.out_full, (unsigned long long)st.out_part);
-	(void)write(STDERR_FILENO, buf, strlen(buf));
-	if (st.swab) {
-		(void)snprintf(buf, sizeof(buf), "%llu odd length swab %s\n",
-		    (unsigned long long)st.swab,
-		    (st.swab == 1) ? "block" : "blocks");
-		(void)write(STDERR_FILENO, buf, strlen(buf));
-	}
-	if (st.trunc) {
-		(void)snprintf(buf, sizeof(buf), "%llu truncated %s\n",
-		    (unsigned long long)st.trunc,
-		    (st.trunc == 1) ? "block" : "blocks");
-		(void)write(STDERR_FILENO, buf, strlen(buf));
-	}
-	if (st.sparse) {
-		(void)snprintf(buf, sizeof(buf), "%llu sparse output %s\n",
-		    (unsigned long long)st.sparse,
-		    (st.sparse == 1) ? "block" : "blocks");
-		(void)write(STDERR_FILENO, buf, strlen(buf));
-	}
-	(void)snprintf(buf, sizeof(buf),
-	    "%llu bytes transferred in %lu.%03d secs (%llu bytes/sec)\n",
-	    (unsigned long long) st.bytes,
-	    (long) (mS / 1000),
-	    (int) (mS % 1000),
-	    (unsigned long long) (st.bytes * 1000LL / mS));
-	(void)write(STDERR_FILENO, buf, strlen(buf));
-}
-
-/* ARGSUSED */
-void
-summaryx(int notused)
-{
-
-	summary();
-}
-
-/* ARGSUSED */
-void
-terminate(int signo)
-{
-
-	summary();
-	(void)raise_default_signal(signo);
-	_exit(127);
-}
-
-#ifndef NO_MSGFMT
-/*
- * Buffer write(2) calls
- */
-static void
-buffer_write(const char *str, size_t size, int flush)
-{
-	static char wbuf[128];
-	static size_t cnt = 0; /* Internal counter to allow wbuf to wrap */
-	
-	unsigned int i;
-
-	for (i = 0; i < size; i++) {
-		if (str != NULL) {
-			wbuf[cnt++] = str[i];
-		}
-		if (cnt >= sizeof(wbuf)) {
-			(void)write(STDERR_FILENO, wbuf, cnt);
-			cnt = 0;
-		}
-	}
-
-	if (flush != 0) {
-		(void)write(STDERR_FILENO, wbuf, cnt);
-		cnt = 0;
-	}
-}
-
-/*
- * Write summary to stderr according to format 'fmt'. If 'enable' is 0, it
- * will not attempt to write anything. Can be used to validate the
- * correctness of the 'fmt' string.
- */
-int
-dd_write_msg(const char *fmt, int enable)
-{
-	char hbuf[7], nbuf[32];
-	const char *ptr;
-	int64_t mS;
-	struct timeval tv;
-
-	(void)gettimeofday(&tv, NULL);
-	mS = tv2mS(tv) - tv2mS(st.start);
-	if (mS == 0)
-		mS = 1;
-
-#define ADDC(c) do { if (enable != 0) buffer_write(&c, 1, 0); } \
-	while (/*CONSTCOND*/0)
-#define ADDS(p) do { if (enable != 0) buffer_write(p, strlen(p), 0); } \
-	while (/*CONSTCOND*/0)
-
-	for (ptr = fmt; *ptr; ptr++) {
-		if (*ptr != '%') {
-			ADDC(*ptr);
-			continue;
-		}
-
- 		switch (*++ptr) {
-		case 'b':
-			(void)snprintf(nbuf, sizeof(nbuf), "%llu",
-			    (unsigned long long)st.bytes);
-			ADDS(nbuf);
-			break;
-		case 'B':
-			if (humanize_number(hbuf, sizeof(hbuf),
-			    st.bytes, "B",
-			    HN_AUTOSCALE, HN_DECIMAL) == -1)
-				warnx("humanize_number (bytes transferred)");
-			ADDS(hbuf);
-			break;
-		case 'e':
-			(void)snprintf(nbuf, sizeof(nbuf), "%llu",
-			    (unsigned long long) (st.bytes * 1000LL / mS));
-			ADDS(nbuf);
-			break;
-		case 'E':
-			if (humanize_number(hbuf, sizeof(hbuf),
-			    st.bytes * 1000LL / mS, "B",
-			    HN_AUTOSCALE, HN_DECIMAL) == -1)
-				warnx("humanize_number (bytes per second)");
-			ADDS(hbuf); ADDS("/sec");
-			break;
-		case 'i':
-			(void)snprintf(nbuf, sizeof(nbuf), "%llu",
-			    (unsigned long long)st.in_part);
-			ADDS(nbuf);
-			break;
-		case 'I':
-			(void)snprintf(nbuf, sizeof(nbuf), "%llu",
-			    (unsigned long long)st.in_full);
-			ADDS(nbuf);
-			break;
-		case 'o':
-			(void)snprintf(nbuf, sizeof(nbuf), "%llu",
-			    (unsigned long long)st.out_part);
-			ADDS(nbuf);
-			break;
-		case 'O':
-			(void)snprintf(nbuf, sizeof(nbuf), "%llu",
-			    (unsigned long long)st.out_full);
-			ADDS(nbuf);
-			break;
-		case 's':
-			(void)snprintf(nbuf, sizeof(nbuf), "%li.%03d",
-			    (long) (mS / 1000), (int) (mS % 1000));
-			ADDS(nbuf);
-			break;
-		case 'p':
-			(void)snprintf(nbuf, sizeof(nbuf), "%llu",
-			    (unsigned long long)st.sparse);
-			ADDS(nbuf);
-			break;
-		case 't':
-			(void)snprintf(nbuf, sizeof(nbuf), "%llu",
-			    (unsigned long long)st.trunc);
-			ADDS(nbuf);
-			break;
-		case 'w':
-			(void)snprintf(nbuf, sizeof(nbuf), "%llu",
-			    (unsigned long long)st.swab);
-			ADDS(nbuf);
-			break;
-		case 'P':
-			ADDS("block");
-			if (st.sparse != 1) ADDS("s");
-			break;
-		case 'T':
-			ADDS("block");
-			if (st.trunc != 1) ADDS("s");
-			break;
-		case 'W':
-			ADDS("block");
-			if (st.swab != 1) ADDS("s");
-			break;
-		case '%':
-			ADDC(*ptr);
-			break;
-		default:
-			if (*ptr == '\0')
-				goto done;
-			errx(EXIT_FAILURE, "unknown specifier '%c' in "
-			    "msgfmt string", *ptr);
-			/* NOTREACHED */
-		}
-	}
-
-done:
-	/* flush buffer */
-	buffer_write(NULL, 0, 1);
-	return 0;
-}
-
-static void
-custom_summary(void)
-{
-
-	dd_write_msg(msgfmt, 1);
-}
-
-static void
-human_summary(void)
-{
-	(void)dd_write_msg("%I+%i records in\n%O+%o records out\n", 1);
-	if (st.swab) {
-		(void)dd_write_msg("%w odd length swab %W\n", 1);
-	}
-	if (st.trunc) {
-		(void)dd_write_msg("%t truncated %T\n", 1);
-	}
-	if (st.sparse) {
-		(void)dd_write_msg("%p sparse output %P\n", 1);
-	}
-	(void)dd_write_msg("%b bytes (%B) transferred in %s secs "
-	    "(%e bytes/sec - %E)\n", 1);
-}
-
-static void
-quiet_summary(void)
-{
-
-	/* stay quiet */
-}
-#endif /* NO_MSGFMT */
diff --git a/toolbox/upstream-netbsd/bin/dd/position.c b/toolbox/upstream-netbsd/bin/dd/position.c
deleted file mode 100644
index 36dd580..0000000
--- a/toolbox/upstream-netbsd/bin/dd/position.c
+++ /dev/null
@@ -1,185 +0,0 @@
-/*	$NetBSD: position.c,v 1.18 2010/11/22 21:04:28 pooka Exp $	*/
-
-/*-
- * Copyright (c) 1991, 1993, 1994
- *	The Regents of the University of California.  All rights reserved.
- *
- * This code is derived from software contributed to Berkeley by
- * Keith Muller of the University of California, San Diego and Lance
- * Visser of Convex Computer Corporation.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- * 3. Neither the name of the University nor the names of its contributors
- *    may be used to endorse or promote products derived from this software
- *    without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#include <sys/cdefs.h>
-#ifndef lint
-#if 0
-static char sccsid[] = "@(#)position.c	8.3 (Berkeley) 4/2/94";
-#else
-__RCSID("$NetBSD: position.c,v 1.18 2010/11/22 21:04:28 pooka Exp $");
-#endif
-#endif /* not lint */
-
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <sys/ioctl.h>
-#include <sys/mtio.h>
-#include <sys/time.h>
-
-#include <err.h>
-#include <errno.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-#include "dd.h"
-#include "extern.h"
-
-/*
- * Position input/output data streams before starting the copy.  Device type
- * dependent.  Seekable devices use lseek, and the rest position by reading.
- * Seeking past the end of file can cause null blocks to be written to the
- * output.
- */
-void
-pos_in(void)
-{
-	int bcnt, cnt, nr, warned;
-
-	/* If not a pipe or tape device, try to seek on it. */
-	if (!(in.flags & (ISPIPE|ISTAPE))) {
-		if (ddop_lseek(in, in.fd,
-		    (off_t)in.offset * (off_t)in.dbsz, SEEK_CUR) == -1) {
-			err(EXIT_FAILURE, "%s", in.name);
-			/* NOTREACHED */
-		}
-		return;
-		/* NOTREACHED */
-	}
-
-	/*
-	 * Read the data.  If a pipe, read until satisfy the number of bytes
-	 * being skipped.  No differentiation for reading complete and partial
-	 * blocks for other devices.
-	 */
-	for (bcnt = in.dbsz, cnt = in.offset, warned = 0; cnt;) {
-		if ((nr = ddop_read(in, in.fd, in.db, bcnt)) > 0) {
-			if (in.flags & ISPIPE) {
-				if (!(bcnt -= nr)) {
-					bcnt = in.dbsz;
-					--cnt;
-				}
-			} else
-				--cnt;
-			continue;
-		}
-
-		if (nr == 0) {
-			if (files_cnt > 1) {
-				--files_cnt;
-				continue;
-			}
-			errx(EXIT_FAILURE, "skip reached end of input");
-			/* NOTREACHED */
-		}
-
-		/*
-		 * Input error -- either EOF with no more files, or I/O error.
-		 * If noerror not set die.  POSIX requires that the warning
-		 * message be followed by an I/O display.
-		 */
-		if (ddflags & C_NOERROR) {
-			if (!warned) {
-
-				warn("%s", in.name);
-				warned = 1;
-				summary();
-			}
-			continue;
-		}
-		err(EXIT_FAILURE, "%s", in.name);
-		/* NOTREACHED */
-	}
-}
-
-void
-pos_out(void)
-{
-	struct mtop t_op;
-	int n;
-	uint64_t cnt;
-
-	/*
-	 * If not a tape, try seeking on the file.  Seeking on a pipe is
-	 * going to fail, but don't protect the user -- they shouldn't
-	 * have specified the seek operand.
-	 */
-	if (!(out.flags & ISTAPE)) {
-		if (ddop_lseek(out, out.fd,
-		    (off_t)out.offset * (off_t)out.dbsz, SEEK_SET) == -1)
-			err(EXIT_FAILURE, "%s", out.name);
-			/* NOTREACHED */
-		return;
-	}
-
-	/* If no read access, try using mtio. */
-	if (out.flags & NOREAD) {
-		t_op.mt_op = MTFSR;
-		t_op.mt_count = out.offset;
-
-		if (ddop_ioctl(out, out.fd, MTIOCTOP, &t_op) < 0)
-			err(EXIT_FAILURE, "%s", out.name);
-			/* NOTREACHED */
-		return;
-	}
-
-	/* Read it. */
-	for (cnt = 0; cnt < out.offset; ++cnt) {
-		if ((n = ddop_read(out, out.fd, out.db, out.dbsz)) > 0)
-			continue;
-
-		if (n < 0)
-			err(EXIT_FAILURE, "%s", out.name);
-			/* NOTREACHED */
-
-		/*
-		 * If reach EOF, fill with NUL characters; first, back up over
-		 * the EOF mark.  Note, cnt has not yet been incremented, so
-		 * the EOF read does not count as a seek'd block.
-		 */
-		t_op.mt_op = MTBSR;
-		t_op.mt_count = 1;
-		if (ddop_ioctl(out, out.fd, MTIOCTOP, &t_op) == -1)
-			err(EXIT_FAILURE, "%s", out.name);
-			/* NOTREACHED */
-
-		while (cnt++ < out.offset)
-			if ((uint64_t)(n = bwrite(&out,
-			    out.db, out.dbsz)) != out.dbsz)
-				err(EXIT_FAILURE, "%s", out.name);
-				/* NOTREACHED */
-		break;
-	}
-}
diff --git a/toolbox/upstream-netbsd/include/namespace.h b/toolbox/upstream-netbsd/include/namespace.h
deleted file mode 100644
index e69de29..0000000
--- a/toolbox/upstream-netbsd/include/namespace.h
+++ /dev/null
diff --git a/toolbox/upstream-netbsd/include/sys/extattr.h b/toolbox/upstream-netbsd/include/sys/extattr.h
deleted file mode 100644
index e69de29..0000000
--- a/toolbox/upstream-netbsd/include/sys/extattr.h
+++ /dev/null
diff --git a/toolbox/upstream-netbsd/include/util.h b/toolbox/upstream-netbsd/include/util.h
deleted file mode 100644
index e69de29..0000000
--- a/toolbox/upstream-netbsd/include/util.h
+++ /dev/null
diff --git a/toolbox/upstream-netbsd/lib/libc/gen/getbsize.c b/toolbox/upstream-netbsd/lib/libc/gen/getbsize.c
deleted file mode 100644
index a9ce2c1..0000000
--- a/toolbox/upstream-netbsd/lib/libc/gen/getbsize.c
+++ /dev/null
@@ -1,118 +0,0 @@
-/*	$NetBSD: getbsize.c,v 1.17 2012/06/25 22:32:43 abs Exp $	*/
-
-/*-
- * Copyright (c) 1991, 1993
- *	The Regents of the University of California.  All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- * 3. Neither the name of the University nor the names of its contributors
- *    may be used to endorse or promote products derived from this software
- *    without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#include <sys/cdefs.h>
-#if defined(LIBC_SCCS) && !defined(lint)
-#if 0
-static char sccsid[] = "@(#)getbsize.c	8.1 (Berkeley) 6/4/93";
-#else
-__RCSID("$NetBSD: getbsize.c,v 1.17 2012/06/25 22:32:43 abs Exp $");
-#endif
-#endif /* not lint */
-
-#include "namespace.h"
-
-#include <assert.h>
-#include <err.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#ifdef __weak_alias
-__weak_alias(getbsize,_getbsize)
-#endif
-
-char *
-getbsize(int *headerlenp, long *blocksizep)
-{
-	static char header[20];
-	long n, max, mul, blocksize;
-	char *ep, *p;
-	const char *form;
-
-#define	KB	(1024L)
-#define	MB	(1024L * 1024L)
-#define	GB	(1024L * 1024L * 1024L)
-#define	MAXB	GB		/* No tera, peta, nor exa. */
-	form = "";
-	if ((p = getenv("BLOCKSIZE")) != NULL && *p != '\0') {
-		if ((n = strtol(p, &ep, 10)) < 0)
-			goto underflow;
-		if (n == 0)
-			n = 1;
-		if (*ep && ep[1])
-			goto fmterr;
-		switch (*ep) {
-		case 'G': case 'g':
-			form = "G";
-			max = MAXB / GB;
-			mul = GB;
-			break;
-		case 'K': case 'k':
-			form = "K";
-			max = MAXB / KB;
-			mul = KB;
-			break;
-		case 'M': case 'm':
-			form = "M";
-			max = MAXB / MB;
-			mul = MB;
-			break;
-		case '\0':
-			max = MAXB;
-			mul = 1;
-			break;
-		default:
-fmterr:			warnx("%s: unknown blocksize", p);
-			n = 512;
-			mul = 1;
-			max = 0;
-			break;
-		}
-		if (n > max) {
-			warnx("maximum blocksize is %ldG", MAXB / GB);
-			n = max;
-		}
-		if ((blocksize = n * mul) < 512) {
-underflow:		warnx("%s: minimum blocksize is 512", p);
-			form = "";
-			blocksize = n = 512;
-		}
-	} else
-		blocksize = n = 512;
-
-	if (headerlenp)
-		*headerlenp =
-		    snprintf(header, sizeof(header), "%ld%s-blocks", n, form);
-	if (blocksizep)
-		*blocksizep = blocksize;
-	return (header);
-}
diff --git a/toolbox/upstream-netbsd/lib/libc/gen/humanize_number.c b/toolbox/upstream-netbsd/lib/libc/gen/humanize_number.c
deleted file mode 100644
index 533560f..0000000
--- a/toolbox/upstream-netbsd/lib/libc/gen/humanize_number.c
+++ /dev/null
@@ -1,161 +0,0 @@
-/*	$NetBSD: humanize_number.c,v 1.16 2012/03/17 20:01:14 christos Exp $	*/
-
-/*
- * Copyright (c) 1997, 1998, 1999, 2002 The NetBSD Foundation, Inc.
- * All rights reserved.
- *
- * This code is derived from software contributed to The NetBSD Foundation
- * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility,
- * NASA Ames Research Center, by Luke Mewburn and by Tomas Svensson.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
- * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
- * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
- * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include <sys/cdefs.h>
-#if defined(LIBC_SCCS) && !defined(lint)
-__RCSID("$NetBSD: humanize_number.c,v 1.16 2012/03/17 20:01:14 christos Exp $");
-#endif /* LIBC_SCCS and not lint */
-
-#include "namespace.h"
-#include <assert.h>
-#include <inttypes.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <locale.h>
-
-int
-humanize_number(char *buf, size_t len, int64_t bytes,
-    const char *suffix, int scale, int flags)
-{
-	const char *prefixes, *sep;
-	int	b, r, s1, s2, sign;
-	int64_t	divisor, max, post = 1;
-	size_t	i, baselen, maxscale;
-
-	_DIAGASSERT(buf != NULL);
-	_DIAGASSERT(suffix != NULL);
-	_DIAGASSERT(scale >= 0);
-
-	if (flags & HN_DIVISOR_1000) {
-		/* SI for decimal multiplies */
-		divisor = 1000;
-		if (flags & HN_B)
-			prefixes = "B\0k\0M\0G\0T\0P\0E";
-		else
-			prefixes = "\0\0k\0M\0G\0T\0P\0E";
-	} else {
-		/*
-		 * binary multiplies
-		 * XXX IEC 60027-2 recommends Ki, Mi, Gi...
-		 */
-		divisor = 1024;
-		if (flags & HN_B)
-			prefixes = "B\0K\0M\0G\0T\0P\0E";
-		else
-			prefixes = "\0\0K\0M\0G\0T\0P\0E";
-	}
-
-#define	SCALE2PREFIX(scale)	(&prefixes[(scale) << 1])
-	maxscale = 7;
-
-	if ((size_t)scale >= maxscale &&
-	    (scale & (HN_AUTOSCALE | HN_GETSCALE)) == 0)
-		return (-1);
-
-	if (buf == NULL || suffix == NULL)
-		return (-1);
-
-	if (len > 0)
-		buf[0] = '\0';
-	if (bytes < 0) {
-		sign = -1;
-		baselen = 3;		/* sign, digit, prefix */
-		if (-bytes < INT64_MAX / 100)
-			bytes *= -100;
-		else {
-			bytes = -bytes;
-			post = 100;
-			baselen += 2;
-		}
-	} else {
-		sign = 1;
-		baselen = 2;		/* digit, prefix */
-		if (bytes < INT64_MAX / 100)
-			bytes *= 100;
-		else {
-			post = 100;
-			baselen += 2;
-		}
-	}
-	if (flags & HN_NOSPACE)
-		sep = "";
-	else {
-		sep = " ";
-		baselen++;
-	}
-	baselen += strlen(suffix);
-
-	/* Check if enough room for `x y' + suffix + `\0' */
-	if (len < baselen + 1)
-		return (-1);
-
-	if (scale & (HN_AUTOSCALE | HN_GETSCALE)) {
-		/* See if there is additional columns can be used. */
-		for (max = 100, i = len - baselen; i-- > 0;)
-			max *= 10;
-
-		/*
-		 * Divide the number until it fits the given column.
-		 * If there will be an overflow by the rounding below,
-		 * divide once more.
-		 */
-		for (i = 0; bytes >= max - 50 && i < maxscale; i++)
-			bytes /= divisor;
-
-		if (scale & HN_GETSCALE) {
-			_DIAGASSERT(__type_fit(int, i));
-			return (int)i;
-		}
-	} else
-		for (i = 0; i < (size_t)scale && i < maxscale; i++)
-			bytes /= divisor;
-	bytes *= post;
-
-	/* If a value <= 9.9 after rounding and ... */
-	if (bytes < 995 && i > 0 && flags & HN_DECIMAL) {
-		/* baselen + \0 + .N */
-		if (len < baselen + 1 + 2)
-			return (-1);
-		b = ((int)bytes + 5) / 10;
-		s1 = b / 10;
-		s2 = b % 10;
-		r = snprintf(buf, len, "%d%s%d%s%s%s",
-		    sign * s1, localeconv()->decimal_point, s2,
-		    sep, SCALE2PREFIX(i), suffix);
-	} else
-		r = snprintf(buf, len, "%" PRId64 "%s%s%s",
-		    sign * ((bytes + 50) / 100),
-		    sep, SCALE2PREFIX(i), suffix);
-
-	return (r);
-}
diff --git a/toolbox/upstream-netbsd/lib/libc/stdlib/strsuftoll.c b/toolbox/upstream-netbsd/lib/libc/stdlib/strsuftoll.c
deleted file mode 100644
index 80fc52f..0000000
--- a/toolbox/upstream-netbsd/lib/libc/stdlib/strsuftoll.c
+++ /dev/null
@@ -1,249 +0,0 @@
-/*	$NetBSD: strsuftoll.c,v 1.9 2011/10/22 22:08:47 christos Exp $	*/
-/*-
- * Copyright (c) 2001-2002,2004 The NetBSD Foundation, Inc.
- * All rights reserved.
- *
- * This code is derived from software contributed to The NetBSD Foundation
- * by Luke Mewburn.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
- * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
- * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
- * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
-/*-
- * Copyright (c) 1991, 1993, 1994
- *	The Regents of the University of California.  All rights reserved.
- *
- * This code is derived from software contributed to Berkeley by
- * Keith Muller of the University of California, San Diego and Lance
- * Visser of Convex Computer Corporation.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- * 3. Neither the name of the University nor the names of its contributors
- *    may be used to endorse or promote products derived from this software
- *    without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#if HAVE_NBTOOL_CONFIG_H
-#include "nbtool_config.h"
-#endif
-
-#include <sys/cdefs.h>
-
-#if defined(LIBC_SCCS) && !defined(lint)
-__RCSID("$NetBSD: strsuftoll.c,v 1.9 2011/10/22 22:08:47 christos Exp $");
-#endif /* LIBC_SCCS and not lint */
-
-#ifdef _LIBC
-#include "namespace.h"
-#endif
-
-#if !HAVE_STRSUFTOLL
-
-#include <sys/types.h>
-#include <sys/time.h>
-
-#include <assert.h>
-#include <ctype.h>
-#include <err.h>
-#include <errno.h>
-#include <limits.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#ifdef _LIBC
-# ifdef __weak_alias
-__weak_alias(strsuftoll, _strsuftoll)
-__weak_alias(strsuftollx, _strsuftollx)
-# endif
-#endif /* LIBC */
-
-/*
- * Convert an expression of the following forms to a (u)int64_t.
- * 	1) A positive decimal number.
- *	2) A positive decimal number followed by a b (mult by 512).
- *	3) A positive decimal number followed by a k (mult by 1024).
- *	4) A positive decimal number followed by a m (mult by 1048576).
- *	5) A positive decimal number followed by a g (mult by 1073741824).
- *	6) A positive decimal number followed by a t (mult by 1099511627776).
- *	7) A positive decimal number followed by a w (mult by sizeof int)
- *	8) Two or more positive decimal numbers (with/without k,b or w).
- *	   separated by x (also * for backwards compatibility), specifying
- *	   the product of the indicated values.
- * Returns the result upon successful conversion, or exits with an
- * appropriate error.
- * 
- */
-/* LONGLONG */
-long long
-strsuftoll(const char *desc, const char *val,
-    long long min, long long max)
-{
-	long long result;
-	char	errbuf[100];
-
-	result = strsuftollx(desc, val, min, max, errbuf, sizeof(errbuf));
-	if (*errbuf != '\0')
-		errx(EXIT_FAILURE, "%s", errbuf);
-	return result;
-}
-
-/*
- * As strsuftoll(), but returns the error message into the provided buffer
- * rather than exiting with it.
- */
-/* LONGLONG */
-static long long
-__strsuftollx(const char *desc, const char *val,
-    long long min, long long max, char *ebuf, size_t ebuflen, size_t depth)
-{
-	long long num, t;
-	char	*expr;
-
-	_DIAGASSERT(desc != NULL);
-	_DIAGASSERT(val != NULL);
-	_DIAGASSERT(ebuf != NULL);
-
-	if (depth > 16) {
-		snprintf(ebuf, ebuflen, "%s: Recursion limit exceeded", desc);
-		return 0;
-	}
-
-	while (isspace((unsigned char)*val))	/* Skip leading space */
-		val++;
-
-	errno = 0;
-	num = strtoll(val, &expr, 10);
-	if (errno == ERANGE)
-		goto erange;			/* Overflow */
-
-	if (expr == val)			/* No digits */
-		goto badnum;
-
-	switch (*expr) {
-	case 'b':
-		t = num;
-		num *= 512;			/* 1 block */
-		if (t > num)
-			goto erange;
-		++expr;
-		break;
-	case 'k':
-		t = num;
-		num *= 1024;			/* 1 kibibyte */
-		if (t > num)
-			goto erange;
-		++expr;
-		break;
-	case 'm':
-		t = num;
-		num *= 1048576;			/* 1 mebibyte */
-		if (t > num)
-			goto erange;
-		++expr;
-		break;
-	case 'g':
-		t = num;
-		num *= 1073741824;		/* 1 gibibyte */
-		if (t > num)
-			goto erange;
-		++expr;
-		break;
-	case 't':
-		t = num;
-		num *= 1099511627776LL;		/* 1 tebibyte */
-		if (t > num)
-			goto erange;
-		++expr;
-		break;
-	case 'w':
-		t = num;
-		num *= sizeof(int);		/* 1 word */
-		if (t > num)
-			goto erange;
-		++expr;
-		break;
-	}
-
-	switch (*expr) {
-	case '\0':
-		break;
-	case '*':				/* Backward compatible */
-	case 'x':
-		t = num;
-		num *= __strsuftollx(desc, expr + 1, min, max, ebuf, ebuflen,
-			depth + 1);
-		if (*ebuf != '\0')
-			return 0;
-		if (t > num) {
- erange:	 	
-			errno = ERANGE;
-			snprintf(ebuf, ebuflen, "%s: %s", desc, strerror(errno));
-			return 0;
-		}
-		break;
-	default:
- badnum:
-		snprintf(ebuf, ebuflen, "%s `%s': illegal number", desc, val);
-		return 0;
-	}
-	if (num < min) {
-		/* LONGLONG */
-		snprintf(ebuf, ebuflen, "%s %lld is less than %lld.",
-		    desc, (long long)num, (long long)min);
-		return 0;
-	}
-	if (num > max) {
-		/* LONGLONG */
-		snprintf(ebuf, ebuflen, "%s %lld is greater than %lld.",
-		    desc, (long long)num, (long long)max);
-		return 0;
-	}
-	*ebuf = '\0';
-	return num;
-}
-
-long long
-strsuftollx(const char *desc, const char *val,
-    long long min, long long max, char *ebuf, size_t ebuflen)
-{
-	return __strsuftollx(desc, val, min, max, ebuf, ebuflen, 0);
-}
-#endif /* !HAVE_STRSUFTOLL */
diff --git a/toolbox/upstream-netbsd/lib/libc/string/swab.c b/toolbox/upstream-netbsd/lib/libc/string/swab.c
deleted file mode 100644
index 392b186..0000000
--- a/toolbox/upstream-netbsd/lib/libc/string/swab.c
+++ /dev/null
@@ -1,80 +0,0 @@
-/*	$NetBSD: swab.c,v 1.18 2011/01/04 17:14:07 martin Exp $	*/
-
-/*
- * Copyright (c) 1988, 1993
- *	The Regents of the University of California.  All rights reserved.
- *
- * This code is derived from software contributed to Berkeley by
- * Jeffrey Mogul.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- * 3. Neither the name of the University nor the names of its contributors
- *    may be used to endorse or promote products derived from this software
- *    without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#include <sys/cdefs.h>
-#if defined(LIBC_SCCS) && !defined(lint)
-#if 0
-static char sccsid[] = "@(#)swab.c	8.1 (Berkeley) 6/4/93";
-#else
-__RCSID("$NetBSD: swab.c,v 1.18 2011/01/04 17:14:07 martin Exp $");
-#endif
-#endif /* LIBC_SCCS and not lint */
-
-#include <assert.h>
-#include <unistd.h>
-
-void
-swab(const void * __restrict from, void * __restrict to, ssize_t len)
-{
-	char temp;
-	const char *fp;
-	char *tp;
-
-	if (len <= 1)
-		return;
-
-	_DIAGASSERT(from != NULL);
-	_DIAGASSERT(to != NULL);
-
-	len /= 2;
-	fp = (const char *)from;
-	tp = (char *)to;
-#define	STEP	temp = *fp++,*tp++ = *fp++,*tp++ = temp
-
-	if (__predict_false(len == 1)) {
-		STEP;
-		return;
-	}
-
-	/* round to multiple of 8 */
-	while ((--len % 8) != 0)
-		STEP;
-	len /= 8;
-	if (len == 0)
-		return;
-	while (len-- != 0) {
-		STEP; STEP; STEP; STEP;
-		STEP; STEP; STEP; STEP;
-	}
-}
diff --git a/toolbox/upstream-netbsd/lib/libutil/raise_default_signal.c b/toolbox/upstream-netbsd/lib/libutil/raise_default_signal.c
deleted file mode 100644
index 50cffd4..0000000
--- a/toolbox/upstream-netbsd/lib/libutil/raise_default_signal.c
+++ /dev/null
@@ -1,117 +0,0 @@
-/*	$NetBSD: raise_default_signal.c,v 1.3 2008/04/28 20:23:03 martin Exp $	 */
-
-/*-
- * Copyright (c) 2007 The NetBSD Foundation, Inc.
- * All rights reserved.
- *
- * This code is derived from software contributed to The NetBSD Foundation
- * by Luke Mewburn.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
- * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
- * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
- * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
-
-#if HAVE_NBTOOL_CONFIG_H
-#include "nbtool_config.h"
-#endif
-
-#include <sys/cdefs.h>
-#if defined(LIBC_SCCS) && !defined(lint)
-__RCSID("$NetBSD: raise_default_signal.c,v 1.3 2008/04/28 20:23:03 martin Exp $");
-#endif
-
-#include <errno.h>
-#include <signal.h>
-#include <stdio.h>
-#include <string.h>
-#include <util.h>
-
-#if ! HAVE_RAISE_DEFAULT_SIGNAL
-/*
- * raise_default_signal sig
- *	Raise the default signal handler for sig, by
- *	- block all signals
- *	- set the signal handler to SIG_DFL
- *	- raise the signal
- *	- unblock the signal to deliver it
- *
- *	The original signal mask and signal handler is restored on exit
- *	(whether successful or not).
- *
- *	Returns 0 on success, or -1 on failure with errno set to
- *	on of the values for sigemptyset(), sigaddset(), sigprocmask(),
- *	sigaction(), or raise().
- */
-int
-raise_default_signal(int sig)
-{
-	struct sigaction origact, act;
-	sigset_t origmask, fullmask, mask;
-	int retval, oerrno;
-
-	retval = -1;
-
-		/* Setup data structures */
-		/* XXX memset(3) isn't async-safe according to signal(7) */
-	(void)memset(&act, 0, sizeof(act));
-	act.sa_handler = SIG_DFL;
-	act.sa_flags = 0;
-	if ((sigemptyset(&act.sa_mask) == -1) ||
-	    (sigfillset(&fullmask) == -1) ||
-	    (sigemptyset(&mask) == -1) ||
-	    (sigaddset(&mask, sig) == -1))
-		goto restore_none;
-
-		/* Block all signals */
-	if (sigprocmask(SIG_BLOCK, &fullmask, &origmask) == -1)
-		goto restore_none;
-		/* (use 'goto restore_mask' to restore state) */
-
-		/* Enable the SIG_DFL handler */
-	if (sigaction(sig, &act, &origact) == -1)
-		goto restore_mask;
-		/* (use 'goto restore_act' to restore state) */
-
-		/* Raise the signal, and unblock the signal to deliver it */
-	if ((raise(sig) == -1) ||
-	    (sigprocmask(SIG_UNBLOCK, &mask, NULL) == -1))
-		goto restore_act;
-
-		/* Flag successful raise() */
-	retval = 0;
-
-		/* Restore the original handler */
- restore_act:
-	oerrno = errno;
-	(void)sigaction(sig, &origact, NULL);
-	errno = oerrno;
-
-		/* Restore the original mask */
- restore_mask:
-	oerrno = errno;
-	(void)sigprocmask(SIG_SETMASK, &origmask, NULL);
-	errno = oerrno;
-
- restore_none:
-	return retval;
-}
-
-#endif	/* ! HAVE_RAISE_DEFAULT_SIGNAL */
diff --git a/trusty/Android.bp b/trusty/Android.bp
index 386fbe6..2fb2e19 100644
--- a/trusty/Android.bp
+++ b/trusty/Android.bp
@@ -2,6 +2,5 @@
     "gatekeeper",
     "keymaster",
     "libtrusty",
-    "nvram",
     "storage/*",
 ]
diff --git a/trusty/OWNERS b/trusty/OWNERS
new file mode 100644
index 0000000..e807d71
--- /dev/null
+++ b/trusty/OWNERS
@@ -0,0 +1,7 @@
+arve@google.com
+dkrahn@google.com
+drewry@google.com
+gmar@google.com
+ncbray@google.com
+rpere@google.com
+swillden@google.com
diff --git a/trusty/gatekeeper/Android.bp b/trusty/gatekeeper/Android.bp
index a9566a1..65b271a 100644
--- a/trusty/gatekeeper/Android.bp
+++ b/trusty/gatekeeper/Android.bp
@@ -22,6 +22,7 @@
 
 cc_library_shared {
     name: "gatekeeper.trusty",
+    vendor: true,
 
     relative_install_path: "hw",
 
@@ -43,4 +44,5 @@
         "libcutils",
         "libtrusty",
     ],
+    header_libs: ["libhardware_headers"],
 }
diff --git a/trusty/keymaster/Android.bp b/trusty/keymaster/Android.bp
index 3b6867c..5428e73 100644
--- a/trusty/keymaster/Android.bp
+++ b/trusty/keymaster/Android.bp
@@ -25,15 +25,21 @@
 // and ECDSA keys.
 cc_binary {
     name: "trusty_keymaster_tipc",
+    vendor: true,
     srcs: [
         "trusty_keymaster_device.cpp",
         "trusty_keymaster_ipc.cpp",
         "trusty_keymaster_main.cpp",
     ],
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
     shared_libs: [
         "libcrypto",
         "libcutils",
-        "libkeymaster1",
+        "libkeymaster_portable",
+        "libkeymaster_staging",
         "libtrusty",
         "libkeymaster_messages",
         "libsoftkeymasterdevice",
@@ -44,6 +50,7 @@
 // keystore.trusty is the HAL used by keystore on Trusty devices.
 cc_library_shared {
     name: "keystore.trusty",
+    vendor: true,
     relative_install_path: "hw",
     srcs: [
         "module.cpp",
@@ -64,4 +71,5 @@
         "liblog",
         "libcutils",
     ],
+    header_libs: ["libhardware_headers"],
 }
diff --git a/trusty/keymaster/keymaster_ipc.h b/trusty/keymaster/keymaster_ipc.h
index b38eb05..d63757b 100644
--- a/trusty/keymaster/keymaster_ipc.h
+++ b/trusty/keymaster/keymaster_ipc.h
@@ -24,7 +24,8 @@
 // Commands
 enum keymaster_command : uint32_t {
     KEYMASTER_RESP_BIT              = 1,
-    KEYMASTER_REQ_SHIFT             = 1,
+    KEYMASTER_STOP_BIT              = 2,
+    KEYMASTER_REQ_SHIFT             = 2,
 
     KM_GENERATE_KEY                 = (0 << KEYMASTER_REQ_SHIFT),
     KM_BEGIN_OPERATION              = (1 << KEYMASTER_REQ_SHIFT),
diff --git a/trusty/keymaster/trusty_keymaster_device.cpp b/trusty/keymaster/trusty_keymaster_device.cpp
index 5f16fd0..b8c2032 100644
--- a/trusty/keymaster/trusty_keymaster_device.cpp
+++ b/trusty/keymaster/trusty_keymaster_device.cpp
@@ -17,6 +17,7 @@
 #define LOG_TAG "TrustyKeymaster"
 
 #include <assert.h>
+#include <errno.h>
 #include <openssl/evp.h>
 #include <openssl/x509.h>
 #include <stddef.h>
@@ -36,7 +37,8 @@
 #include "trusty_keymaster_device.h"
 #include "trusty_keymaster_ipc.h"
 
-const uint32_t RECV_BUF_SIZE = PAGE_SIZE;
+// Maximum size of message from Trusty is 8K (for RSA attestation key and chain)
+const uint32_t RECV_BUF_SIZE = 2*PAGE_SIZE;
 const uint32_t SEND_BUF_SIZE = (PAGE_SIZE - sizeof(struct keymaster_message) - 16 /* tipc header */);
 
 const size_t kMaximumAttestationChallengeLength = 128;
@@ -176,14 +178,14 @@
     }
 
     AuthorizationSet params_copy(*params);
-    ConfigureRequest request;
+    ConfigureRequest request(message_version_);
     if (!params_copy.GetTagValue(TAG_OS_VERSION, &request.os_version) ||
         !params_copy.GetTagValue(TAG_OS_PATCHLEVEL, &request.os_patchlevel)) {
         ALOGD("Configuration parameters must contain OS version and patch level");
         return KM_ERROR_INVALID_ARGUMENT;
     }
 
-    ConfigureResponse response;
+    ConfigureResponse response(message_version_);
     keymaster_error_t err = Send(KM_CONFIGURE, request, &response);
     if (err != KM_ERROR_OK) {
         return err;
@@ -199,9 +201,9 @@
         return error_;
     }
 
-    AddEntropyRequest request;
+    AddEntropyRequest request(message_version_);
     request.random_data.Reinitialize(data, data_length);
-    AddEntropyResponse response;
+    AddEntropyResponse response(message_version_);
     return Send(KM_ADD_RNG_ENTROPY, request, &response);
 }
 
@@ -260,11 +262,11 @@
         return KM_ERROR_OUTPUT_PARAMETER_NULL;
     }
 
-    GetKeyCharacteristicsRequest request;
+    GetKeyCharacteristicsRequest request(message_version_);
     request.SetKeyMaterial(*key_blob);
     AddClientAndAppData(client_id, app_data, &request);
 
-    GetKeyCharacteristicsResponse response;
+    GetKeyCharacteristicsResponse response(message_version_);
     keymaster_error_t err = Send(KM_GET_KEY_CHARACTERISTICS, request, &response);
     if (err != KM_ERROR_OK) {
         return err;
@@ -378,7 +380,7 @@
     cert_chain->entry_count = 0;
     cert_chain->entries = nullptr;
 
-    AttestKeyRequest request;
+    AttestKeyRequest request(message_version_);
     request.SetKeyMaterial(*key_to_attest);
     request.attest_params.Reinitialize(*attest_params);
 
@@ -390,7 +392,7 @@
         return KM_ERROR_INVALID_INPUT_LENGTH;
     }
 
-    AttestKeyResponse response;
+    AttestKeyResponse response(message_version_);
     keymaster_error_t err = Send(KM_ATTEST_KEY, request, &response);
     if (err != KM_ERROR_OK) {
         return err;
@@ -438,11 +440,11 @@
         return KM_ERROR_OUTPUT_PARAMETER_NULL;
     }
 
-    UpgradeKeyRequest request;
+    UpgradeKeyRequest request(message_version_);
     request.SetKeyMaterial(*key_to_upgrade);
     request.upgrade_params.Reinitialize(*upgrade_params);
 
-    UpgradeKeyResponse response;
+    UpgradeKeyResponse response(message_version_);
     keymaster_error_t err = Send(KM_UPGRADE_KEY, request, &response);
     if (err != KM_ERROR_OK) {
         return err;
@@ -479,12 +481,12 @@
         *out_params = {};
     }
 
-    BeginOperationRequest request;
+    BeginOperationRequest request(message_version_);
     request.purpose = purpose;
     request.SetKeyMaterial(*key);
     request.additional_params.Reinitialize(*in_params);
 
-    BeginOperationResponse response;
+    BeginOperationResponse response(message_version_);
     keymaster_error_t err = Send(KM_BEGIN_OPERATION, request, &response);
     if (err != KM_ERROR_OK) {
         return err;
@@ -527,7 +529,7 @@
         *output = {};
     }
 
-    UpdateOperationRequest request;
+    UpdateOperationRequest request(message_version_);
     request.op_handle = operation_handle;
     if (in_params) {
         request.additional_params.Reinitialize(*in_params);
@@ -537,7 +539,7 @@
         request.input.Reinitialize(input->data, std::min(input->data_length, max_input_size));
     }
 
-    UpdateOperationResponse response;
+    UpdateOperationResponse response(message_version_);
     keymaster_error_t err = Send(KM_UPDATE_OPERATION, request, &response);
     if (err != KM_ERROR_OK) {
         return err;
@@ -576,7 +578,9 @@
         return error_;
     }
     if (input && input->data_length > kMaximumFinishInputLength) {
-        return KM_ERROR_INVALID_ARGUMENT;
+        ALOGE("%zu-byte input to finish; only %zu bytes allowed",
+              input->data_length, kMaximumFinishInputLength);
+        return KM_ERROR_INVALID_INPUT_LENGTH;
     }
 
     if (out_params) {
@@ -586,7 +590,7 @@
         *output = {};
     }
 
-    FinishOperationRequest request;
+    FinishOperationRequest request(message_version_);
     request.op_handle = operation_handle;
     if (signature && signature->data && signature->data_length > 0) {
         request.signature.Reinitialize(signature->data, signature->data_length);
@@ -598,7 +602,7 @@
         request.additional_params.Reinitialize(*in_params);
     }
 
-    FinishOperationResponse response;
+    FinishOperationResponse response(message_version_);
     keymaster_error_t err = Send(KM_FINISH_OPERATION, request, &response);
     if (err != KM_ERROR_OK) {
         return err;
@@ -631,9 +635,9 @@
         return error_;
     }
 
-    AbortOperationRequest request;
+    AbortOperationRequest request(message_version_);
     request.op_handle = operation_handle;
-    AbortOperationResponse response;
+    AbortOperationResponse response(message_version_);
     return Send(KM_ABORT_OPERATION, request, &response);
 }
 
@@ -768,6 +772,9 @@
     ALOGV("Sending %d byte request\n", (int)req.SerializedSize());
     int rc = trusty_keymaster_call(command, send_buf, req_size, recv_buf, &rsp_size);
     if (rc < 0) {
+        // Reset the connection on tipc error
+        trusty_keymaster_disconnect();
+        trusty_keymaster_connect();
         ALOGE("tipc error: %d\n", rc);
         // TODO(swillden): Distinguish permanent from transient errors and set error_ appropriately.
         return translate_error(rc);
@@ -775,8 +782,7 @@
         ALOGV("Received %d byte response\n", rsp_size);
     }
 
-    const keymaster_message* msg = (keymaster_message*)recv_buf;
-    const uint8_t* p = msg->payload;
+    const uint8_t* p = recv_buf;
     if (!rsp->Deserialize(&p, p + rsp_size)) {
         ALOGE("Error deserializing response of size %d\n", (int)rsp_size);
         return KM_ERROR_UNKNOWN_ERROR;
diff --git a/trusty/keymaster/trusty_keymaster_device_test.cpp b/trusty/keymaster/trusty_keymaster_device_test.cpp
index 3bb5430..9227964 100644
--- a/trusty/keymaster/trusty_keymaster_device_test.cpp
+++ b/trusty/keymaster/trusty_keymaster_device_test.cpp
@@ -15,8 +15,8 @@
  */
 #include <algorithm>
 #include <fstream>
+#include <memory>
 
-#include <UniquePtr.h>
 #include <gtest/gtest.h>
 #include <openssl/engine.h>
 
@@ -181,7 +181,7 @@
 
     keymaster_rsa_sign_params_t sig_params = {DIGEST_NONE, PADDING_NONE};
     size_t message_len = params.modulus_size / 8;
-    UniquePtr<uint8_t[]> message(build_message(message_len));
+    std::unique_ptr<uint8_t[]> message(build_message(message_len));
     uint8_t* signature;
     size_t siglen;
     EXPECT_EQ(KM_ERROR_OK, device.sign_data(&sig_params, ptr, size, message.get(), message_len,
@@ -200,7 +200,7 @@
 
     keymaster_rsa_sign_params_t sig_params = {DIGEST_NONE, PADDING_NONE};
     size_t message_len = params.modulus_size / 8 - 1;
-    UniquePtr<uint8_t[]> message(build_message(message_len));
+    std::unique_ptr<uint8_t[]> message(build_message(message_len));
     uint8_t* signature;
     size_t siglen;
     EXPECT_EQ(KM_ERROR_UNKNOWN_ERROR, device.sign_data(&sig_params, ptr, size, message.get(),
@@ -217,7 +217,7 @@
 
     keymaster_rsa_sign_params_t sig_params = {DIGEST_NONE, PADDING_NONE};
     size_t message_len = params.modulus_size / 8 + 1;
-    UniquePtr<uint8_t[]> message(build_message(message_len));
+    std::unique_ptr<uint8_t[]> message(build_message(message_len));
     uint8_t* signature;
     size_t siglen;
     EXPECT_EQ(KM_ERROR_UNKNOWN_ERROR, device.sign_data(&sig_params, ptr, size, message.get(),
@@ -272,7 +272,7 @@
 
     keymaster_ec_sign_params_t sig_params = {DIGEST_NONE};
     size_t message_len = 1024 * 7;
-    UniquePtr<uint8_t[]> message(new uint8_t[message_len]);
+    std::unique_ptr<uint8_t[]> message(new uint8_t[message_len]);
     // contents of message don't matter.
     uint8_t* signature;
     size_t siglen;
@@ -294,7 +294,7 @@
 
     keymaster_rsa_sign_params_t sig_params = {DIGEST_NONE, PADDING_NONE};
     size_t message_len = params.modulus_size / 8;
-    UniquePtr<uint8_t[]> message(build_message(message_len));
+    std::unique_ptr<uint8_t[]> message(build_message(message_len));
     uint8_t* signature;
     size_t siglen;
     EXPECT_EQ(KM_ERROR_OK, device.sign_data(&sig_params, ptr, size, message.get(), message_len,
@@ -315,7 +315,7 @@
 
     keymaster_rsa_sign_params_t sig_params = {DIGEST_NONE, PADDING_NONE};
     size_t message_len = params.modulus_size / 8;
-    UniquePtr<uint8_t[]> message(build_message(message_len));
+    std::unique_ptr<uint8_t[]> message(build_message(message_len));
     uint8_t* signature;
     size_t siglen;
     EXPECT_EQ(KM_ERROR_OK, device.sign_data(&sig_params, ptr, size, message.get(), message_len,
@@ -338,7 +338,7 @@
 
     keymaster_rsa_sign_params_t sig_params = {DIGEST_NONE, PADDING_NONE};
     size_t message_len = params.modulus_size / 8;
-    UniquePtr<uint8_t[]> message(build_message(message_len));
+    std::unique_ptr<uint8_t[]> message(build_message(message_len));
     uint8_t* signature;
     size_t siglen;
     EXPECT_EQ(KM_ERROR_OK, device.sign_data(&sig_params, ptr, size, message.get(), message_len,
@@ -360,7 +360,7 @@
 
     keymaster_rsa_sign_params_t sig_params = {DIGEST_NONE, PADDING_NONE};
     size_t message_len = params.modulus_size / 8;
-    UniquePtr<uint8_t[]> message(build_message(message_len));
+    std::unique_ptr<uint8_t[]> message(build_message(message_len));
     uint8_t* signature;
     size_t siglen;
     EXPECT_EQ(KM_ERROR_OK, device.sign_data(&sig_params, ptr, size, message.get(), message_len,
@@ -382,7 +382,7 @@
 
     keymaster_rsa_sign_params_t sig_params = {DIGEST_NONE, PADDING_NONE};
     size_t message_len = params.modulus_size / 8;
-    UniquePtr<uint8_t[]> message(build_message(message_len + 1));
+    std::unique_ptr<uint8_t[]> message(build_message(message_len + 1));
     uint8_t* signature;
     size_t siglen;
     EXPECT_EQ(KM_ERROR_OK, device.sign_data(&sig_params, ptr, size, message.get(), message_len,
@@ -422,7 +422,7 @@
 
     keymaster_ec_sign_params_t sig_params = {DIGEST_NONE};
     size_t message_len = 1024 * 7;
-    UniquePtr<uint8_t[]> message(new uint8_t[message_len]);
+    std::unique_ptr<uint8_t[]> message(new uint8_t[message_len]);
     // contents of message don't matter.
     uint8_t* signature;
     size_t siglen;
@@ -453,7 +453,7 @@
 
     keymaster_rsa_sign_params_t sig_params = {DIGEST_NONE, PADDING_NONE};
     size_t message_size = 1024 /* key size */ / 8;
-    UniquePtr<uint8_t[]> message(new uint8_t[message_size]);
+    std::unique_ptr<uint8_t[]> message(new uint8_t[message_size]);
     memset(message.get(), 'a', message_size);
     uint8_t* signature;
     size_t siglen;
@@ -491,9 +491,9 @@
 
 static void VerifySignature(const uint8_t* key, size_t key_len, const uint8_t* signature,
                             size_t signature_len, const uint8_t* message, size_t message_len) {
-    UniquePtr<EVP_PKEY, EVP_PKEY_Delete> pkey(d2i_PUBKEY(NULL, &key, key_len));
+    std::unique_ptr<EVP_PKEY, EVP_PKEY_Delete> pkey(d2i_PUBKEY(NULL, &key, key_len));
     ASSERT_TRUE(pkey.get() != NULL);
-    UniquePtr<EVP_PKEY_CTX, EVP_PKEY_CTX_Delete> ctx(EVP_PKEY_CTX_new(pkey.get(), NULL));
+    std::unique_ptr<EVP_PKEY_CTX, EVP_PKEY_CTX_Delete> ctx(EVP_PKEY_CTX_new(pkey.get(), NULL));
     ASSERT_TRUE(ctx.get() != NULL);
     ASSERT_EQ(1, EVP_PKEY_verify_init(ctx.get()));
     if (EVP_PKEY_type(pkey->type) == EVP_PKEY_RSA)
@@ -518,7 +518,7 @@
     // Sign a message so we can verify it with the exported pubkey.
     keymaster_rsa_sign_params_t sig_params = {DIGEST_NONE, PADDING_NONE};
     size_t message_len = params.modulus_size / 8;
-    UniquePtr<uint8_t[]> message(build_message(message_len));
+    std::unique_ptr<uint8_t[]> message(build_message(message_len));
     uint8_t* signature;
     size_t siglen;
     EXPECT_EQ(KM_ERROR_OK, device.sign_data(&sig_params, ptr, size, message.get(), message_len,
diff --git a/trusty/keymaster/trusty_keymaster_ipc.cpp b/trusty/keymaster/trusty_keymaster_ipc.cpp
index cdc2778..686e7ae 100644
--- a/trusty/keymaster/trusty_keymaster_ipc.cpp
+++ b/trusty/keymaster/trusty_keymaster_ipc.cpp
@@ -21,8 +21,11 @@
 #include <errno.h>
 #include <stdlib.h>
 #include <string.h>
+#include <sys/uio.h>
 #include <unistd.h>
 
+#include <algorithm>
+
 #include <log/log.h>
 #include <trusty/tipc.h>
 
@@ -31,7 +34,7 @@
 
 #define TRUSTY_DEVICE_NAME "/dev/trusty-ipc-dev0"
 
-static int handle_ = 0;
+static int handle_ = -1;
 
 int trusty_keymaster_connect() {
     int rc = tipc_connect(TRUSTY_DEVICE_NAME, KEYMASTER_PORT);
@@ -45,13 +48,18 @@
 
 int trusty_keymaster_call(uint32_t cmd, void* in, uint32_t in_size, uint8_t* out,
                           uint32_t* out_size) {
-    if (handle_ == 0) {
+    if (handle_ < 0) {
         ALOGE("not connected\n");
         return -EINVAL;
     }
 
     size_t msg_size = in_size + sizeof(struct keymaster_message);
     struct keymaster_message* msg = reinterpret_cast<struct keymaster_message*>(malloc(msg_size));
+    if (!msg) {
+        ALOGE("failed to allocate msg buffer\n");
+        return -EINVAL;
+    }
+
     msg->cmd = cmd;
     memcpy(msg->payload, in, in_size);
 
@@ -62,32 +70,43 @@
         ALOGE("failed to send cmd (%d) to %s: %s\n", cmd, KEYMASTER_PORT, strerror(errno));
         return -errno;
     }
+    size_t out_max_size = *out_size;
+    *out_size = 0;
+    struct iovec iov[2];
+    struct keymaster_message header;
+    iov[0] = {.iov_base = &header, .iov_len = sizeof(struct keymaster_message)};
+    while (true) {
+        iov[1] = {
+            .iov_base = out + *out_size,
+            .iov_len = std::min<uint32_t>(KEYMASTER_MAX_BUFFER_LENGTH, out_max_size - *out_size)};
+        rc = readv(handle_, iov, 2);
+        if (rc < 0) {
+            ALOGE("failed to retrieve response for cmd (%d) to %s: %s\n", cmd, KEYMASTER_PORT,
+                  strerror(errno));
+            return -errno;
+        }
 
-    rc = read(handle_, out, *out_size);
-    if (rc < 0) {
-        ALOGE("failed to retrieve response for cmd (%d) to %s: %s\n", cmd, KEYMASTER_PORT,
-              strerror(errno));
-        return -errno;
+        if ((size_t)rc < sizeof(struct keymaster_message)) {
+            ALOGE("invalid response size (%d)\n", (int)rc);
+            return -EINVAL;
+        }
+
+        if ((cmd | KEYMASTER_RESP_BIT) != (header.cmd & ~(KEYMASTER_STOP_BIT))) {
+            ALOGE("invalid command (%d)", header.cmd);
+            return -EINVAL;
+        }
+        *out_size += ((size_t)rc - sizeof(struct keymaster_message));
+        if (header.cmd & KEYMASTER_STOP_BIT) {
+            break;
+        }
     }
 
-    if ((size_t)rc < sizeof(struct keymaster_message)) {
-        ALOGE("invalid response size (%d)\n", (int)rc);
-        return -EINVAL;
-    }
-
-    msg = (struct keymaster_message*)out;
-
-    if ((cmd | KEYMASTER_RESP_BIT) != msg->cmd) {
-        ALOGE("invalid command (%d)", msg->cmd);
-        return -EINVAL;
-    }
-
-    *out_size = ((size_t)rc) - sizeof(struct keymaster_message);
     return rc;
 }
 
 void trusty_keymaster_disconnect() {
-    if (handle_ != 0) {
+    if (handle_ >= 0) {
         tipc_close(handle_);
     }
+    handle_ = -1;
 }
diff --git a/trusty/keymaster/trusty_keymaster_main.cpp b/trusty/keymaster/trusty_keymaster_main.cpp
index 9c2ae2d..ed78b7f 100644
--- a/trusty/keymaster/trusty_keymaster_main.cpp
+++ b/trusty/keymaster/trusty_keymaster_main.cpp
@@ -289,7 +289,6 @@
     std::unique_ptr<const uint8_t[]> deleter(key.key_material);
 
     printf("=== Signing with imported ECDSA key ===\n");
-    keymaster_ec_sign_params_t sign_params = {DIGEST_NONE};
     size_t message_len = 30 /* arbitrary */;
     std::unique_ptr<uint8_t[]> message(new uint8_t[message_len]);
     memset(message.get(), 'a', message_len);
diff --git a/trusty/libtrusty/Android.bp b/trusty/libtrusty/Android.bp
index f316da2..f6e9bee 100644
--- a/trusty/libtrusty/Android.bp
+++ b/trusty/libtrusty/Android.bp
@@ -18,9 +18,14 @@
 
 cc_library {
     name: "libtrusty",
+    vendor: true,
 
     srcs: ["trusty.c"],
     export_include_dirs: ["include"],
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
 
     shared_libs: ["liblog"],
 }
diff --git a/trusty/libtrusty/tipc-test/Android.bp b/trusty/libtrusty/tipc-test/Android.bp
index cb00fe7..32499e3 100644
--- a/trusty/libtrusty/tipc-test/Android.bp
+++ b/trusty/libtrusty/tipc-test/Android.bp
@@ -14,13 +14,19 @@
 
 cc_test {
     name: "tipc-test",
-    static_executable: true,
+    vendor: true,
 
     srcs: ["tipc_test.c"],
     static_libs: [
-        "libc",
         "libtrusty",
+    ],
+    shared_libs: [
+        "libc",
         "liblog",
     ],
     gtest: false,
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
 }
diff --git a/trusty/libtrusty/tipc-test/tipc_test.c b/trusty/libtrusty/tipc-test/tipc_test.c
index 1fb34c9..d20d4ee 100644
--- a/trusty/libtrusty/tipc-test/tipc_test.c
+++ b/trusty/libtrusty/tipc-test/tipc_test.c
@@ -587,8 +587,15 @@
 
 static int ta2ta_ipc_test(void)
 {
+	enum test_message_header {
+		TEST_PASSED = 0,
+		TEST_FAILED = 1,
+		TEST_MESSAGE = 2,
+	};
+
 	int fd;
-	char rx_buf[64];
+	int ret;
+	unsigned char rx_buf[256];
 
 	if (!opt_silent) {
 		printf("%s:\n", __func__);
@@ -601,12 +608,31 @@
 		return fd;
 	}
 
-	/* wait for test to complete */
-	(void) read(fd, rx_buf, sizeof(rx_buf));
+	/* Wait for tests to complete and read status */
+	while (true) {
+		ret = read(fd, rx_buf, sizeof(rx_buf));
+		if (ret <= 0 || ret >= (int)sizeof(rx_buf)) {
+			fprintf(stderr, "%s: Read failed: %d\n", __func__, ret);
+			tipc_close(fd);
+			return -1;
+		}
+
+		if (rx_buf[0] == TEST_PASSED) {
+			break;
+		} else if (rx_buf[0] == TEST_FAILED) {
+			break;
+		} else if (rx_buf[0] == TEST_MESSAGE) {
+			write(STDOUT_FILENO, rx_buf + 1, ret - 1);
+		} else {
+			fprintf(stderr, "%s: Bad message header: %d\n",
+			        __func__, rx_buf[0]);
+			break;
+		}
+	}
 
 	tipc_close(fd);
 
-	return 0;
+	return rx_buf[0] == TEST_PASSED ? 0 : -1;
 }
 
 typedef struct uuid
diff --git a/trusty/nvram/Android.bp b/trusty/nvram/Android.bp
deleted file mode 100644
index 15e6c3e..0000000
--- a/trusty/nvram/Android.bp
+++ /dev/null
@@ -1,61 +0,0 @@
-//
-// Copyright (C) 2016 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.
-//
-
-// nvram.trusty is the Trusty NVRAM HAL module.
-cc_library_shared {
-    name: "nvram.trusty",
-    relative_install_path: "hw",
-    srcs: [
-        "module.c",
-        "trusty_nvram_device.cpp",
-        "trusty_nvram_implementation.cpp",
-    ],
-
-    cflags: [
-        "-Wall",
-        "-Werror",
-        "-Wextra",
-        "-fvisibility=hidden",
-    ],
-    static_libs: ["libnvram-hal"],
-    shared_libs: [
-        "libtrusty",
-        "libnvram-messages",
-        "liblog",
-    ],
-}
-
-// nvram-wipe is a helper tool for clearing NVRAM state.
-cc_binary {
-    name: "nvram-wipe",
-    srcs: [
-        "nvram_wipe.cpp",
-        "trusty_nvram_implementation.cpp",
-    ],
-
-    cflags: [
-        "-Wall",
-        "-Werror",
-        "-Wextra",
-        "-fvisibility=hidden",
-    ],
-    static_libs: ["libnvram-hal"],
-    shared_libs: [
-        "libtrusty",
-        "libnvram-messages",
-        "liblog",
-    ],
-}
diff --git a/trusty/nvram/module.c b/trusty/nvram/module.c
deleted file mode 100644
index a2e64d3..0000000
--- a/trusty/nvram/module.c
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright (C) 2016 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 <hardware/nvram.h>
-
-// This function is defined in trusty_nvram_device.cpp.
-int trusty_nvram_open(const hw_module_t* module,
-                      const char* device_id,
-                      hw_device_t** device_ptr);
-
-static struct hw_module_methods_t nvram_module_methods = {
-    .open = trusty_nvram_open,
-};
-
-struct nvram_module HAL_MODULE_INFO_SYM
-    __attribute__((visibility("default"))) = {
-        .common = {.tag = HARDWARE_MODULE_TAG,
-                   .module_api_version = NVRAM_MODULE_API_VERSION_0_1,
-                   .hal_api_version = HARDWARE_HAL_API_VERSION,
-                   .id = NVRAM_HARDWARE_MODULE_ID,
-                   .name = "Trusty NVRAM HAL",
-                   .author = "The Android Open Source Project",
-                   .methods = &nvram_module_methods,
-                   .dso = 0,
-                   .reserved = {}},
-};
diff --git a/trusty/nvram/nvram_wipe.cpp b/trusty/nvram/nvram_wipe.cpp
deleted file mode 100644
index d0f4fad..0000000
--- a/trusty/nvram/nvram_wipe.cpp
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright (C) 2016 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 <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include <nvram/messages/nvram_messages.h>
-
-#include "trusty_nvram_implementation.h"
-
-void usage(const char* program_name) {
-  fprintf(stderr, "Usage: %s [status|disable|wipe]\n", program_name);
-  exit(-1);
-}
-
-int main(int argc, char* argv[]) {
-  if (argc < 2) {
-    usage(argv[0]);
-  }
-
-  nvram::TrustyNvramImplementation nvram_proxy;
-  nvram::Request request;
-  nvram::Response response;
-
-  if (!strcmp(argv[1], "status")) {
-    request.payload.Activate<nvram::COMMAND_GET_INFO>();
-    nvram_proxy.Execute(request, &response);
-    const nvram::GetInfoResponse* get_info_response =
-        response.payload.get<nvram::COMMAND_GET_INFO>();
-    if (response.result == NV_RESULT_SUCCESS) {
-      int status = get_info_response && get_info_response->wipe_disabled;
-      printf("Wiping disabled: %d\n", status);
-      return status;
-    }
-  } else if (!strcmp(argv[1], "disable")) {
-    request.payload.Activate<nvram::COMMAND_DISABLE_WIPE>();
-    nvram_proxy.Execute(request, &response);
-  } else if (!strcmp(argv[1], "wipe")) {
-    request.payload.Activate<nvram::COMMAND_WIPE_STORAGE>();
-    nvram_proxy.Execute(request, &response);
-  } else {
-    usage(argv[0]);
-  }
-
-  if (response.result != NV_RESULT_SUCCESS) {
-    fprintf(stderr, "Command execution failure: %u\n", response.result);
-    return -1;
-  }
-
-  return 0;
-}
-
diff --git a/trusty/nvram/trusty_nvram_device.cpp b/trusty/nvram/trusty_nvram_device.cpp
deleted file mode 100644
index 2c50915..0000000
--- a/trusty/nvram/trusty_nvram_device.cpp
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright (C) 2016 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 <nvram/hal/nvram_device_adapter.h>
-
-#include "trusty_nvram_implementation.h"
-
-extern "C" int trusty_nvram_open(const hw_module_t* module,
-                                 const char* device_id,
-                                 hw_device_t** device_ptr) {
-  if (strcmp(NVRAM_HARDWARE_DEVICE_ID, device_id) != 0) {
-    return -EINVAL;
-  }
-
-  nvram::NvramDeviceAdapter* adapter = new nvram::NvramDeviceAdapter(
-      module, new nvram::TrustyNvramImplementation);
-  *device_ptr = adapter->as_device();
-  return 0;
-}
diff --git a/trusty/nvram/trusty_nvram_implementation.cpp b/trusty/nvram/trusty_nvram_implementation.cpp
deleted file mode 100644
index 9215c85..0000000
--- a/trusty/nvram/trusty_nvram_implementation.cpp
+++ /dev/null
@@ -1,113 +0,0 @@
-/*
- * Copyright (C) 2016 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.
- */
-
-#define LOG_TAG "TrustyNVRAM"
-
-#include "trusty_nvram_implementation.h"
-
-#include <errno.h>
-#include <string.h>
-#include <unistd.h>
-
-#include <hardware/nvram.h>
-#include <log/log.h>
-#include <trusty/tipc.h>
-
-#include <nvram/messages/blob.h>
-
-namespace nvram {
-namespace {
-
-// Character device to open for Trusty IPC connections.
-const char kTrustyDeviceName[] = "/dev/trusty-ipc-dev0";
-
-// App identifier of the NVRAM app.
-const char kTrustyNvramAppId[] = "com.android.trusty.nvram";
-
-}  // namespace
-
-TrustyNvramImplementation::~TrustyNvramImplementation() {
-  if (tipc_nvram_fd_ != -1) {
-    tipc_close(tipc_nvram_fd_);
-    tipc_nvram_fd_ = -1;
-  }
-}
-
-void TrustyNvramImplementation::Execute(const nvram::Request& request,
-                                        nvram::Response* response) {
-  if (!SendRequest(request, response)) {
-    response->result = NV_RESULT_INTERNAL_ERROR;
-  }
-}
-
-bool TrustyNvramImplementation::Connect() {
-  if (tipc_nvram_fd_ != -1) {
-    return true;
-  }
-
-  int rc = tipc_connect(kTrustyDeviceName, kTrustyNvramAppId);
-  if (rc < 0) {
-    ALOGE("Failed to connect to Trusty NVRAM app: %s\n", strerror(-rc));
-    return false;
-  }
-
-  tipc_nvram_fd_ = rc;
-  return true;
-}
-
-bool TrustyNvramImplementation::SendRequest(const nvram::Request& request,
-                                            nvram::Response* response) {
-  if (!Connect()) {
-    return false;
-  }
-
-  nvram::Blob request_buffer;
-  if (!nvram::Encode(request, &request_buffer)) {
-    ALOGE("Failed to encode NVRAM request.\n");
-    return false;
-  }
-
-  ssize_t rc =
-      write(tipc_nvram_fd_, request_buffer.data(), request_buffer.size());
-  if (rc < 0) {
-    ALOGE("Failed to send NVRAM request: %s\n", strerror(-rc));
-    return false;
-  }
-  if (static_cast<size_t>(rc) != request_buffer.size()) {
-    ALOGE("Failed to send full request buffer: %zd\n", rc);
-    return false;
-  }
-
-  rc = read(tipc_nvram_fd_, response_buffer_, sizeof(response_buffer_));
-  if (rc < 0) {
-    ALOGE("Failed to read NVRAM response: %s\n", strerror(-rc));
-    return false;
-  }
-
-  if (static_cast<size_t>(rc) >= sizeof(response_buffer_)) {
-    ALOGE("NVRAM response exceeds response buffer size.\n");
-    return false;
-  }
-
-  if (!nvram::Decode(response_buffer_, static_cast<size_t>(rc), response)) {
-    ALOGE("Failed to decode NVRAM response.\n");
-    return false;
-  }
-
-  return true;
-}
-
-}  // namespace nvram
diff --git a/trusty/nvram/trusty_nvram_implementation.h b/trusty/nvram/trusty_nvram_implementation.h
deleted file mode 100644
index 60758f7..0000000
--- a/trusty/nvram/trusty_nvram_implementation.h
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Copyright (C) 2016 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 TRUSTY_NVRAM_TRUSTY_NVRAM_IMPLEMENTATION_H_
-#define TRUSTY_NVRAM_TRUSTY_NVRAM_IMPLEMENTATION_H_
-
-#include <stdint.h>
-
-#include <nvram/hal/nvram_device_adapter.h>
-#include <nvram/messages/nvram_messages.h>
-
-namespace nvram {
-
-// |TrustyNvramImplementation| proxies requests to the Trusty NVRAM app. It
-// serializes the request objects, sends it to the Trusty app and finally reads
-// back the result and decodes it.
-class TrustyNvramImplementation : public nvram::NvramImplementation {
- public:
-  ~TrustyNvramImplementation() override;
-
-  void Execute(const nvram::Request& request,
-               nvram::Response* response) override;
-
- private:
-  // Connects the IPC channel to the Trusty app if it is not already open.
-  // Returns true if the channel is open, false on errors.
-  bool Connect();
-
-  // Dispatches a command to the trust app. Returns true if successful (note
-  // that the response may still indicate an error on the Trusty side), false if
-  // there are any I/O or encoding/decoding errors.
-  bool SendRequest(const nvram::Request& request,
-                   nvram::Response* response);
-
-  // The file descriptor for the IPC connection to the Trusty app.
-  int tipc_nvram_fd_ = -1;
-
-  // Response buffer. This puts a hard size limit on the responses from the
-  // Trusty app. 4096 matches the maximum IPC message size currently supported
-  // by Trusty.
-  uint8_t response_buffer_[4096];
-};
-
-}  // namespace nvram
-
-#endif  // TRUSTY_NVRAM_TRUSTY_NVRAM_IMPLEMENTATION_H_
diff --git a/trusty/storage/interface/Android.bp b/trusty/storage/interface/Android.bp
index a551c37..18b4a5f 100644
--- a/trusty/storage/interface/Android.bp
+++ b/trusty/storage/interface/Android.bp
@@ -16,5 +16,6 @@
 
 cc_library_static {
     name: "libtrustystorageinterface",
+    vendor: true,
     export_include_dirs: ["include"],
 }
diff --git a/trusty/storage/lib/Android.bp b/trusty/storage/lib/Android.bp
index 5eb3f07..2fba17e 100644
--- a/trusty/storage/lib/Android.bp
+++ b/trusty/storage/lib/Android.bp
@@ -16,20 +16,23 @@
 
 cc_library_static {
     name: "libtrustystorage",
+    vendor: true,
 
     srcs: ["storage.c"],
 
     export_include_dirs: ["include"],
 
     static_libs: [
-        "liblog",
         "libtrusty",
         "libtrustystorageinterface",
     ],
+    shared_libs: [
+        "liblog",
+    ],
 
     cflags: [
         "-fvisibility=hidden",
         "-Wall",
         "-Werror",
-    ]
+    ],
 }
diff --git a/trusty/storage/proxy/Android.bp b/trusty/storage/proxy/Android.bp
index eb34df0..b93facb 100644
--- a/trusty/storage/proxy/Android.bp
+++ b/trusty/storage/proxy/Android.bp
@@ -16,6 +16,7 @@
 
 cc_binary {
     name: "storageproxyd",
+    vendor: true,
 
     srcs: [
         "ipc.c",
@@ -25,6 +26,7 @@
     ],
 
     shared_libs: ["liblog"],
+    header_libs: ["libcutils_headers"],
 
     static_libs: [
         "libtrustystorageinterface",
@@ -34,5 +36,5 @@
     cflags: [
         "-Wall",
         "-Werror",
-    ]
+    ],
 }
diff --git a/trusty/storage/proxy/proxy.c b/trusty/storage/proxy/proxy.c
index 27e5891..41263e5 100644
--- a/trusty/storage/proxy/proxy.c
+++ b/trusty/storage/proxy/proxy.c
@@ -24,7 +24,7 @@
 #include <sys/stat.h>
 #include <unistd.h>
 
-#include <private/android_filesystem_config.h>
+#include <cutils/android_filesystem_config.h>
 
 #include "ipc.h"
 #include "log.h"
diff --git a/trusty/storage/proxy/storage.c b/trusty/storage/proxy/storage.c
index c61e89d..5b83e21 100644
--- a/trusty/storage/proxy/storage.c
+++ b/trusty/storage/proxy/storage.c
@@ -379,7 +379,7 @@
     }
 
     if (req->size > MAX_READ_SIZE) {
-        ALOGW("%s: request is too large (%zd > %zd) - refusing\n",
+        ALOGW("%s: request is too large (%u > %d) - refusing\n",
               __func__, req->size, MAX_READ_SIZE);
         msg->result = STORAGE_ERR_NOT_VALID;
         goto err_response;
diff --git a/trusty/storage/tests/Android.bp b/trusty/storage/tests/Android.bp
index 3eff3f2..536c3ca 100644
--- a/trusty/storage/tests/Android.bp
+++ b/trusty/storage/tests/Android.bp
@@ -16,12 +16,12 @@
 
 cc_test {
     name: "secure-storage-unit-test",
+    vendor: true,
 
     cflags: [
         "-g",
         "-Wall",
         "-Werror",
-        "-std=gnu++11",
         "-Wno-missing-field-initializers",
     ],
 
@@ -29,6 +29,8 @@
         "libtrustystorageinterface",
         "libtrustystorage",
         "libtrusty",
+    ],
+    shared_libs: [
         "liblog",
     ],
 
diff --git a/trusty/storage/tests/main.cpp b/trusty/storage/tests/main.cpp
index 1fd6f8d..4529136 100644
--- a/trusty/storage/tests/main.cpp
+++ b/trusty/storage/tests/main.cpp
@@ -16,7 +16,6 @@
 
 #include <assert.h>
 #include <stdint.h>
-#include <stdbool.h>
 #include <gtest/gtest.h>
 
 #include <trusty/lib/storage.h>
diff --git a/usbd/Android.bp b/usbd/Android.bp
new file mode 100644
index 0000000..3afa7a9
--- /dev/null
+++ b/usbd/Android.bp
@@ -0,0 +1,15 @@
+cc_binary {
+    name: "usbd",
+    init_rc: ["usbd.rc"],
+    srcs: ["usbd.cpp"],
+    shared_libs: [
+        "libbase",
+        "libhidlbase",
+        "libhidltransport",
+        "liblog",
+        "libutils",
+        "libhardware",
+        "android.hardware.usb.gadget@1.0",
+        "libcutils",
+    ],
+}
diff --git a/usbd/usbd.cpp b/usbd/usbd.cpp
new file mode 100644
index 0000000..41cd8dd
--- /dev/null
+++ b/usbd/usbd.cpp
@@ -0,0 +1,56 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "usbd"
+
+#include <string>
+
+#include <android-base/logging.h>
+#include <android-base/properties.h>
+#include <android/hardware/usb/gadget/1.0/IUsbGadget.h>
+
+#define PERSISTENT_USB_CONFIG "persist.sys.usb.config"
+
+using android::base::GetProperty;
+using android::base::SetProperty;
+using android::hardware::usb::gadget::V1_0::GadgetFunction;
+using android::hardware::usb::gadget::V1_0::IUsbGadget;
+using android::hardware::Return;
+
+int main(int /*argc*/, char** /*argv*/) {
+    android::sp<IUsbGadget> gadget = IUsbGadget::getService();
+    Return<void> ret;
+
+    if (gadget != nullptr) {
+        LOG(INFO) << "Usb HAL found.";
+        std::string function = GetProperty(PERSISTENT_USB_CONFIG, "");
+        if (function == "adb") {
+            LOG(INFO) << "peristent prop is adb";
+            SetProperty("ctl.start", "adbd");
+            ret = gadget->setCurrentUsbFunctions(static_cast<uint64_t>(GadgetFunction::ADB),
+                                                 nullptr, 0);
+        } else {
+            LOG(INFO) << "Signal MTP to enable default functions";
+            ret = gadget->setCurrentUsbFunctions(static_cast<uint64_t>(GadgetFunction::MTP),
+                                                 nullptr, 0);
+        }
+
+        if (!ret.isOk()) LOG(ERROR) << "Error while invoking usb hal";
+    } else {
+        LOG(INFO) << "Usb HAL not found";
+    }
+    exit(0);
+}
diff --git a/usbd/usbd.rc b/usbd/usbd.rc
new file mode 100644
index 0000000..809044a
--- /dev/null
+++ b/usbd/usbd.rc
@@ -0,0 +1,5 @@
+service usbd /system/bin/usbd
+    class late_start
+    oneshot
+    user root
+    group root usb system